Compare commits
21 Commits
cf09f51380
...
99a5aa5312
| Author | SHA1 | Date | |
|---|---|---|---|
| 99a5aa5312 | |||
| 7bbf43a725 | |||
| 4c15526d1b | |||
| c741cfee51 | |||
| 012b8c8cf1 | |||
| 4c3f7a8bfc | |||
| fe6f54b402 | |||
| 49f3f3e09f | |||
| 0d1dce4868 | |||
| d18ab2507c | |||
| 7e69356f79 | |||
| 9863f46f38 | |||
| 45f31d0678 | |||
| 49a6320fef | |||
| 51f67ac908 | |||
| 7da437212d | |||
| 740379d7b2 | |||
| 19c4fb5033 | |||
| f57452ece2 | |||
| 4590d66105 | |||
| d4a772e88b |
4
bin/repl
4
bin/repl
|
|
@ -145,7 +145,7 @@ async function repl() {
|
|||
}
|
||||
|
||||
try {
|
||||
const compiler = new Compiler(trimmed, Object.keys(globals))
|
||||
const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()])
|
||||
|
||||
// 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)
|
||||
const compiler = new Compiler(trimmed, [...Object.keys(globals), ...vm.vars()])
|
||||
vm.appendBytecode(compiler.bytecode)
|
||||
await vm.continue()
|
||||
codeHistory.push(trimmed)
|
||||
|
|
|
|||
8
bun.lock
8
bun.lock
|
|
@ -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.1", "", { "dependencies": { "bun-types": "1.3.1" } }, "sha512-4jNMk2/K9YJtfqwoAa28c8wK+T7nvJFOjxI4h/7sORWcypRNxBpr+TPNaCfVWq70tLCJsqoFwcf0oI0JU/fvMQ=="],
|
||||
"@types/bun": ["@types/bun@1.3.2", "", { "dependencies": { "bun-types": "1.3.2" } }, "sha512-t15P7k5UIgHKkxwnMNkJbWlh/617rkDGEdSsDbu+qNHTaz9SKf7aC8fiIlUdD5RPpH6GEkP0cK7WlvmrEBRtWg=="],
|
||||
|
||||
"@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.1", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-NMrcy7smratanWJ2mMXdpatalovtxVggkj11bScuWuiOoXTiKIu2eVS1/7qbyI/4yHedtsn175n4Sm4JcdHLXw=="],
|
||||
"bun-types": ["bun-types@1.3.2", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-i/Gln4tbzKNuxP70OWhJRZz1MRfvqExowP7U6JKoI8cntFrtxg7RJK3jvz7wQW54UuvNC8tbKHHri5fy74FVqg=="],
|
||||
|
||||
"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,11 +62,11 @@
|
|||
|
||||
"hono": ["hono@4.10.4", "", {}, "sha512-YG/fo7zlU3KwrBL5vDpWKisLYiM+nVstBQqfr7gCPbSYURnNEP9BDxEMz8KfsDR9JX0lJWDRNc6nXX31v7ZEyg=="],
|
||||
|
||||
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#bffb83a5280a4d74e424c4e0f4fbd46f790227a3", { "peerDependencies": { "typescript": "^5" } }, "bffb83a5280a4d74e424c4e0f4fbd46f790227a3"],
|
||||
"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=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@4.1.16", "", {}, "sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA=="],
|
||||
"tailwindcss": ["tailwindcss@4.1.17", "", {}, "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
@ -31,4 +31,4 @@
|
|||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -51,6 +51,7 @@ function processEscapeSeq(escapeSeq: string): string {
|
|||
|
||||
export class Compiler {
|
||||
instructions: ProgramItem[] = []
|
||||
labelCount = 0
|
||||
fnLabelCount = 0
|
||||
ifLabelCount = 0
|
||||
tryLabelCount = 0
|
||||
|
|
@ -105,11 +106,21 @@ export class Compiler {
|
|||
|
||||
switch (node.type.id) {
|
||||
case terms.Number:
|
||||
const number = Number(value)
|
||||
if (Number.isNaN(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))
|
||||
throw new CompilerError(`Invalid number literal: ${value}`, node.from, node.to)
|
||||
|
||||
return [[`PUSH`, number]]
|
||||
return [[`PUSH`, numberValue]]
|
||||
|
||||
case terms.String: {
|
||||
const { parts, hasInterpolation } = getStringParts(node, input)
|
||||
|
|
@ -232,6 +243,24 @@ export class Compiler {
|
|||
case '%':
|
||||
instructions.push(['MOD'])
|
||||
break
|
||||
case 'band':
|
||||
instructions.push(['BIT_AND'])
|
||||
break
|
||||
case 'bor':
|
||||
instructions.push(['BIT_OR'])
|
||||
break
|
||||
case 'bxor':
|
||||
instructions.push(['BIT_XOR'])
|
||||
break
|
||||
case '<<':
|
||||
instructions.push(['BIT_SHL'])
|
||||
break
|
||||
case '>>':
|
||||
instructions.push(['BIT_SHR'])
|
||||
break
|
||||
case '>>>':
|
||||
instructions.push(['BIT_USHR'])
|
||||
break
|
||||
default:
|
||||
throw new CompilerError(`Unsupported binary operator: ${opValue}`, op.from, op.to)
|
||||
}
|
||||
|
|
@ -367,7 +396,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]]
|
||||
|
|
|
|||
178
src/compiler/tests/bitwise.test.ts
Normal file
178
src/compiler/tests/bitwise.test.ts
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { expect, describe, test } from 'bun:test'
|
||||
|
||||
describe('bitwise operators', () => {
|
||||
describe('band (bitwise AND)', () => {
|
||||
test('basic AND operation', () => {
|
||||
expect('5 band 3').toEvaluateTo(1)
|
||||
// 5 = 0101, 3 = 0011, result = 0001 = 1
|
||||
})
|
||||
|
||||
test('AND with zero', () => {
|
||||
expect('5 band 0').toEvaluateTo(0)
|
||||
})
|
||||
|
||||
test('AND with all bits set', () => {
|
||||
expect('15 band 7').toEvaluateTo(7)
|
||||
// 15 = 1111, 7 = 0111, result = 0111 = 7
|
||||
})
|
||||
|
||||
test('AND in assignment', () => {
|
||||
expect('x = 12 band 10').toEvaluateTo(8)
|
||||
// 12 = 1100, 10 = 1010, result = 1000 = 8
|
||||
})
|
||||
})
|
||||
|
||||
describe('bor (bitwise OR)', () => {
|
||||
test('basic OR operation', () => {
|
||||
expect('5 bor 3').toEvaluateTo(7)
|
||||
// 5 = 0101, 3 = 0011, result = 0111 = 7
|
||||
})
|
||||
|
||||
test('OR with zero', () => {
|
||||
expect('5 bor 0').toEvaluateTo(5)
|
||||
})
|
||||
|
||||
test('OR with all bits set', () => {
|
||||
expect('8 bor 4').toEvaluateTo(12)
|
||||
// 8 = 1000, 4 = 0100, result = 1100 = 12
|
||||
})
|
||||
})
|
||||
|
||||
describe('bxor (bitwise XOR)', () => {
|
||||
test('basic XOR operation', () => {
|
||||
expect('5 bxor 3').toEvaluateTo(6)
|
||||
// 5 = 0101, 3 = 0011, result = 0110 = 6
|
||||
})
|
||||
|
||||
test('XOR with itself returns zero', () => {
|
||||
expect('5 bxor 5').toEvaluateTo(0)
|
||||
})
|
||||
|
||||
test('XOR with zero returns same value', () => {
|
||||
expect('7 bxor 0').toEvaluateTo(7)
|
||||
})
|
||||
|
||||
test('XOR in assignment', () => {
|
||||
expect('result = 8 bxor 12').toEvaluateTo(4)
|
||||
// 8 = 1000, 12 = 1100, result = 0100 = 4
|
||||
})
|
||||
})
|
||||
|
||||
describe('bnot (bitwise NOT)', () => {
|
||||
test('NOT of positive number', () => {
|
||||
expect('bnot 5').toEvaluateTo(-6)
|
||||
// ~5 = -6 (two\'s complement)
|
||||
})
|
||||
|
||||
test('NOT of zero', () => {
|
||||
expect('bnot 0').toEvaluateTo(-1)
|
||||
})
|
||||
|
||||
test('NOT of negative number', () => {
|
||||
expect('bnot -1').toEvaluateTo(0)
|
||||
})
|
||||
|
||||
test('double NOT returns original', () => {
|
||||
expect('bnot (bnot 5)').toEvaluateTo(5)
|
||||
})
|
||||
})
|
||||
|
||||
describe('<< (left shift)', () => {
|
||||
test('basic left shift', () => {
|
||||
expect('5 << 2').toEvaluateTo(20)
|
||||
// 5 << 2 = 20
|
||||
})
|
||||
|
||||
test('shift by zero', () => {
|
||||
expect('5 << 0').toEvaluateTo(5)
|
||||
})
|
||||
|
||||
test('shift by one', () => {
|
||||
expect('3 << 1').toEvaluateTo(6)
|
||||
})
|
||||
|
||||
test('large shift', () => {
|
||||
expect('1 << 10').toEvaluateTo(1024)
|
||||
})
|
||||
})
|
||||
|
||||
describe('>> (signed right shift)', () => {
|
||||
test('basic right shift', () => {
|
||||
expect('20 >> 2').toEvaluateTo(5)
|
||||
// 20 >> 2 = 5
|
||||
})
|
||||
|
||||
test('shift by zero', () => {
|
||||
expect('20 >> 0').toEvaluateTo(20)
|
||||
})
|
||||
|
||||
test('preserves sign for negative numbers', () => {
|
||||
expect('-20 >> 2').toEvaluateTo(-5)
|
||||
// Sign is preserved
|
||||
})
|
||||
|
||||
test('negative number right shift', () => {
|
||||
expect('-8 >> 1').toEvaluateTo(-4)
|
||||
})
|
||||
})
|
||||
|
||||
describe('>>> (unsigned right shift)', () => {
|
||||
test('basic unsigned right shift', () => {
|
||||
expect('20 >>> 2').toEvaluateTo(5)
|
||||
})
|
||||
|
||||
test('unsigned shift of -1', () => {
|
||||
expect('-1 >>> 1').toEvaluateTo(2147483647)
|
||||
// -1 >>> 1 = 2147483647 (unsigned, no sign extension)
|
||||
})
|
||||
|
||||
test('unsigned shift of negative number', () => {
|
||||
expect('-8 >>> 1').toEvaluateTo(2147483644)
|
||||
})
|
||||
})
|
||||
|
||||
describe('compound expressions', () => {
|
||||
test('multiple bitwise operations', () => {
|
||||
expect('(5 band 3) bor (8 bxor 12)').toEvaluateTo(5)
|
||||
// (5 & 3) | (8 ^ 12) = 1 | 4 = 5
|
||||
})
|
||||
|
||||
test('bitwise with variables', () => {
|
||||
expect(`
|
||||
a = 5
|
||||
b = 3
|
||||
a bor b
|
||||
`).toEvaluateTo(7)
|
||||
})
|
||||
|
||||
test('shift operations with variables', () => {
|
||||
expect(`
|
||||
x = 16
|
||||
y = 2
|
||||
x >> y
|
||||
`).toEvaluateTo(4)
|
||||
})
|
||||
|
||||
test('mixing shifts and bitwise', () => {
|
||||
expect('(8 << 1) band 15').toEvaluateTo(0)
|
||||
// (8 << 1) & 15 = 16 & 15 = 0
|
||||
})
|
||||
|
||||
test('mixing shifts and bitwise 2', () => {
|
||||
expect('(7 << 1) band 15').toEvaluateTo(14)
|
||||
// (7 << 1) & 15 = 14 & 15 = 14
|
||||
})
|
||||
})
|
||||
|
||||
describe('precedence', () => {
|
||||
test('bitwise has correct precedence with arithmetic', () => {
|
||||
expect('1 + 2 band 3').toEvaluateTo(3)
|
||||
// (1 + 2) & 3 = 3 & 3 = 3
|
||||
})
|
||||
|
||||
test('shift has correct precedence', () => {
|
||||
expect('4 + 8 << 1').toEvaluateTo(24)
|
||||
// (4 + 8) << 1 = 12 << 1 = 24
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -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'])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,44 @@
|
|||
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])
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ type Operator = { str: string; tokenName: keyof typeof terms }
|
|||
const operators: Array<Operator> = [
|
||||
{ str: 'and', tokenName: 'And' },
|
||||
{ str: 'or', tokenName: 'Or' },
|
||||
{ str: 'band', tokenName: 'Band' },
|
||||
{ str: 'bor', tokenName: 'Bor' },
|
||||
{ str: 'bxor', tokenName: 'Bxor' },
|
||||
{ str: '>>>', tokenName: 'Ushr' }, // Must come before >>
|
||||
{ str: '>>', tokenName: 'Shr' },
|
||||
{ str: '<<', tokenName: 'Shl' },
|
||||
{ str: '>=', tokenName: 'Gte' },
|
||||
{ str: '<=', tokenName: 'Lte' },
|
||||
{ str: '!=', tokenName: 'Neq' },
|
||||
|
|
|
|||
|
|
@ -6,19 +6,23 @@
|
|||
|
||||
@top Program { item* }
|
||||
|
||||
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq }
|
||||
@external tokens operatorTokenizer from "./operatorTokenizer" { 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 }
|
||||
|
||||
@tokens {
|
||||
@precedence { Number Regex }
|
||||
|
||||
StringFragment { !['\\$]+ }
|
||||
NamedArgPrefix { $[a-z-]+ "=" }
|
||||
Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? }
|
||||
NamedArgPrefix { $[a-z] $[a-z0-9-]* "=" }
|
||||
Number {
|
||||
("-" | "+")? "0x" $[0-9a-fA-F]+ |
|
||||
("-" | "+")? "0b" $[01]+ |
|
||||
("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)?
|
||||
}
|
||||
Boolean { "true" | "false" }
|
||||
newlineOrSemicolon { "\n" | ";" }
|
||||
eof { @eof }
|
||||
space { " " | "\t" }
|
||||
Comment { "#" " " ![\n]* }
|
||||
Comment { "#" ![\n]* }
|
||||
leftParen { "(" }
|
||||
rightParen { ")" }
|
||||
colon[closedBy="end", @name="colon"] { ":" }
|
||||
|
|
@ -47,7 +51,9 @@ null { @specialize[@name=Null]<Identifier, "null"> }
|
|||
comparison @left,
|
||||
multiplicative @left,
|
||||
additive @left,
|
||||
call
|
||||
bitwise @left,
|
||||
call,
|
||||
functionWithNewlines
|
||||
}
|
||||
|
||||
item {
|
||||
|
|
@ -184,11 +190,31 @@ BinOp {
|
|||
(expression | BinOp) !multiplicative Star (expression | BinOp) |
|
||||
(expression | BinOp) !multiplicative Slash (expression | BinOp) |
|
||||
(expression | BinOp) !additive Plus (expression | BinOp) |
|
||||
(expression | BinOp) !additive Minus (expression | BinOp)
|
||||
(expression | BinOp) !additive Minus (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Band (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Bor (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Bxor (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Shl (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Shr (expression | BinOp) |
|
||||
(expression | BinOp) !bitwise Ushr (expression | 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 {
|
||||
|
|
|
|||
|
|
@ -19,48 +19,55 @@ export const
|
|||
StarEq = 17,
|
||||
SlashEq = 18,
|
||||
ModuloEq = 19,
|
||||
Identifier = 20,
|
||||
AssignableIdentifier = 21,
|
||||
Word = 22,
|
||||
IdentifierBeforeDot = 23,
|
||||
Do = 24,
|
||||
Comment = 25,
|
||||
Program = 26,
|
||||
PipeExpr = 27,
|
||||
WhileExpr = 29,
|
||||
keyword = 70,
|
||||
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
|
||||
Band = 20,
|
||||
Bor = 21,
|
||||
Bxor = 22,
|
||||
Shl = 23,
|
||||
Shr = 24,
|
||||
Ushr = 25,
|
||||
Identifier = 26,
|
||||
AssignableIdentifier = 27,
|
||||
Word = 28,
|
||||
IdentifierBeforeDot = 29,
|
||||
Do = 30,
|
||||
Comment = 31,
|
||||
Program = 32,
|
||||
PipeExpr = 33,
|
||||
WhileExpr = 35,
|
||||
keyword = 77,
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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:72, null:104, catch:110, finally:116, end:118, if:126, else:132, try:150, throw:154}
|
||||
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: "<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,
|
||||
context: trackScope,
|
||||
nodeProps: [
|
||||
["closedBy", 49,"end"]
|
||||
["closedBy", 53,"end"]
|
||||
],
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0,25],
|
||||
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|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qU|S!xYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[U|S#YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%sW|SOp#{pq&]qt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^&dZiY|SOY&]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(VU|S!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(pU|S#]QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U)XW|SOt#{uw#{x!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U)xY|SuQOt#{uw#{x!O#{!O!P*h!P!Q#{!Q![)q![#O#{#P;'S#{;'S;=`$d<%lO#{U*mW|SOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+^W|SuQOt#{uw#{x!Q#{!Q![+V![#O#{#P;'S#{;'S;=`$d<%lO#{U+{^|SOt#{uw#{x}#{}!O,w!O!Q#{!Q![)q![!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U,|[|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{U-yUzQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U.bW|SOt#{uw#{x!P#{!P!Q.z!Q#O#{#P;'S#{;'S;=`$d<%lO#{U/P^|SOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q#{!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{U0S^|S!aQOY/{YZ#{Zt/{tu1Ouw/{wx1Ox!P/{!P!Q3s!Q!}/{!}#O5q#O#P3^#P;'S/{;'S;=`6r<%lO/{Q1TX!aQOY1OZ!P1O!P!Q1p!Q!}1O!}#O2_#O#P3^#P;'S1O;'S;=`3m<%lO1OQ1sP!P!Q1vQ1{U!aQ#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|SOt#{uw#{x!P#{!P!Q4b!Q#O#{#P;'S#{;'S;=`$d<%lO#{U4ib|S!aQOt#{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[|SOY5qYZ#{Zt5qtu2_uw5qwx2_x#O5q#O#P2w#P#Q/{#Q;'S5q;'S;=`6l<%lO5qU6oP;=`<%l5qU6uP;=`<%l/{U7PU|S!RQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U7jW#_Q|SOt#{uw#{x!_#{!_!`8S!`#O#{#P;'S#{;'S;=`$d<%lO#{U8XV|SOt#{uw#{x#O#{#P#Q8n#Q;'S#{;'S;=`$d<%lO#{U8uU#^Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~9^O#U~U9eU#`Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:OU|S!YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U:g]|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#U;`#U#o,w#o;'S#{;'S;=`$d<%lO#{U;e^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#`,w#`#a<a#a#o,w#o;'S#{;'S;=`$d<%lO#{U<f^|SOt#{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^|SOt#{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[!PQ|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^?g[#VW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^@d[#XW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#o,w#o;'S#{;'S;=`$d<%lO#{^Aa^#WW|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#f,w#f#gB]#g#o,w#o;'S#{;'S;=`$d<%lO#{UBb^|SOt#{uw#{x}#{}!O,w!O!_#{!_!`-r!`#O#{#P#T#{#T#i,w#i#j=b#j#o,w#o;'S#{;'S;=`$d<%lO#{UCeUlQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C|O#a~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#P~~", 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
|
||||
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)],
|
||||
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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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', () => {
|
||||
|
|
@ -639,7 +751,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 =
|
||||
|
|
@ -649,7 +761,7 @@ y = 2 # two bananas`).toMatchTree(`
|
|||
AssignableIdentifier y
|
||||
Eq =
|
||||
Number 2
|
||||
Comment # two bananas`)
|
||||
Comment #two bananas`)
|
||||
|
||||
expect(`
|
||||
# some comment
|
||||
|
|
@ -670,11 +782,11 @@ basename = 5 # very astute
|
|||
})
|
||||
|
||||
test('words with # are not considered comments', () => {
|
||||
expect('find #hashtag-file.txt').toMatchTree(`
|
||||
expect('find my#hashtag-file.txt').toMatchTree(`
|
||||
FunctionCall
|
||||
Identifier find
|
||||
PositionalArg
|
||||
Word #hashtag-file.txt`)
|
||||
Word my#hashtag-file.txt`)
|
||||
})
|
||||
|
||||
test('hastags in strings are not comments', () => {
|
||||
|
|
|
|||
72
src/parser/tests/bitwise.test.ts
Normal file
72
src/parser/tests/bitwise.test.ts
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
import { expect, describe, test } from 'bun:test'
|
||||
|
||||
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||
|
||||
describe('bitwise operators - grammar', () => {
|
||||
test('parses band (bitwise AND)', () => {
|
||||
expect('5 band 3').toMatchTree(`
|
||||
BinOp
|
||||
Number 5
|
||||
Band band
|
||||
Number 3`)
|
||||
})
|
||||
|
||||
test('parses bor (bitwise OR)', () => {
|
||||
expect('5 bor 3').toMatchTree(`
|
||||
BinOp
|
||||
Number 5
|
||||
Bor bor
|
||||
Number 3`)
|
||||
})
|
||||
|
||||
test('parses bxor (bitwise XOR)', () => {
|
||||
expect('5 bxor 3').toMatchTree(`
|
||||
BinOp
|
||||
Number 5
|
||||
Bxor bxor
|
||||
Number 3`)
|
||||
})
|
||||
|
||||
test('parses << (left shift)', () => {
|
||||
expect('5 << 2').toMatchTree(`
|
||||
BinOp
|
||||
Number 5
|
||||
Shl <<
|
||||
Number 2`)
|
||||
})
|
||||
|
||||
test('parses >> (signed right shift)', () => {
|
||||
expect('20 >> 2').toMatchTree(`
|
||||
BinOp
|
||||
Number 20
|
||||
Shr >>
|
||||
Number 2`)
|
||||
})
|
||||
|
||||
test('parses >>> (unsigned right shift)', () => {
|
||||
expect('-1 >>> 1').toMatchTree(`
|
||||
BinOp
|
||||
Number -1
|
||||
Ushr >>>
|
||||
Number 1`)
|
||||
})
|
||||
|
||||
test('parses bnot (bitwise NOT) as function call', () => {
|
||||
expect('bnot 5').toMatchTree(`
|
||||
FunctionCall
|
||||
Identifier bnot
|
||||
PositionalArg
|
||||
Number 5`)
|
||||
})
|
||||
|
||||
test('bitwise operators work in expressions', () => {
|
||||
expect('x = 5 band 3').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier x
|
||||
Eq =
|
||||
BinOp
|
||||
Number 5
|
||||
Band band
|
||||
Number 3`)
|
||||
})
|
||||
})
|
||||
|
|
@ -2,6 +2,65 @@ 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(`
|
||||
|
|
|
|||
|
|
@ -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,11 @@ 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',
|
||||
|
|
@ -56,6 +63,7 @@ export const globals = {
|
|||
|
||||
// boolean/logic
|
||||
not: (v: any) => !v,
|
||||
bnot: (n: number) => ~(n | 0),
|
||||
|
||||
// utilities
|
||||
inc: (n: number) => n + 1,
|
||||
|
|
|
|||
7
src/prelude/json.ts
Normal file
7
src/prelude/json.ts
Normal 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
|
||||
|
|
@ -14,6 +14,13 @@ 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)
|
||||
|
|
@ -66,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(),
|
||||
|
|
@ -135,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
|
||||
|
|
@ -77,3 +77,29 @@ 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')
|
||||
})
|
||||
|
|
|
|||
84
src/prelude/tests/json.test.ts
Normal file
84
src/prelude/tests/json.test.ts
Normal 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()
|
||||
})
|
||||
})
|
||||
|
|
@ -66,6 +66,7 @@ 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 () => {
|
||||
|
|
@ -193,6 +194,15 @@ 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:
|
||||
|
|
@ -339,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])
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user