Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: Support Svelte JS labels #5367

Open
n-smits opened this issue Aug 14, 2021 · 15 comments
Open

Request: Support Svelte JS labels #5367

n-smits opened this issue Aug 14, 2021 · 15 comments

Comments

@n-smits
Copy link

n-smits commented Aug 14, 2021

There was no need for JS labels before, but Svelte found a good use for the syntax. Currently it can only be passed with backticks, but it's very limited and unwieldy.

`$: {`
foo 'hi' # must not properly indent
`}`

All other fail

`$:` foo 'hi'
# produces ({ $: foo 'hi' })

`$:`
foo 'hi'
# erroneous
# $:;
# foo('hi');

`$: {` foo 'hi' `}`
# won't compile

Simplest and very Coffee solution would be to spell out the syntax name, but honestly whatevs, whatever names you have left reserved or can reappropriate such as case outside of switch.

label $: foo 'hi' # or = or space
label $
  foo 'hi'
  foo 'hi'

It's true this label use isn't idiomatic and that it's a solitary kinda-framework example. But, it is actually using the standard syntax, and its unidiomatic use became innovative, because unlike before, it's actually a good use. Past addition of JSX could be considered similar, and a good place for it in documentation (since it is after all a rare good example of Js labels, so it's mostly about Svelte).

In my very humble general opinion here, Coffeescript should adapt here and there because Javascript is a hackable text based language for better and for worse. Otherwise is excessively conservative and necessarily keeps the bad part of Js (Js is what it is), while losing the better part (it's hackable, here for the better). Good guideline for when is enough of offshoots should include standard syntax when it's a good use even if an unconventional one. (So-so unconvencional actually, it's not like Svelte is so unheard of.)

@edemaine
Copy link
Contributor

I agree that Svelte is an important growing use-case to support. Thanks for raising the issue.

Unfortunately, label $: foo 'hi' already has a meaning in CoffeeScript, namely, label({"$": foo('hi')}).

The following doesn't have a meaning yet though:

label $
  foo 'hi'

This matches the syntax style of if and for and so on, which seems good. This suggests that the inline syntax could be:

label $ then foo 'hi'

which currently has no meaning, so that seems good.

@GeoffreyBooth
Copy link
Collaborator

One of the primary reasons for adding support for new features is compatibility. Users shouldn’t need to have to choose between CoffeeScript and Svelte, for example; we added support for JSX (and before that, ES6 classes) to enable full compatibility with React. So yes, we should find some way to support this.

I don’t know about this particular proposed syntax, though. Since label $ compiles, it feels awfully brittle for users to know they need a second line (or then) to get the desired JS label syntax rather than label($). I think it would be better if we could find some new symbol that currently doesn’t compile at all, or always throws at the start of a line; but not :=, as that one might get used either for static types or for block assignment, if we ever add either of those.

@johndeighan
Copy link

Very happy that this is even being discussed. I'm working on an extension to sveltekit and my way around this (which uses backticks) is the most fragile part of my code.

@n-smits
Copy link
Author

n-smits commented Oct 27, 2021

I don’t know about this particular proposed syntax, though. Since label $ compiles, it feels awfully brittle for users to know they need a second line (or then) to get the desired JS label syntax rather than label($). I think it would be better if we could find some new symbol that currently doesn’t compile at all, or always throws at the start of a line

How about on $? It's an alias for true in Coffee which is kinda confusing...

  1. but on the other hand, word 'on' has other meanings, specifically, it matches meaning of label
  2. it's very short, like JS colon :
  3. it's non breaking (Coffee doesn't allow on foo)
on $ then foo 'hi'
on $
  foo 'hi'

Alternatively, from also satisfies. from $ then foo 'hi'

@GeoffreyBooth
Copy link
Collaborator

The Svelte syntax is $:, which we can’t use because it already compiles; but :$ is available. How do these look?

# https://svelte.dev/tutorial/reactive-declarations
:$ doubled = count * 2

# https://svelte.dev/tutorial/reactive-statements
:$ if count >= 10
  alert 'count is dangerously high!'
  count = 9

@n-smits
Copy link
Author

n-smits commented Oct 27, 2021

Ok-ish, because it uses characters not words. Also second example bothers me, look like if-postfix (no then). Otherwise, yeah it works and isn't too difficult to get used to. I would wait a bit more, perhaps something better comes along

@GeoffreyBooth
Copy link
Collaborator

All of the possible words are listed here:

coffeescript/src/lexer.coffee

Lines 1225 to 1264 in ed6733d

# Keywords that CoffeeScript shares in common with JavaScript.
JS_KEYWORDS = [
'true', 'false', 'null', 'this'
'new', 'delete', 'typeof', 'in', 'instanceof'
'return', 'throw', 'break', 'continue', 'debugger', 'yield', 'await'
'if', 'else', 'switch', 'for', 'while', 'do', 'try', 'catch', 'finally'
'class', 'extends', 'super'
'import', 'export', 'default'
]
# CoffeeScript-only keywords.
COFFEE_KEYWORDS = [
'undefined', 'Infinity', 'NaN'
'then', 'unless', 'until', 'loop', 'of', 'by', 'when'
]
COFFEE_ALIAS_MAP =
and : '&&'
or : '||'
is : '=='
isnt : '!='
not : '!'
yes : 'true'
no : 'false'
on : 'true'
off : 'false'
COFFEE_ALIASES = (key for key of COFFEE_ALIAS_MAP)
COFFEE_KEYWORDS = COFFEE_KEYWORDS.concat COFFEE_ALIASES
# The list of keywords that are reserved by JavaScript, but not used, or are
# used by CoffeeScript internally. We throw an error when these are encountered,
# to avoid having a JavaScript error at runtime.
RESERVED = [
'case', 'function', 'var', 'void', 'with', 'const', 'let', 'enum'
'native', 'implements', 'interface', 'package', 'private'
'protected', 'public', 'static'
]
STRICT_PROSCRIBED = ['arguments', 'eval']

We can’t add any more reserved keywords without breaking changes (which basically can’t happen anymore). These are mostly spoken for, and the few that aren’t (like enum) would probably get used as part of adding support for TypeScript, if we ever get that far. The few that could get repurposed because of quirks of grammar as to where they can be used, like on and from, don’t really make much sense in this context.

Anything after : is available, so if we want to use a word we could do :label say. But I don’t really consider that more readable than :$. The advantage of words is when they spell out something resembling a readable sentence, and a label prefix doesn’t have an obvious English corollary the way unless or when do.

@n-smits
Copy link
Author

n-smits commented Oct 27, 2021

You're right. Actually, now when you put it that way, :anyword is perfect for label. My only reservation is labels didn't get much good usage, but hey who knows.

@edemaine
Copy link
Contributor

If we added a general :label, it would enable adding nifty features like break label (which exists in JS but not CS). I've definitely wanted to break two levels out in the past. And it would be a general solution that solves the Svelte problem.

An alternative syntax would be :label:. I'm not sure whether it's better though.

:$: doubled = count * 2

:$: if count >= 10
  alert 'count is dangerously high!'
  count = 9

@n-smits
Copy link
Author

n-smits commented Nov 1, 2021

Why the extra colon? As for :label (with or without extra colon), I still like it a week later

@johndeighan
Copy link

The first thing you should be asking is whether your intent is to support the concept of labels in general, or if what you really want to support is reactive assignments and/or statements. Most older uses for labels were very bad (think 'goto'). However, at the end of this post, I'll show a use case that I used often in Perl.

If it's reactive assignments that you want to support, then here's a syntax that I use in something I'm working on. I have no idea if it would work in CoffeeScript - just something to think about:

doubled <== 2 * count

This means "whenever a variable in the expression following <== changes, recompute the variable 'doubled'

<==
	ctx.fillStyle = $prefs.color
	ctx.fillRect(10, 10, 80, 80)

This means whenever any variable in the enclosed code changes, re-run the code.

Personally, I rarely use the 2nd syntax since it's possible to just call a function using the 1st as long as you pass any variables that you want to trigger a re-run to that function. But then you have to define the function somewhere, so a bit less convenient.

It seems like supporting this syntax, assuming it's converted directly to $: or $: { code block} in the JS would only be supporting svelte programmers. But I think it's also possible to convert it to plain JS code that accomplishes the same thing. A bit complex, I know, but ultimately, svelte does just that.

Now, the argument for simply supporting labels, which would be simpler to implement anyway. Here is a pattern that I've often used in Perl (this may not be valid Perl, but the intent is obvious):

FILE:
for filename in lFiles
   content = readFile(filename)
   lLines = content.split('\n')
   LINE:
   for line in lLines
      next LINE if (line == '__END__')
      procLine(line)

Now, as written, it will skip any lines like 'END'. But if instead, I wanted to skip the rest of the current file, I would just change 'next LINE' to 'next FILE'. The point is that when I have nested loops, being able to put a label on each loop allows me to specify unambiguously which loop iteration I'm prematurely exiting. I think this is basically the only good use case I've found for labels. Well, except for supporting svelte, where it's used less as a true label, and more as a way of tagging code as being 'reactive'.

@gcxfd
Copy link

gcxfd commented Mar 24, 2022

any update ?

@gcxfd
Copy link

gcxfd commented Mar 25, 2022

image

https://livescript.net/#functions I see livescript support label , but It seems dead ...

@gcxfd
Copy link

gcxfd commented Mar 25, 2022

I write a coffeescript label patch for svelte : https://github.com/rmw-lib/coffee-label-patch

Make coffeescript support :label syntax (similar to livescript) so that it can be used for svelte.

It's too hard to modify the compiler based on lex, ast, and I did this based on string substitution.

This is just a crude hack, but it does work.

Expect coffee to officially add this syntax.

y = 0

:$ x=y*2

:$ if y>2 then x+=y else x-=y

:$
  if x > y
    x = y/2
  else
    x = y+9
  x += 1

do =>
  :out
    for i in [1,2,3]
      for j in [4,5,6]
        console.log i,j
        if i > 1
          break out
  return

output :

var x, y;

y = 0;

$ : x = y * 2;

$ : y > 2 ? x += y : x -= y;

$ : {
  if (x > y) {
    x = y / 2;
  } else {
    x = y + 9;
  }
  x += 1;
}

(() => {
  var i, j, k, l, len, len1, ref, ref1;
  out : {
    ref = [1, 2, 3];
    for (k = 0, len = ref.length; k < len; k++) {
      i = ref[k];
      ref1 = [4, 5, 6];
      for (l = 0, len1 = ref1.length; l < len1; l++) {
        j = ref1[l];
        console.log(i, j);
        if (i > 1) {
          break out;
        }
      }
    }
  }
})();

it work , i release a preview in @rmw/svelte-preprocess

yarn add -D @rmw/svelte-preprocess @rmw/coffee-label-patch

demo video : https://www.loom.com/share/a45ffe7eeecb4115ad335b7db21f9b04

the code for demo video https://github.com/rmw-lib/svelte-pug-stylus-coffee

and also I create a pull request for svelte-preprocess sveltejs/svelte-preprocess#493

@adminy
Copy link

adminy commented Oct 18, 2024

svelte 5 removes the need for the label hack with runes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants