Skip to content

Commit

Permalink
""" interpolations require coffeeInterpolation, /// respects `c…
Browse files Browse the repository at this point in the history
…offeeInterpolation` and `coffeeDiv`
  • Loading branch information
edemaine committed Dec 5, 2024
1 parent 5dd201c commit 18c659e
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 151 deletions.
4 changes: 2 additions & 2 deletions civet.dev/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ For now, we have the following related options:
| [`coffeeBooleans`](reference#coffeescript-booleans) | `yes`, `no`, `on`, `off` |
| [`coffeeClasses`](reference#coffeescript-classes) | CoffeeScript-style `class` methods via `->` functions |
| [`coffeeComment`](reference#coffeescript-comments) | `# single line comments` |
| [`coffeeDiv`](reference#coffeescript-comments) | `x // y` integer division |
| [`coffeeDiv`](reference#coffeescript-comments) | `x // y` integer division instead of JS comment |
| [`coffeeDo`](reference#coffeescript-do) | `do ->`; disables [ES6 `do...while` loops](reference#do-while-until-loop) and [Civet `do` blocks](reference#do-blocks) |
| [`coffeeEq`](reference#coffeescript-operators) | `==``===`, `!=``!==` |
| [`coffeeForLoops`](reference#coffeescript-for-loops) | `for in`/`of`/`from` loops behave like they do in CoffeeScript (like Civet's `for each of`/`in`/`of` respectively) |
| [`coffeeInterpolation`](reference#double-quoted-strings) | `"a string with #{myVar}"` |
| [`coffeeInterpolation`](reference#double-quoted-strings) | `"a string with #{myVar}"`, `///regex #{myVar}///` |
| [`coffeeIsnt`](reference#coffeescript-operators) | `isnt``!==` |
| [`coffeeJSX`](reference#indentation) | JSX children ignore indentation; tags need to be explicitly closed |
| [`coffeeLineContinuation`](reference#coffeescript-line-continuations) | `\` at end of line continues to next line |
Expand Down
26 changes: 23 additions & 3 deletions civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ console.log '''
<Playground>
console.log """
<div>
Civet #{version}
Civet
</div>
"""
</Playground>
Expand All @@ -552,7 +552,8 @@ first slash is not immediately followed by a space. Instead of `/ x /`
write `/\ x /` or `/[ ]x /` (or more escaped forms like `/[ ]x[ ]/`).
In addition, you can use `///...///` to write multi-line regular expressions
that ignore top-level whitespace and single-line comments:
that ignore top-level whitespace and single-line comments, and interpolates
`${expression}` like in template literals:
<Playground>
phoneNumber := ///
Expand All @@ -564,6 +565,10 @@ phoneNumber := ///
///
</Playground>
<Playground>
r := /// ${prefix} \s+ ${suffix} ///
</Playground>
:::info
`///` is treated as a comment if it appears at the top of your file,
to support [TypeScript triple-slash directives](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html).
Expand Down Expand Up @@ -3231,11 +3236,19 @@ do (url) ->
await fetch url
</Playground>
### Double-Quoted Strings
### CoffeeScript Interpolation
<Playground>
"civet coffeeInterpolation"
console.log "Hello #{name}!"
console.log """
Goodbye #{name}!
"""
</Playground>
<Playground>
"civet coffeeInterpolation"
r = /// #{prefix} \s+ #{suffix} ///
</Playground>
### CoffeeScript Operators
Expand Down Expand Up @@ -3308,6 +3321,13 @@ you can enable `#` for single-line comments:
# one-line comment
</Playground>
<Playground>
"civet coffeeComment"
r = ///
\s+ # whitespace
///
</Playground>
[`###...###` block comments](#block-comments) are always available.
### CoffeeScript Line Continuations
Expand Down
15 changes: 8 additions & 7 deletions notes/Comparison-to-CoffeeScript.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ Things Kept from CoffeeScript
- Chained comparisons: `a < b < c``a < b && b < c`
- Postfix `if/unless/while/until/for`
- Block Strings `"""` / `'''`
- `#{exp}` interpolation in `"""` strings
- `when` inside `switch` automatically breaks
- Multiple `,` separated `case`/`when` expressions
- `else``default` in `switch`
Expand Down Expand Up @@ -109,20 +108,20 @@ Things Changed from CoffeeScript
- Generators don't implicitly return the last value (as this is rarely useful)
- Backtick embedded JS has been replaced with JS template literals.
- No longer allowing multiple postfix `if/unless` on the same line (use `&&` or `and` to combine conditions).
- `#{}` interpolation in `""` strings only when `"civet coffeeCompat"` or `"civet coffeeInterpolation"`
- `#{}` interpolation in `"..."` and `"""..."""` strings only when `"civet coffeeCompat"` or `"civet coffeeInterpolation"`
- Expanded chained comparisons to work on more operators `a in b instanceof C``a in b && b instanceof C`
- Postfix iteration/conditionals always wrap the statement [#5431](https://github.com/jashkenas/coffeescript/issues/5431):
`try x() if y``if (y) try x()`
- Civet tries to keep the transpiled output verbatim as much as possible.
In Coffee `(x)``x;` but in Civet `(x)``(x)`. Spacing and comments are also preserved as much as possible.
- Heregex / re.X
- Stay closer to the [Python spec](https://docs.python.org/3/library/re.html#re.X)
- Allows both kinds of substitutions `#{..}`, `${..}`.
- Also allows both kinds of single line comments `//`, `#`.
- Allows JS-style substitutions `${..}`. For Coffee-style substitutions `#{..}`, use `"civet coffeeCompat"` or `"civet coffeeInterpolation"`.
- Allows JS-style comments `//` unless `"civet coffeeDiv"` is set (including by `"civet coffeeCompat"`). For Coffee-style comments `#`, use `"civet coffeeCompat"` or `"civet coffeeInterpolation"`.
- With `coffeeComment` on, `#` is always the start of a comment outside of character classes regardless of leading space (CoffeeScript treats
`\s+#` as comment starts inside and outside of character classes).
- Keeps non-newline whitespace inside of character classes.
- Doesn't require escaping `#` after space inside of character classes.
- `#` is always the start of a comment outside of character classes regardless of leading space (CoffeeScript treats
`\s+#` as comment starts inside and outside of character classes).
- Might later add a compat flag to get more CoffeeScript compatibility.
- Might also later add a compat flag to only use ES interpolations and comments inside Heregexes.
- JSX children need to be properly indented
Expand Down Expand Up @@ -167,15 +166,17 @@ Civet provides a compatibility prologue directive that aims to be 97+% compatibl
| coffeeBooleans | `yes`, `no`, `on`, `off` |
| coffeeClasses | CoffeeScript-style `class` methods via `->` functions |
| coffeeComment | `# single line comments` |
| coffeeDiv | `x // y` integer division instead of JS comment |
| coffeeDo | `do ->`, disables ES6 do/while |
| coffeeEq | `==``===`, `!=``!==` |
| coffeeForLoops | for in, of, from loops behave like they do in CoffeeScript |
| coffeeInterpolation | `"a string with #{myVar}"` |
| coffeeInterpolation | `"a string with #{myVar}"`, `///regex #{myVar}///` |
| coffeeIsnt | `isnt``!==` |
| coffeeLineContinuation | `\` at end of line continues to next line |
| coffeeNot | `not``!`, disabling Civet extensions like `is not` |
| coffeeOf | `a of b``a in b`, `a not of b``!(a in b)`, `a in b``b.indexOf(a) >= 0`, `a not in b``b.indexOf(a) < 0` |
| coffeePrototype | `x::` -> `x.prototype`, `x::y` -> `x.prototype.y` |
| coffeeRange | `[a..b]` increases or decreases depending on whether `a < b` or `a > b` |

You can use these with `"civet coffeeCompat"` to opt in to all or use them bit by bit with `"civet coffeeComment coffeeEq coffeeInterpolation"`.
Another possibility is to slowly remove them to provide a way to migrate files a little at a time `"civet coffeeCompat -coffeeBooleans -coffeeComment -coffeeEq"`.
Expand Down
24 changes: 17 additions & 7 deletions source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -6123,10 +6123,18 @@ SingleStringCharacters
/(?:\\.|[^'])*/ ->
return { $loc, token: $0 }

TripleDoubleStringCharacters
TripleDoubleStringContents
CoffeeInterpolationEnabled ( CoffeeTripleDoubleStringCharacters / CoffeeStringSubstitution )* -> $2
!CoffeeInterpolationEnabled TripleDoubleStringCharacters -> [$2]

CoffeeTripleDoubleStringCharacters
/(?:"(?!"")|#(?!\{)|\\.|[^#"])+/ ->
return { $loc, token: $0 }

TripleDoubleStringCharacters
/(?:"(?!"")|\\.|[^"])+/ ->
return { $loc, token: $0 }

TripleSingleStringCharacters
/(?:'(?!'')|\\.|[^'])*/ ->
return { $loc, token: $0 }
Expand Down Expand Up @@ -6200,7 +6208,7 @@ HeregexBody
HeregexPart
RegularExpressionClass

CoffeeStringSubstitution -> { type: "Substitution", children: $1 }
CoffeeInterpolationEnabled CoffeeStringSubstitution -> { type: "Substitution", children: $2 }
TemplateSubstitution -> { type: "Substitution", children: $1 }

/(?:\\.)/ ->
Expand All @@ -6223,14 +6231,16 @@ HeregexPart
# Escape forward slashes (that aren't part of a triple slash)
/\/(?!\/\/)/ ->
return { $loc, token: "\\/" }
/[^[\/\s#\\]+/ ->
# Don't swallow up # and $ which might be interpolations,
# but handle them as single characters if they're not
/[^[\/\s#$\\]+|[#$]/ ->
return { $loc, token: $0 }

HeregexComment
# NOTE: CoffeeScript doesn't treat JS comments as regex comments
# TODO: this behavior should be toggled by a coffeeCompat directive
JSSingleLineComment
CoffeeSingleLineComment
# We disable them when coffeeDiv flag (// operator) is enabled
!CoffeeDivEnabled JSSingleLineComment
CoffeeCommentEnabled CoffeeSingleLineComment -> $2

# https://262.ecma-international.org/#prod-RegularExpressionBody
# NOTE: Simplified a little from the spec, ignoring <PS>, <LS>
Expand Down Expand Up @@ -6265,7 +6275,7 @@ _TemplateLiteral
}

# NOTE: actual CoffeeScript """ string behaviors are pretty weird, this is simplified
TripleDoubleQuote ( TripleDoubleStringCharacters / CoffeeStringSubstitution )* TripleDoubleQuote ->
TripleDoubleQuote TripleDoubleStringContents TripleDoubleQuote ->
return dedentBlockSubstitutions($0, config.tab)

# NOTE: ''' don't have interpolation so could be converted into a regular
Expand Down
2 changes: 1 addition & 1 deletion source/parser/string.civet
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function getIndentOfBlockString(str: string, tab: TabConfig)

