Compare commits

..

13 Commits

14 changed files with 355 additions and 54 deletions

View File

@ -62,7 +62,7 @@
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#d7a971db24aea5ddcaae2c18ce9f10dab793db19", { "peerDependencies": { "typescript": "^5" } }, "d7a971db24aea5ddcaae2c18ce9f10dab793db19"],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#3e2e68b31f504347225a4d705c7568a0957d629e", { "peerDependencies": { "typescript": "^5" } }, "3e2e68b31f504347225a4d705c7568a0957d629e"],
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],

View File

@ -10,7 +10,7 @@
"repl": "bun generate-parser && bun bin/repl",
"update-reef": "rm -rf ~/.bun/install/cache/ && rm bun.lock && bun update reefvm",
"cli:install": "ln -s \"$(pwd)/bin/shrimp\" ~/.bun/bin/shrimp",
"cli:remove": "rm ~/.bun/bin/shrimp",
"cli:remove": "rm ~/.bun/bin/shrimp"
},
"dependencies": {
"@codemirror/view": "^6.38.3",

View File

@ -51,6 +51,7 @@ function processEscapeSeq(escapeSeq: string): string {
export class Compiler {
instructions: ProgramItem[] = []
labelCount = 0
fnLabelCount = 0
ifLabelCount = 0
tryLabelCount = 0
@ -377,7 +378,29 @@ export class Compiler {
case terms.FunctionCallOrIdentifier: {
if (node.firstChild?.type.id === terms.DotGet) {
return this.#compileNode(node.firstChild, input)
const instructions: ProgramItem[] = []
const callLabel = `.call_dotget_${++this.labelCount}`
const afterLabel = `.after_dotget_${++this.labelCount}`
instructions.push(...this.#compileNode(node.firstChild, input))
instructions.push(['DUP'])
instructions.push(['TYPE'])
instructions.push(['PUSH', 'function'])
instructions.push(['EQ'])
instructions.push(['JUMP_IF_TRUE', callLabel])
instructions.push(['DUP'])
instructions.push(['TYPE'])
instructions.push(['PUSH', 'native'])
instructions.push(['EQ'])
instructions.push(['JUMP_IF_TRUE', callLabel])
instructions.push(['JUMP', afterLabel])
instructions.push([`${callLabel}:`])
instructions.push(['PUSH', 0])
instructions.push(['PUSH', 0])
instructions.push(['CALL'])
instructions.push([`${afterLabel}:`])
return instructions
}
return [['TRY_CALL', value]]

View File

@ -110,7 +110,10 @@ describe('compiler', () => {
})
test('function call with no args', () => {
expect(`bloop = do: 'bloop' end; bloop`).toEvaluateTo('bloop')
expect(`bloop = do: 'bleep' end; bloop`).toEvaluateTo('bleep')
expect(`bloop = [ go=do: 'bleep' end ]; bloop.go`).toEvaluateTo('bleep')
expect(`bloop = [ go=do: 'bleep' end ]; abc = do x: x end; abc (bloop.go)`).toEvaluateTo('bleep')
expect(`num = ((math.random) * 10 + 1) | math.floor; num >= 1 and num <= 10 `).toEvaluateTo(true)
})
test('function call with if statement and multiple expressions', () => {
@ -298,6 +301,23 @@ describe('default params', () => {
expect('multiply = do x y=5: x * y end; multiply 5 2').toEvaluateTo(10)
})
test('null triggers default value', () => {
expect('test = do n=true: n end; test').toEvaluateTo(true)
expect('test = do n=true: n end; test false').toEvaluateTo(false)
expect('test = do n=true: n end; test null').toEvaluateTo(true)
})
test('null triggers default for named parameters', () => {
expect("greet = do name='World': name end; greet name=null").toEvaluateTo('World')
expect("greet = do name='World': name end; greet name='Bob'").toEvaluateTo('Bob')
})
test('null triggers default with multiple parameters', () => {
expect('calc = do x=10 y=20: x + y end; calc null 5').toEvaluateTo(15)
expect('calc = do x=10 y=20: x + y end; calc 3 null').toEvaluateTo(23)
expect('calc = do x=10 y=20: x + y end; calc null null').toEvaluateTo(30)
})
test.skip('array default', () => {
expect('abc = do alpha=[a b c]: alpha end; abc').toEvaluateTo(['a', 'b', 'c'])
expect('abc = do alpha=[a b c]: alpha end; abc [x y z]').toEvaluateTo(['x', 'y', 'z'])

View File

@ -12,11 +12,11 @@
@precedence { Number Regex }
StringFragment { !['\\$]+ }
NamedArgPrefix { $[a-z-]+ "=" }
NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" }
Number {
("-" | "+")? "0x" $[0-9a-fA-F]+ |
("-" | "+")? "0b" $[01]+ |
("-" | "+")? $[0-9]+ ('.' $[0-9]+)?
("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)?
}
Boolean { "true" | "false" }
newlineOrSemicolon { "\n" | ";" }
@ -51,7 +51,8 @@ null { @specialize[@name=Null]<Identifier, "null"> }
comparison @left,
multiplicative @left,
additive @left,
call
call,
functionWithNewlines
}
item {
@ -192,7 +193,21 @@ BinOp {
}
ParenExpr {
leftParen (IfExpr | ambiguousFunctionCall | BinOp | expressionWithoutIdentifier | ConditionalOp | PipeExpr | FunctionDef) rightParen
leftParen newlineOrSemicolon* (
FunctionCallWithNewlines |
IfExpr |
ambiguousFunctionCall |
BinOp newlineOrSemicolon* |
expressionWithoutIdentifier |
ConditionalOp newlineOrSemicolon* |
PipeExpr |
FunctionDef
)
rightParen
}
FunctionCallWithNewlines[@name=FunctionCall] {
(DotGet | Identifier | ParenExpr) newlineOrSemicolon+ arg !functionWithNewlines (newlineOrSemicolon+ arg)* newlineOrSemicolon*
}
expression {

View File

@ -28,39 +28,40 @@ export const
Program = 26,
PipeExpr = 27,
WhileExpr = 29,
keyword = 70,
keyword = 71,
ConditionalOp = 31,
ParenExpr = 32,
IfExpr = 33,
FunctionCall = 35,
DotGet = 36,
Number = 37,
PositionalArg = 38,
FunctionDef = 39,
Params = 40,
NamedParam = 41,
NamedArgPrefix = 42,
String = 43,
StringFragment = 44,
Interpolation = 45,
EscapeSeq = 46,
Boolean = 47,
Null = 48,
colon = 49,
CatchExpr = 50,
Block = 52,
FinallyExpr = 53,
Underscore = 56,
NamedArg = 57,
ElseIfExpr = 58,
ElseExpr = 60,
FunctionCallOrIdentifier = 61,
BinOp = 62,
Regex = 63,
Dict = 64,
Array = 65,
FunctionCallWithBlock = 66,
TryExpr = 67,
Throw = 69,
CompoundAssign = 71,
Assign = 72
FunctionCallWithNewlines = 33,
DotGet = 34,
Number = 35,
PositionalArg = 36,
FunctionDef = 37,
Params = 38,
NamedParam = 39,
NamedArgPrefix = 40,
String = 41,
StringFragment = 42,
Interpolation = 43,
EscapeSeq = 44,
Boolean = 45,
Null = 46,
colon = 47,
CatchExpr = 48,
Block = 50,
FinallyExpr = 51,
Underscore = 54,
NamedArg = 55,
IfExpr = 56,
FunctionCall = 58,
ElseIfExpr = 59,
ElseExpr = 61,
FunctionCallOrIdentifier = 62,
BinOp = 63,
Regex = 64,
Dict = 65,
Array = 66,
FunctionCallWithBlock = 67,
TryExpr = 68,
Throw = 70,
CompoundAssign = 72,
Assign = 73

View File

@ -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,while:60, if:68, null:96, catch:102, finally:108, end:110, else:118, try:136, throw:140}
const spec_Identifier = {__proto__:null,while:60, null:92, catch:98, finally:104, end:106, if:114, else:120, try:138, throw:142}
export const parser = LRParser.deserialize({
version: 14,
states: "9UQYQbOOO!dOpO'#DQO!iOSO'#DXO$_QcO'#DkO&rQcO'#EYOOQ`'#Eh'#EhO'uQRO'#DlO)[QcO'#EWO)lQbO'#C|OOQa'#Dn'#DnO+nQbO'#DoOOQa'#EY'#EYO+uQcO'#EYO+|QcO'#EXO,yQcO'#EWO-TQRO'#DuOOQ`'#EW'#EWO-iQbO'#EWO-pQQO'#EVOOQ`'#EV'#EVOOQ`'#Dw'#DwQYQbOOO-{QbO'#DTO.WQbO'#C}O.{QbO'#CyO/pQQO'#DqO.{QbO'#DsO/uObO,59lO0QQbO'#DZO0YQWO'#D[OOOO'#E`'#E`OOOO'#D|'#D|O0nOSO,59sOOQa,59s,59sOOQ`'#DS'#DSO0|QbO'#DgOOQ`'#E^'#E^OOQ`'#Dy'#DyO1WQbO,59kOOQa'#EX'#EXO.{QbO,5:WO.{QbO,5:WO.{QbO,5:WO.{QbO,59gO.{QbO,59gO.{QbO,59gO2QQRO,59hO2^QQO,59hO2fQQO,59hO2qQRO,59hO3[QRO,59hO3jQQO'#CwOOQ`'#EP'#EPO3oQbO,5:ZO3vQQO,5:YOOQa,5:Z,5:ZO4RQbO,5:ZO)lQbO,5:bO)lQbO,5:aO4]QbO,5:[O4dQbO,59cOOQ`,5:q,5:qO)lQbO'#DxOOQ`-E7u-E7uOOQ`'#Dz'#DzO5OQbO'#DUO5ZQbO'#DVOOQO'#D{'#D{O5RQQO'#DUO5iQQO,59oO5nQcO'#EXO5uQRO'#E[O6lQRO'#E[OOQO'#E['#E[O6sQQO,59iO6xQRO,59eO7PQRO,59eO4]QbO,5:]O7[QcO,5:_O8dQcO,5:_O8tQcO,5:_OOQa1G/W1G/WOOOO,59u,59uOOOO,59v,59vOOOO-E7z-E7zOOQa1G/_1G/_OOQ`,5:R,5:ROOQ`-E7w-E7wOOQa1G/r1G/rO9sQcO1G/rO9}QcO1G/rO:XQcO1G/rOOQa1G/R1G/RO<TQcO1G/RO<[QcO1G/RO<cQcO1G/ROOQa1G/S1G/SOOQ`-E7}-E7}O<jQQO1G/tOOQa1G/u1G/uO<uQbO1G/uOOQO'#EQ'#EQO<jQQO1G/tOOQa1G/t1G/tOOQ`'#ER'#ERO<uQbO1G/uO=PQbO1G/|O=kQbO1G/{O>VQbO'#DbO>hQbO'#DbO>{QbO1G/vOOQ`-E7v-E7vOOQ`,5:d,5:dOOQ`-E7x-E7xO?WQQO,59pOOQO,59q,59qOOQO-E7y-E7yO?`QbO1G/ZO4]QbO1G/TO4]QbO1G/PO?gQbO1G/wO?rQQO7+%`OOQa7+%`7+%`O?}QbO7+%aOOQa7+%a7+%aOOQO-E8O-E8OOOQ`-E8P-E8POOQ`'#D}'#D}O@XQQO'#D}O@aQbO'#EgOOQ`,59|,59|O@tQbO'#D`O@yQQO'#DcOOQ`7+%b7+%bOAOQbO7+%bOATQbO7+%bOA]QbO7+$uOAkQbO7+$uOA{QbO7+$oOBTQbO7+$kOOQ`7+%c7+%cOBYQbO7+%cOB_QbO7+%cOOQa<<Hz<<HzOOQa<<H{<<H{OOQ`,5:i,5:iOOQ`-E7{-E7{OBgQQO,59zO4]QbO,59}OOQ`<<H|<<H|OBlQbO<<H|OOQ`<<Ha<<HaOBqQbO<<HaOBvQbO<<HaOCOQbO<<HaOOQ`'#EO'#EOOCZQbO<<HZOCcQbO'#DjOOQ`<<HZ<<HZOCkQbO<<HZOOQ`<<HV<<HVOOQ`<<H}<<H}OCpQbO<<H}O4]QbO1G/fOOQ`1G/i1G/iOOQ`AN>hAN>hOOQ`AN={AN={OCuQbOAN={OCzQbOAN={OOQ`-E7|-E7|OOQ`AN=uAN=uODSQbOAN=uO.WQbO,5:SO4]QbO,5:UOOQ`AN>iAN>iOOQ`7+%Q7+%QOOQ`G23gG23gODXQbOG23gPD^QbO'#DhOOQ`G23aG23aODcQQO1G/nOOQ`1G/p1G/pOOQ`LD)RLD)RO4]QbO7+%YOOQ`<<Ht<<Ht",
stateData: "Dk~O!xOSiOS~OdROe_OfZOgPOhfOnhOrgOuZO!PZO!QZO!aZO!fiO!hjO!}WO#RQO#YcO#^XO#_YO~O#PkO~O|nO#RqO#TlO#UmO~OdwOfZOgPOhfOuZOzsO!PZO!QZO!YrO!aZO!}WO#RQO#^XO#_YOT!{XU!{XW!{XX!{XY!{XZ!{X[!{X]!{X~OP!{XQ!{XR!{XS!{X^!{Xl!_X!R!_X#Y!_X#a!_X#]!_X!T!_X!W!_X!X!_X!]!_X~P!wOP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|Xl!|X#Y!|X#a!|X#]!|X!T!|X!W!|X!X!|X!]!|X~OdwOfZOgPOhfOuZOzsO!PZO!QZO!YrO!aZO!}WO#RQO#^XO#_YO!R!|X~P%_OPyOQyORzOSzOT|OU}OW{OX{OY{OZ{O[{O]{O^xO~Ol!zX#Y!zX#a!zX!T!zX!W!zX!X!zX#]!zX!]!zX~OPyOQyORzOSzO~P(pOdROe_OfZOgPOhfOnhOrgOuZO!PZO!QZO!aZO!fiO!hjO!}WO#RQO#^XO#_YO~OdwOfZOgPOuZOzsO!PZO!QZO!aZO!}WO#RQO#Y!UO#^XO#_YO~O#`!XO~P*sOV!ZO~P%_OP!{XQ!{XR!{XS!{XT!{XU!{XW!{XX!{XY!{XZ!{X[!{X]!{X^!{X~P(pOT|OU}O~P(pOV!ZO_![O`![Oa![Ob![Oc![O~O!R!]O~P(pOl!`O#Y!_O#a!_O~Od!bOz!dO!RxP~Od!hOfZOgPOuZO!PZO!QZO!aZO!}WO#RQO#^XO#_YO~OdwOfZOgPOuZO!PZO!QZO!aZO!}WO#RQO#^XO#_YO~O!R!oO~Od!sOu!sO!}WO~Od!tO!}WO~O#R!uO#T!uO#U!uO#V!uO#W!uO#X!uO~O|nO#R!wO#TlO#UmO~OhfO!Y!xO~P.{OhfOzsO!YrOlsa!Rsa#Ysa#asa#]sa!Tsa!Wsa!Xsa!]sa~P.{OPyOQyORzOSzO#]#SOl!zX~O!R!]O#]#SOl!zX~O#]#SOP!{XQ!{XR!{XS!{X^!{Xl!zX~P#sOT|OU}O#]#SOl!zX~Ol!`O~O#`#VO~P*sOzsO#Y#XO#`#ZO~O#Y#[O#`#VO~P.{O#Y#aO~P)lOl!`O#Yka#aka#]ka!Tka!Wka!Xka!]ka~Od!bOz!dO!RxX~Ou#gO!P#gO!Q#gO#RQO~O!R#iO~O!R!{X~P!wOT|OU}O!R#OX~OT|OU}OW{OX{OY{OZ{O[{O]{O~O!R#OX~P6QO!R#jO~O!R#kO~P6QOT|OU}O!R#kO~Ol!ga#Y!ga#a!ga!T!ga!W!ga!X!ga#]!ga!]!ga~P'uOl!ga#Y!ga#a!ga!T!ga!W!ga!X!ga#]!ga!]!ga~OPyOQyORzOSzO~P7xOT|OU}O~P7xO^xOR!`iS!`il!`i#Y!`i#a!`i#]!`i!T!`i!W!`i!X!`i!]!`i~OP!`iQ!`i~P9OOPyOQyO~P9OOPyOQyOR!`iS!`il!`i#Y!`i#a!`i#]!`i!T!`i!W!`i!X!`i!]!`i~OW{OX{OY{OZ{O[{O]{OToiloi#Yoi#aoi#]oi!Roi!Toi!Woi!Xoi!]oi~OU}O~P;POU}O~P;cOUoi~P;POzsO#Y#XO#`#nO~O#Y#[O#`#pO~P.{Ol!`O#Y!ji#a!ji!T!ji!W!ji!X!ji#]!ji!]!ji~Ol!`O#Y!ii#a!ii!T!ii!W!ii!X!ii#]!ii!]!ii~Ol!`O!T!UX!W!UX!X!UX!]!UX~O#Y#sO!T#ZP!W#ZP!X#ZP!]#ZP~P)lO!T#wO!W#xO!X#yO~Oz!dO!Rxa~O#Y#}O~P)lO!T#wO!W#xO!X$QO~OzsO#Y#XO#`$TO~O#Y#[O#`$UO~P.{Ol!`O#Y$VO~O#Y#sO!T#ZX!W#ZX!X#ZX!]#ZX~P)lOd$XO~O!R$YO~O!X$ZO~O!W#xO!X$ZO~Ol!`O!T#wO!W#xO!X$]O~O#Y#sO!T#ZP!W#ZP!X#ZP~P)lO!X$dO!]$cO~O!X$fO~O!X$gO~O!W#xO!X$gO~O!R$iO~O!X$kO~O!X$lO~O!W#xO!X$lO~O!T#wO!W#xO!X$lO~O!X$pO!]$cO~Or$rO!R$sO~O!X$pO~O!X$tO~O!X$vO~O!W#xO!X$vO~O!X$yO~O!X$|O~Or$rO~O!R$}O~Ou!a~",
goto: "4l#]PPPPPPPPPPPPPPPPPPPPPPPPPPP#^P#tP$Y%Q#^P&T&mP'l'r(c(fP(lP)j)jPPP)nP)z*dPPP*z+^P+b+h+|P,m-h#t#tP#tP#t#t.e.k.w/P/V/a/g/n/t/z0UPPP0`0d1W2oP3nP3tP3zPPPPPP4O4Ur`Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}R!PWu`OWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}r^Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}Q!SWS!ig$rQ!nhQ!rjQ#O}R#Q|xSOWeg!Z![!]!`!o#a#i#j#k#u#}$Y$i$r$s$}vZRSYhjsvxyz{|}!V!Y!h#W#]#oQ!skR!tltTOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}T!kg$rtROWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}vwRSYhjsvxyz{|}!V!Y!h#W#]#oT!hg$rXtRSv!hr`Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}WrRSv!hQ!PWR!xsR!gfX!ef!c!f#f!pZORSWYeghjsvxyz{|}!V!Y!Z![!]!`!h!o#W#]#a#i#j#k#o#u#}$Y$i$r$s$}R#g!dTnQpQ#{#bQ$S#lQ$_#|R$n$`Q#b!]Q#l!oQ$O#jQ$P#kQ$j$YQ$u$iQ${$sR%O$}Q#z#bQ$R#lQ$[#{Q$^#|Q$h$SS$m$_$`R$w$nWtRSv!hQ!WYQ#U!VX#X!W#U#Y#mT$a$O$bQ$e$OR$q$buTOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}rVOe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}Q!OWQ!qjQ!zyR!}z!qZORSWYeghjsvxyz{|}!V!Y!Z![!]!`!h!o#W#]#a#i#j#k#o#u#}$Y$i$r$s$}zZRSYghjsvxyz{|}!V!Y!h#W#]#o$ru[OWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}QeOR!ae^!^b!T#^#_#`#t#|R#c!^UvRS!hR!yvQ!cfR#e!cQ!ffQ#f!cT#h!f#fQpQR!vpS#u#a#}R$W#uQ$b$OR$o$bQ!VYR#T!VQ#Y!WQ#m#UT#q#Y#mQ#]!YQ#o#WT#r#]#oTdOeSbOeQ!TWQ#^!ZQ#_![`#`!]!o#j#k$Y$i$s$}Q#d!`U#t#a#u#}R#|#itUOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}WrRSv!hQ!YYS!jg$rQ!mhQ!pjQ!xsQ!zxQ!{yQ!|zQ#O{Q#P|Q#R}Q#W!VX#[!Y#W#]#or]Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}zwRSYghjsvxyz{|}!V!Y!h#W#]#o$rR!RWQ!lgR$z$rXuRSv!hToQpQ#v#aR$`#}raOe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}R!QW",
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 operator WhileExpr keyword ConditionalOp ParenExpr IfExpr keyword FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
maxTerm: 109,
states: "<OQYQbOOO!dOpO'#DOO!iOSO'#DVO$wQcO'#DlO'[QcO'#E[OOQ`'#Ej'#EjO'uQRO'#DmO)[QcO'#EYO)lQbO'#C|OOQa'#Do'#DoO+qQbO'#DpOOQa'#E['#E[O+xQcO'#E[O,cQcO'#EZO-`QcO'#EYO-jQRO'#DvOOQ`'#EY'#EYO.OQbO'#EYO.VQQO'#EXOOQ`'#EX'#EXOOQ`'#Dx'#DxQYQbOOO.bQbO'#DRO.mQbO'#DfO/bQbO'#CyO0VQQO'#DrO/bQbO'#DtO0[ObO,59jO0gQbO'#DXO0oQWO'#DYOOOO'#Eb'#EbOOOO'#D}'#D}O1TOSO,59qOOQa,59q,59qOOQ`'#DQ'#DQO1cQbO'#DeOOQ`'#E`'#E`OOQ`'#EQ'#EQO1mQbO,5:SOOQa'#EZ'#EZO/bQbO,5:XO/bQbO,5:XO/bQbO,5:XO/bQbO,59gO/bQbO,59gO/bQbO,59gOOQ`'#Dz'#DzO)lQbO,59hO2gQcO'#DlO2nQcO'#E[O3ZQRO,59hO3bQQO,59hO3gQQO,59hO3oQQO,59hO4xQRO,59hO5PQRO,59hO5bQQO'#CwO5gQbO,5:[O5nQQO,5:ZOOQa,5:[,5:[O5yQbO,5:[O6TQbO,5:cO6TQbO,5:bO7[QbO,5:]O7cQbO,59cOOQ`,5:s,5:sO6TQbO'#DyOOQ`-E7v-E7vOOQ`'#D{'#D{O7}QbO'#DSO8YQbO'#DTOOQO'#D|'#D|O8QQQO'#DSO8hQQO,59mO8mQcO'#EZO9gQRO'#EiO:^QRO'#EiOOQO'#Ei'#EiO:eQQO,5:QO:jQRO,59eO:qQRO,59eO7[QbO,5:^O:|QcO,5:`O<UQcO,5:`O<fQcO,5:`OOQa1G/U1G/UOOOO,59s,59sOOOO,59t,59tOOOO-E7{-E7{OOQa1G/]1G/]OOQ`,5:P,5:POOQ`-E8O-E8OOOQa1G/s1G/sO=eQcO1G/sO=oQcO1G/sO=yQcO1G/sOOQa1G/R1G/RO?uQcO1G/RO?|QcO1G/RO@TQcO1G/ROOQ`-E7x-E7xO@[QRO1G/SO@cQQO1G/SO@hQQO1G/SO@pQQO1G/SO@{QRO1G/SOASQRO1G/SOAeQbO,59iOAoQQO1G/SOOQa1G/S1G/SOAwQQO1G/uOOQa1G/v1G/vOBSQbO1G/vOOQO'#ES'#ESOAwQQO1G/uOOQa1G/u1G/uOOQ`'#ET'#ETOBSQbO1G/vOB^QbO1G/}OBxQbO1G/|OCdQbO'#D`OCuQbO'#D`ODYQbO1G/wOOQ`-E7w-E7wOOQ`,5:e,5:eOOQ`-E7y-E7yODeQQO,59nOOQO,59o,59oOOQO-E7z-E7zODmQbO1G/XO7[QbO1G/lO7[QbO1G/PODtQbO1G/xOEPQQO7+$nOOQa7+$n7+$nOEXQQO1G/TOEaQQO7+%aOOQa7+%a7+%aOElQbO7+%bOOQa7+%b7+%bOOQO-E8Q-E8QOOQ`-E8R-E8ROOQ`'#EO'#EOOEvQQO'#EOOFOQbO'#EhOOQ`,59z,59zOFcQbO'#D^OFhQQO'#DaOOQ`7+%c7+%cOFmQbO7+%cOFrQbO7+%cOFzQbO7+$sOGYQbO7+$sOGjQbO7+%WOGrQbO7+$kOOQ`7+%d7+%dOGwQbO7+%dOG|QbO7+%dOOQa<<HY<<HYOHUQbO7+$oOHcQQO7+$oOOQa<<H{<<H{OOQa<<H|<<H|OOQ`,5:j,5:jOOQ`-E7|-E7|OHkQQO,59xO7[QbO,59{OOQ`<<H}<<H}OHpQbO<<H}OOQ`<<H_<<H_OHuQbO<<H_OHzQbO<<H_OISQbO<<H_OOQ`'#ER'#EROI_QbO<<HrOIgQbO'#DkOOQ`<<Hr<<HrOIoQbO<<HrOOQ`<<HV<<HVOOQ`<<IO<<IOOItQbO<<IOOOQO,5:k,5:kOIyQbO<<HZOOQO-E7}-E7}O7[QbO1G/dOOQ`1G/g1G/gOOQ`AN>iAN>iOOQ`AN=yAN=yOJWQbOAN=yOJ]QbOAN=yOOQ`-E8P-E8POOQ`AN>^AN>^OJeQbOAN>^O.mQbO,5:TO7[QbO,5:VOOQ`AN>jAN>jPAeQbO'#DzOOQ`7+%O7+%OOOQ`G23eG23eOJjQbOG23ePIjQbO'#DiOOQ`G23xG23xOJoQQO1G/oOOQ`1G/q1G/qOOQ`LD)PLD)PO7[QbO7+%ZOOQ`<<Hu<<Hu",
stateData: "Jw~O!zOSiOS~OdROe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#QcO#TQO#`XO#aYO~O#RkO~OznO#TqO#VlO#WmO~OdwOfZOgPOhfOsZOxsO}ZO!OZO!WrO!bZO#PWO#TQO#`XO#aYOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}Xl!`X!P!`X#_!`X~O#Q!`X#c!`X!R!`X!U!`X!V!`X!^!`X~P!wOdwOfZOgPOhfOsZOxsO}ZO!OZO!WrO!bZO#PWO#TQO#`XO#aYOP#OXQ#OXR#OXS#OXT#OXU#OXW#OXX#OXY#OXZ#OX[#OX]#OX^#OXl#OX#_#OX~O#Q#OX#c#OX!P#OX!R#OX!U#OX!V#OX!^#OX~P%_OPyOQyORzOSzOT|OU}OW{OX{OY{OZ{O[{O]{O^xO~Ol!|X#Q!|X#c!|X!R!|X!U!|X!V!|X#_!|X!^!|X~OPyOQyORzOSzO~P(pOd!QOe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#Q!OO#TQO#`XO#aYO~OdwOfZOgPOsZOxsO}ZO!OZO!bZO#PWO#Q!OO#TQO#`XO#aYO~O#b!]O~P*vOV!_O#Q#OX#c#OX!R#OX!U#OX!V#OX!^#OX~P&ZOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}X~P(pOT|OU}O~P(pOV!_O_!`O`!`Oa!`Ob!`Oc!`O~O!P!aO~P(pOl!dO#Q!cO#c!cO~Od!fOx!hO!PvP~Od!lOfZOgPOsZO}ZO!OZO!bZO#PWO#TQO#`XO#aYO~OdwOfZOgPOsZO}ZO!OZO!bZO#PWO#TQO#`XO#aYO~O!P!sO~Od!wOs!wO#PWO~Od!xO#PWO~O#T!yO#V!yO#W!yO#X!yO#Y!yO#Z!yO~OznO#T!{O#VlO#WmO~OhfO!W!|O~P/bOhfOxsO!WrOl![a!P![a#Q![a#c![a#_![a!R![a!U![a!V![a!^![a~P/bO#Q!OO~P!wO#Q!OO~P%_OPyOQyORzOSzO#Q!OOl!|X~O#_#aO~P2uO#_#aO~O#_#aOl!|X~O!P!aO#_#aOl!|X~OP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}Xl!|X~O#_#aO~P3zOT|OU}O#Q!OO#_#aOl!|X~Ol!dO~O#b#cO~P*vOxsO#Q#eO#b#gO~O#Q#hO#b#cO~P/bOdROe_OfZOgPOhfOnhOsZO}ZO!OZO!ZgO!bZO!giO!ijO#PWO#TQO#`XO#aYO~O#Q#mO~P6TOl!dO#Qka#cka#_ka!Rka!Uka!Vka!^ka~Od!fOx!hO!PvX~Os#sO}#sO!O#sO#TQO~O!P#uO~OhfOxsO!WrOT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X!P!}X~P/bOT|OU}O!P#]X~OT|OU}OW{OX{OY{OZ{O[{O]{O~O!P#]X~P9rO!P#vO~O!P#wO~P9rOT|OU}O!P#wO~Ol!ha#Q!ha#c!ha!R!ha!U!ha!V!ha#_!ha!^!ha~P'uOl!ha#Q!ha#c!ha!R!ha!U!ha!V!ha#_!ha!^!ha~OPyOQyORzOSzO~P;jOT|OU}O~P;jO^xOR!aiS!ail!ai#Q!ai#c!ai#_!ai!R!ai!U!ai!V!ai!^!ai~OP!aiQ!ai~P<pOPyOQyO~P<pOPyOQyOR!aiS!ail!ai#Q!ai#c!ai#_!ai!R!ai!U!ai!V!ai!^!ai~OW{OX{OY{OZ{O[{O]{OToiloi#Qoi#coi#_oi!Poi!Roi!Uoi!Voi!^oi~OU}O~P>qOU}O~P?TOUoi~P>qO#_#zO~P2uO#_#zO~O#_#zOl!|X~O!P!aO#_#zOl!|X~O#_#zO~P3zOT|OU}O#Q!OO#_#zOl!|X~OhfO!WrO~P*vO#Q!OO#_#zO~OxsO#Q#eO#b#}O~O#Q#hO#b$PO~P/bOl!dO#Q!ki#c!ki!R!ki!U!ki!V!ki#_!ki!^!ki~Ol!dO#Q!ji#c!ji!R!ji!U!ji!V!ji#_!ji!^!ji~Ol!dO!R!SX!U!SX!V!SX!^!SX~O#Q$SO!R#[P!U#[P!V#[P!^#[P~P6TO!R$WO!U$XO!V$YO~Ox!hO!Pva~O#Q$^O~P6TO!R$WO!U$XO!V$aO~O#Q!OO#_$dO~O#Q!OO#_qi~OxsO#Q#eO#b$gO~O#Q#hO#b$hO~P/bOl!dO#Q$iO~O#Q$SO!R#[X!U#[X!V#[X!^#[X~P6TOd$kO~O!P$lO~O!V$mO~O!U$XO!V$mO~Ol!dO!R$WO!U$XO!V$oO~O#Q$SO!R#[P!U#[P!V#[P~P6TO!V$vO!^$uO~O!V$xO~O!V$yO~O!U$XO!V$yO~OhfO!WrO#_qq~P*vO#Q!OO#_qq~O!P%OO~O!V%QO~O!V%RO~O!U$XO!V%RO~O!R$WO!U$XO!V%RO~O!V%VO!^$uO~O!P%YO!Z%XO~O!V%VO~O!V%ZO~OhfO!WrO#_qy~P*vO!V%^O~O!U$XO!V%^O~O!V%aO~O!V%dO~O!P%eO~Os!b~",
goto: "7t#_PPPPPPPPPPPPPPPPPPPPPPPPPPP#`P#yP$`%Z&g&mP'u(R({)OP)UP*Z*ZPPP*_P*k+TPPP+k#`P,T,nP,r,x-_P.R/T#y#yP#yP#y#y0X0_0k1_1e1o1u1|2S2^2d2nPPP2x2|3q5aPPP6iP6yPPPPP6}7T7Zr`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!UWR#Z!Pw`OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%er^Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!XWS!mg%XQ!rhQ!vjQ#S}Q#U|R#^!PvSOeg!_!`!a!d!s#m#u#v#w$U$^$l%O%X%Y%e!SZRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!RW!PQ!wkR!xlQ!TWR#Y!PrROe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!SwRSYhjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%[S!QW!PT!lg%XetRSv!Q!R!l#_$e$|%[r`Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!UWQ!|sR#Z!PR!kfX!if!g!j#r#OZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%eR#s!hTnQpQ$[#nQ$c#xQ$q$]R%T$rQ#n!aQ#x!sQ$_#vQ$`#wQ%P$lQ%]%OQ%c%YR%f%eQ$Z#nQ$b#xQ$n$[Q$p$]Q$z$cS%S$q$rR%_%TdtRSv!Q!R!l#_$e$|%[Q![YQ#b!ZX#e![#b#f#|vTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eT!og%XT$s$_$tQ$w$_R%W$twTOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%erVOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!SWQ!ujQ#OyQ#RzR#X!P#PZORSWYeghjsvxyz{|}!P!Q!R!Z!^!_!`!a!d!l!s#_#d#i#m#u#v#w$O$U$^$e$l$|%O%X%Y%[%e!WZRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[w[OWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQeOR!ee^!bb!Y#j#k#l$T$]R#o!bQ!PWQ!ZY`#W!P!Z#_#`#y$e$|%[S#_!Q!RS#`!S!XS#y#X#^Q$e#{R$|$fQ!gfR#q!gQ!jfQ#r!gT#t!j#rQpQR!zpS$U#m$^R$j$UQ$f#{R$}$fYvRS!Q!R!lR!}vQ$t$_R%U$tQ#f![Q#|#bT$Q#f#|Q#i!^Q$O#dT$R#i$OTdOeSbOeS!YW!PQ#j!_Q#k!``#l!a!s#v#w$l%O%Y%eQ#p!dU$T#m$U$^R$]#uvUOWe!P!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%edrRSv!Q!R!l#_$e$|%[Q!^YS!ng%XQ!qhQ!tjQ!|sQ#OxQ#PyQ#QzQ#S{Q#T|Q#V}Q#d!ZX#h!^#d#i$Or]Oe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%e!WwRSYghjsvxyz{|}!Q!R!Z!^!l#_#d#i$O$e$|%X%[Q!WWR#]!P[uRSv!Q!R!lQ#{#_V${$e$|%[ToQpQ$V#mR$r$^Q!pgR%b%XraOe!_!`!a!d!s#m#u#v#w$U$^$l%O%Y%eQ!VWR#[!P",
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 operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
maxTerm: 111,
context: trackScope,
nodeProps: [
["closedBy", 49,"end"]
["closedBy", 47,"end"]
],
propSources: [highlighting],
skippedNodes: [0,25],
repeatNodeCount: 11,
tokenData: "Gw~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O/n!O!P$O!P!Q2W!Q!R)Y!R![+k![!]:s!]!^%W!^!}$O!}#O;^#O#P=S#P#Q=X#Q#R$O#R#S=r#S#T$O#T#Y0r#Y#Z>]#Z#b0r#b#cCZ#c#f0r#f#gDW#g#h0r#h#iET#i#o0r#o#p$O#p#qGX#q;'S$O;'S;=`$g<%l~$O~O$O~~GrS$TU|SOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tU|S!xYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_U|S#YQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^%xZiY|SOY%qYZ$OZt%qtu&kuw%qwx&kx#O%q#O#P&k#P;'S%q;'S;=`'S<%lO%qY&pSiYOY&kZ;'S&k;'S;=`&|<%lO&kY'PP;=`<%l&k^'VP;=`<%l%q~'_O#T~~'dO#R~U'kU|S!}QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(UU|S#]QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(mX|SOt$Ouw$Ox!Q$O!Q!R)Y!R![+k![#O$O#P;'S$O;'S;=`$g<%lO$OU)a^|SuQOt$Ouw$Ox!O$O!O!P*]!P!Q$O!Q![+k![#O$O#P#U$O#U#V,b#V#l$O#l#m-v#m;'S$O;'S;=`$g<%lO$OU*bW|SOt$Ouw$Ox!Q$O!Q![*z![#O$O#P;'S$O;'S;=`$g<%lO$OU+RW|SuQOt$Ouw$Ox!Q$O!Q![*z![#O$O#P;'S$O;'S;=`$g<%lO$OU+rY|SuQOt$Ouw$Ox!O$O!O!P*]!P!Q$O!Q![+k![#O$O#P;'S$O;'S;=`$g<%lO$OU,gX|SOt$Ouw$Ox!Q$O!Q!R-S!R!S-S!S#O$O#P;'S$O;'S;=`$g<%lO$OU-ZX|SuQOt$Ouw$Ox!Q$O!Q!R-S!R!S-S!S#O$O#P;'S$O;'S;=`$g<%lO$OU-{[|SOt$Ouw$Ox!Q$O!Q![.q![!c$O!c!i.q!i#O$O#P#T$O#T#Z.q#Z;'S$O;'S;=`$g<%lO$OU.x[|SuQOt$Ouw$Ox!Q$O!Q![.q![!c$O!c!i.q!i#O$O#P#T$O#T#Z.q#Z;'S$O;'S;=`$g<%lO$OU/s_|SOt$Ouw$Ox}$O}!O0r!O!Q$O!Q!R)Y!R![+k![!_$O!_!`1m!`#O$O#P#T$O#T#o0r#o;'S$O;'S;=`$g<%lO$OU0w[|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#o0r#o;'S$O;'S;=`$g<%lO$OU1tUzQ|SOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU2]W|SOt$Ouw$Ox!P$O!P!Q2u!Q#O$O#P;'S$O;'S;=`$g<%lO$OU2z^|SOY3vYZ$OZt3vtu4yuw3vwx4yx!P3v!P!Q$O!Q!}3v!}#O9l#O#P7X#P;'S3v;'S;=`:m<%lO3vU3}^|S!aQOY3vYZ$OZt3vtu4yuw3vwx4yx!P3v!P!Q7n!Q!}3v!}#O9l#O#P7X#P;'S3v;'S;=`:m<%lO3vQ5OX!aQOY4yZ!P4y!P!Q5k!Q!}4y!}#O6Y#O#P7X#P;'S4y;'S;=`7h<%lO4yQ5nP!P!Q5qQ5vU!aQ#Z#[5q#]#^5q#a#b5q#g#h5q#i#j5q#m#n5qQ6]VOY6YZ#O6Y#O#P6r#P#Q4y#Q;'S6Y;'S;=`7R<%lO6YQ6uSOY6YZ;'S6Y;'S;=`7R<%lO6YQ7UP;=`<%l6YQ7[SOY4yZ;'S4y;'S;=`7h<%lO4yQ7kP;=`<%l4yU7sW|SOt$Ouw$Ox!P$O!P!Q8]!Q#O$O#P;'S$O;'S;=`$g<%lO$OU8db|S!aQOt$Ouw$Ox#O$O#P#Z$O#Z#[8]#[#]$O#]#^8]#^#a$O#a#b8]#b#g$O#g#h8]#h#i$O#i#j8]#j#m$O#m#n8]#n;'S$O;'S;=`$g<%lO$OU9q[|SOY9lYZ$OZt9ltu6Yuw9lwx6Yx#O9l#O#P6r#P#Q3v#Q;'S9l;'S;=`:g<%lO9lU:jP;=`<%l9lU:pP;=`<%l3vU:zU|S!RQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU;eW#_Q|SOt$Ouw$Ox!_$O!_!`;}!`#O$O#P;'S$O;'S;=`$g<%lO$OU<SV|SOt$Ouw$Ox#O$O#P#Q<i#Q;'S$O;'S;=`$g<%lO$OU<pU#^Q|SOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~=XO#U~U=`U#`Q|SOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU=yU|S!YQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU>b]|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#U?Z#U#o0r#o;'S$O;'S;=`$g<%lO$OU?`^|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#`0r#`#a@[#a#o0r#o;'S$O;'S;=`$g<%lO$OU@a^|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#g0r#g#hA]#h#o0r#o;'S$O;'S;=`$g<%lO$OUAb^|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#X0r#X#YB^#Y#o0r#o;'S$O;'S;=`$g<%lO$OUBe[!PQ|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#o0r#o;'S$O;'S;=`$g<%lO$O^Cb[#VW|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#o0r#o;'S$O;'S;=`$g<%lO$O^D_[#XW|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#o0r#o;'S$O;'S;=`$g<%lO$O^E[^#WW|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#f0r#f#gFW#g#o0r#o;'S$O;'S;=`$g<%lO$OUF]^|SOt$Ouw$Ox}$O}!O0r!O!_$O!_!`1m!`#O$O#P#T$O#T#i0r#i#jA]#j#o0r#o;'S$O;'S;=`$g<%lO$OUG`UlQ|SOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~GwO#a~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#P~~", 11)],
repeatNodeCount: 12,
tokenData: "IS~R}OX$OXY$mYZ%WZp$Opq$mqs$Ost%qtu'Yuw$Owx'_xy'dyz'}z{$O{|(h|}$O}!O(h!O!P$O!P!Q0o!Q!R)Y!R![+w![!]9[!]!^%W!^!}$O!}#O9u#O#P;k#P#Q;p#Q#R$O#R#S<Z#S#T$O#T#Y<t#Y#Z>`#Z#b<t#b#cC|#c#f<t#f#gEP#g#h<t#h#iFS#i#o<t#o#p$O#p#qHd#q;'S$O;'S;=`$g<%l~$O~O$O~~H}S$TUzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tUzS!zYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_UzS#QQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^%xZiYzSOY%qYZ$OZt%qtu&kuw%qwx&kx#O%q#O#P&k#P;'S%q;'S;=`'S<%lO%qY&pSiYOY&kZ;'S&k;'S;=`&|<%lO&kY'PP;=`<%l&k^'VP;=`<%l%q~'_O#V~~'dO#T~U'kUzS#PQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(UUzS#_QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(mXzSOt$Ouw$Ox!Q$O!Q!R)Y!R![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU)a`zSsQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S#U$O#U#V-c#V#l$O#l#m.w#m;'S$O;'S;=`$g<%lO$OU*hWzSOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P;'S$O;'S;=`$g<%lO$OU+XYzSsQOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P#R$O#R#S*c#S;'S$O;'S;=`$g<%lO$OU,O[zSsQOt$Ouw$Ox!O$O!O!P*c!P!Q$O!Q![+w![#O$O#P#R$O#R#S,t#S;'S$O;'S;=`$g<%lO$OU,yWzSOt$Ouw$Ox!Q$O!Q![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU-hXzSOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.[XzSsQOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.|[zSOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU/y[zSsQOt$Ouw$Ox!Q$O!Q![/r![!c$O!c!i/r!i#O$O#P#T$O#T#Z/r#Z;'S$O;'S;=`$g<%lO$OU0tWzSOt$Ouw$Ox!P$O!P!Q1^!Q#O$O#P;'S$O;'S;=`$g<%lO$OU1c^zSOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q$O!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_U2f^zS!bQOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q6V!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_Q3gX!bQOY3bZ!P3b!P!Q4S!Q!}3b!}#O4q#O#P5p#P;'S3b;'S;=`6P<%lO3bQ4VP!P!Q4YQ4_U!bQ#Z#[4Y#]#^4Y#a#b4Y#g#h4Y#i#j4Y#m#n4YQ4tVOY4qZ#O4q#O#P5Z#P#Q3b#Q;'S4q;'S;=`5j<%lO4qQ5^SOY4qZ;'S4q;'S;=`5j<%lO4qQ5mP;=`<%l4qQ5sSOY3bZ;'S3b;'S;=`6P<%lO3bQ6SP;=`<%l3bU6[WzSOt$Ouw$Ox!P$O!P!Q6t!Q#O$O#P;'S$O;'S;=`$g<%lO$OU6{bzS!bQOt$Ouw$Ox#O$O#P#Z$O#Z#[6t#[#]$O#]#^6t#^#a$O#a#b6t#b#g$O#g#h6t#h#i$O#i#j6t#j#m$O#m#n6t#n;'S$O;'S;=`$g<%lO$OU8Y[zSOY8TYZ$OZt8Ttu4quw8Twx4qx#O8T#O#P5Z#P#Q2_#Q;'S8T;'S;=`9O<%lO8TU9RP;=`<%l8TU9XP;=`<%l2_U9cUzS!PQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU9|W#aQzSOt$Ouw$Ox!_$O!_!`:f!`#O$O#P;'S$O;'S;=`$g<%lO$OU:kVzSOt$Ouw$Ox#O$O#P#Q;Q#Q;'S$O;'S;=`$g<%lO$OU;XU#`QzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~;pO#W~U;wU#bQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<bUzS!WQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<y^zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$OU=|UxQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU>e_zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#U?d#U#o<t#o;'S$O;'S;=`$g<%lO$OU?i`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#`<t#`#a@k#a#o<t#o;'S$O;'S;=`$g<%lO$OU@p`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#g<t#g#hAr#h#o<t#o;'S$O;'S;=`$g<%lO$OUAw`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#X<t#X#YBy#Y#o<t#o;'S$O;'S;=`$g<%lO$OUCQ^}QzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^DT^#XWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^EW^#ZWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#o<t#o;'S$O;'S;=`$g<%lO$O^FZ`#YWzSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#f<t#f#gG]#g#o<t#o;'S$O;'S;=`$g<%lO$OUGb`zSOt$Ouw$Ox}$O}!O<t!O!Q$O!Q![<t![!_$O!_!`=u!`#O$O#P#T$O#T#i<t#i#jAr#j#o<t#o;'S$O;'S;=`$g<%lO$OUHkUlQzSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~ISO#c~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#R~~", 11)],
topRules: {"Program":[0,26]},
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: 1922
})

View File

@ -368,6 +368,118 @@ describe('Parentheses', () => {
PositionalArg
Number 3`)
})
test('function call with named args on multiple lines in parens', () => {
expect(`(tail
arg1=true
arg2=30
)`).toMatchTree(`
ParenExpr
FunctionCall
Identifier tail
NamedArg
NamedArgPrefix arg1=
Boolean true
NamedArg
NamedArgPrefix arg2=
Number 30
`)
expect(`(
tail
arg1=true
arg2=30
)`).toMatchTree(`
ParenExpr
FunctionCall
Identifier tail
NamedArg
NamedArgPrefix arg1=
Boolean true
NamedArg
NamedArgPrefix arg2=
Number 30
`)
})
test('binop with newlines in parens', () => {
expect(`(
1 + 2
)`).toMatchTree(`
ParenExpr
BinOp
Number 1
Plus +
Number 2`)
})
test('comparison with newlines in parens', () => {
expect(`(
1 < 2
)`).toMatchTree(`
ParenExpr
ConditionalOp
Number 1
Lt <
Number 2`)
})
test('function call with multiple identifiers on separate lines in parens', () => {
expect(`(echo
arg1
arg2
arg3
)`).toMatchTree(`
ParenExpr
FunctionCall
Identifier echo
PositionalArg
Identifier arg1
PositionalArg
Identifier arg2
PositionalArg
Identifier arg3`)
})
})
describe('Number literals', () => {
test('allows underscores in integer literals', () => {
expect('10_000').toMatchTree(`Number 10_000`)
expect('1_000_000').toMatchTree(`Number 1_000_000`)
expect('100_000').toMatchTree(`Number 100_000`)
})
test('allows underscores in decimal literals', () => {
expect('3.14_159').toMatchTree(`Number 3.14_159`)
expect('1_000.50').toMatchTree(`Number 1_000.50`)
expect('0.000_001').toMatchTree(`Number 0.000_001`)
})
test('allows underscores in negative numbers', () => {
expect('-10_000').toMatchTree(`Number -10_000`)
expect('-3.14_159').toMatchTree(`Number -3.14_159`)
})
test('allows underscores in positive numbers with explicit sign', () => {
expect('+10_000').toMatchTree(`Number +10_000`)
expect('+3.14_159').toMatchTree(`Number +3.14_159`)
})
test('works in expressions', () => {
expect('1_000 + 2_000').toMatchTree(`
BinOp
Number 1_000
Plus +
Number 2_000`)
})
test('works in function calls', () => {
expect('echo 10_000').toMatchTree(`
FunctionCall
Identifier echo
PositionalArg
Number 10_000`)
})
})
describe('BinOp', () => {

View File

@ -6,6 +6,7 @@ import {
} from 'reefvm'
import { dict } from './dict'
import { json } from './json'
import { load } from './load'
import { list } from './list'
import { math } from './math'
@ -13,6 +14,7 @@ import { str } from './str'
export const globals = {
dict,
json,
load,
list,
math,
@ -40,6 +42,7 @@ export const globals = {
'var?': function (this: VM, v: string) {
return typeof v !== 'string' || this.scope.has(v)
},
ref: (fn: Function) => fn,
// env
args: Bun.argv.slice(1),

7
src/prelude/json.ts Normal file
View File

@ -0,0 +1,7 @@
export const json = {
encode: (s: any) => JSON.stringify(s),
decode: (s: string) => JSON.parse(s),
}
; (json as any).parse = json.decode
; (json as any).stringify = json.encode

View File

@ -73,6 +73,13 @@ export const list = {
const realItems = items.map(item => item.value)
return toValue(realList.splice(realStart, realDeleteCount, ...realItems))
},
insert: (list: Value, index: Value, item: Value) => {
if (list.type !== 'array') return toNull()
const realList = list.value as any[]
const realIndex = index.value as number
realList.splice(realIndex, 0, item)
return toValue(realList.length)
},
// sequence operations
reverse: (list: any[]) => list.slice().reverse(),
@ -142,4 +149,5 @@ export const list = {
; (list.push as any).raw = true
; (list.pop as any).raw = true
; (list.shift as any).raw = true
; (list.unshift as any).raw = true
; (list.unshift as any).raw = true
; (list.insert as any).raw = true

View File

@ -91,3 +91,15 @@ describe('environment', () => {
await expect(`list.first args | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true)
})
})
describe('ref', () => {
expect(`rnd = do x: true end; rnd | type`).toEvaluateTo('boolean')
expect(`rnd = do x: true end; ref rnd | type`).toEvaluateTo('function')
expect(`math.random | type`).toEvaluateTo('number')
expect(`ref math.random | type`).toEvaluateTo('native')
expect(`rnd = math.random; rnd | type`).toEvaluateTo('number')
expect(`rnd = ref math.random; rnd | type`).toEvaluateTo('number')
expect(`rnd = ref math.random; ref rnd | type`).toEvaluateTo('native')
})

View File

@ -0,0 +1,84 @@
import { expect, describe, test } from 'bun:test'
describe('json', () => {
test('json.decode', () => {
expect(`json.decode '[1,2,3]'`).toEvaluateTo([1, 2, 3])
expect(`json.decode '"heya"'`).toEvaluateTo('heya')
expect(`json.decode '[true, false, null]'`).toEvaluateTo([true, false, null])
expect(`json.decode '{"a": true, "b": false, "c": "yeah"}'`).toEvaluateTo({ a: true, b: false, c: "yeah" })
})
test('json.encode', () => {
expect(`json.encode [1 2 3]`).toEvaluateTo('[1,2,3]')
expect(`json.encode 'heya'`).toEvaluateTo('"heya"')
expect(`json.encode [true false null]`).toEvaluateTo('[true,false,null]')
expect(`json.encode [a=true b=false c='yeah'] | json.decode`).toEvaluateTo({ a: true, b: false, c: "yeah" })
})
test('edge cases - empty structures', () => {
expect(`json.decode '[]'`).toEvaluateTo([])
expect(`json.decode '{}'`).toEvaluateTo({})
expect(`json.encode []`).toEvaluateTo('[]')
expect(`json.encode [=]`).toEvaluateTo('{}')
})
test('edge cases - special characters in strings', () => {
expect(`json.decode '"hello\\\\nworld"'`).toEvaluateTo('hello\nworld')
expect(`json.decode '"tab\\\\there"'`).toEvaluateTo('tab\there')
expect(`json.decode '"forward/slash"'`).toEvaluateTo('forward/slash')
expect(`json.decode '"with\\\\\\\\backslash"'`).toEvaluateTo('with\\backslash')
})
test('numbers - integers and floats', () => {
expect(`json.decode '42'`).toEvaluateTo(42)
expect(`json.decode '0'`).toEvaluateTo(0)
expect(`json.decode '-17'`).toEvaluateTo(-17)
expect(`json.decode '3.14159'`).toEvaluateTo(3.14159)
expect(`json.decode '-0.5'`).toEvaluateTo(-0.5)
})
test('numbers - scientific notation', () => {
expect(`json.decode '1e10'`).toEvaluateTo(1e10)
expect(`json.decode '2.5e-3'`).toEvaluateTo(2.5e-3)
expect(`json.decode '1.23E+5'`).toEvaluateTo(1.23e5)
})
test('unicode - emoji and special characters', () => {
expect(`json.decode '"hello 👋"'`).toEvaluateTo('hello 👋')
expect(`json.decode '"🎉🚀✨"'`).toEvaluateTo('🎉🚀✨')
expect(`json.encode '你好'`).toEvaluateTo('"你好"')
expect(`json.encode 'café'`).toEvaluateTo('"café"')
})
test('nested structures - arrays', () => {
expect(`json.decode '[[1,2],[3,4],[5,6]]'`).toEvaluateTo([[1, 2], [3, 4], [5, 6]])
expect(`json.decode '[1,[2,[3,[4]]]]'`).toEvaluateTo([1, [2, [3, [4]]]])
})
test('nested structures - objects', () => {
expect(`json.decode '{"user":{"name":"Alice","age":30}}'`).toEvaluateTo({
user: { name: 'Alice', age: 30 }
})
expect(`json.decode '{"a":{"b":{"c":"deep"}}}'`).toEvaluateTo({
a: { b: { c: 'deep' } }
})
})
test('nested structures - mixed arrays and objects', () => {
expect(`json.decode '[{"id":1,"tags":["a","b"]},{"id":2,"tags":["c"]}]'`).toEvaluateTo([
{ id: 1, tags: ['a', 'b'] },
{ id: 2, tags: ['c'] }
])
expect(`json.decode '{"items":[1,2,3],"meta":{"count":3}}'`).toEvaluateTo({
items: [1, 2, 3],
meta: { count: 3 }
})
})
test('error handling - invalid json', () => {
expect(`json.decode '{invalid}'`).toFailEvaluation()
expect(`json.decode '[1,2,3'`).toFailEvaluation()
expect(`json.decode 'undefined'`).toFailEvaluation()
expect(`json.decode ''`).toFailEvaluation()
})
})

View File

@ -349,6 +349,22 @@ describe('collections', () => {
await expect(`arr = [1 2 3 4 5]; list.splice arr 3 2; arr`).toEvaluateTo([1, 2, 3])
})
test('list.insert adds element at index and mutates array', async () => {
await expect(`arr = [1 2 4 5]; list.insert arr 2 3; arr`).toEvaluateTo([1, 2, 3, 4, 5])
})
test('list.insert returns array length', async () => {
await expect(`list.insert [1 2 4] 2 3`).toEvaluateTo(4)
})
test('list.insert at start', async () => {
await expect(`arr = [2 3]; list.insert arr 0 1; arr`).toEvaluateTo([1, 2, 3])
})
test('list.insert at end', async () => {
await expect(`arr = [1 2]; list.insert arr 2 99; arr`).toEvaluateTo([1, 2, 99])
})
test('list.sort with no callback sorts ascending', async () => {
await expect(`list.sort [3 1 4 1 5] null`).toEvaluateTo([1, 1, 3, 4, 5])
})