Compare commits
11 Commits
fd4594da03
...
57b582a0c2
| Author | SHA1 | Date | |
|---|---|---|---|
| 57b582a0c2 | |||
| 32c47cfd24 | |||
| a161679903 | |||
| da77bf1686 | |||
| 458371b802 | |||
|
|
402748d1da | ||
|
|
cc06bdf2a7 | ||
| fec4b626df | |||
| fa034d4bd4 | |||
| 8addb77e90 | |||
| 1791e5a6c7 |
|
|
@ -12,7 +12,7 @@ Go to http://localhost:3000 to try out the playground.
|
||||||
tail log.txt lines=50
|
tail log.txt lines=50
|
||||||
|
|
||||||
name = "Shrimp"
|
name = "Shrimp"
|
||||||
greet = fn person: echo "Hello" person
|
greet = do person: echo "Hello" person
|
||||||
|
|
||||||
result = tail log.txt lines=10
|
result = tail log.txt lines=10
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ Go to http://localhost:3000 to try out the playground.
|
||||||
## Architecture
|
## Architecture
|
||||||
|
|
||||||
**parser/** - Lezer grammar and tokenizers that parse Shrimp code into syntax trees
|
**parser/** - Lezer grammar and tokenizers that parse Shrimp code into syntax trees
|
||||||
**editor/** - CodeMirror integration with syntax highlighting and language support
|
**editor/** - CodeMirror integration with syntax highlighting and language support
|
||||||
**compiler/** - Transforms syntax trees into ReefVM bytecode for execution
|
**compiler/** - Transforms syntax trees into ReefVM bytecode for execution
|
||||||
|
|
||||||
The flow: Shrimp source → parser (CST) → compiler (bytecode) → ReefVM (execution)
|
The flow: Shrimp source → parser (CST) → compiler (bytecode) → ReefVM (execution)
|
||||||
|
|
|
||||||
26
bin/shrimp
26
bin/shrimp
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
import { Compiler } from '../src/compiler/compiler'
|
import { Compiler } from '../src/compiler/compiler'
|
||||||
import { colors, globals } from '../src/prelude'
|
import { colors, globals } from '../src/prelude'
|
||||||
|
import { parser } from '../src/parser/shrimp'
|
||||||
|
import { treeToString } from '../src/utils/tree'
|
||||||
import { VM, fromValue, bytecodeToString } from 'reefvm'
|
import { VM, fromValue, bytecodeToString } from 'reefvm'
|
||||||
import { readFileSync, writeFileSync, mkdirSync } from 'fs'
|
import { readFileSync, writeFileSync, mkdirSync } from 'fs'
|
||||||
import { randomUUID } from "crypto"
|
import { randomUUID } from 'crypto'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
|
|
||||||
|
|
@ -32,6 +34,17 @@ async function compileFile(filePath: string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function parseFile(filePath: string) {
|
||||||
|
try {
|
||||||
|
const code = readFileSync(filePath, 'utf-8')
|
||||||
|
const tree = parser.parse(code)
|
||||||
|
return treeToString(tree, code)
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`${colors.red}Error:${colors.reset} ${error.message}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showHelp() {
|
function showHelp() {
|
||||||
console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell.
|
console.log(`${colors.bright}${colors.magenta}🦐 Shrimp${colors.reset} is a scripting language in a shell.
|
||||||
|
|
||||||
|
|
@ -39,6 +52,7 @@ ${colors.bright}Usage:${colors.reset} shrimp <command> [...args]
|
||||||
|
|
||||||
${colors.bright}Commands:${colors.reset}
|
${colors.bright}Commands:${colors.reset}
|
||||||
${colors.cyan}run ${colors.yellow}./my-file.sh${colors.reset} Execute a file with Shrimp
|
${colors.cyan}run ${colors.yellow}./my-file.sh${colors.reset} Execute a file with Shrimp
|
||||||
|
${colors.cyan}parse ${colors.yellow}./my-file.sh${colors.reset} Print parse tree for Shrimp file
|
||||||
${colors.cyan}bytecode ${colors.yellow}./my-file.sh${colors.reset} Print bytecode for Shrimp file
|
${colors.cyan}bytecode ${colors.yellow}./my-file.sh${colors.reset} Print bytecode for Shrimp file
|
||||||
${colors.cyan}eval ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code
|
${colors.cyan}eval ${colors.yellow}'some code'${colors.reset} Evaluate a line of Shrimp code
|
||||||
${colors.cyan}repl${colors.reset} Start REPL
|
${colors.cyan}repl${colors.reset} Start REPL
|
||||||
|
|
@ -102,6 +116,16 @@ async function main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (['parse', '-parse', '--parse', '-p'].includes(command)) {
|
||||||
|
const file = args[1]
|
||||||
|
if (!file) {
|
||||||
|
console.log(`${colors.bright}usage: shrimp parse <file>${colors.reset}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log(await parseFile(file))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (['run', '-run', '--run', '-r'].includes(command)) {
|
if (['run', '-run', '--run', '-r'].includes(command)) {
|
||||||
const file = args[1]
|
const file = args[1]
|
||||||
if (!file) {
|
if (!file) {
|
||||||
|
|
|
||||||
2
bun.lock
2
bun.lock
|
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
"hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="],
|
"hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="],
|
||||||
|
|
||||||
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#c69b172c78853756ec8acba5bc33d93eb6a571c6", { "peerDependencies": { "typescript": "^5" } }, "c69b172c78853756ec8acba5bc33d93eb6a571c6"],
|
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#0f39e9401eb7a0a7c906e150127f9829458a79b6", { "peerDependencies": { "typescript": "^5" } }, "0f39e9401eb7a0a7c906e150127f9829458a79b6"],
|
||||||
|
|
||||||
"style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="],
|
"style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="],
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "bun-react-template",
|
"name": "shrimp",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"exports": "./src/index.ts",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -405,6 +405,51 @@ export class Compiler {
|
||||||
return instructions
|
return instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case terms.FunctionCallWithBlock: {
|
||||||
|
const [fn, _colon, ...block] = getAllChildren(node)
|
||||||
|
let instructions: ProgramItem[] = []
|
||||||
|
|
||||||
|
const fnLabel: Label = `.func_${this.fnLabelCount++}`
|
||||||
|
const afterLabel: Label = `.after_${fnLabel}`
|
||||||
|
|
||||||
|
instructions.push(['JUMP', afterLabel])
|
||||||
|
instructions.push([`${fnLabel}:`])
|
||||||
|
instructions.push(
|
||||||
|
...block.filter(x => x.type.name !== 'keyword')
|
||||||
|
.map(x => this.#compileNode(x!, input))
|
||||||
|
.flat()
|
||||||
|
)
|
||||||
|
instructions.push(['RETURN'])
|
||||||
|
instructions.push([`${afterLabel}:`])
|
||||||
|
|
||||||
|
if (fn?.type.id === terms.FunctionCallOrIdentifier) {
|
||||||
|
instructions.push(['LOAD', input.slice(fn!.from, fn!.to)])
|
||||||
|
instructions.push(['MAKE_FUNCTION', [], fnLabel])
|
||||||
|
instructions.push(['PUSH', 1])
|
||||||
|
instructions.push(['PUSH', 0])
|
||||||
|
instructions.push(['CALL'])
|
||||||
|
} else if (fn?.type.id === terms.FunctionCall) {
|
||||||
|
let body = this.#compileNode(fn!, input)
|
||||||
|
const namedArgCount = (body[body.length - 2]![1] as number) * 2
|
||||||
|
const startSlice = body.length - namedArgCount - 3
|
||||||
|
|
||||||
|
body = [
|
||||||
|
...body.slice(0, startSlice),
|
||||||
|
['MAKE_FUNCTION', [], fnLabel],
|
||||||
|
...body.slice(startSlice)
|
||||||
|
]
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
body[body.length - 3]![1] += 1
|
||||||
|
instructions.push(...body)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`FunctionCallWithBlock: Expected FunctionCallOrIdentifier or FunctionCall`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instructions
|
||||||
|
}
|
||||||
|
|
||||||
case terms.TryExpr: {
|
case terms.TryExpr: {
|
||||||
const { tryBlock, catchVariable, catchBody, finallyBody } = getTryExprParts(node, input)
|
const { tryBlock, catchVariable, catchBody, finallyBody } = getTryExprParts(node, input)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -282,3 +282,31 @@ describe('dot get', () => {
|
||||||
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('default params', () => {
|
||||||
|
test('function with single default parameter', () => {
|
||||||
|
expect('add1 = do x=1: x + 1 end; add1').toEvaluateTo(2)
|
||||||
|
expect('add1 = do x=1: x + 1 end; add1 5').toEvaluateTo(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('function with multiple default parameters', () => {
|
||||||
|
expect(`weird = do x='something' y=true: [x y] end; weird`).toEvaluateTo(['something', true])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('function with mixed parameters', () => {
|
||||||
|
expect('multiply = do x y=5: x * y end; multiply 5').toEvaluateTo(25)
|
||||||
|
expect('multiply = do x y=5: x * y end; multiply 5 2').toEvaluateTo(10)
|
||||||
|
})
|
||||||
|
|
||||||
|
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'])
|
||||||
|
})
|
||||||
|
|
||||||
|
test.skip('dict default', () => {
|
||||||
|
expect('make-person = do person=[name=Bob age=60]: person end; make-person')
|
||||||
|
.toEvaluateTo({ name: 'Bob', age: 60 })
|
||||||
|
expect('make-person = do person=[name=Bob age=60]: person end; make-person [name=Jon age=21]')
|
||||||
|
.toEvaluateTo({ name: 'Jon', age: 21 })
|
||||||
|
})
|
||||||
|
})
|
||||||
55
src/compiler/tests/function-blocks.test.ts
Normal file
55
src/compiler/tests/function-blocks.test.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { expect, describe, test } from 'bun:test'
|
||||||
|
|
||||||
|
describe('single line function blocks', () => {
|
||||||
|
test('work with no args', () => {
|
||||||
|
expect(`trap = do x: x end; trap: true end`).toEvaluateTo(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with one arg', () => {
|
||||||
|
expect(`trap = do x y: [ x (y) ] end; trap EXIT: true end`).toEvaluateTo(['EXIT', true])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with named args', () => {
|
||||||
|
expect(`attach = do signal fn: [ signal (fn) ] end; attach signal='exit': true end`).toEvaluateTo(['exit', true])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('work with dot-get', () => {
|
||||||
|
expect(`signals = [trap=do x y: [x (y)] end]; signals.trap 'EXIT': true end`).toEvaluateTo(['EXIT', true])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('multi line function blocks', () => {
|
||||||
|
test('work with no args', () => {
|
||||||
|
expect(`
|
||||||
|
trap = do x: x end
|
||||||
|
trap:
|
||||||
|
true
|
||||||
|
end`).toEvaluateTo(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with one arg', () => {
|
||||||
|
expect(`
|
||||||
|
trap = do x y: [ x (y) ] end
|
||||||
|
trap EXIT:
|
||||||
|
true
|
||||||
|
end`).toEvaluateTo(['EXIT', true])
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with named args', () => {
|
||||||
|
expect(`
|
||||||
|
attach = do signal fn: [ signal (fn) ] end
|
||||||
|
attach signal='exit':
|
||||||
|
true
|
||||||
|
end`).toEvaluateTo(['exit', true])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('work with dot-get', () => {
|
||||||
|
expect(`
|
||||||
|
signals = [trap=do x y: [x (y)] end]
|
||||||
|
signals.trap 'EXIT':
|
||||||
|
true
|
||||||
|
end`).toEvaluateTo(['EXIT', true])
|
||||||
|
})
|
||||||
|
})
|
||||||
112
src/compiler/tests/ribbit.test.ts
Normal file
112
src/compiler/tests/ribbit.test.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { expect, describe, test, beforeEach } from 'bun:test'
|
||||||
|
|
||||||
|
const buffer: string[] = []
|
||||||
|
|
||||||
|
const ribbitGlobals = {
|
||||||
|
ribbit: async (cb: Function) => {
|
||||||
|
await cb()
|
||||||
|
return buffer.join("\n")
|
||||||
|
},
|
||||||
|
tag: async (tagFn: Function, atDefaults = {}) => {
|
||||||
|
return (atNamed = {}, ...args: any[]) => tagFn(Object.assign({}, atDefaults, atNamed), ...args)
|
||||||
|
},
|
||||||
|
head: (atNamed: {}, ...args: any[]) => tag('head', atNamed, ...args),
|
||||||
|
title: (atNamed: {}, ...args: any[]) => tag('title', atNamed, ...args),
|
||||||
|
meta: (atNamed: {}, ...args: any[]) => tag('meta', atNamed, ...args),
|
||||||
|
p: (atNamed: {}, ...args: any[]) => tag('p', atNamed, ...args),
|
||||||
|
h1: (atNamed: {}, ...args: any[]) => tag('h1', atNamed, ...args),
|
||||||
|
h2: (atNamed: {}, ...args: any[]) => tag('h2', atNamed, ...args),
|
||||||
|
b: (atNamed: {}, ...args: any[]) => tag('b', atNamed, ...args),
|
||||||
|
ul: (atNamed: {}, ...args: any[]) => tag('ul', atNamed, ...args),
|
||||||
|
li: (atNamed: {}, ...args: any[]) => tag('li', atNamed, ...args),
|
||||||
|
nospace: () => NOSPACE_TOKEN,
|
||||||
|
echo: (...args: any[]) => console.log(...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
function raw(fn: Function) { (fn as any).raw = true }
|
||||||
|
|
||||||
|
const tagBlock = async (tagName: string, props = {}, fn: Function) => {
|
||||||
|
const attrs = Object.entries(props).map(([key, value]) => `${key}="${value}"`)
|
||||||
|
const space = attrs.length ? ' ' : ''
|
||||||
|
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')}>`)
|
||||||
|
await fn()
|
||||||
|
buffer.push(`</${tagName}>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tagCall = (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||||
|
const attrs = Object.entries(atNamed).map(([key, value]) => `${key}="${value}"`)
|
||||||
|
const space = attrs.length ? ' ' : ''
|
||||||
|
const children = args
|
||||||
|
.reverse()
|
||||||
|
.map(a => a === null ? buffer.pop() : a)
|
||||||
|
.reverse().join(' ')
|
||||||
|
.replaceAll(` ${NOSPACE_TOKEN} `, '')
|
||||||
|
|
||||||
|
if (SELF_CLOSING.includes(tagName))
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')} />`)
|
||||||
|
else
|
||||||
|
buffer.push(`<${tagName}${space}${attrs.join(' ')}>${children}</${tagName}>`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = async (tagName: string, atNamed = {}, ...args: any[]) => {
|
||||||
|
if (typeof args[0] === 'function')
|
||||||
|
await tagBlock(tagName, atNamed, args[0])
|
||||||
|
else
|
||||||
|
tagCall(tagName, atNamed, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOSPACE_TOKEN = '!!ribbit-nospace!!'
|
||||||
|
const SELF_CLOSING = ["area", "base", "br", "col", "embed", "hr", "img", "input", "link", "meta", "param", "source", "track", "wbr"]
|
||||||
|
|
||||||
|
describe('ribbit', () => {
|
||||||
|
beforeEach(() => buffer.length = 0)
|
||||||
|
|
||||||
|
test('head tag', () => {
|
||||||
|
expect(`
|
||||||
|
ribbit:
|
||||||
|
head:
|
||||||
|
title What up
|
||||||
|
meta charset=UTF-8
|
||||||
|
meta name=viewport content='width=device-width, initial-scale=1, viewport-fit=cover'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`).toEvaluateTo(`<head>
|
||||||
|
<title>What up</title>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||||
|
</head>`, ribbitGlobals)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('custom tags', () => {
|
||||||
|
expect(`
|
||||||
|
list = tag ul class=list
|
||||||
|
ribbit:
|
||||||
|
list:
|
||||||
|
li border-bottom='1px solid black' one
|
||||||
|
li two
|
||||||
|
li three
|
||||||
|
end
|
||||||
|
end`).toEvaluateTo(`<ul class="list">
|
||||||
|
<li border-bottom="1px solid black">one</li>
|
||||||
|
<li>two</li>
|
||||||
|
<li>three</li>
|
||||||
|
</ul>`, ribbitGlobals)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inline expressions', () => {
|
||||||
|
expect(`
|
||||||
|
ribbit:
|
||||||
|
p class=container:
|
||||||
|
h1 class=bright style='font-family: helvetica' Heya
|
||||||
|
h2 man that is (b wild) (nospace) !
|
||||||
|
p Double the fun.
|
||||||
|
end
|
||||||
|
end`).toEvaluateTo(
|
||||||
|
`<p class="container">
|
||||||
|
<h1 class="bright" style="font-family: helvetica">Heya</h1>
|
||||||
|
<h2>man that is <b>wild</b>!</h2>
|
||||||
|
<p>Double the fun.</p>
|
||||||
|
</p>`, ribbitGlobals)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -99,9 +99,9 @@ export const getFunctionDefParts = (node: SyntaxNode, input: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramNames = getAllChildren(paramsNode).map((param) => {
|
const paramNames = getAllChildren(paramsNode).map((param) => {
|
||||||
if (param.type.id !== terms.Identifier) {
|
if (param.type.id !== terms.Identifier && param.type.id !== terms.NamedParam) {
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
`FunctionDef params must be Identifier, got ${param.type.name}`,
|
`FunctionDef params must be Identifier or NamedParam, got ${param.type.name}`,
|
||||||
param.from,
|
param.from,
|
||||||
param.to
|
param.to
|
||||||
)
|
)
|
||||||
|
|
|
||||||
39
src/index.ts
Normal file
39
src/index.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
import { readFileSync } from 'fs'
|
||||||
|
import { VM, fromValue, type Bytecode } from 'reefvm'
|
||||||
|
import { Compiler } from '#compiler/compiler'
|
||||||
|
import { globals, colors } from '#prelude'
|
||||||
|
|
||||||
|
export { Compiler } from '#compiler/compiler'
|
||||||
|
export { parser } from '#parser/shrimp'
|
||||||
|
export { globals } from '#prelude'
|
||||||
|
|
||||||
|
export async function runFile(path: string): Promise<any> {
|
||||||
|
const code = readFileSync(path, 'utf-8')
|
||||||
|
return await runCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runCode(code: string): Promise<any> {
|
||||||
|
const compiler = new Compiler(code, Object.keys(globals))
|
||||||
|
return await runBytecode(compiler.bytecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function runBytecode(bytecode: Bytecode): Promise<any> {
|
||||||
|
try {
|
||||||
|
const vm = new VM(bytecode, globals)
|
||||||
|
await vm.run()
|
||||||
|
return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!) : null
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(`${colors.red}Error:${colors.reset} ${error.message}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileFile(path: string): Bytecode {
|
||||||
|
const code = readFileSync(path, 'utf-8')
|
||||||
|
return compileCode(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compileCode(code: string): Bytecode {
|
||||||
|
const compiler = new Compiler(code, Object.keys(globals))
|
||||||
|
return compiler.bytecode
|
||||||
|
}
|
||||||
|
|
@ -50,6 +50,7 @@ item {
|
||||||
|
|
||||||
consumeToTerminator {
|
consumeToTerminator {
|
||||||
PipeExpr |
|
PipeExpr |
|
||||||
|
FunctionCallWithBlock |
|
||||||
ambiguousFunctionCall |
|
ambiguousFunctionCall |
|
||||||
TryExpr |
|
TryExpr |
|
||||||
Throw |
|
Throw |
|
||||||
|
|
@ -70,6 +71,18 @@ pipeOperand {
|
||||||
FunctionCall | FunctionCallOrIdentifier
|
FunctionCall | FunctionCallOrIdentifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionCallWithBlock {
|
||||||
|
singleLineFunctionCallWithBlock | multiLineFunctionCallWithBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
singleLineFunctionCallWithBlock {
|
||||||
|
ambiguousFunctionCall colon consumeToTerminator CatchExpr? FinallyExpr? @specialize[@name=keyword]<Identifier, "end">
|
||||||
|
}
|
||||||
|
|
||||||
|
multiLineFunctionCallWithBlock {
|
||||||
|
ambiguousFunctionCall colon newlineOrSemicolon block CatchExpr? FinallyExpr? @specialize[@name=keyword]<Identifier, "end">
|
||||||
|
}
|
||||||
|
|
||||||
FunctionCallOrIdentifier {
|
FunctionCallOrIdentifier {
|
||||||
DotGet | Identifier
|
DotGet | Identifier
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +188,11 @@ ConditionalOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
Params {
|
Params {
|
||||||
Identifier*
|
Identifier* NamedParam*
|
||||||
|
}
|
||||||
|
|
||||||
|
NamedParam {
|
||||||
|
NamedArgPrefix (String | Number | Boolean | @specialize[@name=Null]<Identifier, "null">)
|
||||||
}
|
}
|
||||||
|
|
||||||
Assign {
|
Assign {
|
||||||
|
|
|
||||||
|
|
@ -43,22 +43,24 @@ export const
|
||||||
NamedArgPrefix = 41,
|
NamedArgPrefix = 41,
|
||||||
FunctionDef = 42,
|
FunctionDef = 42,
|
||||||
Params = 43,
|
Params = 43,
|
||||||
colon = 44,
|
NamedParam = 44,
|
||||||
CatchExpr = 45,
|
Null = 45,
|
||||||
keyword = 68,
|
colon = 46,
|
||||||
TryBlock = 47,
|
CatchExpr = 47,
|
||||||
FinallyExpr = 48,
|
keyword = 70,
|
||||||
Underscore = 51,
|
TryBlock = 49,
|
||||||
Array = 52,
|
FinallyExpr = 50,
|
||||||
Null = 53,
|
Underscore = 53,
|
||||||
ConditionalOp = 54,
|
Array = 54,
|
||||||
PositionalArg = 55,
|
ConditionalOp = 55,
|
||||||
TryExpr = 57,
|
PositionalArg = 56,
|
||||||
Throw = 59,
|
FunctionCallWithBlock = 58,
|
||||||
IfExpr = 61,
|
TryExpr = 59,
|
||||||
SingleLineThenBlock = 63,
|
Throw = 61,
|
||||||
ThenBlock = 64,
|
IfExpr = 63,
|
||||||
ElseIfExpr = 65,
|
SingleLineThenBlock = 65,
|
||||||
ElseExpr = 67,
|
ThenBlock = 66,
|
||||||
CompoundAssign = 69,
|
ElseIfExpr = 67,
|
||||||
Assign = 70
|
ElseExpr = 69,
|
||||||
|
CompoundAssign = 71,
|
||||||
|
Assign = 72
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
|
||||||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||||
import {trackScope} from "./scopeTracker"
|
import {trackScope} from "./scopeTracker"
|
||||||
import {highlighting} from "./highlight"
|
import {highlighting} from "./highlight"
|
||||||
const spec_Identifier = {__proto__:null,catch:92, finally:98, end:100, null:106, try:116, throw:120, if:124, elseif:132, else:136}
|
const spec_Identifier = {__proto__:null,null:90, catch:96, finally:102, end:104, try:120, throw:124, if:128, elseif:136, else:140}
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "9[QYQbOOO#tQcO'#C{O$qOSO'#C}O%PQbO'#EfOOQ`'#DW'#DWOOQa'#DT'#DTO&SQbO'#DbO'eQcO'#EZOOQa'#EZ'#EZO(hQcO'#EZO)jQcO'#EYO)}QRO'#C|O+ZQcO'#EUO+kQcO'#EUO+uQbO'#CzO,mOpO'#CxOOQ`'#EV'#EVO,rQbO'#EUO,yQQO'#ElOOQ`'#Dg'#DgO-OQbO'#DiO-OQbO'#EnOOQ`'#Dk'#DkO-sQRO'#DsOOQ`'#EU'#EUO.XQQO'#ETOOQ`'#ET'#ETOOQ`'#Du'#DuQYQbOOO.aQbO'#DUOOQa'#EY'#EYOOQ`'#De'#DeOOQ`'#Ek'#EkOOQ`'#D|'#D|O.kQbO,59cO/_QbO'#DPO/gQWO'#DQOOOO'#E]'#E]OOOO'#Dv'#DvO/{OSO,59iOOQa,59i,59iOOQ`'#Dx'#DxO0ZQbO'#DXO0cQQO,5;QOOQ`'#Dw'#DwO0hQbO,59|O0oQQO,59oOOQa,59|,59|O0zQbO,59|O1UQbO,5:`O-OQbO,59hO-OQbO,59hO-OQbO,59hO-OQbO,5:OO-OQbO,5:OO-OQbO,5:OO1fQRO,59fO1mQRO,59fO2OQRO,59fO1yQQO,59fO2ZQQO,59fO2cObO,59dO2nQbO'#D}O2yQbO,59bO3bQbO,5;WO3uQcO,5:TO4kQcO,5:TO4{QcO,5:TO5qQRO,5;YO5xQRO,5;YO1UQbO,5:_OOQ`,5:o,5:oOOQ`-E7s-E7sOOQ`,59p,59pOOQ`-E7z-E7zOOOO,59k,59kOOOO,59l,59lOOOO-E7t-E7tOOQa1G/T1G/TOOQ`-E7v-E7vO6TQbO1G0lOOQ`-E7u-E7uO6hQQO1G/ZOOQa1G/h1G/hO6sQbO1G/hOOQO'#Dz'#DzO6hQQO1G/ZOOQa1G/Z1G/ZOOQ`'#D{'#D{O6sQbO1G/hOOQ`1G/z1G/zOOQa1G/S1G/SO7lQcO1G/SO7vQcO1G/SO8QQcO1G/SOOQa1G/j1G/jO9pQcO1G/jO9wQcO1G/jO:OQcO1G/jOOQa1G/Q1G/QOOQa1G/O1G/OO!aQbO'#C{O:VQbO'#CwOOQ`,5:i,5:iOOQ`-E7{-E7{O:dQbO1G0rO:oQbO1G0sO;]QbO1G0tOOQ`1G/y1G/yO;pQbO7+&WO:oQbO7+&YO;{QQO7+$uOOQa7+$u7+$uO<WQbO7+%SOOQa7+%S7+%SOOQO-E7x-E7xOOQ`-E7y-E7yO<bQbO'#DZO<gQQO'#D^OOQ`7+&^7+&^O<lQbO7+&^O<qQbO7+&^OOQ`'#Dy'#DyO<yQQO'#DyO=OQbO'#EgOOQ`'#D]'#D]O=rQbO7+&_OOQ`'#Dm'#DmO=}QbO7+&`O>SQbO7+&aOOQ`<<Ir<<IrO>pQbO<<IrO>uQbO<<IrO>}QbO<<ItOOQa<<Ha<<HaOOQa<<Hn<<HnO?YQQO,59uO?_QbO,59xOOQ`<<Ix<<IxO?rQbO<<IxOOQ`,5:e,5:eOOQ`-E7w-E7wOOQ`<<Iy<<IyO?wQbO<<IyO?|QbO<<IyOOQ`<<Iz<<IzOOQ`'#Dn'#DnO@UQbO<<I{OOQ`AN?^AN?^O@aQbOAN?^OOQ`AN?`AN?`O@fQbOAN?`O@kQbOAN?`O@sQbO1G/aOAWQbO1G/dOOQ`1G/d1G/dOOQ`AN?dAN?dOOQ`AN?eAN?eOAnQbOAN?eO-OQbO'#DoOOQ`'#EO'#EOOAsQbOAN?gOBOQQO'#DqOOQ`AN?gAN?gOBTQbOAN?gOOQ`G24xG24xOOQ`G24zG24zOBYQbOG24zOB_QbO7+${OOQ`7+${7+${OOQ`7+%O7+%OOOQ`G25PG25POBxQRO,5:ZOCPQRO,5:ZOOQ`-E7|-E7|OOQ`G25RG25ROC[QbOG25ROCaQQO,5:]OOQ`LD*fLD*fOOQ`<<Hg<<HgOCfQQO1G/uOOQ`LD*mLD*mOAWQbO1G/wO>SQbO7+%aOOQ`7+%c7+%cOOQ`<<H{<<H{",
|
states: "<[QYQbOOO#tQcO'#C{O$tOSO'#C}O%SQbO'#EiOOQ`'#DW'#DWOOQa'#DT'#DTO&YQbO'#DdO'kQcO'#E^OOQa'#E^'#E^O(nQcO'#E^O)pQcO'#E]O*TQRO'#C|O+aQcO'#EXO+qQcO'#EXO+{QbO'#CzO,sOpO'#CxOOQ`'#EY'#EYO,xQbO'#EXOOQ`'#Dh'#DhO-SQQO'#EqOOQ`'#Di'#DiO-XQbO'#DkO-XQbO'#EsOOQ`'#Dm'#DmO-|QRO'#DuOOQ`'#EX'#EXO.bQQO'#EWOOQ`'#EW'#EWOOQ`'#Dw'#DwQYQbOOO.jQbO'#DUOOQa'#E]'#E]OOQ`'#Df'#DfOOQ`'#En'#EnOOQ`'#EP'#EPO.tQbO,59cO/kQbO'#DPO/sQWO'#DQOOOO'#E`'#E`OOOO'#Dx'#DxO0XOSO,59iOOQa,59i,59iOOQ`'#Dz'#DzO0gQbO'#DXO0rQbO'#DYOOQO'#D{'#D{O0jQQO'#DXO1QQQO,5;TOOQ`'#Dy'#DyO1VQbO,5:OO1^QQO,59oOOQa,5:O,5:OO1iQbO,5:OO1sQbO,5:bO-XQbO,59hO-XQbO,59hO-XQbO,59hO-XQbO,5:PO-XQbO,5:PO-XQbO,5:PO2TQRO,59fO2[QRO,59fO2mQRO,59fO2hQQO,59fO2xQQO,59fO3QObO,59dO3]QbO'#EQO3hQbO,59bO4PQbO,5;ZO4dQbO,5;]O4wQcO,5:VO5mQcO,5:VO5}QcO,5:VO6sQRO,5;_O6zQRO,5;_O1sQbO,5:aOOQ`,5:r,5:rOOQ`-E7u-E7uOOQ`,59p,59pOOQ`-E7}-E7}OOOO,59k,59kOOOO,59l,59lOOOO-E7v-E7vOOQa1G/T1G/TOOQ`-E7x-E7xO7VQQO,59sOOQO,59t,59tOOQO-E7y-E7yO7_QbO1G0oOOQ`-E7w-E7wO7rQQO1G/ZOOQa1G/j1G/jO7}QbO1G/jOOQO'#D}'#D}O7rQQO1G/ZOOQa1G/Z1G/ZOOQ`'#EO'#EOO7}QbO1G/jOOQ`1G/|1G/|OOQa1G/S1G/SO8vQcO1G/SO9QQcO1G/SO9[QcO1G/SOOQa1G/k1G/kO:zQcO1G/kO;RQcO1G/kO;YQcO1G/kOOQa1G/Q1G/QOOQa1G/O1G/OO!aQbO'#C{O;aQbO'#CwOOQ`,5:l,5:lOOQ`-E8O-E8OO;nQbO1G0uO;yQbO1G0vO<gQbO1G0wO;yQbO1G0xO<rQbO1G0yOOQ`1G/{1G/{O=VQbO7+&ZO;yQbO7+&]O=bQQO7+$uOOQa7+$u7+$uO=mQbO7+%UOOQa7+%U7+%UOOQO-E7{-E7{OOQ`-E7|-E7|O=wQbO'#D]O=|QQO'#D`OOQ`7+&a7+&aO>RQbO7+&aO>WQbO7+&aOOQ`'#D|'#D|O>`QQO'#D|O>eQbO'#EjO?XQbO7+&bOOQ`7+&c7+&cO?dQbO7+&cO?iQbO7+&cOOQ`'#D_'#D_O?qQbO7+&dOOQ`'#Do'#DoO?|QbO7+&eO@RQbO7+&fOOQ`<<Iu<<IuO@oQbO<<IuO@tQbO<<IuO@|QbO<<IwOOQa<<Ha<<HaOOQa<<Hp<<HpOAXQQO,59wOA^QbO,59zOOQ`<<I{<<I{OAqQbO<<I{OOQ`,5:h,5:hOOQ`-E7z-E7zOOQ`<<I|<<I|OAvQbO<<I|OA{QbO<<I|OOQ`<<I}<<I}OBTQbO<<I}OOQ`<<JO<<JOOBYQbO<<JOOB_QbO<<JOOOQ`<<JP<<JPOOQ`'#Dp'#DpOBgQbO<<JQOOQ`AN?aAN?aOBrQbOAN?aOOQ`AN?cAN?cOBwQbOAN?cOB|QbOAN?cOCUQbO1G/cOCiQbO1G/fOOQ`1G/f1G/fOOQ`AN?gAN?gOOQ`AN?hAN?hODPQbOAN?hOOQ`AN?iAN?iOOQ`AN?jAN?jODUQbOAN?jO-XQbO'#DqOOQ`'#ER'#ERODZQbOAN?lODfQQO'#DsOOQ`AN?lAN?lODkQbOAN?lOOQ`G24{G24{OOQ`G24}G24}ODpQbOG24}ODuQbO7+$}OOQ`7+$}7+$}OOQ`7+%Q7+%QOOQ`G25SG25SOOQ`G25UG25UOE`QRO,5:]OEgQRO,5:]OOQ`-E8P-E8POOQ`G25WG25WOErQbOG25WOEwQQO,5:_OOQ`LD*iLD*iOOQ`<<Hi<<HiOE|QQO1G/wOOQ`LD*rLD*rOCiQbO1G/yO@RQbO7+%cOOQ`7+%e7+%eOOQ`<<H}<<H}",
|
||||||
stateData: "Cn~O!uOS!vOS~OdPOegOfWOg_OhROmWOuWOvWO!VWO![bO!^dO!`eO!{^O#OQO#VTO#WUO#XjO~OdnOfWOg_OhROmWOuWOvWOymO!ToO!VWO!{^O#OQO#VTO#WUO!YoX#XoX#doX#^oX!OoX!RoX!SoX~OP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~P!aOruO#OxO#QsO#RtO~OdyO|{P~OdnOfWOg_OmWOuWOvWOymO!VWO!{^O#OQO#VTO#WUO#X|O~O#]!PO~P%XOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}X#X!}X#d!}X!O!}X!R!}X!S!}X~OdnOfWOg_OhROmWOuWOvWOymO!ToO!VWO!{^O#OQO#VTO#WUO#^!}X~P&ZOV!RO~P&ZOP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~O#X!xX#d!xX!O!xX!R!xX!S!xX~P(oOP!TOQ!TOR!UOS!UOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO^!SO~O#X!xX#d!xX!O!xX!R!xX!S!xX~OP!TOQ!TOR!UOS!UO~P*xOT!WOU!XO~P*xOdPOfWOg_OhROmWOuWOvWO!VWO!{^O#OQO#VTO#WUO~O!z!_O~O!Y!`O~P*xO|!bO~OdnOfWOg_OmWOuWOvWO!VWO!{^O#OQO#VTO#WUO~OV!RO_!hO`!hOa!hOb!hOc!hO~O#X!iO#d!iO~OhRO!T!kO~P-OOhROymO!ToO!Yka#Xka#dka#^ka!Oka!Rka!Ska~P-OOd!mO!{^O~O#O!nO#Q!nO#R!nO#S!nO#T!nO#U!nO~OruO#O!pO#QsO#RtO~OdyO|{X~O|!rO~O#]!uO~P%XOymO#X!wO#]!yO~O#X!zO#]!uO~P-OOegO![bO!^dO!`eO~P+uO#^#VO~P(oOP!TOQ!TOR!UOS!UO#^#VO~OT!WOU!XO#^#VO~O!Y!`O#^#VO~Od#WOm#WO!{^O~Od#XOg_O!{^O~O!Y!`O#Xja#dja#^ja!Oja!Rja!Sja~OegO![bO!^dO!`eO#X#^O~P+uO#X!]a#d!]a!O!]a!R!]a!S!]a~P)}O#X!]a#d!]a!O!]a!R!]a!S!]a~OP!TOQ!TOR!UOS!UO~P4YOT!WOU!XO~P4YOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO~O|#_O~P5VOT!WOU!XO|#_O~OegO![bO!^dO!`eO#X#bO~P+uOymO#X!wO#]#dO~O#X!zO#]#fO~P-OO^!SORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OPpiQpi~P6}OP!TOQ!TO~P6}OP!TOQ!TORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OW!VOX!VOY!VOZ!VO[!VO]!VOT!Wi#X!Wi#d!Wi#^!Wi|!Wi!O!Wi!R!Wi!S!Wi~OU!XO~P8rOU!XO~P9UOU!Wi~P8rOhROymO!ToO~P-OO!O#iO!R#jO!S#kO~OegO![bO!^dO!`eO#X#nO!O#ZP!R#ZP!S#ZP~P+uOegO![bO!^dO!`eO#X#uO~P+uO!O#iO!R#jO!S#vO~OymO#X!wO#]#zO~O#X!zO#]#{O~P-OOd#|O~O|#}O~O!S$OO~O!R#jO!S$OO~O#X$QO~OegO![bO!^dO!`eO#X#nO!O#ZX!R#ZX!S#ZX!d#ZX!f#ZX~P+uO!O#iO!R#jO!S$SO~O!S$VO~OegO![bO!^dO!`eO#X#nO!S#ZP!d#ZP!f#ZP~P+uO!S$YO~O!R#jO!S$YO~O!O#iO!R#jO!S$[O~O|$_O~OegO![bO!^dO!`eO#X$`O~P+uO!S$bO~O!S$cO~O!R#jO!S$cO~O!S$iO!d$eO!f$hO~O!S$kO~O!S$lO~O!R#jO!S$lO~OegO![bO!^dO!`eO#X$nO~P+uOegO![bO!^dO!`eO#X#nO!S#ZP~P+uO!S$qO~O!S$uO!d$eO!f$hO~O|$wO~O!S$uO~O!S$xO~OegO![bO!^dO!`eO#X#nO!R#ZP!S#ZP~P+uO|$zO~P5VOT!WOU!XO|$zO~O!S${O~O#X$|O~O#X$}O~Omv~",
|
stateData: "FU~O!xOS!yOS~OdPOehOfWOg_OhROmWOuWOvWO}WO!^cO!`eO!bfO#O^O#RQO#YTO#ZUO#[kO~OdoOfWOg_OhROmWOuWOvWOynO}WO!VpO#O^O#RQO#YTO#ZUO!ZoX#[oX#ioX#aoX!QoX!ToX!UoX~OP#PXQ#PXR#PXS#PXT#PXU#PXW#PXX#PXY#PXZ#PX[#PX]#PX^#PX!OoX~P!aOrvO#RyO#TtO#UuO~OdzOy|O!O{P~OdoOfWOg_OmWOuWOvWOynO}WO#O^O#RQO#YTO#ZUO#[!QO~O#`!TO~P%_OP#QXQ#QXR#QXS#QXT#QXU#QXW#QXX#QXY#QXZ#QX[#QX]#QX^#QX#[#QX#i#QX!Q#QX!T#QX!U#QX~OdoOfWOg_OhROmWOuWOvWOynO}WO!VpO#O^O#RQO#YTO#ZUO#a#QX~P&aOV!VO~P&aOP#PXQ#PXR#PXS#PXT#PXU#PXW#PXX#PXY#PXZ#PX[#PX]#PX^#PX~O#[!{X#i!{X!Q!{X!T!{X!U!{X~P(uOP!XOQ!XOR!YOS!YOT![OU!]OW!ZOX!ZOY!ZOZ!ZO[!ZO]!ZO^!WO~O#[!{X#i!{X!Q!{X!T!{X!U!{X~OP!XOQ!XOR!YOS!YO~P+OOT![OU!]O~P+OOdPOfWOg_OhROmWOuWOvWO}WO#O^O#RQO#YTO#ZUO~O!}!cO~O!O!fO!Z!dO~P+OO!O!gO~OdoOfWOg_OmWOuWOvWO}WO#O^O#RQO#YTO#ZUO~OV!VO_!mO`!mOa!mOb!mOc!mO~O#[!nO#i!nO~OhRO!V!pO~P-XOhROynO!VpO!Oka!Zka#[ka#ika#aka!Qka!Tka!Uka~P-XOd!rO#O^O~O#R!sO#T!sO#U!sO#V!sO#W!sO#X!sO~OrvO#R!uO#TtO#UuO~OdzOy|O!O{X~Om!xOu!xO}!xO#RQO~O!O!zO~O#`!}O~P%_OynO#[#PO#`#RO~O#[#SO#`!}O~P-XOehO!^cO!`eO!bfO~P+{O#a#_O~P(uOP!XOQ!XOR!YOS!YO#a#_O~OT![OU!]O#a#_O~O!Z!dO#a#_O~Od#`Om#`O#O^O~Od#aOg_O#O^O~O!Z!dO#[ja#ija#aja!Qja!Tja!Uja~OehO!^cO!`eO!bfO#[#fO~P+{OehO!^cO!`eO!bfO#[#hO~P+{O#[!_a#i!_a!Q!_a!T!_a!U!_a~P*TO#[!_a#i!_a!Q!_a!T!_a!U!_a~OP!XOQ!XOR!YOS!YO~P5[OT![OU!]O~P5[OT![OU!]OW!ZOX!ZOY!ZOZ!ZO[!ZO]!ZO~O!O#iO~P6XOT![OU!]O!O#iO~Oy|O!O{a~OehO!^cO!`eO!bfO#[#lO~P+{OynO#[#PO#`#nO~O#[#SO#`#pO~P-XO^!WORpiSpi#[pi#ipi#api!Qpi!Tpi!Upi~OPpiQpi~P8XOP!XOQ!XO~P8XOP!XOQ!XORpiSpi#[pi#ipi#api!Qpi!Tpi!Upi~OW!ZOX!ZOY!ZOZ!ZO[!ZO]!ZOT!Xi#[!Xi#i!Xi#a!Xi!O!Xi!Q!Xi!T!Xi!U!Xi~OU!]O~P9|OU!]O~P:`OU!Xi~P9|OhROynO!VpO~P-XO!Q#sO!T#tO!U#uO~OehO!^cO!`eO!bfO#[#xO!Q#^P!T#^P!U#^P~P+{O!Q#sO!T#tO!U#|O~OehO!^cO!`eO!bfO#[$TO~P+{O!Q#sO!T#tO!U$UO~OynO#[#PO#`$YO~O#[#SO#`$ZO~P-XOd$[O~O!O$]O~O!U$^O~O!T#tO!U$^O~O#[$`O~OehO!^cO!`eO!bfO#[#xO!Q#^X!T#^X!U#^X!f#^X!h#^X~P+{O!Q#sO!T#tO!U$bO~O!U$eO~O!T#tO!U$eO~O!Q#sO!T#tO!U$gO~O!U$jO~OehO!^cO!`eO!bfO#[#xO!U#^P!f#^P!h#^P~P+{O!U$mO~O!T#tO!U$mO~O!Q#sO!T#tO!U$oO~O!O$rO~OehO!^cO!`eO!bfO#[$sO~P+{O!U$uO~O!U$vO~O!T#tO!U$vO~O!U$xO~O!U$yO~O!T#tO!U$yO~O!U%PO!f${O!h%OO~O!U%RO~O!U%SO~O!T#tO!U%SO~OehO!^cO!`eO!bfO#[%UO~P+{OehO!^cO!`eO!bfO#[#xO!U#^P~P+{O!U%XO~O!U%YO~O!U%^O!f${O!h%OO~O!O%`O~O!U%^O~O!U%aO~OehO!^cO!`eO!bfO#[#xO!T#^P!U#^P~P+{O!O%cO~P6XOT![OU!]O!O%cO~O!U%dO~O#[%eO~O#[%fO~Omv~",
|
||||||
goto: "4k#dPPPPPPPPPPPPPPPPPPPPPPPPPP#e#{$bP%b#{&h'XP(S(SPP'X(WP(k)]P)`P)l)uPPP*_P+[,RP,YP,YP,YP,m,p,yP,}P,Y,Y-T-Z-a-g-m-y.T._.h.oPPPP.u.y/nPP0X1pP2oPPPPPPPP2s3_2sPP3l3s3s4W4WrhOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}R!]^w`O^l!R!`!b!h!r#^#_#b#p#u#}$_$`$n$|$}tPO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}znPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eR#X!`tVO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}zWPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eQ!msQ#W!_R#Y!`r[Ol!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q!Z^Q!ddQ!}!TR#Q!U!qWOPUV^delmr}!Q!R!S!T!U!V!W!X!b!h!r!v!{#X#Y#^#_#b#e#p#u#}$_$`$e$n$|$}TuQwYpPVr#X#YQ!OUQ!t}X!w!O!t!x#crhOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}YoPVr#X#YQ!]^R!kmR{RQ#m#]Q#x#aQ$U#rR$^#yQ#r#^Q$p$`R$y$nQ#l#]Q#w#aQ$P#mQ$T#rQ$Z#xQ$]#yQ$d$UR$m$^|WPUV^demr}!Q!S!T!U!V!W!X!v!{#X#Y#e$esXOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}r]Ol!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q![^Q!edQ!geQ#R!XQ#T!WR$s$eZpPVr#X#YshOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}R#t#_Q$X#uQ%O$|R%P$}T$f$X$gQ$j$XR$v$gQlOR!jlQwQR!owQ}UR!s}QzRR!qz^#p#^#b#u$`$n$|$}R$R#pQ!x!OQ#c!tT#g!x#cQ!{!QQ#e!vT#h!{#eWrPV#X#YR!lrS!aa!^R#[!aQ$g$XR$t$gTkOlSiOlQ!|!RQ#]!bQ#`!hQ#a!r`#o#^#b#p#u$`$n$|$}Q#s#_Q$a#}R$o$_raOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q!^^R#Z!`tZO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}YoPVr#X#YQ!QUQ!cdQ!feQ!kmQ!v}W!z!Q!v!{#eQ!}!SQ#O!TQ#P!UQ#R!VQ#S!WQ#U!XR$r$erYOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}znPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eR!Y^TvQw!RSOPV^lmr!R!b!h!r#X#Y#^#_#b#p#u#}$_$`$n$|$}U#q#^$`$nQ#y#bV$W#u$|$}ZqPVr#X#YscOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}sfOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}",
|
goto: "7s#iPPPPPPPPPPPPPPPPPPPPPPPPPP#j$S$kP%m$S&u'hP(h(hPP(l)iP)|*p*sPP*yP+]+fPPP,[-Z.SP.Z.ZP.ZP.ZP.p.s.|P/QP.Z.Z/W/^/d/j/p/z0X0c0m0v0}PPPP1T1X2QPP2m4WP5XPPPPPPPP5]5y5]PP6Z6b6b6w6w7^7^viOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fR!a^{`O^m!V!d!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fxPO^m!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fzoPUVefns!R!U!W!X!Y!Z![!]#O#T#a#b#o${R#a!dxVO^m!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fzWPUVefns!R!U!W!X!Y!Z![!]#O#T#a#b#o${Q!rtQ#`!cR#b!dv[Om!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fQ!_^Q!ieQ#V!XR#Y!Y!tWOPUV^efmns!R!U!V!W!X!Y!Z![!]!f!g!m!z#O#T#a#b#f#h#i#l#o#z$T$]$r$s${%U%e%fR!x|TvQx!uWOPUV^efmns!R!U!V!W!X!Y!Z![!]!f!g!m!z#O#T#a#b#f#h#i#l#o#z$T$]$r$s${%U%e%fYqPVs#a#bQ!SUQ!|!RX#P!S!|#Q#mviOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fYpPVs#a#bQ!a^R!pnR!PRX}R{!O!wQ#w#eQ$O#gQ$W#kQ$d#{Q$i$QR$q$XQ$Q#hQ%W$sR%b%UQ#v#eQ#}#gQ$V#kQ$_#wQ$c#{Q$f$OQ$h$QQ$n$WQ$p$XQ$w$dQ$z$iR%T$q|WPUV^efns!R!U!W!X!Y!Z![!]#O#T#a#b#o${wXOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fv]Om!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fQ!`^Q!jeQ!lfQ#Z!]Q#]![R%[${ZqPVs#a#bwiOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fR$S#iQ$l$TQ%g%eR%h%fT$|$l$}Q%Q$lR%_$}QmOR!omQxQR!txQ!RUR!{!RQ{RR!v{Q!ORQ!w{T!y!O!w`#z#f#h#l$T$s%U%e%fR$a#zQ#Q!SQ#m!|T#q#Q#mQ#T!UQ#o#OT#r#T#oWsPV#a#bR!qsS!ea!bR#d!eQ$}$lR%]$}TlOmSjOmQ#U!VQ#e!fQ#g!gQ#j!mQ#k!zb#y#f#h#l#z$T$s%U%e%fQ$R#iQ$t$]R%V$rvaOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fQ!b^R#c!dxZO^m!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fYpPVs#a#bQ!UUQ!heQ!kfQ!pnQ#O!RW#S!U#O#T#oQ#V!WQ#W!XQ#X!YQ#Z!ZQ#[![Q#^!]R%Z${vYOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fzoPUVefns!R!U!W!X!Y!Z![!]#O#T#a#b#o${R!^^TwQx!VSOPV^mns!V!f!g!m!z#a#b#f#h#i#l#z$T$]$r$s%U%e%fQ#{#fU$P#h$s%UQ$X#lV$k$T%e%fZrPVs#a#bwbOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fwdOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%fwgOm!V!f!g!m!z#f#h#i#l#z$T$]$r$s%U%e%f",
|
||||||
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 Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon CatchExpr keyword TryBlock FinallyExpr keyword keyword Underscore Array Null ConditionalOp PositionalArg operator TryExpr keyword Throw keyword IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword CompoundAssign Assign",
|
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 Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params NamedParam Null colon CatchExpr keyword TryBlock FinallyExpr keyword keyword Underscore Array ConditionalOp PositionalArg operator FunctionCallWithBlock TryExpr keyword Throw keyword IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword CompoundAssign Assign",
|
||||||
maxTerm: 112,
|
maxTerm: 117,
|
||||||
context: trackScope,
|
context: trackScope,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
["closedBy", 44,"end"]
|
["closedBy", 46,"end"]
|
||||||
],
|
],
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 10,
|
repeatNodeCount: 11,
|
||||||
tokenData: "C_~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O+X!O!P#{!P!Q-n!Q![)S![!]6Z!]!^%T!^!}#{!}#O6t#O#P8j#P#Q8o#Q#R#{#R#S9Y#S#T#{#T#Y,Y#Y#Z9s#Z#b,Y#b#c>q#c#f,Y#f#g?n#g#h,Y#h#i@k#i#o,Y#o#p#{#p#qBo#q;'S#{;'S;=`$d<%l~#{~O#{~~CYS$QUrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUrS!uYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UrS#XQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZrS!vYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!vYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#Q~~'aO#O~U'hUrS!{QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUrS#^QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWrSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYrSmQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWrSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWrSmQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^^rSOt#{uw#{x}#{}!O,Y!O!Q#{!Q![)S![!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U,_[rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U-[UyQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U-sWrSOt#{uw#{x!P#{!P!Q.]!Q#O#{#P;'S#{;'S;=`$d<%lO#{U.b^rSOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q#{!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^U/e^rSvQOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q3U!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^Q0fXvQOY0aZ!P0a!P!Q1R!Q!}0a!}#O1p#O#P2o#P;'S0a;'S;=`3O<%lO0aQ1UP!P!Q1XQ1^UvQ#Z#[1X#]#^1X#a#b1X#g#h1X#i#j1X#m#n1XQ1sVOY1pZ#O1p#O#P2Y#P#Q0a#Q;'S1p;'S;=`2i<%lO1pQ2]SOY1pZ;'S1p;'S;=`2i<%lO1pQ2lP;=`<%l1pQ2rSOY0aZ;'S0a;'S;=`3O<%lO0aQ3RP;=`<%l0aU3ZWrSOt#{uw#{x!P#{!P!Q3s!Q#O#{#P;'S#{;'S;=`$d<%lO#{U3zbrSvQOt#{uw#{x#O#{#P#Z#{#Z#[3s#[#]#{#]#^3s#^#a#{#a#b3s#b#g#{#g#h3s#h#i#{#i#j3s#j#m#{#m#n3s#n;'S#{;'S;=`$d<%lO#{U5X[rSOY5SYZ#{Zt5Stu1puw5Swx1px#O5S#O#P2Y#P#Q/^#Q;'S5S;'S;=`5}<%lO5SU6QP;=`<%l5SU6WP;=`<%l/^U6bUrS|QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6{W#WQrSOt#{uw#{x!_#{!_!`7e!`#O#{#P;'S#{;'S;=`$d<%lO#{U7jVrSOt#{uw#{x#O#{#P#Q8P#Q;'S#{;'S;=`$d<%lO#{U8WU#VQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~8oO#R~U8vU#]QrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9aUrS!TQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9x]rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#U:q#U#o,Y#o;'S#{;'S;=`$d<%lO#{U:v^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#`,Y#`#a;r#a#o,Y#o;'S#{;'S;=`$d<%lO#{U;w^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#g,Y#g#h<s#h#o,Y#o;'S#{;'S;=`$d<%lO#{U<x^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#X,Y#X#Y=t#Y#o,Y#o;'S#{;'S;=`$d<%lO#{U={[uQrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^>x[#SWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^?u[#UWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^@r^#TWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#f,Y#f#gAn#g#o,Y#o;'S#{;'S;=`$d<%lO#{UAs^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#i,Y#i#j<s#j#o,Y#o;'S#{;'S;=`$d<%lO#{UBvU!YQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C_O#d~",
|
tokenData: "C_~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O+X!O!P#{!P!Q-n!Q![)S![!]6Z!]!^%T!^!}#{!}#O6t#O#P8j#P#Q8o#Q#R#{#R#S9Y#S#T#{#T#Y,Y#Y#Z9s#Z#b,Y#b#c>q#c#f,Y#f#g?n#g#h,Y#h#i@k#i#o,Y#o#p#{#p#qBo#q;'S#{;'S;=`$d<%l~#{~O#{~~CYS$QUrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qUrS!xYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[UrS#[QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZrS!yYOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mS!yYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#T~~'aO#R~U'hUrS#OQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RUrS#aQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jWrSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZYrSmQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OWrSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oWrSmQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^^rSOt#{uw#{x}#{}!O,Y!O!Q#{!Q![)S![!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U,_[rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U-[UyQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U-sWrSOt#{uw#{x!P#{!P!Q.]!Q#O#{#P;'S#{;'S;=`$d<%lO#{U.b^rSOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q#{!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^U/e^rSvQOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q3U!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^Q0fXvQOY0aZ!P0a!P!Q1R!Q!}0a!}#O1p#O#P2o#P;'S0a;'S;=`3O<%lO0aQ1UP!P!Q1XQ1^UvQ#Z#[1X#]#^1X#a#b1X#g#h1X#i#j1X#m#n1XQ1sVOY1pZ#O1p#O#P2Y#P#Q0a#Q;'S1p;'S;=`2i<%lO1pQ2]SOY1pZ;'S1p;'S;=`2i<%lO1pQ2lP;=`<%l1pQ2rSOY0aZ;'S0a;'S;=`3O<%lO0aQ3RP;=`<%l0aU3ZWrSOt#{uw#{x!P#{!P!Q3s!Q#O#{#P;'S#{;'S;=`$d<%lO#{U3zbrSvQOt#{uw#{x#O#{#P#Z#{#Z#[3s#[#]#{#]#^3s#^#a#{#a#b3s#b#g#{#g#h3s#h#i#{#i#j3s#j#m#{#m#n3s#n;'S#{;'S;=`$d<%lO#{U5X[rSOY5SYZ#{Zt5Stu1puw5Swx1px#O5S#O#P2Y#P#Q/^#Q;'S5S;'S;=`5}<%lO5SU6QP;=`<%l5SU6WP;=`<%l/^U6bUrS!OQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6{W#ZQrSOt#{uw#{x!_#{!_!`7e!`#O#{#P;'S#{;'S;=`$d<%lO#{U7jVrSOt#{uw#{x#O#{#P#Q8P#Q;'S#{;'S;=`$d<%lO#{U8WU#YQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~8oO#U~U8vU#`QrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9aUrS!VQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9x]rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#U:q#U#o,Y#o;'S#{;'S;=`$d<%lO#{U:v^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#`,Y#`#a;r#a#o,Y#o;'S#{;'S;=`$d<%lO#{U;w^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#g,Y#g#h<s#h#o,Y#o;'S#{;'S;=`$d<%lO#{U<x^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#X,Y#X#Y=t#Y#o,Y#o;'S#{;'S;=`$d<%lO#{U={[uQrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^>x[#VWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^?u[#XWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^@r^#WWrSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#f,Y#f#gAn#g#o,Y#o;'S#{;'S;=`$d<%lO#{UAs^rSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#i,Y#i#j<s#j#o,Y#o;'S#{;'S;=`$d<%lO#{UBvU!ZQrSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C_O#i~",
|
||||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!z~~", 11)],
|
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!}~~", 11)],
|
||||||
topRules: {"Program":[0,25]},
|
topRules: {"Program":[0,25]},
|
||||||
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}],
|
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: 1591
|
tokenPrec: 1705
|
||||||
})
|
})
|
||||||
|
|
|
||||||
292
src/parser/tests/function-blocks.test.ts
Normal file
292
src/parser/tests/function-blocks.test.ts
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
import { expect, describe, test } from 'bun:test'
|
||||||
|
|
||||||
|
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||||
|
|
||||||
|
describe('single line function blocks', () => {
|
||||||
|
test('work with no args', () => {
|
||||||
|
expect(`trap: echo bye bye end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier trap
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with one arg', () => {
|
||||||
|
expect(`trap EXIT: echo bye bye end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
Identifier trap
|
||||||
|
PositionalArg
|
||||||
|
Word EXIT
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with named args', () => {
|
||||||
|
expect(`attach signal='exit': echo bye bye end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
Identifier attach
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix signal=
|
||||||
|
String
|
||||||
|
StringFragment exit
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('work with dot-get', () => {
|
||||||
|
expect(`signals = [=]; signals.trap 'EXIT': echo bye bye end`).toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier signals
|
||||||
|
Eq =
|
||||||
|
Dict [=]
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot signals
|
||||||
|
Identifier trap
|
||||||
|
PositionalArg
|
||||||
|
String
|
||||||
|
StringFragment EXIT
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('multi line function blocks', () => {
|
||||||
|
test('work with no args', () => {
|
||||||
|
expect(`
|
||||||
|
trap:
|
||||||
|
echo bye bye
|
||||||
|
end
|
||||||
|
`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier trap
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with one arg', () => {
|
||||||
|
expect(`
|
||||||
|
trap EXIT:
|
||||||
|
echo bye bye
|
||||||
|
end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
Identifier trap
|
||||||
|
PositionalArg
|
||||||
|
Word EXIT
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('work with named args', () => {
|
||||||
|
expect(`
|
||||||
|
attach signal='exit' code=1:
|
||||||
|
echo bye bye
|
||||||
|
end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
Identifier attach
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix signal=
|
||||||
|
String
|
||||||
|
StringFragment exit
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix code=
|
||||||
|
Number 1
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
test('work with dot-get', () => {
|
||||||
|
expect(`
|
||||||
|
signals = [=]
|
||||||
|
signals.trap 'EXIT':
|
||||||
|
echo bye bye
|
||||||
|
end`).toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier signals
|
||||||
|
Eq =
|
||||||
|
Dict [=]
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCall
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot signals
|
||||||
|
Identifier trap
|
||||||
|
PositionalArg
|
||||||
|
String
|
||||||
|
StringFragment EXIT
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
PositionalArg
|
||||||
|
Identifier bye
|
||||||
|
keyword end`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('ribbit', () => {
|
||||||
|
test('head tag', () => {
|
||||||
|
expect(`
|
||||||
|
head:
|
||||||
|
title What up
|
||||||
|
meta charSet=UTF-8
|
||||||
|
meta name='viewport' content='width=device-width, initial-scale=1, viewport-fit=cover'
|
||||||
|
end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier head
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier title
|
||||||
|
PositionalArg
|
||||||
|
Word What
|
||||||
|
PositionalArg
|
||||||
|
Identifier up
|
||||||
|
FunctionCall
|
||||||
|
Identifier meta
|
||||||
|
PositionalArg
|
||||||
|
Word charSet=UTF-8
|
||||||
|
FunctionCall
|
||||||
|
Identifier meta
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix name=
|
||||||
|
String
|
||||||
|
StringFragment viewport
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix content=
|
||||||
|
String
|
||||||
|
StringFragment width=device-width, initial-scale=1, viewport-fit=cover
|
||||||
|
keyword end
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('li', () => {
|
||||||
|
expect(`
|
||||||
|
list:
|
||||||
|
li border-bottom='1px solid black' one
|
||||||
|
li two
|
||||||
|
li three
|
||||||
|
end`).toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier list
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier li
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix border-bottom=
|
||||||
|
String
|
||||||
|
StringFragment 1px solid black
|
||||||
|
PositionalArg
|
||||||
|
Identifier one
|
||||||
|
FunctionCall
|
||||||
|
Identifier li
|
||||||
|
PositionalArg
|
||||||
|
Identifier two
|
||||||
|
FunctionCall
|
||||||
|
Identifier li
|
||||||
|
PositionalArg
|
||||||
|
Identifier three
|
||||||
|
keyword end`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('inline expressions', () => {
|
||||||
|
expect(`
|
||||||
|
p:
|
||||||
|
h1 class=bright style='font-family: helvetica' Heya
|
||||||
|
h2 man that is (b wild)!
|
||||||
|
end`)
|
||||||
|
.toMatchTree(`
|
||||||
|
FunctionCallWithBlock
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier p
|
||||||
|
colon :
|
||||||
|
FunctionCall
|
||||||
|
Identifier h1
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix class=
|
||||||
|
Identifier bright
|
||||||
|
NamedArg
|
||||||
|
NamedArgPrefix style=
|
||||||
|
String
|
||||||
|
StringFragment font-family: helvetica
|
||||||
|
PositionalArg
|
||||||
|
Word Heya
|
||||||
|
FunctionCall
|
||||||
|
Identifier h2
|
||||||
|
PositionalArg
|
||||||
|
Identifier man
|
||||||
|
PositionalArg
|
||||||
|
Identifier that
|
||||||
|
PositionalArg
|
||||||
|
Identifier is
|
||||||
|
PositionalArg
|
||||||
|
ParenExpr
|
||||||
|
FunctionCall
|
||||||
|
Identifier b
|
||||||
|
PositionalArg
|
||||||
|
Identifier wild
|
||||||
|
PositionalArg
|
||||||
|
Word !
|
||||||
|
keyword end`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -177,3 +177,58 @@ end`).toMatchTree(`
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('default params', () => {
|
||||||
|
test('parses function with single default parameter', () => {
|
||||||
|
expect('do x=1: x + 1 end').toMatchTree(`
|
||||||
|
FunctionDef
|
||||||
|
Do do
|
||||||
|
Params
|
||||||
|
NamedParam
|
||||||
|
NamedArgPrefix x=
|
||||||
|
Number 1
|
||||||
|
colon :
|
||||||
|
BinOp
|
||||||
|
Identifier x
|
||||||
|
Plus +
|
||||||
|
Number 1
|
||||||
|
keyword end`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses function with multiple default parameters', () => {
|
||||||
|
expect(`do x='something' y=true: x * y end`).toMatchTree(`
|
||||||
|
FunctionDef
|
||||||
|
Do do
|
||||||
|
Params
|
||||||
|
NamedParam
|
||||||
|
NamedArgPrefix x=
|
||||||
|
String
|
||||||
|
StringFragment something
|
||||||
|
NamedParam
|
||||||
|
NamedArgPrefix y=
|
||||||
|
Boolean true
|
||||||
|
colon :
|
||||||
|
BinOp
|
||||||
|
Identifier x
|
||||||
|
Star *
|
||||||
|
Identifier y
|
||||||
|
keyword end`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses function with mixed parameters', () => {
|
||||||
|
expect('do x y=true: x * y end').toMatchTree(`
|
||||||
|
FunctionDef
|
||||||
|
Do do
|
||||||
|
Params
|
||||||
|
Identifier x
|
||||||
|
NamedParam
|
||||||
|
NamedArgPrefix y=
|
||||||
|
Boolean true
|
||||||
|
colon :
|
||||||
|
BinOp
|
||||||
|
Identifier x
|
||||||
|
Star *
|
||||||
|
Identifier y
|
||||||
|
keyword end`)
|
||||||
|
})
|
||||||
|
})
|
||||||
Loading…
Reference in New Issue
Block a user