Compare commits
7 Commits
69bbe17992
...
c273429b24
| Author | SHA1 | Date | |
|---|---|---|---|
| c273429b24 | |||
| c127566abe | |||
| b7a65e07dc | |||
| 8299022b4f | |||
| 131c943fc6 | |||
| 866da86862 | |||
| 5ac0b02044 |
|
|
@ -2,6 +2,7 @@ import { CompilerError } from '#compiler/compilerError.ts'
|
|||
import { parser } from '#parser/shrimp.ts'
|
||||
import * as terms from '#parser/shrimp.terms'
|
||||
import { setGlobals } from '#parser/tokenizer'
|
||||
import { tokenizeCurlyString } from '#parser/curlyTokenizer'
|
||||
import type { SyntaxNode, Tree } from '@lezer/common'
|
||||
import { assert, errorMessage } from '#utils/utils'
|
||||
import { toBytecode, type Bytecode, type ProgramItem, bytecodeToString } from 'reefvm'
|
||||
|
|
@ -112,6 +113,9 @@ export class Compiler {
|
|||
return [[`PUSH`, number]]
|
||||
|
||||
case terms.String: {
|
||||
if (node.firstChild?.type.id === terms.CurlyString)
|
||||
return this.#compileCurlyString(value, input)
|
||||
|
||||
const { parts, hasInterpolation } = getStringParts(node, input)
|
||||
|
||||
// Simple string without interpolation or escapes - extract text directly
|
||||
|
|
@ -772,4 +776,26 @@ export class Compiler {
|
|||
|
||||
return instructions
|
||||
}
|
||||
|
||||
#compileCurlyString(value: string, input: string): ProgramItem[] {
|
||||
const instructions: ProgramItem[] = []
|
||||
const nodes = tokenizeCurlyString(value)
|
||||
|
||||
nodes.forEach((node) => {
|
||||
if (typeof node === 'string') {
|
||||
instructions.push(['PUSH', node])
|
||||
} else {
|
||||
const [input, topNode] = node
|
||||
let child = topNode.firstChild
|
||||
while (child) {
|
||||
instructions.push(...this.#compileNode(child, input))
|
||||
child = child.nextSibling
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
instructions.push(['STR_CONCAT', nodes.length])
|
||||
|
||||
return instructions
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,3 +155,69 @@ describe('dict literals', () => {
|
|||
c=3]`).toEvaluateTo({ a: 1, b: 2, c: 3 })
|
||||
})
|
||||
})
|
||||
|
||||
describe('curly strings', () => {
|
||||
test('work on one line', () => {
|
||||
expect('{ one two three }').toEvaluateTo(" one two three ")
|
||||
})
|
||||
|
||||
test('work on multiple lines', () => {
|
||||
expect(`{
|
||||
one
|
||||
two
|
||||
three
|
||||
}`).toEvaluateTo("\n one\n two\n three\n ")
|
||||
})
|
||||
|
||||
test('can contain other curlies', () => {
|
||||
expect(`{
|
||||
{ one }
|
||||
two
|
||||
{ three }
|
||||
}`).toEvaluateTo("\n { one }\n two\n { three }\n ")
|
||||
})
|
||||
|
||||
test('interpolates variables', () => {
|
||||
expect(`name = Bob; { Hello $name! }`).toEvaluateTo(` Hello Bob! `)
|
||||
})
|
||||
|
||||
test("doesn't interpolate escaped variables ", () => {
|
||||
expect(`name = Bob; { Hello \\$name }`).toEvaluateTo(` Hello $name `)
|
||||
expect(`a = 1; b = 2; { sum is \\$(a + b)! }`).toEvaluateTo(` sum is $(a + b)! `)
|
||||
})
|
||||
|
||||
test('interpolates expressions', () => {
|
||||
expect(`a = 1; b = 2; { sum is $(a + b)! }`).toEvaluateTo(` sum is 3! `)
|
||||
expect(`a = 1; b = 2; { sum is { $(a + b) }! }`).toEvaluateTo(` sum is { 3 }! `)
|
||||
expect(`a = 1; b = 2; { sum is $(a + (b * b))! }`).toEvaluateTo(` sum is 5! `)
|
||||
expect(`{ This is $({twisted}). }`).toEvaluateTo(` This is twisted. `)
|
||||
expect(`{ This is $({{twisted}}). }`).toEvaluateTo(` This is {twisted}. `)
|
||||
})
|
||||
|
||||
test('interpolation edge cases', () => {
|
||||
expect(`{[a=1 b=2 c={wild}]}`).toEvaluateTo(`[a=1 b=2 c={wild}]`)
|
||||
expect(`a = 1;b = 2;c = 3;{$a $b $c}`).toEvaluateTo(`1 2 3`)
|
||||
expect(`a = 1;b = 2;c = 3;{$a$b$c}`).toEvaluateTo(`123`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('double quoted strings', () => {
|
||||
test("work", () => {
|
||||
expect(`"hello world"`).toEvaluateTo('hello world')
|
||||
})
|
||||
|
||||
test("don't interpolate", () => {
|
||||
expect(`"hello $world"`).toEvaluateTo('hello $world')
|
||||
expect(`"hello $(1 + 2)"`).toEvaluateTo('hello $(1 + 2)')
|
||||
})
|
||||
|
||||
test("equal regular strings", () => {
|
||||
expect(`"hello world" == 'hello world'`).toEvaluateTo(true)
|
||||
})
|
||||
|
||||
test("can contain newlines", () => {
|
||||
expect(`
|
||||
"hello
|
||||
world"`).toEvaluateTo('hello\n world')
|
||||
})
|
||||
})
|
||||
|
|
@ -89,7 +89,7 @@ describe('pipe expressions', () => {
|
|||
test('pipe with prelude function (echo)', () => {
|
||||
expect(`
|
||||
get-msg = do: 'hello' end
|
||||
get-msg | echo
|
||||
`).toEvaluateTo(null)
|
||||
get-msg | length
|
||||
`).toEvaluateTo(5)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ end
|
|||
|
||||
test('custom tags', () => {
|
||||
expect(`
|
||||
list = tag ul class=list
|
||||
list = tag ul class='list'
|
||||
ribbit:
|
||||
list:
|
||||
li border-bottom='1px solid black' one
|
||||
|
|
|
|||
|
|
@ -251,7 +251,9 @@ export const getStringParts = (node: SyntaxNode, input: string) => {
|
|||
return (
|
||||
child.type.id === terms.StringFragment ||
|
||||
child.type.id === terms.Interpolation ||
|
||||
child.type.id === terms.EscapeSeq
|
||||
child.type.id === terms.EscapeSeq ||
|
||||
child.type.id === terms.CurlyString
|
||||
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -260,7 +262,8 @@ export const getStringParts = (node: SyntaxNode, input: string) => {
|
|||
if (
|
||||
part.type.id !== terms.StringFragment &&
|
||||
part.type.id !== terms.Interpolation &&
|
||||
part.type.id !== terms.EscapeSeq
|
||||
part.type.id !== terms.EscapeSeq &&
|
||||
part.type.id !== terms.CurlyString
|
||||
) {
|
||||
throw new CompilerError(
|
||||
`String child must be StringFragment, Interpolation, or EscapeSeq, got ${part.type.name}`,
|
||||
|
|
|
|||
62
src/parser/curlyTokenizer.ts
Normal file
62
src/parser/curlyTokenizer.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { parser } from '#parser/shrimp.ts'
|
||||
import type { SyntaxNode } from '@lezer/common'
|
||||
import { isIdentStart, isIdentChar } from './tokenizer'
|
||||
|
||||
// Turns a { curly string } into strings and nodes for interpolation
|
||||
export const tokenizeCurlyString = (value: string): (string | [string, SyntaxNode])[] => {
|
||||
let pos = 1
|
||||
let start = 1
|
||||
let char = value[pos]
|
||||
const tokens: (string | [string, SyntaxNode])[] = []
|
||||
|
||||
while (pos < value.length) {
|
||||
if (char === '$') {
|
||||
// escaped \$
|
||||
if (value[pos - 1] === '\\' && value[pos - 2] !== '\\') {
|
||||
tokens.push(value.slice(start, pos - 1))
|
||||
start = pos
|
||||
char = value[++pos]
|
||||
continue
|
||||
}
|
||||
|
||||
tokens.push(value.slice(start, pos))
|
||||
start = pos
|
||||
|
||||
if (value[pos + 1] === '(') {
|
||||
pos++ // slip opening '('
|
||||
|
||||
char = value[++pos]
|
||||
if (!char) break
|
||||
|
||||
let depth = 0
|
||||
while (char) {
|
||||
if (char === '(') depth++
|
||||
if (char === ')') depth--
|
||||
if (depth < 0) break
|
||||
char = value[++pos]
|
||||
}
|
||||
|
||||
const input = value.slice(start + 2, pos) // skip '$('
|
||||
tokens.push([input, parser.parse(input).topNode])
|
||||
start = ++pos // skip ')'
|
||||
} else {
|
||||
char = value[++pos]
|
||||
if (!char) break
|
||||
if (!isIdentStart(char.charCodeAt(0))) break
|
||||
|
||||
while (char && isIdentChar(char.charCodeAt(0)))
|
||||
char = value[++pos]
|
||||
|
||||
const input = value.slice(start + 1, pos) // skip '$'
|
||||
tokens.push([input, parser.parse(input).topNode])
|
||||
start = pos-- // backtrack and start over
|
||||
}
|
||||
}
|
||||
|
||||
char = value[++pos]
|
||||
}
|
||||
|
||||
tokens.push(value.slice(start, pos - 1))
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
@precedence { Number Regex }
|
||||
|
||||
StringFragment { !['\\$]+ }
|
||||
DoubleQuote { '"' !["]* '"' }
|
||||
NamedArgPrefix { $[a-z-]+ "=" }
|
||||
Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? }
|
||||
Boolean { "true" | "false" }
|
||||
|
|
@ -37,7 +38,7 @@ finally { @specialize[@name=keyword]<Identifier, "finally"> }
|
|||
throw { @specialize[@name=keyword]<Identifier, "throw"> }
|
||||
null { @specialize[@name=Null]<Identifier, "null"> }
|
||||
|
||||
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
|
||||
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, CurlyString }
|
||||
@external specialize {Identifier} specializeKeyword from "./tokenizer" { Do }
|
||||
|
||||
@precedence {
|
||||
|
|
@ -205,7 +206,9 @@ expression {
|
|||
IdentifierBeforeDot dot (Number | Identifier | ParenExpr)
|
||||
}
|
||||
|
||||
String { "'" stringContent* "'" }
|
||||
String {
|
||||
"'" stringContent* "'" | CurlyString | DoubleQuote
|
||||
}
|
||||
}
|
||||
|
||||
stringContent {
|
||||
|
|
|
|||
|
|
@ -23,44 +23,46 @@ export const
|
|||
AssignableIdentifier = 21,
|
||||
Word = 22,
|
||||
IdentifierBeforeDot = 23,
|
||||
Do = 24,
|
||||
Comment = 25,
|
||||
Program = 26,
|
||||
PipeExpr = 27,
|
||||
FunctionCall = 28,
|
||||
DotGet = 29,
|
||||
Number = 30,
|
||||
ParenExpr = 31,
|
||||
IfExpr = 32,
|
||||
keyword = 70,
|
||||
ConditionalOp = 34,
|
||||
String = 35,
|
||||
StringFragment = 36,
|
||||
Interpolation = 37,
|
||||
EscapeSeq = 38,
|
||||
Boolean = 39,
|
||||
Regex = 40,
|
||||
Dict = 41,
|
||||
NamedArg = 42,
|
||||
NamedArgPrefix = 43,
|
||||
FunctionDef = 44,
|
||||
Params = 45,
|
||||
NamedParam = 46,
|
||||
Null = 47,
|
||||
colon = 48,
|
||||
CatchExpr = 49,
|
||||
Block = 51,
|
||||
FinallyExpr = 52,
|
||||
Underscore = 55,
|
||||
Array = 56,
|
||||
ElseIfExpr = 57,
|
||||
ElseExpr = 59,
|
||||
FunctionCallOrIdentifier = 60,
|
||||
BinOp = 61,
|
||||
PositionalArg = 62,
|
||||
WhileExpr = 64,
|
||||
FunctionCallWithBlock = 66,
|
||||
TryExpr = 67,
|
||||
Throw = 69,
|
||||
CompoundAssign = 71,
|
||||
Assign = 72
|
||||
CurlyString = 24,
|
||||
Do = 25,
|
||||
Comment = 26,
|
||||
Program = 27,
|
||||
PipeExpr = 28,
|
||||
FunctionCall = 29,
|
||||
DotGet = 30,
|
||||
Number = 31,
|
||||
ParenExpr = 32,
|
||||
IfExpr = 33,
|
||||
keyword = 72,
|
||||
ConditionalOp = 35,
|
||||
String = 36,
|
||||
StringFragment = 37,
|
||||
Interpolation = 38,
|
||||
EscapeSeq = 39,
|
||||
DoubleQuote = 40,
|
||||
Boolean = 41,
|
||||
Regex = 42,
|
||||
Dict = 43,
|
||||
NamedArg = 44,
|
||||
NamedArgPrefix = 45,
|
||||
FunctionDef = 46,
|
||||
Params = 47,
|
||||
NamedParam = 48,
|
||||
Null = 49,
|
||||
colon = 50,
|
||||
CatchExpr = 51,
|
||||
Block = 53,
|
||||
FinallyExpr = 54,
|
||||
Underscore = 57,
|
||||
Array = 58,
|
||||
ElseIfExpr = 59,
|
||||
ElseExpr = 61,
|
||||
FunctionCallOrIdentifier = 62,
|
||||
BinOp = 63,
|
||||
PositionalArg = 64,
|
||||
WhileExpr = 66,
|
||||
FunctionCallWithBlock = 68,
|
||||
TryExpr = 69,
|
||||
Throw = 71,
|
||||
CompoundAssign = 73,
|
||||
Assign = 74
|
||||
|
|
|
|||
|
|
@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
|
|||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||
import {trackScope} from "./parserScopeContext"
|
||||
import {highlighting} from "./highlight"
|
||||
const spec_Identifier = {__proto__:null,if:66, null:94, catch:100, finally:106, end:108, else:116, while:130, try:136, throw:140}
|
||||
const spec_Identifier = {__proto__:null,if:68, null:98, catch:104, finally:110, end:112, else:120, while:134, try:140, throw:144}
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "9[QYQbOOO!dOSO'#DPOOQa'#DV'#DVO#mQbO'#DfO%RQcO'#E^OOQa'#E^'#E^O&XQcO'#E^O'ZQcO'#E]O'qQcO'#E]O)^QRO'#DOO*mQcO'#EWO*wQcO'#EWO+XQbO'#C{O,SOpO'#CyOOQ`'#EX'#EXO,XQbO'#EWO,cQRO'#DuOOQ`'#EW'#EWO,wQQO'#EVOOQ`'#EV'#EVOOQ`'#Dw'#DwQYQbOOO-PQbO'#DYO-[QbO'#C|O.PQbO'#DnO.tQQO'#DqO.PQbO'#DsO.yQbO'#DRO/RQWO'#DSOOOO'#E`'#E`OOOO'#Dx'#DxO/gOSO,59kOOQa,59k,59kOOQ`'#Dy'#DyO/uQbO,5:QO/|QbO'#DWO0WQQO,59qOOQa,5:Q,5:QO0cQbO,5:QOOQa'#E]'#E]OOQ`'#Dl'#DlOOQ`'#El'#ElOOQ`'#EQ'#EQO0mQbO,59dO1gQbO,5:bO.PQbO,59jO.PQbO,59jO.PQbO,59jO.PQbO,5:VO.PQbO,5:VO.PQbO,5:VO1wQRO,59gO2OQRO,59gO2ZQRO,59gO2UQQO,59gO2lQQO,59gO2tObO,59eO3PQbO'#ERO3[QbO,59cO3vQbO,5:[O1gQbO,5:aOOQ`,5:q,5:qOOQ`-E7u-E7uOOQ`'#Dz'#DzO4ZQbO'#DZO4fQbO'#D[OOQO'#D{'#D{O4^QQO'#DZO4tQQO,59tO4yQcO'#E]O6_QRO'#E[O6fQRO'#E[OOQO'#E['#E[O6qQQO,59hO6vQRO,5:YO6}QRO,5:YO3vQbO,5:]O7YQcO,5:_O8UQcO,5:_O8`QcO,5:_OOOO,59m,59mOOOO,59n,59nOOOO-E7v-E7vOOQa1G/V1G/VOOQ`-E7w-E7wO8pQQO1G/]OOQa1G/l1G/lO8{QbO1G/lOOQ`,59r,59rOOQO'#D}'#D}O8pQQO1G/]OOQa1G/]1G/]OOQ`'#EO'#EOO8{QbO1G/lOOQ`-E8O-E8OOOQ`1G/|1G/|OOQa1G/U1G/UO:WQcO1G/UO:_QcO1G/UO:fQcO1G/UOOQa1G/q1G/qO;_QcO1G/qO;iQcO1G/qO;sQcO1G/qOOQa1G/R1G/ROOQa1G/P1G/PO<hQbO'#DjO=_QbO'#CxOOQ`,5:m,5:mOOQ`-E8P-E8POOQ`'#Da'#DaO=lQbO'#DaO>]QbO1G/vOOQ`1G/{1G/{OOQ`-E7x-E7xO>hQQO,59uOOQO,59v,59vOOQO-E7y-E7yO>pQbO1G/`O3vQbO1G/SO3vQbO1G/tO?TQbO1G/wO?`QQO7+$wOOQa7+$w7+$wO?kQbO7+%WOOQa7+%W7+%WOOQO-E7{-E7{OOQ`-E7|-E7|OOQ`'#D|'#D|O?uQQO'#D|O?zQbO'#EiOOQ`,59{,59{O@kQbO'#D_O@pQQO'#DbOOQ`7+%b7+%bO@uQbO7+%bO@zQbO7+%bOASQbO7+$zOA_QbO7+$zOA{QbO7+$nOBTQbO7+%`OOQ`7+%c7+%cOBYQbO7+%cOB_QbO7+%cOOQa<<Hc<<HcOOQa<<Hr<<HrOOQ`,5:h,5:hOOQ`-E7z-E7zOBgQQO,59yO3vQbO,59|OOQ`<<H|<<H|OBlQbO<<H|OOQ`<<Hf<<HfOBqQbO<<HfOBvQbO<<HfOCOQbO<<HfOOQ`'#EP'#EPOCZQbO<<HYOCcQbO'#DiOOQ`<<HY<<HYOCkQbO<<HYOOQ`<<Hz<<HzOOQ`<<H}<<H}OCpQbO<<H}O3vQbO1G/eOOQ`1G/h1G/hOOQ`AN>hAN>hOOQ`AN>QAN>QOCuQbOAN>QOCzQbOAN>QOOQ`-E7}-E7}OOQ`AN=tAN=tODSQbOAN=tO-[QbO,5:RO3vQbO,5:TOOQ`AN>iAN>iOOQ`7+%P7+%POOQ`G23lG23lODXQbOG23lPD^QbO'#DgOOQ`G23`G23`ODcQQO1G/mOOQ`1G/o1G/oOOQ`LD)WLD)WO3vQbO7+%XOOQ`<<Hs<<Hs",
|
||||
stateData: "Dk~O!xOSiOS~OdWOe`OfTOg]OhfOnTOqgOwTOxTO!PTO!chO!fiO!hjO!}[O#RPO#YQO#ZRO#[cO~OtmO#RpO#TkO#UlO~OdwOfTOg]OnTOwTOxTO{sO!PTO!}[O#RPO#YQO#ZRO#[qO~O#^uO~P!rOP#QXQ#QXR#QXS#QXT#QXU#QXW#QXX#QXY#QXZ#QX[#QX]#QX^#QX#[#QX#a#QX!S#QX!V#QX!W#QX![#QX~OdwOfTOg]OhfOnTOwTOxTO{sO!PTO!XxO!}[O#RPO#YQO#ZRO#_#QX!Q#QX~P#tOV|O~P#tOP#PXQ#PXR#PXS#PXT#PXU#PXW#PXX#PXY#PXZ#PX[#PX]#PX^#PX~O#[!zX#a!zX!S!zX!V!zX!W!zX![!zX~P&`OdwOfTOg]OhfOnTOwTOxTO{sO!PTO!XxO!}[O#RPO#YQO#ZRO!Q!^X!a!^X#[!^X#a!^X#_!^X!S!^X!V!^X!W!^X![!^X~P&`OP!ROQ!ROR!SOS!SOT!OOU!POW}OX}OY}OZ}O[}O]}O^!QO~O#[!zX#a!zX!S!zX!V!zX!W!zX![!zX~OT!OOU!PO~P*XOP!ROQ!ROR!SOS!SO~P*XOdWOfTOg]OhfOnTOqgOwTOxTO!PTO!}[O#RPO#YQO#ZRO~O!|!YO~O!Q!]O!a!ZO~P*XOV|O_!^O`!^Oa!^Ob!^Oc!^O~O#[!_O#a!_O~Od!aO{!cO!Q}P~Od!gOfTOg]OnTOwTOxTO!PTO!}[O#RPO#YQO#ZRO~OdwOfTOg]OnTOwTOxTO!PTO!}[O#RPO#YQO#ZRO~O!Q!nO~Od!rO!}[O~O#R!sO#T!sO#U!sO#V!sO#W!sO#X!sO~OtmO#R!uO#TkO#UlO~O#^!xO~P!rOhfO!X!zO~P.PO{sO#[!{O#^!}O~O#[#OO#^!xO~P.POhfO{sO!XxO!Qla!ala#[la#ala#_la!Sla!Vla!Wla![la~P.POe`O!chO!fiO!hjO~P+XO#_#[O~P&`OT!OOU!PO#_#[O~OP!ROQ!ROR!SOS!SO#_#[O~O!a!ZO#_#[O~Od#]On#]O!}[O~Od#^Og]O!}[O~O!a!ZO#[ka#aka#_ka!Ska!Vka!Wka![ka~Oe`O!chO!fiO!hjO#[#cO~P+XOd!aO{!cO!Q}X~On#hOw#hO!P#hO#RPO~O!Q#jO~OhfO{sO!XxOT#PXU#PXW#PXX#PXY#PXZ#PX[#PX]#PX!Q#PX~P.POT!OOU!POW}OX}OY}OZ}O[}O]}O~O!Q#OX~P5sOT!OOU!PO!Q#OX~O!Q#kO~O!Q#lO~P5sOT!OOU!PO!Q#lO~O#[!ga#a!ga!S!ga!V!ga!W!ga![!ga~P)^O#[!ga#a!ga!S!ga!V!ga!W!ga![!ga~OT!OOU!PO~P7pOP!ROQ!ROR!SOS!SO~P7pO{sO#[!{O#^#oO~O#[#OO#^#qO~P.POW}OX}OY}OZ}O[}O]}OTri#[ri#ari#_ri!Qri!Sri!Vri!Wri![ri~OU!PO~P9VOU!PO~P9iOUri~P9VO^!QOR!_iS!_i#[!_i#a!_i#_!_i!S!_i!V!_i!W!_i![!_i~OP!_iQ!_i~P:mOP!ROQ!RO~P:mOP!ROQ!ROR!_iS!_i#[!_i#a!_i#_!_i!S!_i!V!_i!W!_i![!_i~OhfO{sO!XxO!a!^X#[!^X#a!^X#_!^X!S!^X!V!^X!W!^X![!^X~P.POhfO{sO!XxO~P.POe`O!chO!fiO!hjO#[#tO!S#]P!V#]P!W#]P![#]P~P+XO!S#xO!V#yO!W#zO~O{!cO!Q}a~Oe`O!chO!fiO!hjO#[$OO~P+XO!S#xO!V#yO!W$RO~O{sO#[!{O#^$UO~O#[#OO#^$VO~P.PO#[$WO~Oe`O!chO!fiO!hjO#[#tO!S#]X!V#]X!W#]X![#]X~P+XOd$YO~O!Q$ZO~O!W$[O~O!V#yO!W$[O~O!S#xO!V#yO!W$^O~Oe`O!chO!fiO!hjO#[#tO!S#]P!V#]P!W#]P~P+XO!W$eO![$dO~O!W$gO~O!W$hO~O!V#yO!W$hO~O!Q$jO~O!W$lO~O!W$mO~O!V#yO!W$mO~O!S#xO!V#yO!W$mO~O!W$qO![$dO~Oq$sO!Q$tO~O!W$qO~O!W$uO~O!W$wO~O!V#yO!W$wO~O!W$zO~O!W$}O~Oq$sO~O!Q%OO~Onx~",
|
||||
goto: "4x#aPPPPPPPPPPPPPPPPPPPPPPPPPPP#b#w$aP%d#bP&k'bP(a(aPP(e)aP)u*g*jPP*pP*|+fPPP+|,zP-O-U-j.YP.bP.b.bP.bP.b.b.t.z/Q/W/^/h/o/y0T0Z0ePPP0l0p1^PP1v1|3fP4fPPPPPPPP4jPP4ppaOe|!]!^!n#c#j#k#l#v$O$Z$j$t%OR!W[t^O[e|!Z!]!^!n#c#j#k#l#v$O$Z$j$t%OT!jg$srWO[e|!]!^!n#c#j#k#l#v$O$Z$j$t%OzwRSWhjrsv{}!O!P!Q!R!S!g!y#P#^#_#pS!gg$sR#^!ZvSO[eg|!]!^!n#c#j#k#l#v$O$Z$j$s$t%OzTRSWhjrsv{}!O!P!Q!R!S!g!y#P#^#_#pQ!rkQ#]!YR#_!ZpYOe|!]!^!n#c#j#k#l#v$O$Z$j$t%OQ!U[S!ig$sQ!mhQ!pjQ#S!PR#U!O!rTORSW[eghjrsv{|}!O!P!Q!R!S!]!^!g!n!y#P#^#_#c#j#k#l#p#v$O$Z$j$s$t%OR#h!cTmPo!sTORSW[eghjrsv{|}!O!P!Q!R!S!]!^!g!n!y#P#^#_#c#j#k#l#p#v$O$Z$j$s$t%OQtR[ySW{!g#^#_Q!wrX!{t!w!|#npaOe|!]!^!n#c#j#k#l#v$O$Z$j$t%O[xSW{!g#^#_Q!W[R!zsR!ffX!df!b!e#gQ#|#dQ$T#mQ$`#}R$o$aQ#d!]Q#m!nQ$P#kQ$Q#lQ$k$ZQ$v$jQ$|$tR%P%OQ#{#dQ$S#mQ$]#|Q$_#}Q$i$TS$n$`$aR$x$o!QTRSW[ghjrsv{}!O!P!Q!R!S!g!y#P#^#_#p$sqUOe|!]!^!n#c#j#k#l#v$O$Z$j$t%OT$b$P$cQ$f$PR$r$cu^O[e|!Z!]!^!n#c#j#k#l#v$O$Z$j$t%OpZOe|!]!^!n#c#j#k#l#v$O$Z$j$t%OQ!V[Q!qjQ#W!RR#Z!S]ySW{!g#^#_qaOe|!]!^!n#c#j#k#l#v$O$Z$j$t%OQeOR!`eQoPR!toQrRR!vrQ!bfR#f!bQ!efQ#g!bT#i!e#gS#v#c$OR$X#vQ!|tQ#n!wT#r!|#nQ#PvQ#p!yT#s#P#pQ$c$PR$p$cY{SW!g#^#_R#Q{S![_!XR#a![TdOeSbOeQ#R|`#b!]!n#k#l$Z$j$t%OQ#e!^U#u#c#v$OR#}#jp_Oe|!]!^!n#c#j#k#l#v$O$Z$j$t%OQ!X[R#`!ZQ!kgR${$srXO[e|!]!^!n#c#j#k#l#v$O$Z$j$t%OQvR[xSW{!g#^#_S!hg$sQ!lhQ!ojQ!yrQ!zsW#Ov!y#P#pQ#S}Q#T!OQ#V!PQ#W!QQ#X!RR#Y!SpVOe|!]!^!n#c#j#k#l#v$O$Z$j$t%O!OwRSWghjrsv{}!O!P!Q!R!S!g!y#P#^#_#p$sR!T[TnPoQ#w#cR$a$O]zSW{!g#^#_",
|
||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Identifier AssignableIdentifier Word IdentifierBeforeDot Do Comment Program PipeExpr FunctionCall DotGet Number ParenExpr IfExpr keyword ConditionalOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params NamedParam Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore Array ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp PositionalArg operator WhileExpr keyword FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
|
||||
maxTerm: 109,
|
||||
states: "9bQYQbOOO!jOSO'#DQOOQa'#DQ'#DQOOQa'#DX'#DXO#yQbO'#DhO%_QcO'#E`OOQa'#E`'#E`O&kQcO'#E`O'mQcO'#E_O(TQcO'#E_O)vQRO'#DPO+VQcO'#EYO+aQcO'#EYO+qQbO'#C|O,rOpO'#CzOOQ`'#EZ'#EZO,wQbO'#EYO-RQRO'#DwOOQ`'#EY'#EYO-gQQO'#EXOOQ`'#EX'#EXOOQ`'#Dy'#DyQYQbOOO-oQbO'#D[O-zQbO'#C}O.uQbO'#DpO/pQQO'#DsO.uQbO'#DuO/uQbO'#DSO/}QWO'#DTOOOO'#Eb'#EbOOOO'#Dz'#DzO0cOSO,59lOOQa,59l,59lOOQ`'#D{'#D{O0qQbO,5:SO0xQbO'#DYO1SQQO,59sOOQa,5:S,5:SO1_QbO,5:SOOQa'#E_'#E_OOQ`'#Dn'#DnOOQ`'#En'#EnOOQ`'#ES'#ESO1iQbO,59eO2cQbO,5:dO.uQbO,59kO.uQbO,59kO.uQbO,59kO.uQbO,5:XO.uQbO,5:XO.uQbO,5:XO2sQRO,59hO2zQRO,59hO3VQRO,59hO3QQQO,59hO3hQQO,59hO3pObO,59fO3{QbO'#ETO4WQbO,59dO4rQbO,5:^O2cQbO,5:cOOQ`,5:s,5:sOOQ`-E7w-E7wOOQ`'#D|'#D|O5VQbO'#D]O5bQbO'#D^OOQO'#D}'#D}O5YQQO'#D]O5vQQO,59vO5{QcO'#E_O7aQRO'#E^O7hQRO'#E^OOQO'#E^'#E^O7sQQO,59iO7xQRO,5:[O8PQRO,5:[O4rQbO,5:_O8[QcO,5:aO9WQcO,5:aO9bQcO,5:aOOOO,59n,59nOOOO,59o,59oOOOO-E7x-E7xOOQa1G/W1G/WOOQ`-E7y-E7yO9rQQO1G/_OOQa1G/n1G/nO9}QbO1G/nOOQ`,59t,59tOOQO'#EP'#EPO9rQQO1G/_OOQa1G/_1G/_OOQ`'#EQ'#EQO9}QbO1G/nOOQ`-E8Q-E8QOOQ`1G0O1G0OOOQa1G/V1G/VO;YQcO1G/VO;aQcO1G/VO;hQcO1G/VOOQa1G/s1G/sO<aQcO1G/sO<kQcO1G/sO<uQcO1G/sOOQa1G/S1G/SOOQa1G/Q1G/QO=jQbO'#DlO>aQbO'#CyOOQ`,5:o,5:oOOQ`-E8R-E8ROOQ`'#Dc'#DcO>nQbO'#DcO?_QbO1G/xOOQ`1G/}1G/}OOQ`-E7z-E7zO?jQQO,59wOOQO,59x,59xOOQO-E7{-E7{O?rQbO1G/bO4rQbO1G/TO4rQbO1G/vO@VQbO1G/yO@bQQO7+$yOOQa7+$y7+$yO@mQbO7+%YOOQa7+%Y7+%YOOQO-E7}-E7}OOQ`-E8O-E8OOOQ`'#EO'#EOO@wQQO'#EOO@|QbO'#EkOOQ`,59},59}OAmQbO'#DaOArQQO'#DdOOQ`7+%d7+%dOAwQbO7+%dOA|QbO7+%dOBUQbO7+$|OBaQbO7+$|OB}QbO7+$oOCVQbO7+%bOOQ`7+%e7+%eOC[QbO7+%eOCaQbO7+%eOOQa<<He<<HeOOQa<<Ht<<HtOOQ`,5:j,5:jOOQ`-E7|-E7|OCiQQO,59{O4rQbO,5:OOOQ`<<IO<<IOOCnQbO<<IOOOQ`<<Hh<<HhOCsQbO<<HhOCxQbO<<HhODQQbO<<HhOOQ`'#ER'#EROD]QbO<<HZODeQbO'#DkOOQ`<<HZ<<HZODmQbO<<HZOOQ`<<H|<<H|OOQ`<<IP<<IPODrQbO<<IPO4rQbO1G/gOOQ`1G/j1G/jOOQ`AN>jAN>jOOQ`AN>SAN>SODwQbOAN>SOD|QbOAN>SOOQ`-E8P-E8POOQ`AN=uAN=uOEUQbOAN=uO-zQbO,5:TO4rQbO,5:VOOQ`AN>kAN>kOOQ`7+%R7+%ROOQ`G23nG23nOEZQbOG23nPE`QbO'#DiOOQ`G23aG23aOEeQQO1G/oOOQ`1G/q1G/qOOQ`LD)YLD)YO4rQbO7+%ZOOQ`<<Hu<<Hu",
|
||||
stateData: "Em~O!zOSjOS~OdXOeaOfUOg^OhQOigOoUOrhOxQOyUOzUO!RUO!eiO!hjO!jkO#P]O#TPO#[RO#]SO#^dO~OunO#TqO#VlO#WmO~OdxOfUOg^OhQOoUOxQOyUOzUO}tO!RUO#P]O#TPO#[RO#]SO#^rO~O#`vO~P!xOP#SXQ#SXR#SXS#SXT#SXU#SXW#SXX#SXY#SXZ#SX[#SX]#SX^#SX#^#SX#c#SX!U#SX!X#SX!Y#SX!^#SX~OdxOfUOg^OhQOigOoUOxQOyUOzUO}tO!RUO!ZyO#P]O#TPO#[RO#]SO#a#SX!S#SX~P$QOV}O~P$QOP#RXQ#RXR#RXS#RXT#RXU#RXW#RXX#RXY#RXZ#RX[#RX]#RX^#RX~O#^!|X#c!|X!U!|X!X!|X!Y!|X!^!|X~P&rOdxOfUOg^OhQOigOoUOxQOyUOzUO}tO!RUO!ZyO#P]O#TPO#[RO#]SO!S!`X!c!`X#^!`X#c!`X#a!`X!U!`X!X!`X!Y!`X!^!`X~P&rOP!SOQ!SOR!TOS!TOT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OO^!RO~O#^!|X#c!|X!U!|X!X!|X!Y!|X!^!|X~OT!POU!QO~P*qOP!SOQ!SOR!TOS!TO~P*qOdXOfUOg^OhQOigOoUOrhOxQOyUOzUO!RUO#P]O#TPO#[RO#]SO~O#O!ZO~O!S!^O!c![O~P*qOV}O_!_O`!_Oa!_Ob!_Oc!_O~O#^!`O#c!`O~Od!bO}!dO!S!PP~Od!hOfUOg^OhQOoUOxQOyUOzUO!RUO#P]O#TPO#[RO#]SO~OdxOfUOg^OhQOoUOxQOyUOzUO!RUO#P]O#TPO#[RO#]SO~O!S!oO~Od!sO#P]O~O#T!tO#V!tO#W!tO#X!tO#Y!tO#Z!tO~OunO#T!vO#VlO#WmO~O#`!yO~P!xOigO!Z!{O~P.uO}tO#^!|O#`#OO~O#^#PO#`!yO~P.uOigO}tO!ZyO!Sma!cma#^ma#cma#ama!Uma!Xma!Yma!^ma~P.uOeaO!eiO!hjO!jkO~P+qO#a#]O~P&rOT!POU!QO#a#]O~OP!SOQ!SOR!TOS!TO#a#]O~O!c![O#a#]O~Od#^Oo#^O#P]O~Od#_Og^O#P]O~O!c![O#^la#cla#ala!Ula!Xla!Yla!^la~OeaO!eiO!hjO!jkO#^#dO~P+qOd!bO}!dO!S!PX~OhQOo#iOxQOy#iO!R#iO#TPO~O!S#kO~OigO}tO!ZyOT#RXU#RXW#RXX#RXY#RXZ#RX[#RX]#RX!S#RX~P.uOT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OO~O!S#QX~P6uOT!POU!QO!S#QX~O!S#lO~O!S#mO~P6uOT!POU!QO!S#mO~O#^!ia#c!ia!U!ia!X!ia!Y!ia!^!ia~P)vO#^!ia#c!ia!U!ia!X!ia!Y!ia!^!ia~OT!POU!QO~P8rOP!SOQ!SOR!TOS!TO~P8rO}tO#^!|O#`#pO~O#^#PO#`#rO~P.uOW!OOX!OOY!OOZ!OO[!OO]!OOTsi#^si#csi#asi!Ssi!Usi!Xsi!Ysi!^si~OU!QO~P:XOU!QO~P:kOUsi~P:XO^!ROR!aiS!ai#^!ai#c!ai#a!ai!U!ai!X!ai!Y!ai!^!ai~OP!aiQ!ai~P;oOP!SOQ!SO~P;oOP!SOQ!SOR!aiS!ai#^!ai#c!ai#a!ai!U!ai!X!ai!Y!ai!^!ai~OigO}tO!ZyO!c!`X#^!`X#c!`X#a!`X!U!`X!X!`X!Y!`X!^!`X~P.uOigO}tO!ZyO~P.uOeaO!eiO!hjO!jkO#^#uO!U#_P!X#_P!Y#_P!^#_P~P+qO!U#yO!X#zO!Y#{O~O}!dO!S!Pa~OeaO!eiO!hjO!jkO#^$PO~P+qO!U#yO!X#zO!Y$SO~O}tO#^!|O#`$VO~O#^#PO#`$WO~P.uO#^$XO~OeaO!eiO!hjO!jkO#^#uO!U#_X!X#_X!Y#_X!^#_X~P+qOd$ZO~O!S$[O~O!Y$]O~O!X#zO!Y$]O~O!U#yO!X#zO!Y$_O~OeaO!eiO!hjO!jkO#^#uO!U#_P!X#_P!Y#_P~P+qO!Y$fO!^$eO~O!Y$hO~O!Y$iO~O!X#zO!Y$iO~O!S$kO~O!Y$mO~O!Y$nO~O!X#zO!Y$nO~O!U#yO!X#zO!Y$nO~O!Y$rO!^$eO~Or$tO!S$uO~O!Y$rO~O!Y$vO~O!Y$xO~O!X#zO!Y$xO~O!Y${O~O!Y%OO~Or$tO~O!S%PO~Ooz~",
|
||||
goto: "4z#cPPPPPPPPPPPPPPPPPPPPPPPPPPPP#d#y$cP%f#dP&m'dP(c(cPPP(g)cP)w*i*lPP*rP+O+hPPP,O,|P-Q-W-l.[P.dP.d.dP.dP.d.d.v.|/S/Y/`/j/q/{0V0]0gPPP0n0r1`PP1x2O3hP4hPPPPPPPP4lPP4rpbOf}!^!_!o#d#k#l#m#w$P$[$k$u%PR!X]t_O]f}![!^!_!o#d#k#l#m#w$P$[$k$u%PT!kh$trXO]f}!^!_!o#d#k#l#m#w$P$[$k$u%PzxSTXikstw|!O!P!Q!R!S!T!h!z#Q#_#`#qS!hh$tR#_![vTO]fh}!^!_!o#d#k#l#m#w$P$[$k$t$u%PzUSTXikstw|!O!P!Q!R!S!T!h!z#Q#_#`#qQ!slQ#^!ZR#`![pZOf}!^!_!o#d#k#l#m#w$P$[$k$u%PQ!V]S!jh$tQ!niQ!qkQ#T!QR#V!P!rUOSTX]fhikstw|}!O!P!Q!R!S!T!^!_!h!o!z#Q#_#`#d#k#l#m#q#w$P$[$k$t$u%PR#i!dTnPp!sUOSTX]fhikstw|}!O!P!Q!R!S!T!^!_!h!o!z#Q#_#`#d#k#l#m#q#w$P$[$k$t$u%PQuS[zTX|!h#_#`Q!xsX!|u!x!}#opbOf}!^!_!o#d#k#l#m#w$P$[$k$u%P[yTX|!h#_#`Q!X]R!{tR!ggX!eg!c!f#hQ#}#eQ$U#nQ$a$OR$p$bQ#e!^Q#n!oQ$Q#lQ$R#mQ$l$[Q$w$kQ$}$uR%Q%PQ#|#eQ$T#nQ$^#}Q$`$OQ$j$US$o$a$bR$y$p!QUSTX]hikstw|!O!P!Q!R!S!T!h!z#Q#_#`#q$tqVOf}!^!_!o#d#k#l#m#w$P$[$k$u%PT$c$Q$dQ$g$QR$s$du_O]f}![!^!_!o#d#k#l#m#w$P$[$k$u%Pp[Of}!^!_!o#d#k#l#m#w$P$[$k$u%PQ!W]Q!rkQ#X!SR#[!T]zTX|!h#_#`qbOf}!^!_!o#d#k#l#m#w$P$[$k$u%PQfOR!afQpPR!upQsSR!wsQ!cgR#g!cQ!fgQ#h!cT#j!f#hS#w#d$PR$Y#wQ!}uQ#o!xT#s!}#oQ#QwQ#q!zT#t#Q#qQ$d$QR$q$dY|TX!h#_#`R#R|S!]`!YR#b!]TeOfScOfQ#S}`#c!^!o#l#m$[$k$u%PQ#f!_U#v#d#w$PR$O#kp`Of}!^!_!o#d#k#l#m#w$P$[$k$u%PQ!Y]R#a![Q!lhR$|$trYO]f}!^!_!o#d#k#l#m#w$P$[$k$u%PQwS[yTX|!h#_#`S!ih$tQ!miQ!pkQ!zsQ!{tW#Pw!z#Q#qQ#T!OQ#U!PQ#W!QQ#X!RQ#Y!SR#Z!TpWOf}!^!_!o#d#k#l#m#w$P$[$k$u%P!OxSTXhikstw|!O!P!Q!R!S!T!h!z#Q#_#`#q$tR!U]ToPpQ#x#dR$b$P]{TX|!h#_#`",
|
||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString Do Comment Program PipeExpr FunctionCall DotGet Number ParenExpr IfExpr keyword ConditionalOp String StringFragment Interpolation EscapeSeq DoubleQuote Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params NamedParam Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore Array ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp PositionalArg operator WhileExpr keyword FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
|
||||
maxTerm: 111,
|
||||
context: trackScope,
|
||||
nodeProps: [
|
||||
["closedBy", 48,"end"]
|
||||
["closedBy", 50,"end"]
|
||||
],
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0,25],
|
||||
skippedNodes: [0,26],
|
||||
repeatNodeCount: 11,
|
||||
tokenData: "C|~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'tuw#{wx'yxy(Oyz(iz{#{{|)S|}#{}!O+v!O!P#{!P!Q.]!Q![)q![!]6x!]!^%T!^!}#{!}#O7c#O#P9X#P#Q9^#Q#R#{#R#S9w#S#T#{#T#Y,w#Y#Z:b#Z#b,w#b#c?`#c#f,w#f#g@]#g#h,w#h#iAY#i#o,w#o#p#{#p#qC^#q;'S#{;'S;=`$d<%l~#{~O#{~~CwS$QUtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUtS!xYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UtS#[QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%sWtSOp#{pq&]qt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^&dZiYtSOY&]YZ#{Zt&]tu'Vuw&]wx'Vx#O&]#O#P'V#P;'S&];'S;=`'n<%lO&]Y'[SiYOY'VZ;'S'V;'S;=`'h<%lO'VY'kP;=`<%l'V^'qP;=`<%l&]~'yO#T~~(OO#R~U(VUtS!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(pUtS#_QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U)XWtSOt#{uw#{x!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U)xYtSnQOt#{uw#{x!O#{!O!P*h!P!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U*mWtSOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+^WtSnQOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+{^tSOt#{uw#{x}#{}!O,w!O!Q#{!Q![)q![!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U,|[tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U-yU{QtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U.bWtSOt#{uw#{x!P#{!P!Q.z!Q#O#{#P;'S#{;'S;=`$d<%lO#{U/P^tSOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q#{!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{U0S^tSxQOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q3s!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{Q1TXxQOY1OZ!P1O!P!Q1p!Q!}1O!}#O2_#O#P3^#P;'S1O;'S;=`3m<%lO1OQ1sP!P!Q1vQ1{UxQ#Z#[1v#]#^1v#a#b1v#g#h1v#i#j1v#m#n1vQ2bVOY2_Z#O2_#O#P2w#P#Q1O#Q;'S2_;'S;=`3W<%lO2_Q2zSOY2_Z;'S2_;'S;=`3W<%lO2_Q3ZP;=`<%l2_Q3aSOY1OZ;'S1O;'S;=`3m<%lO1OQ3pP;=`<%l1OU3xWtSOt#{uw#{x!P#{!P!Q4b!Q#O#{#P;'S#{;'S;=`$d<%lO#{U4ibtSxQOt#{uw#{x#O#{#P#Z#{#Z#[4b#[#]#{#]#^4b#^#a#{#a#b4b#b#g#{#g#h4b#h#i#{#i#j4b#j#m#{#m#n4b#n;'S#{;'S;=`$d<%lO#{U5v[tSOY5qYZ#{Zt5qtu2_uw5qwx2_x#O5q#O#P2w#P#Q/{#Q;'S5q;'S;=`6l<%lO5qU6oP;=`<%l5qU6uP;=`<%l/{U7PUtS!QQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7jW#ZQtSOt#{uw#{x!_#{!_!`8S!`#O#{#P;'S#{;'S;=`$d<%lO#{U8XVtSOt#{uw#{x#O#{#P#Q8n#Q;'S#{;'S;=`$d<%lO#{U8uU#YQtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~9^O#U~U9eU#^QtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:OUtS!XQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:g]tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#U;`#U#o,w#o;'S#{;'S;=`$d<%lO#{U;e^tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#`,w#`#a<a#a#o,w#o;'S#{;'S;=`$d<%lO#{U<f^tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#g,w#g#h=b#h#o,w#o;'S#{;'S;=`$d<%lO#{U=g^tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#X,w#X#Y>c#Y#o,w#o;'S#{;'S;=`$d<%lO#{U>j[wQtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#VWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#XWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#WWtSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^tSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeU!aQtSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#a~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!|~~", 11)],
|
||||
topRules: {"Program":[0,26]},
|
||||
tokenData: "FV~R}OX$OXY$mYZ%WZp$Opq$mqr$Ors%qst'wtu)}uw$Owx*Sxy*Xyz*rz{$O{|+]|}$O}!O.P!O!P$O!P!Q0f!Q![+z![!]9R!]!^%W!^!}$O!}#O9l#O#P;b#P#Q;g#Q#R$O#R#S<Q#S#T$O#T#Y/Q#Y#Z<k#Z#b/Q#b#cAi#c#f/Q#f#gBf#g#h/Q#h#iCc#i#o/Q#o#p$O#p#qEg#q;'S$O;'S;=`$g<%l~$O~O$O~~FQS$TUuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tUuS!zYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_UuS#^QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%vZuSOr%qrs&ist%qtu'Suw%qwx'Sx#O%q#O#P'S#P;'S%q;'S;=`'q<%lO%qU&pUxQuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OQ'VTOr'Srs'fs;'S'S;'S;=`'k<%lO'SQ'kOxQQ'nP;=`<%l'SU'tP;=`<%l%q^'|WuSOp$Opq(fqt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^(mZjYuSOY(fYZ$OZt(ftu)`uw(fwx)`x#O(f#O#P)`#P;'S(f;'S;=`)w<%lO(fY)eSjYOY)`Z;'S)`;'S;=`)q<%lO)`Y)tP;=`<%l)`^)zP;=`<%l(f~*SO#V~~*XO#T~U*`UuS#PQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU*yUuS#aQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU+bWuSOt$Ouw$Ox!Q$O!Q![+z![#O$O#P;'S$O;'S;=`$g<%lO$OU,RYuSoQOt$Ouw$Ox!O$O!O!P,q!P!Q$O!Q![+z![#O$O#P;'S$O;'S;=`$g<%lO$OU,vWuSOt$Ouw$Ox!Q$O!Q![-`![#O$O#P;'S$O;'S;=`$g<%lO$OU-gWuSoQOt$Ouw$Ox!Q$O!Q![-`![#O$O#P;'S$O;'S;=`$g<%lO$OU.U^uSOt$Ouw$Ox}$O}!O/Q!O!Q$O!Q![+z![!_$O!_!`/{!`#O$O#P#T$O#T#o/Q#o;'S$O;'S;=`$g<%lO$OU/V[uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#o/Q#o;'S$O;'S;=`$g<%lO$OU0SU}QuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU0kWuSOt$Ouw$Ox!P$O!P!Q1T!Q#O$O#P;'S$O;'S;=`$g<%lO$OU1Y^uSOY2UYZ$OZt2Utu3Xuw2Uwx3Xx!P2U!P!Q$O!Q!}2U!}#O7z#O#P5g#P;'S2U;'S;=`8{<%lO2UU2]^uSzQOY2UYZ$OZt2Utu3Xuw2Uwx3Xx!P2U!P!Q5|!Q!}2U!}#O7z#O#P5g#P;'S2U;'S;=`8{<%lO2UQ3^XzQOY3XZ!P3X!P!Q3y!Q!}3X!}#O4h#O#P5g#P;'S3X;'S;=`5v<%lO3XQ3|P!P!Q4PQ4UUzQ#Z#[4P#]#^4P#a#b4P#g#h4P#i#j4P#m#n4PQ4kVOY4hZ#O4h#O#P5Q#P#Q3X#Q;'S4h;'S;=`5a<%lO4hQ5TSOY4hZ;'S4h;'S;=`5a<%lO4hQ5dP;=`<%l4hQ5jSOY3XZ;'S3X;'S;=`5v<%lO3XQ5yP;=`<%l3XU6RWuSOt$Ouw$Ox!P$O!P!Q6k!Q#O$O#P;'S$O;'S;=`$g<%lO$OU6rbuSzQOt$Ouw$Ox#O$O#P#Z$O#Z#[6k#[#]$O#]#^6k#^#a$O#a#b6k#b#g$O#g#h6k#h#i$O#i#j6k#j#m$O#m#n6k#n;'S$O;'S;=`$g<%lO$OU8P[uSOY7zYZ$OZt7ztu4huw7zwx4hx#O7z#O#P5Q#P#Q2U#Q;'S7z;'S;=`8u<%lO7zU8xP;=`<%l7zU9OP;=`<%l2UU9YUuS!SQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU9sW#]QuSOt$Ouw$Ox!_$O!_!`:]!`#O$O#P;'S$O;'S;=`$g<%lO$OU:bVuSOt$Ouw$Ox#O$O#P#Q:w#Q;'S$O;'S;=`$g<%lO$OU;OU#[QuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~;gO#W~U;nU#`QuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<XUuS!ZQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<p]uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#U=i#U#o/Q#o;'S$O;'S;=`$g<%lO$OU=n^uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#`/Q#`#a>j#a#o/Q#o;'S$O;'S;=`$g<%lO$OU>o^uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#g/Q#g#h?k#h#o/Q#o;'S$O;'S;=`$g<%lO$OU?p^uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#X/Q#X#Y@l#Y#o/Q#o;'S$O;'S;=`$g<%lO$OU@s[yQuSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#o/Q#o;'S$O;'S;=`$g<%lO$O^Ap[#XWuSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#o/Q#o;'S$O;'S;=`$g<%lO$O^Bm[#ZWuSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#o/Q#o;'S$O;'S;=`$g<%lO$O^Cj^#YWuSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#f/Q#f#gDf#g#o/Q#o;'S$O;'S;=`$g<%lO$OUDk^uSOt$Ouw$Ox}$O}!O/Q!O!_$O!_!`/{!`#O$O#P#T$O#T#i/Q#i#j?k#j#o/Q#o;'S$O;'S;=`$g<%lO$OUEnU!cQuSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~FVO#c~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#O~~", 11)],
|
||||
topRules: {"Program":[0,27]},
|
||||
specialized: [{term: 20, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||
tokenPrec: 1634
|
||||
tokenPrec: 1682
|
||||
})
|
||||
|
|
|
|||
|
|
@ -127,3 +127,52 @@ describe('string escape sequences', () => {
|
|||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('curly strings', () => {
|
||||
test('work on one line', () => {
|
||||
expect('{ one two three }').toMatchTree(`
|
||||
String
|
||||
CurlyString { one two three }
|
||||
`)
|
||||
})
|
||||
|
||||
test('work on multiple lines', () => {
|
||||
expect(`{
|
||||
one
|
||||
two
|
||||
three }`).toMatchTree(`
|
||||
String
|
||||
CurlyString {
|
||||
one
|
||||
two
|
||||
three }`)
|
||||
})
|
||||
|
||||
test('can contain other curlies', () => {
|
||||
expect(`{ { one }
|
||||
two
|
||||
{ three } }`).toMatchTree(`
|
||||
String
|
||||
CurlyString { { one }
|
||||
two
|
||||
{ three } }`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('double quoted strings', () => {
|
||||
test("work", () => {
|
||||
expect(`"hello world"`).toMatchTree(`
|
||||
String
|
||||
DoubleQuote "hello world"`)
|
||||
})
|
||||
|
||||
test("don't interpolate", () => {
|
||||
expect(`"hello $world"`).toMatchTree(`
|
||||
String
|
||||
DoubleQuote "hello $world"`)
|
||||
|
||||
expect(`"hello $(1 + 2)"`).toMatchTree(`
|
||||
String
|
||||
DoubleQuote "hello $(1 + 2)"`)
|
||||
})
|
||||
})
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr'
|
||||
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do } from './shrimp.terms'
|
||||
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString } from './shrimp.terms'
|
||||
|
||||
// doobie doobie do (we need the `do` keyword to know when we're defining params)
|
||||
export function specializeKeyword(ident: string) {
|
||||
|
|
@ -18,6 +18,10 @@ export const setGlobals = (newGlobals: string[]) => {
|
|||
export const tokenizer = new ExternalTokenizer(
|
||||
(input: InputStream, stack: Stack) => {
|
||||
const ch = getFullCodePoint(input, 0)
|
||||
|
||||
// Handle curly strings
|
||||
if (ch === 123 /* { */) return consumeCurlyString(input, stack)
|
||||
|
||||
if (!isWordChar(ch)) return
|
||||
|
||||
// Don't consume things that start with digits - let Number token handle it
|
||||
|
|
@ -26,7 +30,7 @@ export const tokenizer = new ExternalTokenizer(
|
|||
// Don't consume things that start with - or + followed by a digit (negative/positive numbers)
|
||||
if ((ch === 45 /* - */ || ch === 43) /* + */ && isDigit(input.peek(1))) return
|
||||
|
||||
const isValidStart = isLowercaseLetter(ch) || isEmojiOrUnicode(ch)
|
||||
const isValidStart = isIdentStart(ch)
|
||||
const canBeWord = stack.canShift(Word)
|
||||
|
||||
// Consume all word characters, tracking if it remains a valid identifier
|
||||
|
|
@ -119,13 +123,7 @@ const consumeWordToken = (
|
|||
}
|
||||
|
||||
// Track identifier validity: must be lowercase, digit, dash, or emoji/unicode
|
||||
if (
|
||||
!isLowercaseLetter(ch) &&
|
||||
!isDigit(ch) &&
|
||||
ch !== 45 /* - */ &&
|
||||
ch !== 63 /* ? */ &&
|
||||
!isEmojiOrUnicode(ch)
|
||||
) {
|
||||
if (!isIdentChar(ch)) {
|
||||
if (!canBeWord) break
|
||||
isValidIdentifier = false
|
||||
}
|
||||
|
|
@ -157,6 +155,32 @@ const consumeRestOfWord = (input: InputStream, startPos: number, canBeWord: bool
|
|||
return pos
|
||||
}
|
||||
|
||||
// Consumes { curly strings } and tracks braces so you can { have { braces { inside { braces } } }
|
||||
const consumeCurlyString = (input: InputStream, stack: Stack) => {
|
||||
if (!stack.canShift(CurlyString)) return
|
||||
|
||||
let depth = 0
|
||||
let pos = 0
|
||||
|
||||
while (true) {
|
||||
const ch = input.peek(pos)
|
||||
if (ch < 0) return // EOF - invalid
|
||||
|
||||
if (ch === 123) depth++ // {
|
||||
else if (ch === 125) { // }
|
||||
depth--
|
||||
if (depth === 0) {
|
||||
pos++ // consume final }
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pos++
|
||||
}
|
||||
|
||||
input.acceptToken(CurlyString, pos)
|
||||
}
|
||||
|
||||
// Check if this identifier is in scope (for property access detection)
|
||||
// Returns IdentifierBeforeDot token if in scope, null otherwise
|
||||
const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number | null => {
|
||||
|
|
@ -219,6 +243,14 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => {
|
|||
}
|
||||
|
||||
// Character classification helpers
|
||||
export const isIdentStart = (ch: number): boolean => {
|
||||
return isLowercaseLetter(ch) || isEmojiOrUnicode(ch)
|
||||
}
|
||||
|
||||
export const isIdentChar = (ch: number): boolean => {
|
||||
return isLowercaseLetter(ch) || isDigit(ch) || ch === 45 /* - */ || ch === 63 /* ? */ || isEmojiOrUnicode(ch)
|
||||
}
|
||||
|
||||
const isWhiteSpace = (ch: number): boolean => {
|
||||
return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 13 /* \r */
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user