minLevel

function dedentBlockString({ $loc, token: str }: ASTLeaf, tab: TabConfig, dedent: number | undefined, trimStart = true, trimEnd = true)
function dedentBlockString({ $loc, token: str }: ASTLeaf, tab: TabConfig, dedent: number?, trimStart = true, trimEnd = true)
// If string begins with a newline then indentation assume that it should be removed for all lines
if not dedent? and /^[ \t]*\r?\n/.test str
// Remove remaining shared indentation
Expand Down
18 changes: 2 additions & 16 deletions test/block-strings.civet
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,13 @@ describe "block strings", ->
'''

testCase '''
CoffeeScript compatible interpolation
attempted CoffeeScript interpolation
---
x = """
Ahoy #{name}
"""
---
x = `Ahoy ${name}`
'''

testCase '''
CoffeeScript compatible interpolation
---
x = """
Hi
Ahoy #{name}

Hello
Mr. #{surname}
"""
---
x = `Hi\nAhoy ${name}\n\nHello\nMr. ${surname}`
x = `Ahoy #{name}`
'''

describe "single quoted", ->
Expand Down
50 changes: 25 additions & 25 deletions test/helper.civet
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ compare := (src: string, result: string, compilerOpts: CompilerOptionsWithWrappe
...compilerOpts
})

assert.equal compileResult, result, """
#{filename}
assert.equal compileResult, result, ```
${filename}
--- Source ---
#{src}
${src}

--- Expected ---
#{result}
${result}

--- Got ---
#{compileResult}
${compileResult}

"""
```

jsCode .= compileResult
wrapper := compilerOpts.wrapper ?? wrappers.-1
Expand All @@ -39,15 +39,15 @@ compare := (src: string, result: string, compilerOpts: CompilerOptionsWithWrappe
loader: if compilerOpts.js then 'jsx' else 'tsx'
jsx: 'preserve'
catch e
assert.fail """
Failed to parse #{if compilerOpts.js then 'JavaScript' else 'TypeScript'}
assert.fail ```
Failed to parse ${if compilerOpts.js then 'JavaScript' else 'TypeScript'}

--- Code ---
#{jsCode}
${jsCode}

--- Error ---
#{e}
"""
${e}
```

/**
* Pass a string with the following format:
Expand Down Expand Up @@ -111,15 +111,15 @@ throws := (text: string, compilerOpts?: CompilerOptions, opt?: "only" | "skip")
assert.throws
=> e && throw e
undefined as any
"""
```

--- Source ---
#{src}
${src}

--- Got ---
#{result!}
${result!}

"""
```
// Then check against desired error message
if error
{name} := e! as {name: string}
Expand All @@ -131,18 +131,18 @@ throws := (text: string, compilerOpts?: CompilerOptions, opt?: "only" | "skip")
s = s.replace /\nExpected:[^]*$/, ''
else // just name
s = name
assert.equal s, error, """
assert.equal s, error, ```

--- Source ---
#{src}
${src}

--- Expected Error ---
#{error}
${error}

--- Got Error ---
#{e!.toString()}
${e!.toString()}

"""
```

throws.only = (text: string, compilerOpts?: CompilerOptions) -> throws text, compilerOpts, "only"
throws.skip = (text: string, compilerOpts?: CompilerOptions) -> throws text, compilerOpts, "skip"
Expand All @@ -152,18 +152,18 @@ evalsTo := (src: string, value: any) ->
js: true
sync: true // TODO: consider wrapping in `it` so we can use async API
}
assert.deepEqual result, value, """
assert.deepEqual result, value, ```

--- Source ---
#{src}
${src}

--- Expected ---
#{value}
${value}

--- Got ---
#{result}
${result}

"""
```

wrapper := (wrap: string) ->
before => wrappers.push wrap
Expand Down
4 changes: 3 additions & 1 deletion test/integration.civet
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ describe "integration", ->
it `should sourcemap correctly, ${mode} mode`, ->
{err, stderr} := await execCmdError `bash -c "(cd integration/example && ../../dist/civet --no-config error-${mode}.civet)"`
assert.match err.message, /Command failed/
assert.match stderr, ///error-#{mode}.civet:6:7///
// The newline in this /// block is to avoid a bug in Civet <0.9:
assert.match stderr, ///error-
${mode}.civet:6:7///
1 change: 1 addition & 0 deletions test/object.civet
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,7 @@ describe "object", ->
testCase '''
triple-quoted template literal key shorthand
---
"civet coffeeInterpolation"
{"""x#{y}z""": value}
---
({[`x${y}z`]: value})
Expand Down
Loading

0 comments on commit 18c659e

Please sign in to comment.