Compare commits

..

2 Commits

Author SHA1 Message Date
cf09f51380 update reef, fix precedence test 2025-11-07 22:55:51 -08:00
3c6d0913c0 bitwise operators 2025-11-07 22:42:26 -08:00
16 changed files with 65 additions and 512 deletions

View File

@ -145,7 +145,7 @@ async function repl() {
}
try {
const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()])
const compiler = new Compiler(trimmed, Object.keys(globals))
// Save VM state before appending bytecode, in case execution fails
const savedInstructions = [...vm.instructions]
@ -235,7 +235,7 @@ async function loadFile(filePath: string): Promise<{ vm: VM; codeHistory: string
if (!trimmed) continue
try {
const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()])
const compiler = new Compiler(trimmed)
vm.appendBytecode(compiler.bytecode)
await vm.continue()
codeHistory.push(trimmed)

View File

@ -44,7 +44,7 @@
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="],
"@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
"@types/bun": ["@types/bun@1.3.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
"@types/node": ["@types/node@24.10.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A=="],
@ -52,7 +52,7 @@
"bun-plugin-tailwind": ["bun-plugin-tailwind@0.0.15", "", { "peerDependencies": { "typescript": "^5.0.0" } }, "sha512-qtAXMNGG4R0UGGI8zWrqm2B7BdXqx48vunJXBPzfDOHPA5WkRUZdTSbE7TFwO4jLhYqSE23YMWsM9NhE6ovobw=="],
"bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
"bun-types": ["bun-types@1.3.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
"codemirror": ["codemirror@6.0.2", "", { "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/commands": "^6.0.0", "@codemirror/language": "^6.0.0", "@codemirror/lint": "^6.0.0", "@codemirror/search": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0" } }, "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw=="],
@ -62,7 +62,7 @@
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#3e2e68b31f504347225a4d705c7568a0957d629e", { "peerDependencies": { "typescript": "^5" } }, "3e2e68b31f504347225a4d705c7568a0957d629e"],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#15884ac239b7ab2310e0f87b82f9f1af485c28c7", { "peerDependencies": { "typescript": "^5" } }, "15884ac239b7ab2310e0f87b82f9f1af485c28c7"],
"style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="],

View File

@ -51,7 +51,6 @@ function processEscapeSeq(escapeSeq: string): string {
export class Compiler {
instructions: ProgramItem[] = []
labelCount = 0
fnLabelCount = 0
ifLabelCount = 0
tryLabelCount = 0
@ -106,21 +105,11 @@ export class Compiler {
switch (node.type.id) {
case terms.Number:
// Handle sign prefix for hex and binary literals
// Number() doesn't parse '-0xFF' or '+0xFF' correctly
let numberValue: number
if (value.startsWith('-') && (value.includes('0x') || value.includes('0b'))) {
numberValue = -Number(value.slice(1))
} else if (value.startsWith('+') && (value.includes('0x') || value.includes('0b'))) {
numberValue = Number(value.slice(1))
} else {
numberValue = Number(value)
}
if (Number.isNaN(numberValue))
const number = Number(value)
if (Number.isNaN(number))
throw new CompilerError(`Invalid number literal: ${value}`, node.from, node.to)
return [[`PUSH`, numberValue]]
return [[`PUSH`, number]]
case terms.String: {
const { parts, hasInterpolation } = getStringParts(node, input)
@ -396,29 +385,7 @@ export class Compiler {
case terms.FunctionCallOrIdentifier: {
if (node.firstChild?.type.id === terms.DotGet) {
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 this.#compileNode(node.firstChild, input)
}
return [['TRY_CALL', value]]

View File

@ -110,10 +110,7 @@ describe('compiler', () => {
})
test('function call with no args', () => {
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)
expect(`bloop = do: 'bloop' end; bloop`).toEvaluateTo('bloop')
})
test('function call with if statement and multiple expressions', () => {
@ -301,23 +298,6 @@ 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

@ -1,44 +1,6 @@
import { describe } from 'bun:test'
import { expect, test } from 'bun:test'
describe('number literals', () => {
test('binary literals', () => {
expect('0b110').toEvaluateTo(6)
expect('0b1010').toEvaluateTo(10)
expect('0b11111111').toEvaluateTo(255)
expect('0b0').toEvaluateTo(0)
expect('0b1').toEvaluateTo(1)
})
test('hex literals', () => {
expect('0xdeadbeef').toEvaluateTo(0xdeadbeef)
expect('0xdeadbeef').toEvaluateTo(3735928559)
expect('0xFF').toEvaluateTo(255)
expect('0xff').toEvaluateTo(255)
expect('0x10').toEvaluateTo(16)
expect('0x0').toEvaluateTo(0)
expect('0xABCDEF').toEvaluateTo(0xabcdef)
})
test('decimal literals still work', () => {
expect('42').toEvaluateTo(42)
expect('3.14').toEvaluateTo(3.14)
expect('0').toEvaluateTo(0)
expect('999999').toEvaluateTo(999999)
})
test('negative hex and binary', () => {
expect('-0xFF').toEvaluateTo(-255)
expect('-0b1010').toEvaluateTo(-10)
})
test('positive prefix', () => {
expect('+0xFF').toEvaluateTo(255)
expect('+0b110').toEvaluateTo(6)
expect('+42').toEvaluateTo(42)
})
})
describe('array literals', () => {
test('work with numbers', () => {
expect('[1 2 3]').toEvaluateTo([1, 2, 3])

View File

@ -12,17 +12,13 @@
@precedence { Number Regex }
StringFragment { !['\\$]+ }
NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" }
Number {
("-" | "+")? "0x" $[0-9a-fA-F]+ |
("-" | "+")? "0b" $[01]+ |
("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)?
}
NamedArgPrefix { $[a-z-]+ "=" }
Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? }
Boolean { "true" | "false" }
newlineOrSemicolon { "\n" | ";" }
eof { @eof }
space { " " | "\t" }
Comment { "#" ![\n]* }
Comment { "#" " " ![\n]* }
leftParen { "(" }
rightParen { ")" }
colon[closedBy="end", @name="colon"] { ":" }
@ -52,8 +48,7 @@ null { @specialize[@name=Null]<Identifier, "null"> }
multiplicative @left,
additive @left,
bitwise @left,
call,
functionWithNewlines
call
}
item {
@ -200,21 +195,7 @@ BinOp {
}
ParenExpr {
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*
leftParen (IfExpr | ambiguousFunctionCall | BinOp | expressionWithoutIdentifier | ConditionalOp | PipeExpr | FunctionDef) rightParen
}
expression {

View File

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

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:72, null:104, catch:110, finally:116, end:118, if:126, else:132, try:150, throw:154}
const spec_Identifier = {__proto__:null,while:72, if:80, null:108, catch:114, finally:120, end:122, else:130, try:148, throw:152}
export const parser = LRParser.deserialize({
version: 14,
states: "<bQYQbOOO!dOpO'#DUO!iOSO'#D]O%ZQcO'#DrO(QQcO'#EbOOQ`'#Ep'#EpO(kQRO'#DsO*mQcO'#E`O+WQbO'#DSOOQa'#Du'#DuO-]QbO'#DvOOQa'#Eb'#EbO-dQcO'#EbO/_QcO'#EaO0dQcO'#E`O0nQRO'#D|OOQ`'#E`'#E`O1SQbO'#E`O1ZQQO'#E_OOQ`'#E_'#E_OOQ`'#EO'#EOQYQbOOO1fQbO'#DXO1qQbO'#DlO2fQbO'#DPO3ZQQO'#DxO2fQbO'#DzO3`ObO,59pO3kQbO'#D_O3sQWO'#D`OOOO'#Eh'#EhOOOO'#ET'#ETO4XOSO,59wOOQa,59w,59wOOQ`'#DW'#DWO4gQbO'#DkOOQ`'#Ef'#EfOOQ`'#EW'#EWO4qQbO,5:YOOQa'#Ea'#EaO2fQbO,5:_O2fQbO,5:_O2fQbO,5:_O2fQbO,5:_O2fQbO,59mO2fQbO,59mO2fQbO,59mOOQ`'#EQ'#EQO+WQbO,59nO5kQcO'#DrO5rQcO'#EbO5yQRO,59nO6TQQO,59nO6YQQO,59nO6bQQO,59nO6mQRO,59nO6tQRO,59nO7VQQO'#C}O7[QbO,5:bO7cQQO,5:aOOQa,5:b,5:bO7nQbO,5:bO7xQbO,5:iO7xQbO,5:hO9PQbO,5:cO9WQbO,59iOOQ`,5:y,5:yO7xQbO'#EPOOQ`-E7|-E7|OOQ`'#ER'#ERO9rQbO'#DYO9}QbO'#DZOOQO'#ES'#ESO9uQQO'#DYO:]QQO,59sO:bQcO'#EaO;[QRO'#EoO<RQRO'#EoOOQO'#Eo'#EoO<YQQO,5:WO<_QRO,59kO<fQRO,59kO9PQbO,5:dO<qQcO,5:fO>PQcO,5:fO>mQcO,5:fOOQa1G/[1G/[OOOO,59y,59yOOOO,59z,59zOOOO-E8R-E8ROOQa1G/c1G/cOOQ`,5:V,5:VOOQ`-E8U-E8UOOQa1G/y1G/yO@fQcO1G/yO@pQcO1G/yOBOQcO1G/yOBYQcO1G/yOBgQcO1G/yOOQa1G/X1G/XOCuQcO1G/XOC|QcO1G/XODTQcO1G/XOOQ`-E8O-E8OOD[QRO1G/YODfQQO1G/YODkQQO1G/YODsQQO1G/YOEOQRO1G/YOEVQRO1G/YOEhQbO,59oOErQQO1G/YOOQa1G/Y1G/YOEzQQO1G/{OOQa1G/|1G/|OFVQbO1G/|OOQO'#EY'#EYOEzQQO1G/{OOQa1G/{1G/{OOQ`'#EZ'#EZOFVQbO1G/|OFaQbO1G0TOF{QbO1G0SOGgQbO'#DfOGxQbO'#DfOH]QbO1G/}OOQ`-E7}-E7}OOQ`,5:k,5:kOOQ`-E8P-E8POHhQQO,59tOOQO,59u,59uOOQO-E8Q-E8QOHpQbO1G/_O9PQbO1G/rO9PQbO1G/VOHwQbO1G0OOISQQO7+$tOOQa7+$t7+$tOI[QQO1G/ZOIdQQO7+%gOOQa7+%g7+%gOIoQbO7+%hOOQa7+%h7+%hOOQO-E8W-E8WOOQ`-E8X-E8XOOQ`'#EU'#EUOIyQQO'#EUOJRQbO'#EnOOQ`,5:Q,5:QOJfQbO'#DdOJkQQO'#DgOOQ`7+%i7+%iOJpQbO7+%iOJuQbO7+%iOJ}QbO7+$yOK]QbO7+$yOKmQbO7+%^OKuQbO7+$qOOQ`7+%j7+%jOKzQbO7+%jOLPQbO7+%jOOQa<<H`<<H`OLXQbO7+$uOLfQQO7+$uOOQa<<IR<<IROOQa<<IS<<ISOOQ`,5:p,5:pOOQ`-E8S-E8SOLnQQO,5:OO9PQbO,5:ROOQ`<<IT<<ITOLsQbO<<ITOOQ`<<He<<HeOLxQbO<<HeOL}QbO<<HeOMVQbO<<HeOOQ`'#EX'#EXOMbQbO<<HxOMjQbO'#DqOOQ`<<Hx<<HxOMrQbO<<HxOOQ`<<H]<<H]OOQ`<<IU<<IUOMwQbO<<IUOOQO,5:q,5:qOM|QbO<<HaOOQO-E8T-E8TO9PQbO1G/jOOQ`1G/m1G/mOOQ`AN>oAN>oOOQ`AN>PAN>PONZQbOAN>PON`QbOAN>POOQ`-E8V-E8VOOQ`AN>dAN>dONhQbOAN>dO1qQbO,5:ZO9PQbO,5:]OOQ`AN>pAN>pPEhQbO'#EQOOQ`7+%U7+%UOOQ`G23kG23kONmQbOG23kPMmQbO'#DoOOQ`G24OG24OONrQQO1G/uOOQ`1G/w1G/wOOQ`LD)VLD)VO9PQbO7+%aOOQ`<<H{<<H{",
stateData: "Nz~O#QOSoOS~OjROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#WcO#ZQO#fXO#gYO~O#XkO~O!QnO#ZqO#]lO#^mO~OjwOlZOmPOnfOyZO!OsO!TZO!UZO!^rO!hZO#VWO#ZQO#fXO#gYOP#TXQ#TXR#TXS#TXT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX^#TXd#TXe#TXf#TXg#TXh#TXi#TXr!fX!V!fX#e!fX~O#W!fX#i!fX!X!fX![!fX!]!fX!d!fX~P!wOjwOlZOmPOnfOyZO!OsO!TZO!UZO!^rO!hZO#VWO#ZQO#fXO#gYOP#UXQ#UXR#UXS#UXT#UXU#UXW#UXX#UXY#UXZ#UX[#UX]#UX^#UXd#UXe#UXf#UXg#UXh#UXi#UXr#UX#e#UX~O#W#UX#i#UX!V#UX!X#UX![#UX!]#UX!d#UX~P%qOPyOQyORzOSzOT}OU!OOW|OX|OY|OZ|O[|O]|O^xOd{Oe{Of{Og{Oh{Oi{O~OPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{Or#SX~O#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~P)xOj!ROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#W!PO#ZQO#fXO#gYO~OjwOlZOmPOyZO!OsO!TZO!UZO!hZO#VWO#W!PO#ZQO#fXO#gYO~O#h!^O~P,bOV!`O#W#UX#i#UX!X#UX![#UX!]#UX!d#UX~P&mOP#TXQ#TXR#TXS#TXT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX^#TXd#TXe#TXf#TXg#TXh#TXi#TXr#SX~O#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~P-}Or#SX#W#SX#i#SX!X#SX![#SX!]#SX#e#SX!d#SX~OT}OU!OO~P/xOV!`O_!aO`!aOa!aOb!aOc!aO~O!V!bO~P/xOr!eO#W!dO#i!dO~Oj!gO!O!iO!V|P~Oj!mOlZOmPOyZO!TZO!UZO!hZO#VWO#ZQO#fXO#gYO~OjwOlZOmPOyZO!TZO!UZO!hZO#VWO#ZQO#fXO#gYO~O!V!tO~Oj!xOy!xO#VWO~Oj!yO#VWO~O#Z!zO#]!zO#^!zO#_!zO#`!zO#a!zO~O!QnO#Z!|O#]lO#^mO~OnfO!^!}O~P2fOnfO!OsO!^rOr!ba!V!ba#W!ba#i!ba#e!ba!X!ba![!ba!]!ba!d!ba~P2fO#W!PO~P!wO#W!PO~P%qO#W!PO#e#dO~P)xO#e#dO~O#e#dOr#SX~O!V!bO#e#dOr#SX~O#e#dO~P-}OT}OU!OO#W!PO#e#dOr#SX~Or!eO~O#h#fO~P,bO!OsO#W#hO#h#jO~O#W#kO#h#fO~P2fOjROk_OlZOmPOnfOthOyZO!TZO!UZO!agO!hZO!miO!ojO#VWO#ZQO#fXO#gYO~O#W#pO~P7xOr!eO#Wqa#iqa#eqa!Xqa![qa!]qa!dqa~Oj!gO!O!iO!V|X~Oy#vO!T#vO!U#vO#ZQO~O!V#xO~OnfO!OsO!^rOT#TXU#TXW#TXX#TXY#TXZ#TX[#TX]#TX!V#TX~P2fOT}OU!OO!V#cX~OT}OU!OOW|OX|OY|OZ|O[|O]|O~O!V#cX~P;gO!V#yO~O!V#zO~P;gOT}OU!OO!V#zO~Or!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~P(kOPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{O~Or!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~P=_OT}OU!OOr!na#W!na#i!na!X!na![!na!]!na#e!na!d!na~O^xOR!giS!gid!gie!gif!gig!gih!gii!gir!gi#W!gi#i!gi#e!gi!X!gi![!gi!]!gi!d!gi~OP!giQ!gi~P?_OPyOQyO~P?_OPyOQyOd!gie!gif!gig!gih!gii!gir!gi#W!gi#i!gi#e!gi!X!gi![!gi!]!gi!d!gi~OR!giS!gi~P@zORzOSzO^xO~P@zORzOSzO~P@zOW|OX|OY|OZ|O[|O]|OTuirui#Wui#iui#eui!Vui!Xui![ui!]ui!dui~OU!OO~PBqOU!OO~PCTOUui~PBqO#W!PO#e#}O~P)xO#e#}O~O#e#}Or#SX~O!V!bO#e#}Or#SX~O#e#}O~P-}OT}OU!OO#W!PO#e#}Or#SX~OnfO!^rO~P,bO#W!PO#e#}O~O!OsO#W#hO#h$QO~O#W#kO#h$SO~P2fOr!eO#W!qi#i!qi!X!qi![!qi!]!qi#e!qi!d!qi~Or!eO#W!pi#i!pi!X!pi![!pi!]!pi#e!pi!d!pi~Or!eO!X!YX![!YX!]!YX!d!YX~O#W$VO!X#bP![#bP!]#bP!d#bP~P7xO!X$ZO![$[O!]$]O~O!O!iO!V|a~O#W$aO~P7xO!X$ZO![$[O!]$dO~O#W!PO#e$gO~O#W!PO#ewi~O!OsO#W#hO#h$jO~O#W#kO#h$kO~P2fOr!eO#W$lO~O#W$VO!X#bX![#bX!]#bX!d#bX~P7xOj$nO~O!V$oO~O!]$pO~O![$[O!]$pO~Or!eO!X$ZO![$[O!]$rO~O#W$VO!X#bP![#bP!]#bP~P7xO!]$yO!d$xO~O!]${O~O!]$|O~O![$[O!]$|O~OnfO!^rO#ewq~P,bO#W!PO#ewq~O!V%RO~O!]%TO~O!]%UO~O![$[O!]%UO~O!X$ZO![$[O!]%UO~O!]%YO!d$xO~O!V%]O!a%[O~O!]%YO~O!]%^O~OnfO!^rO#ewy~P,bO!]%aO~O![$[O!]%aO~O!]%dO~O!]%gO~O!V%hO~Oy!h~",
goto: "8W#ePPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#fP$PP$f%a&n&tP'}(Z)T)WP)^P*d*dPPP*hP*t+^PPP+t#fP,^,wP,{-R-hP._/b$P$PP$PP$P$P0g0m0y1m1s1}2T2[2b2l2r2|PPP3W3[4P5rPPP6{P7]PPPPP7a7g7mr`Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!VWR#^!Qw`OWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hr^Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!YWS!ng%[Q!shQ!wjQ#V!OQ#X}R#a!QvSOeg!`!a!b!e!t#p#x#y#z$X$a$o%R%[%]%h!UZRSYhjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%_S!SW!QQ!xkR!ylQ!UWR#]!QrROe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%h!UwRSYhjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%_S!RW!QT!mg%[etRSv!R!S!m#b$h%P%_r`Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hdrRSv!R!S!m#b$h%P%_Q!VWQ!}sR#^!QR!lfX!jf!h!k#u#QZORSWYeghjsvxyz{|}!O!Q!R!S![!_!`!a!b!e!m!t#b#g#l#p#x#y#z$R$X$a$h$o%P%R%[%]%_%hR#v!iTnQpQ$_#qQ$f#{Q$t$`R%W$uQ#q!bQ#{!tQ$b#yQ$c#zQ%S$oQ%`%RQ%f%]R%i%hQ$^#qQ$e#{Q$q$_Q$s$`Q$}$fS%V$t$uR%b%WdtRSv!R!S!m#b$h%P%_Q!]YQ#e![X#h!]#e#i$PvTOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hT!pg%[T$v$b$wQ$z$bR%Z$wwTOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hrVOe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!TWQ!vjQ#PyQ#SzQ#U{R#[!Q#RZORSWYeghjsvxyz{|}!O!Q!R!S![!_!`!a!b!e!m!t#b#g#l#p#x#y#z$R$X$a$h$o%P%R%[%]%_%h!YZRSYghjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%[%_w[OWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQeOR!fe^!cb!Z#m#n#o$W$`R#r!cQ!QWQ![Y`#Z!Q![#b#c#|$h%P%_S#b!R!SS#c!T!YS#|#[#aQ$h$OR%P$iQ!hfR#t!hQ!kfQ#u!hT#w!k#uQpQR!{pS$X#p$aR$m$XQ$i$OR%Q$iYvRS!R!S!mR#OvQ$w$bR%X$wQ#i!]Q$P#eT$T#i$PQ#l!_Q$R#gT$U#l$RTdOeSbOeS!ZW!QQ#m!`Q#n!a`#o!b!t#y#z$o%R%]%hQ#s!eU$W#p$X$aR$`#xvUOWe!Q!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hdrRSv!R!S!m#b$h%P%_Q!_YS!og%[Q!rhQ!ujQ!}sQ#PxQ#QyQ#RzQ#T{Q#V|Q#W}Q#Y!OQ#g![X#k!_#g#l$Rr]Oe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%h!YwRSYghjsvxyz{|}!O!R!S![!_!m#b#g#l$R$h%P%[%_Q!XWR#`!Q[uRSv!R!S!mQ$O#bV%O$h%P%_ToQpQ$Y#pR$u$aQ!qgR%e%[raOe!`!a!b!e!t#p#x#y#z$X$a$o%R%]%hQ!WWR#_!Q",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr 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: 117,
states: "9hQYQbOOO!dOpO'#DWO!iOSO'#D_O$_QcO'#DqO'hQcO'#E`OOQ`'#En'#EnO(kQRO'#DrO*mQcO'#E^O+WQbO'#DSOOQa'#Dt'#DtO-YQbO'#DuOOQa'#E`'#E`O-aQcO'#E`O.xQcO'#E_O/}QcO'#E^O0XQRO'#D{OOQ`'#E^'#E^O0mQbO'#E^O0tQQO'#E]OOQ`'#E]'#E]OOQ`'#D}'#D}QYQbOOO1PQbO'#DZO1[QbO'#DTO2PQbO'#DPO2tQQO'#DwO2PQbO'#DyO2yObO,59rO3UQbO'#DaO3^QWO'#DbOOOO'#Ef'#EfOOOO'#ES'#ESO3rOSO,59yOOQa,59y,59yOOQ`'#DY'#DYO4QQbO'#DmOOQ`'#Ed'#EdOOQ`'#EP'#EPO4[QbO,59qOOQa'#E_'#E_O2PQbO,5:^O2PQbO,5:^O2PQbO,5:^O2PQbO,5:^O2PQbO,59mO2PQbO,59mO2PQbO,59mO5UQRO,59nO5]QQO,59nO5eQQO,59nO5pQRO,59nO5wQRO,59nO6VQQO'#C}OOQ`'#EV'#EVO6[QbO,5:aO6cQQO,5:`OOQa,5:a,5:aO6nQbO,5:aO+WQbO,5:hO+WQbO,5:gO6xQbO,5:bO7PQbO,59iOOQ`,5:w,5:wO+WQbO'#EOOOQ`-E7{-E7{OOQ`'#EQ'#EQO7kQbO'#D[O7vQbO'#D]OOQO'#ER'#ERO7nQQO'#D[O8UQQO,59uO8ZQcO'#E_O8bQRO'#EbO9XQRO'#EbOOQO'#Eb'#EbO9`QQO,59oO9eQRO,59kO9lQRO,59kO6xQbO,5:cO9wQcO,5:eO;PQcO,5:eO;sQcO,5:eOOQa1G/^1G/^OOOO,59{,59{OOOO,59|,59|OOOO-E8Q-E8QOOQa1G/e1G/eOOQ`,5:X,5:XOOQ`-E7}-E7}OOQa1G/x1G/xO=UQcO1G/xO=`QcO1G/xO>nQcO1G/xO>xQcO1G/xO?VQcO1G/xOOQa1G/X1G/XO@eQcO1G/XO@lQcO1G/XO@sQcO1G/XOOQa1G/Y1G/YOOQ`-E8T-E8TO@zQQO1G/zOOQa1G/{1G/{OAVQbO1G/{OOQO'#EW'#EWO@zQQO1G/zOOQa1G/z1G/zOOQ`'#EX'#EXOAVQbO1G/{OAaQbO1G0SOA{QbO1G0ROBgQbO'#DhOBxQbO'#DhOC]QbO1G/|OOQ`-E7|-E7|OOQ`,5:j,5:jOOQ`-E8O-E8OOChQQO,59vOOQO,59w,59wOOQO-E8P-E8POCpQbO1G/aO6xQbO1G/ZO6xQbO1G/VOCwQbO1G/}ODSQQO7+%fOOQa7+%f7+%fOD_QbO7+%gOOQa7+%g7+%gOOQO-E8U-E8UOOQ`-E8V-E8VOOQ`'#ET'#ETODiQQO'#ETODqQbO'#EmOOQ`,5:S,5:SOEUQbO'#DfOEZQQO'#DiOOQ`7+%h7+%hOE`QbO7+%hOEeQbO7+%hOEmQbO7+${OE{QbO7+${OF]QbO7+$uOFeQbO7+$qOOQ`7+%i7+%iOFjQbO7+%iOFoQbO7+%iOOQa<<IQ<<IQOOQa<<IR<<IROOQ`,5:o,5:oOOQ`-E8R-E8ROFwQQO,5:QO6xQbO,5:TOOQ`<<IS<<ISOF|QbO<<ISOOQ`<<Hg<<HgOGRQbO<<HgOGWQbO<<HgOG`QbO<<HgOOQ`'#EU'#EUOGkQbO<<HaOGsQbO'#DpOOQ`<<Ha<<HaOG{QbO<<HaOOQ`<<H]<<H]OOQ`<<IT<<ITOHQQbO<<ITO6xQbO1G/lOOQ`1G/o1G/oOOQ`AN>nAN>nOOQ`AN>RAN>ROHVQbOAN>ROH[QbOAN>ROOQ`-E8S-E8SOOQ`AN={AN={OHdQbOAN={O1[QbO,5:YO6xQbO,5:[OOQ`AN>oAN>oOOQ`7+%W7+%WOOQ`G23mG23mOHiQbOG23mPHnQbO'#DnOOQ`G23gG23gOHsQQO1G/tOOQ`1G/v1G/vOOQ`LD)XLD)XO6xQbO7+%`OOQ`<<Hz<<Hz",
stateData: "H{~O#OOSoOS~OjROk_OlZOmPOnfOthOxgO{ZO!VZO!WZO!gZO!liO!njO#TWO#XQO#`cO#dXO#eYO~O#VkO~O!SnO#XqO#ZlO#[mO~OjwOlZOmPOnfO{ZO!QsO!VZO!WZO!`rO!gZO#TWO#XQO#dXO#eYOT#RXU#RXW#RXX#RXY#RXZ#RX[#RX]#RX~OP#RXQ#RXR#RXS#RX^#RXd#RXe#RXf#RXg#RXh#RXi#RXr!eX!X!eX#`!eX#g!eX#c!eX!Z!eX!^!eX!_!eX!c!eX~P!wOP#SXQ#SXR#SXS#SXT#SXU#SXW#SXX#SXY#SXZ#SX[#SX]#SX^#SXd#SXe#SXf#SXg#SXh#SXi#SXr#SX#`#SX#g#SX#c#SX!Z#SX!^#SX!_#SX!c#SX~OjwOlZOmPOnfO{ZO!QsO!VZO!WZO!`rO!gZO#TWO#XQO#dXO#eYO!X#SX~P%qOPyOQyORzOSzOT}OU!OOW|OX|OY|OZ|O[|O]|O^xOd{Oe{Of{Og{Oh{Oi{O~OPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{Or#QX~O#`#QX#g#QX!Z#QX!^#QX!_#QX#c#QX!c#QX~P)xOjROk_OlZOmPOnfOthOxgO{ZO!VZO!WZO!gZO!liO!njO#TWO#XQO#dXO#eYO~OjwOlZOmPO{ZO!QsO!VZO!WZO!gZO#TWO#XQO#`!VO#dXO#eYO~O#f!YO~P,_OV![O~P%qOP#RXQ#RXR#RXS#RXT#RXU#RXW#RXX#RXY#RXZ#RX[#RX]#RX^#RXd#RXe#RXf#RXg#RXh#RXi#RXr#QX~O#`#QX#g#QX!Z#QX!^#QX!_#QX#c#QX!c#QX~P-hOr#QX#`#QX#g#QX!Z#QX!^#QX!_#QX#c#QX!c#QX~OT}OU!OO~P/cOV![O_!]O`!]Oa!]Ob!]Oc!]O~O!X!^O~P/cOr!aO#`!`O#g!`O~Oj!cO!Q!eO!X!OP~Oj!iOlZOmPO{ZO!VZO!WZO!gZO#TWO#XQO#dXO#eYO~OjwOlZOmPO{ZO!VZO!WZO!gZO#TWO#XQO#dXO#eYO~O!X!pO~Oj!tO{!tO#TWO~Oj!uO#TWO~O#X!vO#Z!vO#[!vO#]!vO#^!vO#_!vO~O!SnO#X!xO#ZlO#[mO~OnfO!`!yO~P2POnfO!QsO!`rOrya!Xya#`ya#gya#cya!Zya!^ya!_ya!cya~P2PO#c#VO~P)xO#c#VOr#QX~O!X!^O#c#VOr#QX~O#c#VO~P-hOT}OU!OO#c#VOr#QX~Or!aO~O#f#YO~P,_O!QsO#`#[O#f#^O~O#`#_O#f#YO~P2PO#`#dO~P+WOr!aO#`qa#gqa#cqa!Zqa!^qa!_qa!cqa~Oj!cO!Q!eO!X!OX~O{#jO!V#jO!W#jO#XQO~O!X#lO~O!X#RX~P!wOT}OU!OO!X#UX~OT}OU!OOW|OX|OY|OZ|O[|O]|O~O!X#UX~P8mO!X#mO~O!X#nO~P8mOT}OU!OO!X#nO~Or!ma#`!ma#g!ma!Z!ma!^!ma!_!ma#c!ma!c!ma~P(kOr!ma#`!ma#g!ma!Z!ma!^!ma!_!ma#c!ma!c!ma~OPyOQyORzOSzOd{Oe{Of{Og{Oh{Oi{O~P:eOT}OU!OO~P:eO^xOR!fiS!fid!fie!fif!fig!fih!fii!fir!fi#`!fi#g!fi#c!fi!Z!fi!^!fi!_!fi!c!fi~OP!fiQ!fi~P;}OPyOQyO~P;}OPyOQyOd!fie!fif!fig!fih!fii!fir!fi#`!fi#g!fi#c!fi!Z!fi!^!fi!_!fi!c!fi~OR!fiS!fi~P=jORzOSzO^xO~P=jORzOSzO~P=jOW|OX|OY|OZ|O[|O]|OTuirui#`ui#gui#cui!Xui!Zui!^ui!_ui!cui~OU!OO~P?aOU!OO~P?sOUui~P?aO!QsO#`#[O#f#qO~O#`#_O#f#sO~P2POr!aO#`!pi#g!pi!Z!pi!^!pi!_!pi#c!pi!c!pi~Or!aO#`!oi#g!oi!Z!oi!^!oi!_!oi#c!oi!c!oi~Or!aO!Z![X!^![X!_![X!c![X~O#`#vO!Z#aP!^#aP!_#aP!c#aP~P+WO!Z#zO!^#{O!_#|O~O!Q!eO!X!Oa~O#`$QO~P+WO!Z#zO!^#{O!_$TO~O!QsO#`#[O#f$WO~O#`#_O#f$XO~P2POr!aO#`$YO~O#`#vO!Z#aX!^#aX!_#aX!c#aX~P+WOj$[O~O!X$]O~O!_$^O~O!^#{O!_$^O~Or!aO!Z#zO!^#{O!_$`O~O#`#vO!Z#aP!^#aP!_#aP~P+WO!_$gO!c$fO~O!_$iO~O!_$jO~O!^#{O!_$jO~O!X$lO~O!_$nO~O!_$oO~O!^#{O!_$oO~O!Z#zO!^#{O!_$oO~O!_$sO!c$fO~Ox$uO!X$vO~O!_$sO~O!_$wO~O!_$yO~O!^#{O!_$yO~O!_$|O~O!_%PO~Ox$uO~O!X%QO~O{!g~",
goto: "5O#cPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#dP#zP$`%W#dP&[&tP't'z(k(nP(tP)s)sPPP)wP*T*mPPP+T+gP+k+q,VP,y-u#z#zP#zP#z#z.s.y/V/_/e/o/u/|0S0Y0dPPP0n0r1f3QP4QP4WP4^PPPPPP4b4hr`Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QR!QWu`OWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%Qr^Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQ!TWS!jg$uQ!ohQ!sjQ#R!OR#T}xSOWeg![!]!^!a!p#d#l#m#n#x$Q$]$l$u$v%QxZRSYhjsvxyz{|}!O!W!Z!i#Z#`#rQ!tkR!ultTOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QT!lg$utROWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QxwRSYhjsvxyz{|}!O!W!Z!i#Z#`#rT!ig$uXtRSv!ir`Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QWrRSv!iQ!QWR!ysR!hfX!ff!d!g#i!rZORSWYeghjsvxyz{|}!O!W!Z![!]!^!a!i!p#Z#`#d#l#m#n#r#x$Q$]$l$u$v%QR#j!eTnQpQ$O#eQ$V#oQ$b$PR$q$cQ#e!^Q#o!pQ$R#mQ$S#nQ$m$]Q$x$lQ%O$vR%R%QQ#}#eQ$U#oQ$_$OQ$a$PQ$k$VS$p$b$cR$z$qWtRSv!iQ!XYQ#X!WX#[!X#X#]#pT$d$R$eQ$h$RR$t$euTOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QrVOe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQ!PWQ!rjQ!{yQ#OzR#Q{!sZORSWYeghjsvxyz{|}!O!W!Z![!]!^!a!i!p#Z#`#d#l#m#n#r#x$Q$]$l$u$v%Q|ZRSYghjsvxyz{|}!O!W!Z!i#Z#`#r$uu[OWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQeOR!be^!_b!U#a#b#c#w$PR#f!_UvRS!iR!zvQ!dfR#h!dQ!gfQ#i!dT#k!g#iQpQR!wpS#x#d$QR$Z#xQ$e$RR$r$eQ!WYR#W!WQ#]!XQ#p#XT#t#]#pQ#`!ZQ#r#ZT#u#`#rTdOeSbOeQ!UWQ#a![Q#b!]`#c!^!p#m#n$]$l$v%QQ#g!aU#w#d#x$QR$P#ltUOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QWrRSv!iQ!ZYS!kg$uQ!nhQ!qjQ!ysQ!{xQ!|yQ!}zQ#P{Q#R|Q#S}Q#U!OQ#Z!WX#_!Z#Z#`#rr]Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%Q|wRSYghjsvxyz{|}!O!W!Z!i#Z#`#r$uR!SWQ!mgR$}$uXuRSv!iToQpQ#y#dR$c$QraOe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QR!RW",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr 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: 115,
context: trackScope,
nodeProps: [
["closedBy", 53,"end"]
["closedBy", 55,"end"]
],
propSources: [highlighting],
skippedNodes: [0,31],
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$TU!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tU!QS#QYOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%_U!QS#WQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O^%xZoY!QSOY%qYZ$OZt%qtu&kuw%qwx&kx#O%q#O#P&k#P;'S%q;'S;=`'S<%lO%qY&pSoYOY&kZ;'S&k;'S;=`&|<%lO&kY'PP;=`<%l&k^'VP;=`<%l%q~'_O#]~~'dO#Z~U'kU!QS#VQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(UU!QS#eQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU(mX!QSOt$Ouw$Ox!Q$O!Q!R)Y!R![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU)a`!QSyQOt$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*hW!QSOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P;'S$O;'S;=`$g<%lO$OU+XY!QSyQOt$Ouw$Ox!Q$O!Q![+Q![#O$O#P#R$O#R#S*c#S;'S$O;'S;=`$g<%lO$OU,O[!QSyQOt$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,yW!QSOt$Ouw$Ox!Q$O!Q![+w![#O$O#P;'S$O;'S;=`$g<%lO$OU-hX!QSOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.[X!QSyQOt$Ouw$Ox!Q$O!Q!R.T!R!S.T!S#O$O#P;'S$O;'S;=`$g<%lO$OU.|[!QSOt$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[!QSyQOt$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$OU0tW!QSOt$Ouw$Ox!P$O!P!Q1^!Q#O$O#P;'S$O;'S;=`$g<%lO$OU1c^!QSOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q$O!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_U2f^!QS!hQOY2_YZ$OZt2_tu3buw2_wx3bx!P2_!P!Q6V!Q!}2_!}#O8T#O#P5p#P;'S2_;'S;=`9U<%lO2_Q3gX!hQOY3bZ!P3b!P!Q4S!Q!}3b!}#O4q#O#P5p#P;'S3b;'S;=`6P<%lO3bQ4VP!P!Q4YQ4_U!hQ#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[W!QSOt$Ouw$Ox!P$O!P!Q6t!Q#O$O#P;'S$O;'S;=`$g<%lO$OU6{b!QS!hQOt$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[!QSOY8TYZ$OZt8Ttu4quw8Twx4qx#O8T#O#P5Z#P#Q2_#Q;'S8T;'S;=`9O<%lO8TU9RP;=`<%l8TU9XP;=`<%l2_U9cU!QS!VQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU9|W#gQ!QSOt$Ouw$Ox!_$O!_!`:f!`#O$O#P;'S$O;'S;=`$g<%lO$OU:kV!QSOt$Ouw$Ox#O$O#P#Q;Q#Q;'S$O;'S;=`$g<%lO$OU;XU#fQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~;pO#^~U;wU#hQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<bU!QS!^QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU<y^!QSOt$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=|U!OQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU>e_!QSOt$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`!QSOt$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`!QSOt$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`!QSOt$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^!TQ!QSOt$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^#_W!QSOt$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^#aW!QSOt$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`#`W!QSOt$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`!QSOt$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$OUHkUrQ!QSOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~ISO#i~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#X~~", 11)],
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$QU!SSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qU!SS#OYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[U!SS#`QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%sW!SSOp#{pq&]qt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^&dZoY!SSOY&]YZ#{Zt&]tu'Vuw&]wx'Vx#O&]#O#P'V#P;'S&];'S;=`'n<%lO&]Y'[SoYOY'VZ;'S'V;'S;=`'h<%lO'VY'kP;=`<%l'V^'qP;=`<%l&]~'yO#Z~~(OO#X~U(VU!SS#TQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(pU!SS#cQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U)XW!SSOt#{uw#{x!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U)xY!SS{QOt#{uw#{x!O#{!O!P*h!P!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U*mW!SSOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+^W!SS{QOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+{^!SSOt#{uw#{x}#{}!O,w!O!Q#{!Q![)q![!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U,|[!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U-yU!QQ!SSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U.bW!SSOt#{uw#{x!P#{!P!Q.z!Q#O#{#P;'S#{;'S;=`$d<%lO#{U/P^!SSOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q#{!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{U0S^!SS!gQOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q3s!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{Q1TX!gQOY1OZ!P1O!P!Q1p!Q!}1O!}#O2_#O#P3^#P;'S1O;'S;=`3m<%lO1OQ1sP!P!Q1vQ1{U!gQ#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;=`<%l1OU3xW!SSOt#{uw#{x!P#{!P!Q4b!Q#O#{#P;'S#{;'S;=`$d<%lO#{U4ib!SS!gQOt#{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[!SSOY5qYZ#{Zt5qtu2_uw5qwx2_x#O5q#O#P2w#P#Q/{#Q;'S5q;'S;=`6l<%lO5qU6oP;=`<%l5qU6uP;=`<%l/{U7PU!SS!XQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7jW#eQ!SSOt#{uw#{x!_#{!_!`8S!`#O#{#P;'S#{;'S;=`$d<%lO#{U8XV!SSOt#{uw#{x#O#{#P#Q8n#Q;'S#{;'S;=`$d<%lO#{U8uU#dQ!SSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~9^O#[~U9eU#fQ!SSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:OU!SS!`QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:g]!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#U;`#U#o,w#o;'S#{;'S;=`$d<%lO#{U;e^!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#`,w#`#a<a#a#o,w#o;'S#{;'S;=`$d<%lO#{U<f^!SSOt#{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^!SSOt#{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[!VQ!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#]W!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#_W!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#^W!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^!SSOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeUrQ!SSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#g~",
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#V~~", 11)],
topRules: {"Program":[0,32]},
specialized: [{term: 26, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 26, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 2109
tokenPrec: 1834
})

View File

@ -368,118 +368,6 @@ 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', () => {
@ -751,7 +639,7 @@ describe('Comments', () => {
test('are greedy', () => {
expect(`
x = 5 # one banana
y = 2 #two bananas`).toMatchTree(`
y = 2 # two bananas`).toMatchTree(`
Assign
AssignableIdentifier x
Eq =
@ -761,7 +649,7 @@ y = 2 #two bananas`).toMatchTree(`
AssignableIdentifier y
Eq =
Number 2
Comment #two bananas`)
Comment # two bananas`)
expect(`
# some comment
@ -782,11 +670,11 @@ basename = 5 # very astute
})
test('words with # are not considered comments', () => {
expect('find my#hashtag-file.txt').toMatchTree(`
expect('find #hashtag-file.txt').toMatchTree(`
FunctionCall
Identifier find
PositionalArg
Word my#hashtag-file.txt`)
Word #hashtag-file.txt`)
})
test('hastags in strings are not comments', () => {

View File

@ -2,65 +2,6 @@ import { expect, describe, test } from 'bun:test'
import '../shrimp.grammar' // Importing this so changes cause it to retest!
describe('number literals', () => {
test('binary numbers', () => {
expect('0b110').toMatchTree(`
Number 0b110
`)
})
test('hex numbers', () => {
expect('0xdeadbeef').toMatchTree(`
Number 0xdeadbeef
`)
})
test('hex numbers uppercase', () => {
expect('0xFF').toMatchTree(`
Number 0xFF
`)
})
test('decimal numbers still work', () => {
expect('42').toMatchTree(`
Number 42
`)
})
test('negative binary', () => {
expect('-0b110').toMatchTree(`
Number -0b110
`)
})
test('negative hex', () => {
expect('-0xFF').toMatchTree(`
Number -0xFF
`)
})
test('positive prefix binary', () => {
expect('+0b110').toMatchTree(`
Number +0b110
`)
})
test('positive prefix hex', () => {
expect('+0xFF').toMatchTree(`
Number +0xFF
`)
})
test('hex and binary in arrays', () => {
expect('[0xFF 0b110 42]').toMatchTree(`
Array
Number 0xFF
Number 0b110
Number 42
`)
})
})
describe('array literals', () => {
test('work with numbers', () => {
expect('[1 2 3]').toMatchTree(`

View File

@ -6,7 +6,6 @@ import {
} from 'reefvm'
import { dict } from './dict'
import { json } from './json'
import { load } from './load'
import { list } from './list'
import { math } from './math'
@ -14,7 +13,6 @@ import { str } from './str'
export const globals = {
dict,
json,
load,
list,
math,
@ -42,11 +40,6 @@ 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),
exit: (num: number) => process.exit(num ?? 0),
// type predicates
'string?': (v: any) => toValue(v).type === 'string',

View File

@ -1,7 +0,0 @@
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

@ -14,13 +14,6 @@ export const list = {
}
return acc
},
reject: async (list: any[], cb: Function) => {
let acc: any[] = []
for (const value of list) {
if (!(await cb(value))) acc.push(value)
}
return acc
},
reduce: async (list: any[], cb: Function, initial: any) => {
let acc = initial
for (const value of list) acc = await cb(acc, value)
@ -73,13 +66,6 @@ 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(),
@ -150,4 +136,3 @@ export const list = {
; (list.pop as any).raw = true
; (list.shift as any).raw = true
; (list.unshift as any).raw = true
; (list.insert as any).raw = true

View File

@ -77,29 +77,3 @@ describe('introspection', () => {
await expect(`describe 'hello'`).toEvaluateTo("#<string: \u001b[32m'hello\u001b[32m'\u001b[0m>", globals)
})
})
describe('environment', () => {
test('args is an array', async () => {
await expect(`array? args`).toEvaluateTo(true, globals)
})
test('args can be accessed', async () => {
await expect(`type args`).toEvaluateTo('array', globals)
})
test('', async () => {
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

@ -1,84 +0,0 @@
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

@ -66,7 +66,6 @@ describe('string operations', () => {
test('slice extracts substring', async () => {
await expect(`str.slice 'hello' 1 3`).toEvaluateTo('el')
await expect(`str.slice 'hello' 2 null`).toEvaluateTo('llo')
await expect(`str.slice 'hello' 2`).toEvaluateTo('llo')
})
test('repeat repeats string', async () => {
@ -194,15 +193,6 @@ describe('collections', () => {
`).toEvaluateTo([3, 4, 5])
})
test('list.reject doesnt keep matching elements', async () => {
await expect(`
is-even = do x:
(x % 2) == 0
end
list.reject [1 2 3 4 5] is-even
`).toEvaluateTo([1, 3, 5])
})
test('list.reduce accumulates values', async () => {
await expect(`
add = do acc x:
@ -349,22 +339,6 @@ 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])
})