Compare commits
7 Commits
8800864fac
...
c4368f24fc
| Author | SHA1 | Date | |
|---|---|---|---|
| c4368f24fc | |||
| dcf94296fa | |||
| 12370361c4 | |||
| 0c6ce16bcd | |||
| c244435ae2 | |||
| b400f48676 | |||
| 793565cafa |
3
bin/repl
3
bin/repl
|
|
@ -7,6 +7,9 @@ import * as readline from 'readline'
|
|||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { basename } from 'path'
|
||||
|
||||
globals.$.script.name = '(repl)'
|
||||
globals.$.script.path = '(repl)'
|
||||
|
||||
async function repl() {
|
||||
const commands = ['/clear', '/reset', '/vars', '/funcs', '/history', '/bytecode', '/exit', '/save', '/quit']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import { colors } from '../src/prelude'
|
||||
import { colors, globals as prelude } from '../src/prelude'
|
||||
import { treeToString } from '../src/utils/tree'
|
||||
import { runCode, runFile, compileFile, parseCode } from '../src'
|
||||
import { resolve } from 'path'
|
||||
import { bytecodeToString } from 'reefvm'
|
||||
import { readFileSync } from 'fs'
|
||||
import { spawn } from 'child_process'
|
||||
|
|
@ -34,6 +35,9 @@ function showVersion() {
|
|||
}
|
||||
|
||||
async function evalCode(code: string, imports: string[]) {
|
||||
const idx = Bun.argv.indexOf('--')
|
||||
prelude.$.args = idx >= 0 ? Bun.argv.slice(idx + 1) : []
|
||||
|
||||
const importStatement = imports.length > 0 ? `import ${imports.join(' ')}` : ''
|
||||
if (importStatement) code = `${importStatement}; ${code}`
|
||||
return await runCode(code)
|
||||
|
|
@ -149,10 +153,12 @@ async function main() {
|
|||
console.log(`${colors.bright}usage: shrimp run <file>${colors.reset}`)
|
||||
process.exit(1)
|
||||
}
|
||||
prelude.$.script.path = resolve(file)
|
||||
await runFile(file)
|
||||
return
|
||||
}
|
||||
|
||||
prelude.$.script.path = resolve(command)
|
||||
await runFile(command)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -211,16 +211,37 @@ export class Compiler {
|
|||
}
|
||||
|
||||
case terms.DotGet: {
|
||||
// DotGet is parsed into a nested tree because it's hard to parse it into a flat one.
|
||||
// However, we want a flat tree - so we're going to pretend like we are getting one from the parser.
|
||||
//
|
||||
// This: DotGet(config, DotGet(script, name))
|
||||
// Becomes: DotGet(config, script, name)
|
||||
const { objectName, property } = getDotGetParts(node, input)
|
||||
const instructions: ProgramItem[] = []
|
||||
|
||||
instructions.push(['TRY_LOAD', objectName])
|
||||
if (property.type.id === terms.ParenExpr) {
|
||||
instructions.push(...this.#compileNode(property, input))
|
||||
} else {
|
||||
const propertyValue = input.slice(property.from, property.to)
|
||||
instructions.push(['PUSH', propertyValue])
|
||||
|
||||
const flattenProperty = (prop: SyntaxNode): void => {
|
||||
if (prop.type.id === terms.DotGet) {
|
||||
const nestedParts = getDotGetParts(prop, input)
|
||||
|
||||
const nestedObjectValue = input.slice(nestedParts.object.from, nestedParts.object.to)
|
||||
instructions.push(['PUSH', nestedObjectValue])
|
||||
instructions.push(['DOT_GET'])
|
||||
|
||||
flattenProperty(nestedParts.property)
|
||||
} else {
|
||||
if (prop.type.id === terms.ParenExpr) {
|
||||
instructions.push(...this.#compileNode(prop, input))
|
||||
} else {
|
||||
const propertyValue = input.slice(prop.from, prop.to)
|
||||
instructions.push(['PUSH', propertyValue])
|
||||
}
|
||||
instructions.push(['DOT_GET'])
|
||||
}
|
||||
}
|
||||
instructions.push(['DOT_GET'])
|
||||
|
||||
flattenProperty(property)
|
||||
return instructions
|
||||
}
|
||||
|
||||
|
|
@ -313,15 +334,17 @@ export class Compiler {
|
|||
if (opValue === '??=') {
|
||||
instructions.push(['LOAD', identifierName])
|
||||
|
||||
const skipLabel: Label = `.skip_${this.labelCount++}`
|
||||
const rightInstructions = this.#compileNode(right, input)
|
||||
|
||||
instructions.push(['DUP'])
|
||||
instructions.push(['PUSH', null])
|
||||
instructions.push(['NEQ'])
|
||||
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_TRUE', skipLabel])
|
||||
instructions.push(['POP'])
|
||||
instructions.push(...rightInstructions)
|
||||
|
||||
instructions.push([`${skipLabel}:`])
|
||||
instructions.push(['DUP'])
|
||||
instructions.push(['STORE', identifierName])
|
||||
|
||||
|
|
@ -419,8 +442,8 @@ export class Compiler {
|
|||
case terms.FunctionCallOrIdentifier: {
|
||||
if (node.firstChild?.type.id === terms.DotGet) {
|
||||
const instructions: ProgramItem[] = []
|
||||
const callLabel = `.call_dotget_${++this.labelCount}`
|
||||
const afterLabel = `.after_dotget_${++this.labelCount}`
|
||||
const callLabel: Label = `.call_dotget_${++this.labelCount}`
|
||||
const afterLabel: Label = `.after_dotget_${++this.labelCount}`
|
||||
|
||||
instructions.push(...this.#compileNode(node.firstChild, input))
|
||||
instructions.push(['DUP'])
|
||||
|
|
@ -459,6 +482,7 @@ export class Compiler {
|
|||
PUSH 1 ; Named count
|
||||
CALL
|
||||
*/
|
||||
case terms.FunctionCallWithNewlines:
|
||||
case terms.FunctionCall: {
|
||||
const { identifierNode, namedArgs, positionalArgs } = getFunctionCallParts(node, input)
|
||||
const instructions: ProgramItem[] = []
|
||||
|
|
@ -582,19 +606,24 @@ export class Compiler {
|
|||
instructions.push(...this.#compileNode(conditionNode, input))
|
||||
this.ifLabelCount++
|
||||
const endLabel: Label = `.end_${this.ifLabelCount}`
|
||||
const elseLabel: Label = `.else_${this.ifLabelCount}`
|
||||
|
||||
const thenBlockInstructions = this.#compileNode(thenBlock, input)
|
||||
instructions.push(['JUMP_IF_FALSE', thenBlockInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_FALSE', elseLabel])
|
||||
instructions.push(...thenBlockInstructions)
|
||||
instructions.push(['JUMP', endLabel])
|
||||
|
||||
instructions.push([`${elseLabel}:`])
|
||||
|
||||
// Else if
|
||||
elseIfBlocks.forEach(({ conditional, thenBlock }) => {
|
||||
elseIfBlocks.forEach(({ conditional, thenBlock }, index) => {
|
||||
instructions.push(...this.#compileNode(conditional, input))
|
||||
const nextLabel: Label = `.elsif_${this.ifLabelCount}_${index}`
|
||||
const elseIfInstructions = this.#compileNode(thenBlock, input)
|
||||
instructions.push(['JUMP_IF_FALSE', elseIfInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_FALSE', nextLabel])
|
||||
instructions.push(...elseIfInstructions)
|
||||
instructions.push(['JUMP', endLabel])
|
||||
instructions.push([`${nextLabel}:`])
|
||||
})
|
||||
|
||||
// Else
|
||||
|
|
@ -643,34 +672,41 @@ export class Compiler {
|
|||
instructions.push(...leftInstructions, ...rightInstructions, ['GTE'])
|
||||
break
|
||||
|
||||
case 'and':
|
||||
case 'and': {
|
||||
const skipLabel: Label = `.skip_${this.labelCount++}`
|
||||
instructions.push(...leftInstructions)
|
||||
instructions.push(['DUP'])
|
||||
instructions.push(['JUMP_IF_FALSE', rightInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_FALSE', skipLabel])
|
||||
instructions.push(['POP'])
|
||||
instructions.push(...rightInstructions)
|
||||
instructions.push([`${skipLabel}:`])
|
||||
break
|
||||
}
|
||||
|
||||
case 'or':
|
||||
case 'or': {
|
||||
const skipLabel: Label = `.skip_${this.labelCount++}`
|
||||
instructions.push(...leftInstructions)
|
||||
instructions.push(['DUP'])
|
||||
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_TRUE', skipLabel])
|
||||
instructions.push(['POP'])
|
||||
instructions.push(...rightInstructions)
|
||||
|
||||
instructions.push([`${skipLabel}:`])
|
||||
break
|
||||
}
|
||||
|
||||
case '??':
|
||||
case '??': {
|
||||
// Nullish coalescing: return left if not null, else right
|
||||
const skipLabel: Label = `.skip_${this.labelCount++}`
|
||||
instructions.push(...leftInstructions)
|
||||
instructions.push(['DUP'])
|
||||
instructions.push(['PUSH', null])
|
||||
instructions.push(['NEQ'])
|
||||
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1])
|
||||
instructions.push(['JUMP_IF_TRUE', skipLabel])
|
||||
instructions.push(['POP'])
|
||||
instructions.push(...rightInstructions)
|
||||
|
||||
instructions.push([`${skipLabel}:`])
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to)
|
||||
|
|
@ -828,7 +864,7 @@ export class Compiler {
|
|||
|
||||
default:
|
||||
throw new CompilerError(
|
||||
`Compiler doesn't know how to handle a "${node.type.name}" node.`,
|
||||
`Compiler doesn't know how to handle a "${node.type.name}" (${node.type.id}) node.`,
|
||||
node.from,
|
||||
node.to
|
||||
)
|
||||
|
|
|
|||
|
|
@ -188,6 +188,16 @@ describe('compiler', () => {
|
|||
test('single line if', () => {
|
||||
expect(`if 3 < 9: shire end`).toEvaluateTo('shire')
|
||||
})
|
||||
|
||||
test('if statement with function definition (bytecode labels)', () => {
|
||||
expect(`
|
||||
if false:
|
||||
abc = do x: x end
|
||||
else:
|
||||
nope
|
||||
end
|
||||
`).toEvaluateTo('nope')
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
|
|
@ -284,6 +294,43 @@ describe('dot get', () => {
|
|||
test('use parens expr with dot-get', () => {
|
||||
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
||||
})
|
||||
|
||||
test('chained dot get: two levels', () => {
|
||||
expect(`obj = [inner=[value=42]]; obj.inner.value`).toEvaluateTo(42)
|
||||
})
|
||||
|
||||
test('chained dot get: three levels', () => {
|
||||
expect(`obj = [a=[b=[c=123]]]; obj.a.b.c`).toEvaluateTo(123)
|
||||
})
|
||||
|
||||
test('chained dot get: four levels', () => {
|
||||
expect(`obj = [w=[x=[y=[z='deep']]]]; obj.w.x.y.z`).toEvaluateTo('deep')
|
||||
})
|
||||
|
||||
test('chained dot get with numeric index', () => {
|
||||
expect(`obj = [items=[1 2 3]]; obj.items.0`).toEvaluateTo(1)
|
||||
})
|
||||
|
||||
test('chained dot get in expression', () => {
|
||||
expect(`config = [server=[port=3000]]; config.server.port + 1`).toEvaluateTo(3001)
|
||||
})
|
||||
|
||||
test('chained dot get as function argument', () => {
|
||||
const double = (x: number) => x * 2
|
||||
expect(`obj = [val=[num=21]]; double obj.val.num`).toEvaluateTo(42, { double })
|
||||
})
|
||||
|
||||
test('chained dot get in binary operation', () => {
|
||||
expect(`a = [x=[y=10]]; b = [x=[y=20]]; a.x.y + b.x.y`).toEvaluateTo(30)
|
||||
})
|
||||
|
||||
test('chained dot get with parens at end', () => {
|
||||
expect(`idx = 1; obj = [items=[10 20 30]]; obj.items.(idx)`).toEvaluateTo(20)
|
||||
})
|
||||
|
||||
test('mixed chained and simple dot get', () => {
|
||||
expect(`obj = [a=1 b=[c=2]]; obj.a + obj.b.c`).toEvaluateTo(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('default params', () => {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ describe('pipe expressions', () => {
|
|||
`).toEvaluateTo(5)
|
||||
})
|
||||
|
||||
|
||||
test('string literals can be piped', () => {
|
||||
expect(`'hey there' | str.to-upper`).toEvaluateTo('HEY THERE')
|
||||
})
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
|
|||
)
|
||||
}
|
||||
|
||||
if (object.type.id !== terms.IdentifierBeforeDot) {
|
||||
if (object.type.id !== terms.IdentifierBeforeDot && object.type.id !== terms.Dollar) {
|
||||
throw new CompilerError(
|
||||
`DotGet object must be an IdentifierBeforeDot, got ${object.type.name}`,
|
||||
object.from,
|
||||
|
|
@ -301,9 +301,9 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
|
|||
)
|
||||
}
|
||||
|
||||
if (![terms.Identifier, terms.Number, terms.ParenExpr].includes(property.type.id)) {
|
||||
if (![terms.Identifier, terms.Number, terms.ParenExpr, terms.DotGet].includes(property.type.id)) {
|
||||
throw new CompilerError(
|
||||
`DotGet property must be an Identifier or Number, got ${property.type.name}`,
|
||||
`DotGet property must be an Identifier, Number, ParenExpr, or DotGet, got ${property.type.name}`,
|
||||
property.from,
|
||||
property.to
|
||||
)
|
||||
|
|
@ -311,7 +311,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
|
|||
|
||||
const objectName = input.slice(object.from, object.to)
|
||||
|
||||
return { objectName, property }
|
||||
return { object, objectName, property }
|
||||
}
|
||||
|
||||
export const getTryExprParts = (node: SyntaxNode, input: string) => {
|
||||
|
|
|
|||
12
src/index.ts
12
src/index.ts
|
|
@ -4,7 +4,7 @@ import { type Tree } from '@lezer/common'
|
|||
import { Compiler } from '#compiler/compiler'
|
||||
import { parser } from '#parser/shrimp'
|
||||
import { globals as parserGlobals, setGlobals as setParserGlobals } from '#parser/tokenizer'
|
||||
import { globals as shrimpGlobals } from '#prelude'
|
||||
import { globals as prelude } from '#prelude'
|
||||
|
||||
export { Compiler } from '#compiler/compiler'
|
||||
export { parser } from '#parser/shrimp'
|
||||
|
|
@ -19,7 +19,7 @@ export class Shrimp {
|
|||
|
||||
constructor(globals?: Record<string, any>) {
|
||||
const emptyBytecode = { instructions: [], constants: [], labels: new Map() }
|
||||
this.vm = new VM(emptyBytecode, Object.assign({}, shrimpGlobals, globals ?? {}))
|
||||
this.vm = new VM(emptyBytecode, Object.assign({}, prelude, globals ?? {}))
|
||||
this.globals = globals
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ export class Shrimp {
|
|||
let bytecode
|
||||
|
||||
if (typeof code === 'string') {
|
||||
const compiler = new Compiler(code, Object.keys(Object.assign({}, shrimpGlobals, this.globals ?? {}, locals ?? {})))
|
||||
const compiler = new Compiler(code, Object.keys(Object.assign({}, prelude, this.globals ?? {}, locals ?? {})))
|
||||
bytecode = compiler.bytecode
|
||||
} else {
|
||||
bytecode = code
|
||||
|
|
@ -79,7 +79,7 @@ export async function runCode(code: string, globals?: Record<string, any>): Prom
|
|||
}
|
||||
|
||||
export async function runBytecode(bytecode: Bytecode, globals?: Record<string, any>): Promise<any> {
|
||||
const vm = new VM(bytecode, Object.assign({}, shrimpGlobals, globals))
|
||||
const vm = new VM(bytecode, Object.assign({}, prelude, globals))
|
||||
await vm.run()
|
||||
return vm.stack.length ? fromValue(vm.stack[vm.stack.length - 1]!, vm) : null
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ export function compileFile(path: string, globals?: Record<string, any>): Byteco
|
|||
}
|
||||
|
||||
export function compileCode(code: string, globals?: Record<string, any>): Bytecode {
|
||||
const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])]
|
||||
const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])]
|
||||
const compiler = new Compiler(code, globalNames)
|
||||
return compiler.bytecode
|
||||
}
|
||||
|
|
@ -102,7 +102,7 @@ export function parseFile(path: string, globals?: Record<string, any>): Tree {
|
|||
|
||||
export function parseCode(code: string, globals?: Record<string, any>): Tree {
|
||||
const oldGlobals = [...parserGlobals]
|
||||
const globalNames = [...Object.keys(shrimpGlobals), ...(globals ? Object.keys(globals) : [])]
|
||||
const globalNames = [...Object.keys(prelude), ...(globals ? Object.keys(globals) : [])]
|
||||
|
||||
setParserGlobals(globalNames)
|
||||
const result = parser.parse(code)
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
("-" | "+")? $[0-9]+ ("_"? $[0-9]+)* ('.' $[0-9]+ ("_"? $[0-9]+)*)?
|
||||
}
|
||||
Boolean { "true" | "false" }
|
||||
newlineOrSemicolon { "\n" | ";" }
|
||||
semicolon { ";" }
|
||||
eof { @eof }
|
||||
space { " " | "\t" }
|
||||
Comment { "#" ![\n]* }
|
||||
|
|
@ -29,10 +29,13 @@
|
|||
rightParen { ")" }
|
||||
colon[closedBy="end", @name="colon"] { ":" }
|
||||
Underscore { "_" }
|
||||
Dollar { "$" }
|
||||
Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
|
||||
"|"[@name=operator]
|
||||
}
|
||||
|
||||
newlineOrSemicolon { newline | semicolon }
|
||||
|
||||
end { @specialize[@name=keyword]<Identifier, "end"> }
|
||||
while { @specialize[@name=keyword]<Identifier, "while"> }
|
||||
if { @specialize[@name=keyword]<Identifier, "if"> }
|
||||
|
|
@ -45,6 +48,7 @@ import { @specialize[@name=keyword]<Identifier, "import"> }
|
|||
null { @specialize[@name=Null]<Identifier, "null"> }
|
||||
|
||||
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, CurlyString }
|
||||
@external tokens pipeStartsLineTokenizer from "./tokenizer" { newline, pipeStartsLine }
|
||||
@external specialize {Identifier} specializeKeyword from "./tokenizer" { Do }
|
||||
|
||||
@precedence {
|
||||
|
|
@ -84,7 +88,7 @@ consumeToTerminator {
|
|||
}
|
||||
|
||||
PipeExpr {
|
||||
pipeOperand (!pipe "|" pipeOperand)+
|
||||
pipeOperand (!pipe (pipeStartsLine? "|") newlineOrSemicolon* pipeOperand)+
|
||||
}
|
||||
|
||||
pipeOperand {
|
||||
|
|
@ -239,7 +243,8 @@ expression {
|
|||
|
||||
@skip {} {
|
||||
DotGet {
|
||||
IdentifierBeforeDot dot (Number | Identifier | ParenExpr)
|
||||
IdentifierBeforeDot dot (DotGet | Number | Identifier | ParenExpr) |
|
||||
Dollar dot (DotGet | Number | Identifier | ParenExpr)
|
||||
}
|
||||
|
||||
String {
|
||||
|
|
@ -254,7 +259,7 @@ stringContent {
|
|||
}
|
||||
|
||||
Interpolation {
|
||||
"$" Identifier |
|
||||
"$" FunctionCallOrIdentifier |
|
||||
"$" ParenExpr
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,47 +32,50 @@ export const
|
|||
Word = 30,
|
||||
IdentifierBeforeDot = 31,
|
||||
CurlyString = 32,
|
||||
newline = 101,
|
||||
pipeStartsLine = 102,
|
||||
Do = 33,
|
||||
Comment = 34,
|
||||
Program = 35,
|
||||
PipeExpr = 36,
|
||||
WhileExpr = 38,
|
||||
keyword = 83,
|
||||
keyword = 84,
|
||||
ConditionalOp = 40,
|
||||
ParenExpr = 41,
|
||||
FunctionCallWithNewlines = 42,
|
||||
DotGet = 43,
|
||||
Number = 44,
|
||||
PositionalArg = 45,
|
||||
FunctionDef = 46,
|
||||
Params = 47,
|
||||
NamedParam = 48,
|
||||
NamedArgPrefix = 49,
|
||||
String = 50,
|
||||
StringFragment = 51,
|
||||
Interpolation = 52,
|
||||
EscapeSeq = 53,
|
||||
DoubleQuote = 54,
|
||||
Boolean = 55,
|
||||
Null = 56,
|
||||
colon = 57,
|
||||
CatchExpr = 58,
|
||||
Block = 60,
|
||||
FinallyExpr = 61,
|
||||
Underscore = 64,
|
||||
NamedArg = 65,
|
||||
IfExpr = 66,
|
||||
FunctionCall = 68,
|
||||
ElseIfExpr = 69,
|
||||
ElseExpr = 71,
|
||||
FunctionCallOrIdentifier = 72,
|
||||
BinOp = 73,
|
||||
Regex = 74,
|
||||
Dict = 75,
|
||||
Array = 76,
|
||||
FunctionCallWithBlock = 77,
|
||||
TryExpr = 78,
|
||||
Throw = 80,
|
||||
Import = 82,
|
||||
CompoundAssign = 84,
|
||||
Assign = 85
|
||||
Dollar = 45,
|
||||
PositionalArg = 46,
|
||||
FunctionDef = 47,
|
||||
Params = 48,
|
||||
NamedParam = 49,
|
||||
NamedArgPrefix = 50,
|
||||
String = 51,
|
||||
StringFragment = 52,
|
||||
Interpolation = 53,
|
||||
FunctionCallOrIdentifier = 54,
|
||||
EscapeSeq = 55,
|
||||
DoubleQuote = 56,
|
||||
Boolean = 57,
|
||||
Null = 58,
|
||||
colon = 59,
|
||||
CatchExpr = 60,
|
||||
Block = 62,
|
||||
FinallyExpr = 63,
|
||||
Underscore = 66,
|
||||
NamedArg = 67,
|
||||
IfExpr = 68,
|
||||
FunctionCall = 70,
|
||||
ElseIfExpr = 71,
|
||||
ElseExpr = 73,
|
||||
BinOp = 74,
|
||||
Regex = 75,
|
||||
Dict = 76,
|
||||
Array = 77,
|
||||
FunctionCallWithBlock = 78,
|
||||
TryExpr = 79,
|
||||
Throw = 81,
|
||||
Import = 83,
|
||||
CompoundAssign = 85,
|
||||
Assign = 86
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||
import {LRParser, LocalTokenGroup} from "@lezer/lr"
|
||||
import {operatorTokenizer} from "./operatorTokenizer"
|
||||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||
import {tokenizer, pipeStartsLineTokenizer, specializeKeyword} from "./tokenizer"
|
||||
import {trackScope} from "./parserScopeContext"
|
||||
import {highlighting} from "./highlight"
|
||||
const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166}
|
||||
const spec_Identifier = {__proto__:null,while:78, null:116, catch:122, finally:128, end:130, if:138, else:144, try:160, throw:164, import:168}
|
||||
export const parser = LRParser.deserialize({
|
||||
version: 14,
|
||||
states: "=|QYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4|ObO,59sO5XQbO'#DbO5aQWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5uOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6TQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6_QbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7XQcO'#DvO7`QcO'#EiO7gQRO,59qO7qQQO,59qO7vQQO,59qO8OQQO,59qO8ZQRO,59qO8sQRO,59qO8zQQO'#DQO9PQbO,5:fO9WQQO,5:eOOQa,5:f,5:fO9cQbO,5:fO9mQbO,5:oO9mQbO,5:nO:}QbO,5:gO;UQbO,59lOOQ`,5;Q,5;QO9mQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;pQbO'#D]O;{QbO'#D^OOQO'#EY'#EYO;sQQO'#D]O<aQQO,59vO<fQcO'#EhO=cQRO'#EvO>`QRO'#EvOOQO'#Ev'#EvO>gQQO,5:[O>lQRO,59nO>sQRO,59nO:}QbO,5:hO?RQcO,5:jO@aQcO,5:jO@}QcO,5:jOArQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCkQcO1G/}OCuQcO1G/}OETQcO1G/}OE_QcO1G/}OElQcO1G/}OOQa1G/[1G/[OF}QcO1G/[OGUQcO1G/[OG]QcO1G/[OH[QcO1G/[OGdQcO1G/[OOQ`-E8U-E8UOHrQRO1G/]OH|QQO1G/]OIRQQO1G/]OIZQQO1G/]OIfQRO1G/]OImQRO1G/]OItQbO,59rOJOQQO1G/]OOQa1G/]1G/]OJWQQO1G0POOQa1G0Q1G0QOJcQbO1G0QOOQO'#E`'#E`OJWQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJcQbO1G0QOJmQbO1G0ZOKXQbO1G0YOKsQbO'#DjOLUQbO'#DjOLiQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLtQQO,59wOOQO,59x,59xOOQO-E8W-E8WOL|QbO1G/bO:}QbO1G/vO:}QbO1G/YOMTQbO1G0SOM`QbO1G0WOM}QbO1G0WOOQ`-E8`-E8`ONUQQO7+$wOOQa7+$w7+$wON^QQO1G/^ONfQQO7+%kOOQa7+%k7+%kONqQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ON{QQO'#E[O! TQbO'#EuOOQ`,5:U,5:UO! hQbO'#DhO! mQQO'#DkOOQ`7+%m7+%mO! rQbO7+%mO! wQbO7+%mO!!PQbO7+$|O!!_QbO7+$|O!!oQbO7+%bO!!wQbO7+$tOOQ`7+%n7+%nO!!|QbO7+%nO!#RQbO7+%nO!#ZQbO7+%rOOQa<<Hc<<HcO!#xQbO7+$xO!$VQQO7+$xOOQa<<IV<<IVOOQa<<IW<<IWOOQ`,5:v,5:vOOQ`-E8Y-E8YO!$_QQO,5:SO:}QbO,5:VOOQ`<<IX<<IXO!$dQbO<<IXOOQ`<<Hh<<HhO!$iQbO<<HhO!$nQbO<<HhO!$vQbO<<HhOOQ`'#E_'#E_O!%RQbO<<H|O!%ZQbO'#DuOOQ`<<H|<<H|O!%cQbO<<H|OOQ`<<H`<<H`OOQ`<<IY<<IYO!%hQbO<<IYOOQO,5:w,5:wO!%mQbO<<HdOOQO-E8Z-E8ZO:}QbO1G/nOOQ`1G/q1G/qOOQ`AN>sAN>sOOQ`AN>SAN>SO!%zQbOAN>SO!&PQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&XQbOAN>hO2yQbO,5:_O:}QbO,5:aOOQ`AN>tAN>tPItQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&^QbOG23nP!%^QbO'#DsOOQ`G24SG24SO!&cQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:}QbO7+%eOOQ`<<IP<<IP",
|
||||
stateData: "!&k~O#XOSrOS~OlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_dO#bQO#mYO#nZO~O#`mO~O!TpO#bsO#dnO#eoO~OlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu!jX!Z!jX#l!jX~O#_!jX#p!jX!]!jX!`!jX!a!jX!h!jX~P#QOlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#]XQ#]XR#]XS#]XT#]XU#]XW#]XX#]XY#]XZ#]X[#]X]#]X^#]Xd#]Xe#]Xf#]Xg#]Xh#]Xi#]Xj#]Xu#]X#l#]X~O#_#]X#p#]X!Z#]X!]#]X!`#]X!a#]X!h#]X~P&TOP{OQ{OR|OS|OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OO^zOd}Oe}Of}Og}Oh}Oi}Oj!RO~OP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}Ou#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P*hOl!UOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_!SO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!RuO!WRO!X[O!Y[O!l[O#^XO#_!SO#bQO#mYO#nZO~O#o!aO~P-ZOV!cO#_#]X#p#]X!]#]X!`#]X!a#]X!h#]X~P'VOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P.|Ou#ZX#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~OT!POU!QOj!RO~P0zOV!cO_!dO`!dOa!dOb!dOc!dOk!dO~O!Z!eO~P0zOu!hO#_!gO#p!gO~Ol!jO!R!lO!Z!PP~Ol!pOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~O!Z!wO~Ol!jO!RuO~Ol#OO|#OO#^XO~Ol#PO#^XO~O#b#QO#d#QO#e#QO#f#QO#g#QO#h#QO~O!TpO#b#SO#dnO#eoO~OqgO!b#TO~P3tOqgO!RuO!btOu!fa!Z!fa#_!fa#p!fa#l!fa!]!fa!`!fa!a!fa!h!fa~P3tO#_!SO~P#QO#_!SO~P&TO#_!SO#l#lO~P*hO#l#lO~O#l#lOu#ZX~O!Z!eO#l#lOu#ZX~O#l#lO~P.|OT!POU!QOj!RO#_!SOu#ZX~O#l#lO~P8bOu!hO~O#o#nO~P-ZO!RuO#_#pO#o#rO~O#_#sO#o#nO~P3tOlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#bQO#mYO#nZO~O#_#xO~P9mOu!hO#_ta#pta#lta!]ta!`ta!ata!hta~Ol!jO!R!lO!Z!PX~OpRO|$OO!WRO!X$OO!Y$OO#bQO~O!Z$QO~OqgO!RuO!btOT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[Xj#[X!Z#[X~P3tOT!POU!QOj!RO!Z#jX~OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OOj!RO~O!Z#jX~P=qO!Z$RO~O!Z$SO~P=qOT!POU!QOj!RO!Z$SO~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P)WOP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}O~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P?oOT!POU!QOj!ROu!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~Ol!jO!RuOu!ta#_!ta#p!ta!]!ta!`!ta!a!ta#l!ta!h!ta~O^zOR!kiS!kid!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OP!kiQ!ki~PBdOP{OQ{O~PBdOP{OQ{Od!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OR!kiS!ki~PDPOR|OS|O^zO~PDPOR|OS|O~PDPOW!OOX!OOY!OOZ!OO[!OO]!OOTxijxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OU!QO~PEvOU!QO~PFYOUxi~PEvOT!POU!QOjxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OW!OOX!OOY!OOZ!OO[!OO]!OO~PGdO#_!SO#l$YO~P*hO#l$YO~O#l$YOu#ZX~O!Z!eO#l$YOu#ZX~O#l$YO~P.|O#l$YO~P8bOqgO!btO~P-ZO#_!SO#l$YO~O!RuO#_#pO#o$]O~O#_#sO#o$_O~P3tOu!hO#_!wi#p!wi!]!wi!`!wi!a!wi#l!wi!h!wi~Ou!hO#_!vi#p!vi!]!vi!`!vi!a!vi#l!vi!h!vi~Ou!hO!]!^X!`!^X!a!^X!h!^X~O#_$bO!]#iP!`#iP!a#iP!h#iP~P9mO!]$fO!`$gO!a$hO~O!R!lO!Z!Pa~O#_$lO~P9mO!]$fO!`$gO!a$oO~O!RuOu!ti#_!ti#p!ti!]!ti!`!ti!a!ti#l!ti!h!ti~Ol!jO~PM`O#_!SO#l$sO~O#_!SO#lzi~O!RuO#_#pO#o$vO~O#_#sO#o$wO~P3tOu!hO#_$xO~O#_$bO!]#iX!`#iX!a#iX!h#iX~P9mOl$zO~O!Z${O~O!a$|O~O!`$gO!a$|O~Ou!hO!]$fO!`$gO!a%OO~O#_$bO!]#iP!`#iP!a#iP~P9mO!a%VO!h%UO~O!a%XO~O!a%YO~O!`$gO!a%YO~O!RuOu!tq#_!tq#p!tq!]!tq!`!tq!a!tq#l!tq!h!tq~OqgO!btO#lzq~P-ZO#_!SO#lzq~O!Z%_O~O!a%aO~O!a%bO~O!`$gO!a%bO~O!]$fO!`$gO!a%bO~O!a%fO!h%UO~O!Z%iO!e%hO~O!a%fO~O!a%jO~OqgO!btO#lzy~P-ZO!a%mO~O!`$gO!a%mO~O!a%pO~O!a%sO~O!Z%tO~O|!l~",
|
||||
goto: "9Z#lPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#mP$WP$m%k&y'PP(Z(g)a)dP)jP*q*qPPPP*uP+R+kPPP,R#mP,s-^P-b-h-}P.t/x$W$WP$WP$WP$W$W1O1U1b2U2d2n2t2{3R3]3c3m3wPPP4V4Z5O6tPPP8OP8`PPPPP8d8j8praOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!YXR#f!TwaOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tr_Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!]XS!qh%hQ!viQ!zkQ#]!QQ#_!PQ#b!RR#i!TvTOfh!c!d!e!h!w#x$Q$R$S$d$l${%_%h%i%t!W[STZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!VX!TQ#OmR#PnQ!XXR#e!TrSOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t!WySTZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!UX!TT!ph%hevSTx!U!V!p#j$t%]%kraOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!YXQ#TuR#f!TR!ogX!mg!k!n#}#S[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%tR$O!lTpQrQ$j#yQ$q$TQ%Q$kR%d%RQ#y!eQ$T!wQ$m$RQ$n$SQ%`${Q%l%_Q%r%iR%u%tQ$i#yQ$p$TQ$}$jQ%P$kQ%Z$qS%c%Q%RR%n%ddvSTx!U!V!p#j$t%]%kQ!`Z[!|l!{!}$U$V$rQ#m!_X#p!`#m#q$[vUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tT!sh%hT%S$m%TQ%W$mR%g%TwUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%trWOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!WXQ!ykQ#V{Q#Y|Q#[}R#d!T#T[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%t![[STZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kw]OXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQfOR!if^!fc!^#u#v#w$c$kR#z!fQ!TXQ!_Z`#c!T!_#j#k$X$t%]%kS#j!U!VS#k!W!]S$X#d#iQ$t$ZR%]$uQ!kgQ!{lU#|!k!{$VR$V!}Q!ngQ#}!kT$P!n#}QrQR#RrS$d#x$lR$y$dQ$u$ZR%^$uYxST!U!V!pR#UxQ%T$mR%e%TQ#q!`Q$[#mT$`#q$[Q#t!bQ$^#oT$a#t$^Q!}lQ$U!{U$W!}$U$rR$r$VTeOfScOfS!^X!TQ#u!cQ#v!d`#w!e!w$R$S${%_%i%tQ#{!hU$c#x$d$lR$k$QvVOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!bZS!rh%hQ!uiQ!xkQ#TuQ#VzQ#W{Q#X|Q#Z}Q#]!OQ#^!PQ#`!QQ#a!RQ#o!_X#s!b#o#t$^r^Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t![ySTZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kQ![XR#h!T[wSTx!U!V!pQ$Z#jV%[$t%]%kTqQrQ$e#xR%R$lQ!thR%q%hrbOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!ZXR#g!T",
|
||||
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 NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq DoubleQuote 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 Import keyword CompoundAssign Assign",
|
||||
maxTerm: 124,
|
||||
states: "?[QYQ!SOOOOQ!Q'#Ek'#EkO!sO!bO'#DXO%kQ!TO'#DdO&UOSO'#DaOOQ!R'#Da'#DaO)SQ!TO'#EnOOQ!Q'#E{'#E{O)pQRO'#DxO+xQ!TO'#EjO,fQ!SO'#DVOOQ!R'#Dz'#DzO/WQ!SO'#D{OOQ!R'#En'#EnO/_Q!TO'#EnO1cQ!TO'#EmO2qQ!TO'#EjO3OQRO'#ETOOQ!Q'#Ej'#EjO3gQ!SO'#EjO3nQrO'#EiOOQ!Q'#Ei'#EiOOQ!Q'#EV'#EVQYQ!SOOO4PQbO'#D]O4[QbO'#DrO5YQbO'#DSO6WQQO'#D}O5YQbO'#EPO6]QbO'#ERO6eObO,59sOOQ!Q'#D['#D[O6vQbO'#DqOOQ!Q'#Eq'#EqOOQ!Q'#E_'#E_O7QQ!SO,5:`OOQ!R'#Em'#EmO8QQbO'#DcO8`QWO'#DeOOOO'#Es'#EsOOOO'#E['#E[O8tOSO,59{OOQ!R,59{,59{O5YQbO,5:dO5YQbO,5:dO5YQbO,5:dO5YQbO,5:dO5YQbO,59pO5YQbO,59pO5YQbO,59pO5YQbO,59pOOQ!Q'#EX'#EXO,fQ!SO,59qO9SQ!TO'#DdO9^Q!TO'#EnO9hQsO,59qO9uQQO,59qO9zQrO,59qO:VQrO,59qO:eQsO,59qO;TQsO,59qO;[QrO'#DQO;dQ!SO,5:gO;kQrO,5:fOOQ!R,5:g,5:gO;yQ!SO,5:gO<WQbO,5:pO<WQbO,5:oOYQ!SO,5:hO=kQ!SO,59lOOQ!Q,5;T,5;TOYQ!SO'#EWO>]QQO'#EWOOQ!Q-E8T-E8TOOQ!Q'#EY'#EYO>bQbO'#D^O>mQbO'#D_OOQO'#EZ'#EZO>eQQO'#D^O?RQQO,59wO?WQcO'#EmO@TQRO'#EzOAQQRO'#EzOOQO'#Ez'#EzOAXQQO,5:^OA^QRO,59nOAeQRO,59nOYQ!SO,5:iOAsQ!TO,5:kOCXQ!TO,5:kOC{Q!TO,5:kODYQ!SO,5:mOOQ!Q'#Ec'#EcO6]QbO,5:mOOQ!R1G/_1G/_OOQ!Q,5:],5:]OOQ!Q-E8]-E8]OOOO'#Dd'#DdOOOO,59},59}OOOO,5:P,5:POOOO-E8Y-E8YOOQ!R1G/g1G/gOOQ!R1G0O1G0OOF_Q!TO1G0OOFiQ!TO1G0OOG}Q!TO1G0OOHXQ!TO1G0OOHfQ!TO1G0OOOQ!R1G/[1G/[OI}Q!TO1G/[OJUQ!TO1G/[OJ]Q!TO1G/[OKbQ!TO1G/[OJdQ!TO1G/[OOQ!Q-E8V-E8VOKxQsO1G/]OLVQQO1G/]OL[QrO1G/]OLgQrO1G/]OLuQsO1G/]OL|QsO1G/]OMTQ!SO,59rOM_QrO1G/]OOQ!R1G/]1G/]OMjQrO1G0QOOQ!R1G0R1G0ROMxQ!SO1G0ROOQp'#Ea'#EaOMjQrO1G0QOOQ!R1G0Q1G0QOOQ!Q'#Eb'#EbOMxQ!SO1G0RONVQ!SO1G0[ONwQ!SO1G0ZO! iQ!SO'#DlO! }Q!SO'#DlO!!_QbO1G0SOOQ!Q-E8U-E8UOYQ!SO,5:rOOQ!Q,5:r,5:rOYQ!SO,5:rOOQ!Q-E8W-E8WO!!jQQO,59xOOQO,59y,59yOOQO-E8X-E8XOYQ!SO1G/cOYQ!SO1G/xOYQ!SO1G/YO!!rQbO1G0TO!!}Q!SO1G0XO!#rQ!SO1G0XOOQ!Q-E8a-E8aO!#yQrO7+$wOOQ!R7+$w7+$wO!$UQrO1G/^O!$aQrO7+%lOOQ!R7+%l7+%lO!$oQ!SO7+%mOOQ!R7+%m7+%mOOQp-E8_-E8_OOQ!Q-E8`-E8`OOQ!Q'#E]'#E]O!$|QrO'#E]O!%[Q!SO'#EyOOQ`,5:W,5:WO!%lQbO'#DjO!%qQQO'#DmOOQ!Q7+%n7+%nO!%vQbO7+%nO!%{QbO7+%nOOQ!Q1G0^1G0^OYQ!SO1G0^O!&TQ!SO7+$}O!&fQ!SO7+$}O!&sQbO7+%dO!&{QbO7+$tOOQ!Q7+%o7+%oO!'QQbO7+%oO!'VQbO7+%oO!'_Q!SO7+%sOOQ!R<<Hc<<HcO!(SQ!SO7+$xO!(aQrO7+$xOOQ!R<<IW<<IWOOQ!R<<IX<<IXOOQ!Q,5:w,5:wOOQ!Q-E8Z-E8ZO!(lQQO,5:UOYQ!SO,5:XOOQ!Q<<IY<<IYO!(qQbO<<IYOOQ!Q7+%x7+%xOOQ!Q<<Hi<<HiO!(vQbO<<HiO!({QbO<<HiO!)TQbO<<HiOOQ`'#E`'#E`O!)`QbO<<IOO!)hQbO'#DwOOQ!Q<<IO<<IOO!)pQbO<<IOOOQ!Q<<H`<<H`OOQ!Q<<IZ<<IZO!)uQbO<<IZOOQp,5:x,5:xO!)zQ!SO<<HdOOQp-E8[-E8[OYQ!SO1G/pOOQ`1G/s1G/sOOQ!QAN>tAN>tOOQ!QAN>TAN>TO!*XQbOAN>TO!*^QbOAN>TOOQ`-E8^-E8^OOQ!QAN>jAN>jO!*fQbOAN>jO4[QbO,5:aOYQ!SO,5:cOOQ!QAN>uAN>uPMTQ!SO'#EXOOQ`7+%[7+%[OOQ!QG23oG23oO!*kQbOG23oP!)kQbO'#DuOOQ!QG24UG24UO!*pQQO1G/{OOQ`1G/}1G/}OOQ!QLD)ZLD)ZOYQ!SO7+%gOOQ`<<IR<<IRO!*uObO,59sO!+WO!bO'#DX",
|
||||
stateData: "!+`~O#[OSrOS~OlROmaOn]OoQOpTOqhOwjO|]O}QO!YTO!Z]O![]O!giO!m]O!rkO!tlO!vmO#XPO#`PO#cYO#fSO#qZO#r[O~O#dnO~OltOn]OoQOpTOqhO|]O}QO!SpO!YTO!Z]O![]O!doO!m]O#cYO#fSO#qZO#r[OP#aXQ#aXR#aXS#aXT#aXU#aXW#aXX#aXY#aXZ#aX[#aX]#aX^#aXd#aXe#aXf#aXg#aXh#aXi#aXj#aXu!WX!]!WX#Y!WX#p!WX~O#X!WX#`!WX#t!WX!_!WX!b!WX!c!WX!j!WX~P!xO!UwO#fzO#huO#ivO~OltOn]OoQOpTOqhO|]O}QO!SpO!YTO!Z]O![]O!doO!m]O#cYO#fSO#qZO#r[OP#bXQ#bXR#bXS#bXT#bXU#bXW#bXX#bXY#bXZ#bX[#bX]#bX^#bXd#bXe#bXf#bXg#bXh#bXi#bXj#bXu#bX#Y#bX#p#bX~O#X#bX#`#bX#t#bX!]#bX!_#bX!b#bX!c#bX!j#bX~P&dOP|OQ|OR}OS}OT!QOU!ROW!POX!POY!POZ!PO[!PO]!PO^{Od!OOe!OOf!OOg!OOh!OOi!OOj!SO~OP|OQ|OR}OS}Od!OOe!OOf!OOg!OOh!OOi!OOu#^X#Y#^X~O#X#^X#`#^X#t#^X!_#^X!b#^X!c#^X#p#^X!j#^X~P+QOl!VOmaOn]OoQOpTOqhOwjO|]O}QO!YTO!Z]O![]O!giO!m]O!rkO!tlO!vmO#XPO#`PO#cYO#fSO#qZO#r[O~OltOn]OoQOpTO|]O}QO!SpO!YTO!Z]O![]O!m]O#XPO#`PO#cYO#fSO#qZO#r[O~O#s!bO~P.POV!dO#X#bX#`#bX#t#bX!_#bX!b#bX!c#bX!j#bX~P'iOP#aXQ#aXR#aXS#aXT#aXU#aXW#aXX#aXY#aXZ#aX[#aX]#aX^#aXd#aXe#aXf#aXg#aXh#aXi#aXj#aXu#^X#Y#^X~O#X#^X#`#^X#t#^X!_#^X!b#^X!c#^X#p#^X!j#^X~P/{Ou#^X#X#^X#Y#^X#`#^X#t#^X!_#^X!b#^X!c#^X#p#^X!j#^X~OT!QOU!ROj!SO~P2POV!dO_!eO`!eOa!eOb!eOc!eOk!eO~O!]!fO~P2POu!iO#XPO#Y!jO#`PO#t!hO~Ol!lO!S!nO!]!QP~Ol!rOn]OoQOpTO|]O}QO!YTO!Z]O![]O!m]O#cYO#fSO#qZO#r[O~OltOn]OoQOpTO|]O}QO!YTO!Z]O![]O!m]O#cYO#fSO#qZO#r[O~O!]!yO~Ol!lO!SpO~Ol#QOoQO|#QO}QO#cYO~OqhO!d#RO~P5YOqhO!SpO!doOu!ha!]!ha#X!ha#Y!ha#`!ha#t!ha#p!ha!_!ha!b!ha!c!ha!j!ha~P5YOl#TOo&PO}&PO#cYO~O#f#VO#h#VO#i#VO#j#VO#k#VO#l#VO~O!UwO#f#XO#huO#ivO~O#XPO#`PO~P!xO#XPO#`PO~P&dO#XPO#`PO#p#oO~P+QO#p#oO~O#p#oOu#^X#Y#^X~O!]!fO#p#oOu#^X#Y#^X~O#p#oO~P/{OT!QOU!ROj!SO#XPO#`POu#^X#Y#^X~O#p#oO~P:lOu!iO#Y!jO~O#s#qO~P.PO!SpO#XPO#`PO#s#uO~O#XPO#`PO#s#qO~P5YOlROmaOn]OoQOpTOqhOwjO|]O}QO!YTO!Z]O![]O!giO!m]O!rkO!tlO!vmO#cYO#fSO#qZO#r[O~Ou!iO#Y!jO#Xta#`ta#tta#pta!_ta!bta!cta!jta~Ou$QO~Ol!lO!S!nO!]!QX~OpTO|$TO!YTO!Z$TO![$TO#fSO~O!]$VO~OqhO!SpO!doOT#aXU#aXW#aXX#aXY#aXZ#aX[#aX]#aXj#aX!]#aX~P5YOT!QOU!ROj!SO!]#nX~OT!QOU!ROW!POX!POY!POZ!PO[!PO]!POj!SO~O!]#nX~P@cO!]$WO~O!]$XO~P@cOT!QOU!ROj!SO!]$XO~Ou!sa#X!sa#Y!sa#`!sa#t!sa!_!sa!b!sa!c!sa#p!sa!j!sa~P)pOu!sa#X!sa#Y!sa#`!sa#t!sa!_!sa!b!sa!c!sa#p!sa!j!sa~OP|OQ|OR}OS}Od!OOe!OOf!OOg!OOh!OOi!OO~PBgOT!QOU!ROj!SO~PBgOl!lO!SpOu!ua#X!ua#Y!ua#`!ua#t!ua!_!ua!b!ua!c!ua#p!ua!j!ua~O^{OR!liS!lid!lie!lif!lig!lih!lii!liu!li#X!li#Y!li#`!li#t!li#p!li!_!li!b!li!c!li!j!li~OP!liQ!li~PEQOP|OQ|O~PEQOP|OQ|Od!lie!lif!lig!lih!lii!liu!li#X!li#Y!li#`!li#t!li#p!li!_!li!b!li!c!li!j!li~OR!liS!li~PFsOR}OS}O^{O~PFsOR}OS}O~PFsOW!POX!POY!POZ!PO[!PO]!POTxijxiuxi#Xxi#Yxi#`xi#txi#pxi!]xi!_xi!bxi!cxi!jxi~OU!RO~PHpOU!RO~PISOUxi~PHpOT!QOU!ROjxiuxi#Xxi#Yxi#`xi#txi#pxi!]xi!_xi!bxi!cxi!jxi~OW!POX!POY!POZ!PO[!PO]!PO~PJdO#XPO#`PO#p$_O~P+QO#p$_O~O#p$_Ou#^X#Y#^X~O!]!fO#p$_Ou#^X#Y#^X~O#p$_O~P/{O#p$_O~P:lOqhO!doO~P.PO#XPO#`PO#p$_O~O!SpO#XPO#`PO#s$bO~O#XPO#`PO#s$dO~P5YOu!iO#Y!jO#X!xi#`!xi#t!xi!_!xi!b!xi!c!xi#p!xi!j!xi~Ou!iO#Y!jO#X!wi#`!wi#t!wi!_!wi!b!wi!c!wi#p!wi!j!wi~Ou!iO#Y!jO!_!`X!b!`X!c!`X!j!`X~O!_#mP!b#mP!c#mP!j#mP~PYO!_$kO!b$lO!c$mO~O!S!nO!]!Qa~O!_$kO!b$lO!c$vO~O!SpOu!ui#X!ui#Y!ui#`!ui#t!ui!_!ui!b!ui!c!ui#p!ui!j!ui~Ol!lO~P!!}O#XPO#`PO#p$zO~O#XPO#`PO#pzi~O!SpO#XPO#`PO#s$}O~O#XPO#`PO#s%OO~P5YOu!iO#XPO#Y!jO#`PO~O!_#mX!b#mX!c#mX!j#mX~PYOl%RO~O!]%SO~O!c%TO~O!b$lO!c%TO~Ou!iO!_$kO!b$lO!c%WO#Y!jO~O!_#mP!b#mP!c#mP~PYO!c%_O!j%^O~O!c%aO~O!c%bO~O!b$lO!c%bO~O!SpOu!uq#X!uq#Y!uq#`!uq#t!uq!_!uq!b!uq!c!uq#p!uq!j!uq~OqhO!doO#pzq~P.PO#XPO#`PO#pzq~O!]%gO~O!c%iO~O!c%jO~O!b$lO!c%jO~O!_$kO!b$lO!c%jO~O!c%nO!j%^O~O!]%qO!g%pO~O!c%nO~O!c%rO~OqhO!doO#pzy~P.PO!c%uO~O!b$lO!c%uO~O!c%xO~O!c%{O~O!]%|O~Ol#QOo&PO|#QO}&PO#cYO~O#d&OO~O|!m~",
|
||||
goto: "<[#pPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#qP$_P$w%x'['bPP(v)S*P*SP*YP+d+h+dPPPP,TP,a,yPPP-a#qP.R.oP.s.yP/s0z$_$_P$_P$_P$_$_2T2Z2g3c3q3{4R4Y4`4j4p4z5UPPPPP5d5h6dP7v9oPP:|P;^PPPPP;b;h;nxbOg!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|Q!ZYR#i!U}bOYg!U!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|x`Og!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|Q!^YS!si%pQ!xjQ!|lQ#`!RQ#b!QQ#e!SR#l!U|UOgi!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%p%q%|!W]RU[jlps{|}!O!P!Q!R!S!V!W!`!c!r#m#r#w$c${%e%sS!WY!US#Qn&OR#UuQ!YYR#h!UxROg!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|!WtRU[jlps{|}!O!P!Q!R!S!V!W!`!c!r#m#r#w$c${%e%sS!VY!US!ri%pS#Qn&OR#TueqRUs!V!W!r#m${%e%sxbOg!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|doRUs!V!W!r#m${%e%sQ!ZYQ#RpR#i!UR!qhX!oh!m!p$S#Y]ORUY[gijlps{|}!O!P!Q!R!S!U!V!W!`!c!d!e!f!i!r!y#m#r#w#{$O$Q$V$W$X$c$i$q$s${%S%e%g%p%q%s%|R$T!nTwSy|VOYg!U!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|R#UuQ$o#|Q$x$YQ%Y$rR%l%ZQ#|!fQ$Y!yQ$t$WQ$u$XQ%h%SQ%t%gQ%z%qR%}%|Q$n#|Q$w$YQ%U$oQ%X$rQ%c$xS%k%Y%ZR%v%ldqRUs!V!W!r#m${%e%sQ!a[[#Om!}#P$Z$[$yQ#p!`X#s!a#p#t$a|VOYg!U!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|T!ui%pT%[$t%]Q%`$tR%o%]xXOg!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|Q!XYQ!{lQ#Y|Q#]}Q#_!OR#g!U#Z]ORUY[gijlps{|}!O!P!Q!R!S!U!V!W!`!c!d!e!f!i!r!y#m#r#w#{$O$Q$V$W$X$c$i$q$s${%S%e%g%p%q%s%|![]RU[ijlps{|}!O!P!Q!R!S!V!W!`!c!r#m#r#w$c${%e%p%s}^OYg!U!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|QgOR!kg^!gd!_#x#y#z$h$rR#}!gQ!UYQ!`[d#f!U!`#m#n$O$^$q${%e%sS#m!V!WS#n!X!^Q$O!iS$^#g#lQ$q$QQ${$`R%e$|Q!mhQ!}mU$R!m!}$[R$[#PQ!phQ$S!mT$U!p$SQySR#WyS$i#{$sR%Q$iQ$|$`R%f$|YsRU!V!W!rR#SsQ%]$tR%m%]Q#t!aQ$a#pT$e#t$aQ#w!cQ$c#rT$f#w$cQ#PmQ$Z!}U$]#P$Z$yR$y$[TfOgSdOgS!_Y!UQ#x!dQ#y!e`#z!f!y$W$X%S%g%q%|Q$P!iU$h#{$i$sS$p$O$QQ$r$VR%V$qSeOg|!TY[!U!V!W!X!^!`!i#g#l#m#n$O$Q$^$`$q${$|%e%sQ!hdW#s!a#p#t$aW#v!c#r#w$c`#{!f!y$W$X%S%g%q%|U$g#{$i$sQ$s$VR%P$h|WOYg!U!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|doRUs!V!W!r#m${%e%sQ!c[S!ti%pQ!wjQ!zlQ#RpQ#Y{Q#Z|Q#[}Q#^!OQ#`!PQ#a!QQ#c!RQ#d!SQ#r!`X#v!c#r#w$cx_Og!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|![tRU[ijlps{|}!O!P!Q!R!S!V!W!`!c!r#m#r#w$c${%e%p%sQ!]YR#k!U[rRUs!V!W!rQ$`#mV%d${%e%sTxSyQ$j#{R%Z$sQ!viR%y%pxcOg!d!e!f!i!y#{$O$Q$V$W$X$i$q$s%S%g%q%|Q![YR#j!U",
|
||||
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 NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number Dollar PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation FunctionCallOrIdentifier EscapeSeq DoubleQuote Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword Import keyword CompoundAssign Assign",
|
||||
maxTerm: 128,
|
||||
context: trackScope,
|
||||
nodeProps: [
|
||||
["closedBy", 57,"end"]
|
||||
["closedBy", 59,"end"]
|
||||
],
|
||||
propSources: [highlighting],
|
||||
skippedNodes: [0,34],
|
||||
repeatNodeCount: 13,
|
||||
tokenData: "Lq~R!OOX$RXY$pYZ%ZZp$Rpq$pqr$Rrs%tst'ztu)cuw$Rwx)hxy)myz*Wz{$R{|*q|}$R}!O*q!O!P$R!P!Q4^!Q!R+c!R![.W![!]<y!]!^%Z!^!}$R!}#O=d#O#P?Y#P#Q?_#Q#R$R#R#S?x#S#T$R#T#Y@c#Y#ZA}#Z#b@c#b#cGk#c#f@c#f#gHn#g#h@c#h#iIq#i#o@c#o#p$R#p#qLR#q;'S$R;'S;=`$j<%l~$R~O$R~~LlS$WU!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RS$mP;=`<%l$R^$wU!TS#XYOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%bU!TS#_QOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU%yZ!TSOr%trs&lst%ttu'Vuw%twx'Vx#O%t#O#P'V#P;'S%t;'S;=`'t<%lO%tU&sU!WQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RQ'YTOr'Vrs'is;'S'V;'S;=`'n<%lO'VQ'nO!WQQ'qP;=`<%l'VU'wP;=`<%l%t^(RZrY!TSOY'zYZ$RZt'ztu(tuw'zwx(tx#O'z#O#P(t#P;'S'z;'S;=`)]<%lO'zY(ySrYOY(tZ;'S(t;'S;=`)V<%lO(tY)YP;=`<%l(t^)`P;=`<%l'z~)hO#d~~)mO#b~U)tU!TS#^QOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*_U!TS#lQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU*vX!TSOt$Ruw$Rx!Q$R!Q!R+c!R![.W![#O$R#P;'S$R;'S;=`$j<%lO$RU+jb!TS|QOt$Ruw$Rx!O$R!O!P,r!P!Q$R!Q![.W![#O$R#P#R$R#R#S/T#S#U$R#U#V/r#V#c$R#c#d1W#d#l$R#l#m2f#m;'S$R;'S;=`$j<%lO$RU,wW!TSOt$Ruw$Rx!Q$R!Q![-a![#O$R#P;'S$R;'S;=`$j<%lO$RU-hY!TS|QOt$Ruw$Rx!Q$R!Q![-a![#O$R#P#R$R#R#S,r#S;'S$R;'S;=`$j<%lO$RU._[!TS|QOt$Ruw$Rx!O$R!O!P,r!P!Q$R!Q![.W![#O$R#P#R$R#R#S/T#S;'S$R;'S;=`$j<%lO$RU/YW!TSOt$Ruw$Rx!Q$R!Q![.W![#O$R#P;'S$R;'S;=`$j<%lO$RU/wX!TSOt$Ruw$Rx!Q$R!Q!R0d!R!S0d!S#O$R#P;'S$R;'S;=`$j<%lO$RU0kX!TS|QOt$Ruw$Rx!Q$R!Q!R0d!R!S0d!S#O$R#P;'S$R;'S;=`$j<%lO$RU1]W!TSOt$Ruw$Rx!Q$R!Q!Y1u!Y#O$R#P;'S$R;'S;=`$j<%lO$RU1|W!TS|QOt$Ruw$Rx!Q$R!Q!Y1u!Y#O$R#P;'S$R;'S;=`$j<%lO$RU2k[!TSOt$Ruw$Rx!Q$R!Q![3a![!c$R!c!i3a!i#O$R#P#T$R#T#Z3a#Z;'S$R;'S;=`$j<%lO$RU3h[!TS|QOt$Ruw$Rx!Q$R!Q![3a![!c$R!c!i3a!i#O$R#P#T$R#T#Z3a#Z;'S$R;'S;=`$j<%lO$RU4cW!TSOt$Ruw$Rx!P$R!P!Q4{!Q#O$R#P;'S$R;'S;=`$j<%lO$RU5Q^!TSOY5|YZ$RZt5|tu7Puw5|wx7Px!P5|!P!Q$R!Q!}5|!}#O;r#O#P9_#P;'S5|;'S;=`<s<%lO5|U6T^!TS!lQOY5|YZ$RZt5|tu7Puw5|wx7Px!P5|!P!Q9t!Q!}5|!}#O;r#O#P9_#P;'S5|;'S;=`<s<%lO5|Q7UX!lQOY7PZ!P7P!P!Q7q!Q!}7P!}#O8`#O#P9_#P;'S7P;'S;=`9n<%lO7PQ7tP!P!Q7wQ7|U!lQ#Z#[7w#]#^7w#a#b7w#g#h7w#i#j7w#m#n7wQ8cVOY8`Z#O8`#O#P8x#P#Q7P#Q;'S8`;'S;=`9X<%lO8`Q8{SOY8`Z;'S8`;'S;=`9X<%lO8`Q9[P;=`<%l8`Q9bSOY7PZ;'S7P;'S;=`9n<%lO7PQ9qP;=`<%l7PU9yW!TSOt$Ruw$Rx!P$R!P!Q:c!Q#O$R#P;'S$R;'S;=`$j<%lO$RU:jb!TS!lQOt$Ruw$Rx#O$R#P#Z$R#Z#[:c#[#]$R#]#^:c#^#a$R#a#b:c#b#g$R#g#h:c#h#i$R#i#j:c#j#m$R#m#n:c#n;'S$R;'S;=`$j<%lO$RU;w[!TSOY;rYZ$RZt;rtu8`uw;rwx8`x#O;r#O#P8x#P#Q5|#Q;'S;r;'S;=`<m<%lO;rU<pP;=`<%l;rU<vP;=`<%l5|U=QU!TS!ZQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU=kW#nQ!TSOt$Ruw$Rx!_$R!_!`>T!`#O$R#P;'S$R;'S;=`$j<%lO$RU>YV!TSOt$Ruw$Rx#O$R#P#Q>o#Q;'S$R;'S;=`$j<%lO$RU>vU#mQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~?_O#e~U?fU#oQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@PU!TS!bQOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RU@h^!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$RUAkU!RQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$RUBS_!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#UCR#U#o@c#o;'S$R;'S;=`$j<%lO$RUCW`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#`@c#`#aDY#a#o@c#o;'S$R;'S;=`$j<%lO$RUD_`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#g@c#g#hEa#h#o@c#o;'S$R;'S;=`$j<%lO$RUEf`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#X@c#X#YFh#Y#o@c#o;'S$R;'S;=`$j<%lO$RUFo^!XQ!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Gr^#fW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Hu^#hW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#o@c#o;'S$R;'S;=`$j<%lO$R^Ix`#gW!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#f@c#f#gJz#g#o@c#o;'S$R;'S;=`$j<%lO$RUKP`!TSOt$Ruw$Rx}$R}!O@c!O!Q$R!Q![@c![!_$R!_!`Ad!`#O$R#P#T$R#T#i@c#i#jEa#j#o@c#o;'S$R;'S;=`$j<%lO$RULYUuQ!TSOt$Ruw$Rx#O$R#P;'S$R;'S;=`$j<%lO$R~LqO#p~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#`~~", 11)],
|
||||
tokenData: "Lp~R}OX$OXY$mYp$Opq$mqr$Ors%Wst'^tu(uuw$Owx(|xy)Ryz)lz{$O{|*V|}$O}!O*V!O!P$O!P!Q3r!Q!R*w!R![-l![!]<_!]!^<x!^!}$O!}#O=c#O#P?X#P#Q?^#Q#R$O#R#S?w#S#T$O#T#Y@b#Y#ZA|#Z#b@b#b#cGj#c#f@b#f#gHm#g#h@b#h#iIp#i#o@b#o#p$O#p#qLQ#q;'S$O;'S;=`$g<%l~$O~O$O~~LkS$TU!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OS$jP;=`<%l$O^$tU!US#[YOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU%]Z!USOr%Wrs&Ost%Wtu&iuw%Wwx&ix#O%W#O#P&i#P;'S%W;'S;=`'W<%lO%WU&VU!YQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OQ&lTOr&irs&{s;'S&i;'S;=`'Q<%lO&iQ'QO!YQQ'TP;=`<%l&iU'ZP;=`<%l%W^'eZrY!USOY'^YZ$OZt'^tu(Wuw'^wx(Wx#O'^#O#P(W#P;'S'^;'S;=`(o<%lO'^Y(]SrYOY(WZ;'S(W;'S;=`(i<%lO(WY(lP;=`<%l(W^(rP;=`<%l'^^(|O#h[}Q~)RO#f~U)YU!US#cQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU)sU!US#pQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU*[X!USOt$Ouw$Ox!Q$O!Q!R*w!R![-l![#O$O#P;'S$O;'S;=`$g<%lO$OU+Ob!US|QOt$Ouw$Ox!O$O!O!P,W!P!Q$O!Q![-l![#O$O#P#R$O#R#S.i#S#U$O#U#V/W#V#c$O#c#d0l#d#l$O#l#m1z#m;'S$O;'S;=`$g<%lO$OU,]W!USOt$Ouw$Ox!Q$O!Q![,u![#O$O#P;'S$O;'S;=`$g<%lO$OU,|Y!US|QOt$Ouw$Ox!Q$O!Q![,u![#O$O#P#R$O#R#S,W#S;'S$O;'S;=`$g<%lO$OU-s[!US|QOt$Ouw$Ox!O$O!O!P,W!P!Q$O!Q![-l![#O$O#P#R$O#R#S.i#S;'S$O;'S;=`$g<%lO$OU.nW!USOt$Ouw$Ox!Q$O!Q![-l![#O$O#P;'S$O;'S;=`$g<%lO$OU/]X!USOt$Ouw$Ox!Q$O!Q!R/x!R!S/x!S#O$O#P;'S$O;'S;=`$g<%lO$OU0PX!US|QOt$Ouw$Ox!Q$O!Q!R/x!R!S/x!S#O$O#P;'S$O;'S;=`$g<%lO$OU0qW!USOt$Ouw$Ox!Q$O!Q!Y1Z!Y#O$O#P;'S$O;'S;=`$g<%lO$OU1bW!US|QOt$Ouw$Ox!Q$O!Q!Y1Z!Y#O$O#P;'S$O;'S;=`$g<%lO$OU2P[!USOt$Ouw$Ox!Q$O!Q![2u![!c$O!c!i2u!i#O$O#P#T$O#T#Z2u#Z;'S$O;'S;=`$g<%lO$OU2|[!US|QOt$Ouw$Ox!Q$O!Q![2u![!c$O!c!i2u!i#O$O#P#T$O#T#Z2u#Z;'S$O;'S;=`$g<%lO$OU3wW!USOt$Ouw$Ox!P$O!P!Q4a!Q#O$O#P;'S$O;'S;=`$g<%lO$OU4f^!USOY5bYZ$OZt5btu6euw5bwx6ex!P5b!P!Q$O!Q!}5b!}#O;W#O#P8s#P;'S5b;'S;=`<X<%lO5bU5i^!US!mQOY5bYZ$OZt5btu6euw5bwx6ex!P5b!P!Q9Y!Q!}5b!}#O;W#O#P8s#P;'S5b;'S;=`<X<%lO5bQ6jX!mQOY6eZ!P6e!P!Q7V!Q!}6e!}#O7t#O#P8s#P;'S6e;'S;=`9S<%lO6eQ7YP!P!Q7]Q7bU!mQ#Z#[7]#]#^7]#a#b7]#g#h7]#i#j7]#m#n7]Q7wVOY7tZ#O7t#O#P8^#P#Q6e#Q;'S7t;'S;=`8m<%lO7tQ8aSOY7tZ;'S7t;'S;=`8m<%lO7tQ8pP;=`<%l7tQ8vSOY6eZ;'S6e;'S;=`9S<%lO6eQ9VP;=`<%l6eU9_W!USOt$Ouw$Ox!P$O!P!Q9w!Q#O$O#P;'S$O;'S;=`$g<%lO$OU:Ob!US!mQOt$Ouw$Ox#O$O#P#Z$O#Z#[9w#[#]$O#]#^9w#^#a$O#a#b9w#b#g$O#g#h9w#h#i$O#i#j9w#j#m$O#m#n9w#n;'S$O;'S;=`$g<%lO$OU;][!USOY;WYZ$OZt;Wtu7tuw;Wwx7tx#O;W#O#P8^#P#Q5b#Q;'S;W;'S;=`<R<%lO;WU<UP;=`<%l;WU<[P;=`<%l5bU<fU!US!]QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU=PU!US#`QOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU=jW#rQ!USOt$Ouw$Ox!_$O!_!`>S!`#O$O#P;'S$O;'S;=`$g<%lO$OU>XV!USOt$Ouw$Ox#O$O#P#Q>n#Q;'S$O;'S;=`$g<%lO$OU>uU#qQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~?^O#i~U?eU#sQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU@OU!US!dQOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OU@g^!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$OUAjU!SQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$OUBR_!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#UCQ#U#o@b#o;'S$O;'S;=`$g<%lO$OUCV`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#`@b#`#aDX#a#o@b#o;'S$O;'S;=`$g<%lO$OUD^`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#g@b#g#hE`#h#o@b#o;'S$O;'S;=`$g<%lO$OUEe`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#X@b#X#YFg#Y#o@b#o;'S$O;'S;=`$g<%lO$OUFn^!ZQ!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Gq^#jW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Ht^#lW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#o@b#o;'S$O;'S;=`$g<%lO$O^Iw`#kW!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#f@b#f#gJy#g#o@b#o;'S$O;'S;=`$g<%lO$OUKO`!USOt$Ouw$Ox}$O}!O@b!O!Q$O!Q![@b![!_$O!_!`Ac!`#O$O#P#T$O#T#i@b#i#jE`#j#o@b#o;'S$O;'S;=`$g<%lO$OULXUuQ!USOt$Ouw$Ox#O$O#P;'S$O;'S;=`$g<%lO$O~LpO#t~",
|
||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, pipeStartsLineTokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#d~~", 11)],
|
||||
topRules: {"Program":[0,35]},
|
||||
specialized: [{term: 28, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||
tokenPrec: 2370
|
||||
tokenPrec: 2589
|
||||
})
|
||||
|
|
|
|||
|
|
@ -440,6 +440,26 @@ describe('Parentheses', () => {
|
|||
PositionalArg
|
||||
Identifier arg3`)
|
||||
})
|
||||
|
||||
test('function call with mulitline identifiers starting 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', () => {
|
||||
|
|
|
|||
|
|
@ -298,4 +298,163 @@ end`).toMatchTree(`
|
|||
Number 2
|
||||
`)
|
||||
})
|
||||
|
||||
// NOTE: these are parsed as DotGet(meta, DotGet(script, name)) because that's easiest,
|
||||
// but the compiler flattens them
|
||||
test('chained dot get: meta.script.name', () => {
|
||||
expect('meta = 42; meta.script.name').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier meta
|
||||
Eq =
|
||||
Number 42
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot meta
|
||||
DotGet
|
||||
IdentifierBeforeDot script
|
||||
Identifier name
|
||||
`)
|
||||
})
|
||||
|
||||
test('chained dot get: a.b.c.d', () => {
|
||||
expect('a = 1; a.b.c.d').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier a
|
||||
Eq =
|
||||
Number 1
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot a
|
||||
DotGet
|
||||
IdentifierBeforeDot b
|
||||
DotGet
|
||||
IdentifierBeforeDot c
|
||||
Identifier d
|
||||
`)
|
||||
})
|
||||
|
||||
test('chained dot get in function call', () => {
|
||||
expect('config = 1; echo config.db.host').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier config
|
||||
Eq =
|
||||
Number 1
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
DotGet
|
||||
IdentifierBeforeDot config
|
||||
DotGet
|
||||
IdentifierBeforeDot db
|
||||
Identifier host
|
||||
`)
|
||||
})
|
||||
|
||||
test('chained dot get with numeric index at end', () => {
|
||||
expect('obj = 1; obj.items.0').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier obj
|
||||
Eq =
|
||||
Number 1
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot obj
|
||||
DotGet
|
||||
IdentifierBeforeDot items
|
||||
Number 0
|
||||
`)
|
||||
})
|
||||
|
||||
test('chained dot get with ParenExpr at end', () => {
|
||||
expect('obj = 1; obj.items.(i)').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier obj
|
||||
Eq =
|
||||
Number 1
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot obj
|
||||
DotGet
|
||||
IdentifierBeforeDot items
|
||||
ParenExpr
|
||||
FunctionCallOrIdentifier
|
||||
Identifier i
|
||||
`)
|
||||
})
|
||||
|
||||
test('not in scope remains Word with chained dots', () => {
|
||||
expect('readme.md.bak').toMatchTree(`Word readme.md.bak`)
|
||||
})
|
||||
|
||||
test('chained dot get in nested functions', () => {
|
||||
expect(`do cfg:
|
||||
do inner:
|
||||
cfg.db.host
|
||||
end
|
||||
end`).toMatchTree(`
|
||||
FunctionDef
|
||||
Do do
|
||||
Params
|
||||
Identifier cfg
|
||||
colon :
|
||||
FunctionDef
|
||||
Do do
|
||||
Params
|
||||
Identifier inner
|
||||
colon :
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot cfg
|
||||
DotGet
|
||||
IdentifierBeforeDot db
|
||||
Identifier host
|
||||
keyword end
|
||||
keyword end
|
||||
`)
|
||||
})
|
||||
|
||||
test('mixed simple and chained dot get', () => {
|
||||
expect('obj = 1; obj.a; obj.b.c').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier obj
|
||||
Eq =
|
||||
Number 1
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot obj
|
||||
Identifier a
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot obj
|
||||
DotGet
|
||||
IdentifierBeforeDot b
|
||||
Identifier c
|
||||
`)
|
||||
})
|
||||
|
||||
test.skip('chained numeric dot get: row.2.1.b', () => {
|
||||
expect('row = []; row.2.1').toMatchTree(`
|
||||
Assign
|
||||
AssignableIdentifier row
|
||||
Eq =
|
||||
Array []
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
IdentifierBeforeDot row
|
||||
DotGet
|
||||
Number 2
|
||||
DotGet
|
||||
Number 1
|
||||
Identifier b
|
||||
`)
|
||||
|
||||
test('parses $.pid just fine', () => {
|
||||
expect(`$.pid`).toMatchTree(`
|
||||
FunctionCallOrIdentifier
|
||||
DotGet
|
||||
Dollar $
|
||||
Identifier pid
|
||||
`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { expect, describe, test } from 'bun:test'
|
||||
import { parser } from '../shrimp'
|
||||
|
||||
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||
|
||||
|
|
@ -176,3 +177,159 @@ describe('pipe expressions', () => {
|
|||
`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('pipe continuation', () => {
|
||||
test('pipe on next line', () => {
|
||||
expect(`hello
|
||||
| echo`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCallOrIdentifier
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier echo
|
||||
`)
|
||||
|
||||
expect(`echo hello
|
||||
| grep h`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier grep
|
||||
PositionalArg
|
||||
Identifier h
|
||||
`)
|
||||
})
|
||||
|
||||
test('pipe on next non-empty line', () => {
|
||||
expect(`hello
|
||||
|
||||
|
||||
| echo`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCallOrIdentifier
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier echo
|
||||
`)
|
||||
})
|
||||
|
||||
test('multi-line pipe chain', () => {
|
||||
expect(`echo hello
|
||||
| grep h
|
||||
| sort`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier grep
|
||||
PositionalArg
|
||||
Identifier h
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier sort
|
||||
`)
|
||||
})
|
||||
|
||||
test('pipe with indentation', () => {
|
||||
expect(`echo hello
|
||||
| grep h
|
||||
| sort`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier grep
|
||||
PositionalArg
|
||||
Identifier h
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier sort
|
||||
`)
|
||||
})
|
||||
|
||||
test('pipe after operand on next line (trailing pipe style)', () => {
|
||||
expect(`echo hello |
|
||||
grep h`).toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier grep
|
||||
PositionalArg
|
||||
Identifier h
|
||||
`)
|
||||
})
|
||||
|
||||
test('same-line pipes still work', () => {
|
||||
expect('echo hello | grep h | sort').toMatchTree(`
|
||||
PipeExpr
|
||||
FunctionCall
|
||||
Identifier echo
|
||||
PositionalArg
|
||||
Identifier hello
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier grep
|
||||
PositionalArg
|
||||
Identifier h
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier sort
|
||||
`)
|
||||
})
|
||||
|
||||
test('lots of pipes', () => {
|
||||
expect(`
|
||||
'this should help readability in long chains'
|
||||
| split ' '
|
||||
| map (ref str.to-upper)
|
||||
| join '-'
|
||||
| echo
|
||||
`).toMatchTree(`
|
||||
PipeExpr
|
||||
String
|
||||
StringFragment this should help readability in long chains
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier split
|
||||
PositionalArg
|
||||
String
|
||||
StringFragment
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier map
|
||||
PositionalArg
|
||||
ParenExpr
|
||||
FunctionCall
|
||||
Identifier ref
|
||||
PositionalArg
|
||||
DotGet
|
||||
IdentifierBeforeDot str
|
||||
Identifier to-upper
|
||||
operator |
|
||||
FunctionCall
|
||||
Identifier join
|
||||
PositionalArg
|
||||
String
|
||||
StringFragment -
|
||||
operator |
|
||||
FunctionCallOrIdentifier
|
||||
Identifier echo
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ describe('string interpolation', () => {
|
|||
String
|
||||
StringFragment ${'hello '}
|
||||
Interpolation
|
||||
Identifier name
|
||||
FunctionCallOrIdentifier
|
||||
Identifier name
|
||||
`)
|
||||
})
|
||||
|
||||
|
|
@ -44,7 +45,8 @@ describe('string interpolation', () => {
|
|||
String
|
||||
StringFragment x/
|
||||
Interpolation
|
||||
Identifier y
|
||||
FunctionCallOrIdentifier
|
||||
Identifier y
|
||||
StringFragment /z
|
||||
`)
|
||||
})
|
||||
|
|
@ -122,7 +124,8 @@ describe('string escape sequences', () => {
|
|||
String
|
||||
StringFragment value:
|
||||
Interpolation
|
||||
Identifier x
|
||||
FunctionCallOrIdentifier
|
||||
Identifier x
|
||||
EscapeSeq \\n
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr'
|
||||
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString } from './shrimp.terms'
|
||||
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString, DotGet, newline, pipeStartsLine } from './shrimp.terms'
|
||||
|
||||
// doobie doobie do (we need the `do` keyword to know when we're defining params)
|
||||
export function specializeKeyword(ident: string) {
|
||||
|
|
@ -187,11 +187,21 @@ const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number |
|
|||
const identifierText = buildIdentifierText(input, pos)
|
||||
const context = stack.context as { scope: { has(name: string): boolean } } | undefined
|
||||
|
||||
// If identifier is in scope, this is property access (e.g., obj.prop)
|
||||
// If not in scope, it should be consumed as a Word (e.g., file.txt)
|
||||
return context?.scope.has(identifierText) || globals.includes(identifierText)
|
||||
? IdentifierBeforeDot
|
||||
: null
|
||||
// Check if identifier is in scope (lexical scope or globals)
|
||||
const inScope = context?.scope.has(identifierText) || globals.includes(identifierText)
|
||||
|
||||
// property access
|
||||
if (inScope) return IdentifierBeforeDot
|
||||
|
||||
// Not in scope - check if we're inside a DotGet chain
|
||||
// Inside the @skip {} block where DotGet is defined, Word cannot be shifted
|
||||
// but Identifier can be. This tells us we're at the RHS of a DotGet.
|
||||
const canShiftIdentifier = stack.canShift(Identifier)
|
||||
const canShiftWord = stack.canShift(Word)
|
||||
const inDotGetChain = canShiftIdentifier && !canShiftWord
|
||||
|
||||
// continue if we're inside a DotGet
|
||||
return inDotGetChain ? IdentifierBeforeDot : null
|
||||
}
|
||||
|
||||
// Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead
|
||||
|
|
@ -346,3 +356,34 @@ const isEmojiOrUnicode = (ch: number): boolean => {
|
|||
}
|
||||
|
||||
const getCharSize = (ch: number) => (ch > 0xffff ? 2 : 1) // emoji takes 2 UTF-16 code units
|
||||
|
||||
export const pipeStartsLineTokenizer = new ExternalTokenizer((input: InputStream, stack: Stack) => {
|
||||
const ch = input.peek(0)
|
||||
|
||||
if (ch !== 10 /* \n */) return
|
||||
|
||||
// ignore whitespace
|
||||
let offset = 1
|
||||
let lastNewlineOffset = 0
|
||||
|
||||
while (true) {
|
||||
const ch = input.peek(offset)
|
||||
if (ch === 10 /* \n */) {
|
||||
lastNewlineOffset = offset
|
||||
offset++
|
||||
} else if (isWhiteSpace(ch)) {
|
||||
offset++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// look for pipe after skipping empty lines
|
||||
if (input.peek(offset) === 124 /* | */) {
|
||||
input.advance(lastNewlineOffset + 1)
|
||||
input.acceptToken(pipeStartsLine)
|
||||
} else {
|
||||
input.advance(1)
|
||||
input.acceptToken(newline)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// The prelude creates all the builtin Shrimp functions.
|
||||
|
||||
import { join, resolve } from 'path'
|
||||
import {
|
||||
type Value, type VM, toValue,
|
||||
extractParamInfo, isWrapped, getOriginalFunction,
|
||||
|
|
@ -22,6 +23,20 @@ export const globals = {
|
|||
math,
|
||||
str,
|
||||
|
||||
// shrimp runtime info
|
||||
$: {
|
||||
args: Bun.argv.slice(3),
|
||||
argv: Bun.argv.slice(1),
|
||||
env: process.env,
|
||||
pid: process.pid,
|
||||
cwd: process.env.PWD,
|
||||
script: {
|
||||
name: Bun.argv[2] || '(shrimp)',
|
||||
path: resolve(join('.', Bun.argv[2] ?? ''))
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
// hello
|
||||
echo: (...args: any[]) => {
|
||||
console.log(...args.map(a => {
|
||||
|
|
@ -66,7 +81,6 @@ export const globals = {
|
|||
},
|
||||
|
||||
// env
|
||||
args: Bun.argv.slice(1),
|
||||
exit: (num: number) => process.exit(num ?? 0),
|
||||
|
||||
// type predicates
|
||||
|
|
|
|||
|
|
@ -80,15 +80,15 @@ describe('introspection', () => {
|
|||
|
||||
describe('environment', () => {
|
||||
test('args is an array', async () => {
|
||||
await expect(`array? args`).toEvaluateTo(true, globals)
|
||||
await expect(`array? $.args`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('args can be accessed', async () => {
|
||||
await expect(`type args`).toEvaluateTo('array', globals)
|
||||
await expect(`type $.args`).toEvaluateTo('array', globals)
|
||||
})
|
||||
|
||||
test('', async () => {
|
||||
await expect(`list.first args | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true)
|
||||
test('argv includes more than just the args', async () => {
|
||||
await expect(`list.first $.argv | str.ends-with? 'shrimp.test.ts'`).toEvaluateTo(true)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -103,3 +103,38 @@ describe('ref', () => {
|
|||
expect(`rnd = ref math.random; rnd | type`).toEvaluateTo('number')
|
||||
expect(`rnd = ref math.random; ref rnd | type`).toEvaluateTo('native')
|
||||
})
|
||||
|
||||
describe('$ global dictionary', () => {
|
||||
test('$.args is an array', async () => {
|
||||
await expect(`$.args | array?`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.args can be accessed', async () => {
|
||||
await expect(`$.args | type`).toEvaluateTo('array', globals)
|
||||
})
|
||||
|
||||
test('$.script.name is a string', async () => {
|
||||
await expect(`$.script.name | string?`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.script.path is a string', async () => {
|
||||
await expect(`$.script.path | string?`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.env is a dict', async () => {
|
||||
await expect(`$.env | dict?`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.pid is a number', async () => {
|
||||
await expect(`$.pid | number?`).toEvaluateTo(true, globals)
|
||||
await expect(`$.pid > 0`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.cwd is a string', async () => {
|
||||
await expect(`$.cwd | string?`).toEvaluateTo(true, globals)
|
||||
})
|
||||
|
||||
test('$.cwd returns current working directory', async () => {
|
||||
await expect(`$.cwd`).toEvaluateTo(process.cwd(), globals)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user