Compare commits
2 Commits
831966323a
...
5988e75939
| Author | SHA1 | Date | |
|---|---|---|---|
| 5988e75939 | |||
| 5234ad9a73 |
27
CLAUDE.md
27
CLAUDE.md
|
|
@ -51,9 +51,9 @@ When exploring Shrimp, focus on these key files in order:
|
||||||
|
|
||||||
3. **src/compiler/compiler.ts** - CST to bytecode transformation
|
3. **src/compiler/compiler.ts** - CST to bytecode transformation
|
||||||
|
|
||||||
- See how functions become labels in `fnLabels` map
|
- See how functions emit inline with JUMP wrappers
|
||||||
- Check short-circuit logic for `and`/`or` (lines 267-282)
|
- Check short-circuit logic for `and`/`or`
|
||||||
- Notice `TRY_CALL` emission for bare identifiers (line 152)
|
- Notice `TRY_CALL` emission for bare identifiers
|
||||||
|
|
||||||
4. **packages/ReefVM/src/vm.ts** - Bytecode execution
|
4. **packages/ReefVM/src/vm.ts** - Bytecode execution
|
||||||
- See `TRY_CALL` fall-through to `CALL` (lines 357-375)
|
- See `TRY_CALL` fall-through to `CALL` (lines 357-375)
|
||||||
|
|
@ -211,14 +211,23 @@ Implementation files:
|
||||||
|
|
||||||
## Compiler Architecture
|
## Compiler Architecture
|
||||||
|
|
||||||
**Function compilation strategy**: The compiler doesn't create inline function objects. Instead it:
|
**Function compilation strategy**: Functions are compiled inline where they're defined, with JUMP instructions to skip over their bodies during linear execution:
|
||||||
|
|
||||||
1. Generates unique labels (`.func_0`, `.func_1`) for each function body (compiler.ts:137)
|
```
|
||||||
2. Stores function body instructions in `fnLabels` map during compilation
|
JUMP .after_.func_0 # Skip over body during definition
|
||||||
3. Appends all function bodies to the end of bytecode with RETURN instructions (compiler.ts:36-41)
|
.func_0: # Function body label
|
||||||
4. Emits `MAKE_FUNCTION` with parameters and label reference
|
(function body code)
|
||||||
|
RETURN
|
||||||
|
.after_.func_0: # Resume here after jump
|
||||||
|
MAKE_FUNCTION (x) .func_0 # Create function object with label
|
||||||
|
```
|
||||||
|
|
||||||
This approach keeps the main program linear and allows ReefVM to jump to function bodies by label.
|
This approach:
|
||||||
|
- Emits function bodies inline (no deferred collection)
|
||||||
|
- Uses JUMP to skip bodies during normal execution flow
|
||||||
|
- Each function is self-contained at its definition site
|
||||||
|
- Works seamlessly in REPL mode (important for `vm.appendBytecode()`)
|
||||||
|
- Allows ReefVM to jump to function bodies by label when called
|
||||||
|
|
||||||
**Short-circuit logic**: ReefVM has no AND/OR opcodes. The compiler implements short-circuit evaluation using:
|
**Short-circuit logic**: ReefVM has no AND/OR opcodes. The compiler implements short-circuit evaluation using:
|
||||||
|
|
||||||
|
|
|
||||||
261
bin/repl
Executable file
261
bin/repl
Executable file
|
|
@ -0,0 +1,261 @@
|
||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
import { Compiler } from '../src/compiler/compiler'
|
||||||
|
import { VM, type Value, Scope } from 'reefvm'
|
||||||
|
import * as readline from 'node:readline'
|
||||||
|
|
||||||
|
const colors = {
|
||||||
|
reset: '\x1b[0m',
|
||||||
|
bright: '\x1b[1m',
|
||||||
|
dim: '\x1b[2m',
|
||||||
|
cyan: '\x1b[36m',
|
||||||
|
yellow: '\x1b[33m',
|
||||||
|
green: '\x1b[32m',
|
||||||
|
red: '\x1b[31m',
|
||||||
|
blue: '\x1b[34m',
|
||||||
|
magenta: '\x1b[35m',
|
||||||
|
pink: '\x1b[38;2;255;105;180m'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function repl() {
|
||||||
|
const commands = ['/clear', '/reset', '/vars', '/funcs', '/history', '/exit', '/quit']
|
||||||
|
|
||||||
|
function completer(line: string): [string[], string] {
|
||||||
|
if (line.startsWith('/')) {
|
||||||
|
const hits = commands.filter(cmd => cmd.startsWith(line))
|
||||||
|
return [hits.length ? hits : commands, line]
|
||||||
|
}
|
||||||
|
return [[], line]
|
||||||
|
}
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: process.stdin,
|
||||||
|
output: process.stdout,
|
||||||
|
prompt: `${colors.pink}>>${colors.reset} `,
|
||||||
|
completer,
|
||||||
|
})
|
||||||
|
|
||||||
|
let codeHistory: string[] = []
|
||||||
|
let vm: VM | null = null
|
||||||
|
|
||||||
|
showWelcome()
|
||||||
|
|
||||||
|
rl.prompt()
|
||||||
|
|
||||||
|
rl.on('line', async (line: string) => {
|
||||||
|
const trimmed = line.trim()
|
||||||
|
|
||||||
|
if (!trimmed) {
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
vm ||= new VM({ instructions: [], constants: [] }, nativeFunctions)
|
||||||
|
|
||||||
|
if (['/exit', 'exit', '/quit', 'quit'].includes(trimmed)) {
|
||||||
|
console.log(`\n${colors.yellow}Goodbye!${colors.reset}`)
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === '/clear') {
|
||||||
|
codeHistory = []
|
||||||
|
vm = null
|
||||||
|
console.clear()
|
||||||
|
showWelcome()
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === '/reset') {
|
||||||
|
codeHistory = []
|
||||||
|
vm = null
|
||||||
|
console.log(`\n${colors.yellow}State reset${colors.reset}`)
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === '/vars') {
|
||||||
|
console.log(`\n${colors.bright}Variables:${colors.reset}`)
|
||||||
|
console.log(formatVariables(vm.scope))
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['/fn', '/fns', '/fun', '/funs', '/func', '/funcs', '/functions'].includes(trimmed)) {
|
||||||
|
console.log(`\n${colors.bright}Functions:${colors.reset}`)
|
||||||
|
console.log(formatVariables(vm.scope, true))
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed === '/history') {
|
||||||
|
if (codeHistory.length === 0) {
|
||||||
|
console.log(`\n${colors.dim}No history yet${colors.reset}`)
|
||||||
|
} else {
|
||||||
|
console.log(`\n${colors.bright}History:${colors.reset}`)
|
||||||
|
codeHistory.forEach((code, i) => {
|
||||||
|
console.log(`${colors.dim}[${i + 1}]${colors.reset} ${code}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
rl.prompt()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
codeHistory.push(trimmed)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const compiler = new Compiler(trimmed)
|
||||||
|
|
||||||
|
vm.appendBytecode(compiler.bytecode)
|
||||||
|
|
||||||
|
const result = await vm.continue()
|
||||||
|
|
||||||
|
console.log(`${colors.dim}=>${colors.reset} ${formatValue(result)}`)
|
||||||
|
} catch (error: any) {
|
||||||
|
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
|
||||||
|
codeHistory.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.prompt()
|
||||||
|
})
|
||||||
|
|
||||||
|
rl.on('close', () => {
|
||||||
|
console.log(`\n${colors.yellow}Goodbye!${colors.reset}`)
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
rl.on('SIGINT', () => {
|
||||||
|
rl.write(null, { ctrl: true, name: 'u' })
|
||||||
|
console.log('\n')
|
||||||
|
rl.prompt()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function formatValue(value: Value, inner = false): string {
|
||||||
|
switch (value.type) {
|
||||||
|
case 'string':
|
||||||
|
return `${colors.green}"${value.value}"${colors.reset}`
|
||||||
|
case 'number':
|
||||||
|
return `${colors.cyan}${value.value}${colors.reset}`
|
||||||
|
case 'boolean':
|
||||||
|
return `${colors.yellow}${value.value}${colors.reset}`
|
||||||
|
case 'null':
|
||||||
|
return `${colors.dim}null${colors.reset}`
|
||||||
|
case 'array': {
|
||||||
|
const items = value.value.map(x => formatValue(x, true)).join(' ')
|
||||||
|
return `${inner ? '(' : ''}${colors.blue}list${colors.reset} ${items}${inner ? ')' : ''}`
|
||||||
|
}
|
||||||
|
case 'dict': {
|
||||||
|
const entries = Array.from(value.value.entries())
|
||||||
|
.map(([k, v]) => `${k}=${formatValue(v, true)}`)
|
||||||
|
.join(' ')
|
||||||
|
return `${inner ? '(' : ''}${colors.magenta}dict${colors.reset} ${entries}${inner ? ')' : ''}`
|
||||||
|
}
|
||||||
|
case 'function': {
|
||||||
|
const params = value.params.join(', ')
|
||||||
|
return `${colors.dim}<fn(${params})>${colors.reset}`
|
||||||
|
}
|
||||||
|
case 'native':
|
||||||
|
return `${colors.dim}<native-fn>${colors.reset}`
|
||||||
|
case 'regex':
|
||||||
|
return `${colors.magenta}${value.value}${colors.reset}`
|
||||||
|
default:
|
||||||
|
return String(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatVariables(scope: Scope, onlyFunctions = false): string {
|
||||||
|
const vars: string[] = []
|
||||||
|
|
||||||
|
function collectVars(s: any, depth = 0) {
|
||||||
|
if (!s) return
|
||||||
|
|
||||||
|
const prefix = depth > 0 ? `${colors.dim}(parent)${colors.reset} ` : ''
|
||||||
|
|
||||||
|
for (const [name, value] of s.locals.entries()) {
|
||||||
|
if (onlyFunctions && (value.type === 'function' || value.type === 'native')) {
|
||||||
|
vars.push(` ${prefix}${colors.bright}${name}${colors.reset} = ${formatValue(value)}`)
|
||||||
|
} else if (!onlyFunctions) {
|
||||||
|
vars.push(` ${prefix}${colors.bright}${name}${colors.reset} = ${formatValue(value)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.parent) {
|
||||||
|
collectVars(s.parent, depth + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
collectVars(scope)
|
||||||
|
|
||||||
|
if (vars.length === 0) {
|
||||||
|
return ` ${colors.dim}[no variables]${colors.reset}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return vars.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function showWelcome() {
|
||||||
|
console.log(
|
||||||
|
`${colors.pink}═══════════════════════════════════════════════════════════════${colors.reset}`
|
||||||
|
)
|
||||||
|
console.log(`${colors.bright}🦐 Shrimp REPL${colors.reset}`)
|
||||||
|
console.log(
|
||||||
|
`${colors.pink}═══════════════════════════════════════════════════════════════${colors.reset}`
|
||||||
|
)
|
||||||
|
console.log(`\nType Shrimp expressions. Press ${colors.bright}Ctrl+D${colors.reset} to exit.`)
|
||||||
|
console.log(`\nCommands:`)
|
||||||
|
console.log(` ${colors.bright}/clear${colors.reset} - Clear screen and reset state`)
|
||||||
|
console.log(` ${colors.bright}/reset${colors.reset} - Reset state (keep history visible)`)
|
||||||
|
console.log(` ${colors.bright}/vars${colors.reset} - Show all variables`)
|
||||||
|
console.log(` ${colors.bright}/funcs${colors.reset} - Show all functions`)
|
||||||
|
console.log(` ${colors.bright}/history${colors.reset} - Show code history`)
|
||||||
|
console.log(` ${colors.bright}/exit${colors.reset} - Quit REPL`)
|
||||||
|
console.log(`\nExamples:`)
|
||||||
|
console.log(` ${colors.cyan}5 + 10${colors.reset}`)
|
||||||
|
console.log(` ${colors.cyan}x = 42${colors.reset}`)
|
||||||
|
console.log(` ${colors.cyan}echo "Hello, world!"${colors.reset}`)
|
||||||
|
console.log(` ${colors.cyan}greet = do name: echo Hello name end${colors.reset}`)
|
||||||
|
console.log()
|
||||||
|
}
|
||||||
|
|
||||||
|
const nativeFunctions = {
|
||||||
|
echo: (...args: any[]) => {
|
||||||
|
console.log(...args)
|
||||||
|
},
|
||||||
|
len: (value: any) => {
|
||||||
|
if (typeof value === 'string') return value.length
|
||||||
|
if (Array.isArray(value)) return value.length
|
||||||
|
if (value && typeof value === 'object') return Object.keys(value).length
|
||||||
|
return 0
|
||||||
|
},
|
||||||
|
type: (value: any) => {
|
||||||
|
if (value === null) return 'null'
|
||||||
|
if (Array.isArray(value)) return 'array'
|
||||||
|
return typeof value
|
||||||
|
},
|
||||||
|
range: (start: number, end: number | null) => {
|
||||||
|
if (end === null) {
|
||||||
|
end = start
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
const result: number[] = []
|
||||||
|
for (let i = start; i <= end; i++) {
|
||||||
|
result.push(i)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
join: (arr: any[], sep: string = ',') => {
|
||||||
|
return arr.join(sep)
|
||||||
|
},
|
||||||
|
split: (str: string, sep: string = ',') => {
|
||||||
|
return str.split(sep)
|
||||||
|
},
|
||||||
|
upper: (str: string) => str.toUpperCase(),
|
||||||
|
lower: (str: string) => str.toLowerCase(),
|
||||||
|
trim: (str: string) => str.trim(),
|
||||||
|
list: (...args: any[]) => args,
|
||||||
|
dict: (atNamed = {}) => atNamed
|
||||||
|
}
|
||||||
|
|
||||||
|
await repl()
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun generate-parser && bun --hot src/server/server.tsx",
|
"dev": "bun generate-parser && bun --hot src/server/server.tsx",
|
||||||
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts"
|
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts",
|
||||||
|
"repl": "bun generate-parser && bun bin/repl"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"reefvm": "workspace:*",
|
"reefvm": "workspace:*",
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ function processEscapeSeq(escapeSeq: string): string {
|
||||||
|
|
||||||
export class Compiler {
|
export class Compiler {
|
||||||
instructions: ProgramItem[] = []
|
instructions: ProgramItem[] = []
|
||||||
fnLabels = new Map<Label, ProgramItem[]>()
|
fnLabelCount = 0
|
||||||
ifLabelCount = 0
|
ifLabelCount = 0
|
||||||
bytecode: Bytecode
|
bytecode: Bytecode
|
||||||
pipeCounter = 0
|
pipeCounter = 0
|
||||||
|
|
@ -64,14 +64,6 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#compileCst(cst, input)
|
this.#compileCst(cst, input)
|
||||||
|
|
||||||
// Add the labels
|
|
||||||
for (const [label, labelInstructions] of this.fnLabels) {
|
|
||||||
this.instructions.push([`${label}:`])
|
|
||||||
this.instructions.push(...labelInstructions)
|
|
||||||
this.instructions.push(['RETURN'])
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bytecode = toBytecode(this.instructions)
|
this.bytecode = toBytecode(this.instructions)
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
|
|
@ -254,18 +246,20 @@ export class Compiler {
|
||||||
case terms.FunctionDef: {
|
case terms.FunctionDef: {
|
||||||
const { paramNames, bodyNodes } = getFunctionDefParts(node, input)
|
const { paramNames, bodyNodes } = getFunctionDefParts(node, input)
|
||||||
const instructions: ProgramItem[] = []
|
const instructions: ProgramItem[] = []
|
||||||
const functionLabel: Label = `.func_${this.fnLabels.size}`
|
const functionLabel: Label = `.func_${this.fnLabelCount++}`
|
||||||
const bodyInstructions: ProgramItem[] = []
|
const afterLabel: Label = `.after_${functionLabel}`
|
||||||
if (this.fnLabels.has(functionLabel)) {
|
|
||||||
throw new CompilerError(`Function name collision: ${functionLabel}`, node.from, node.to)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fnLabels.set(functionLabel, bodyInstructions)
|
instructions.push(['JUMP', afterLabel])
|
||||||
|
|
||||||
|
instructions.push([`${functionLabel}:`])
|
||||||
|
bodyNodes.forEach((bodyNode) => {
|
||||||
|
instructions.push(...this.#compileNode(bodyNode, input))
|
||||||
|
})
|
||||||
|
instructions.push(['RETURN'])
|
||||||
|
|
||||||
|
instructions.push([`${afterLabel}:`])
|
||||||
|
|
||||||
instructions.push(['MAKE_FUNCTION', paramNames, functionLabel])
|
instructions.push(['MAKE_FUNCTION', paramNames, functionLabel])
|
||||||
bodyNodes.forEach((bodyNode) => {
|
|
||||||
bodyInstructions.push(...this.#compileNode(bodyNode, input))
|
|
||||||
})
|
|
||||||
|
|
||||||
return instructions
|
return instructions
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -112,18 +112,18 @@ describe('compiler', () => {
|
||||||
end`).toEvaluateTo('white')
|
end`).toEvaluateTo('white')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if elsif', () => {
|
test('if elseif', () => {
|
||||||
expect(`if false:
|
expect(`if false:
|
||||||
boromir
|
boromir
|
||||||
elsif true:
|
elseif true:
|
||||||
frodo
|
frodo
|
||||||
end`).toEvaluateTo('frodo')
|
end`).toEvaluateTo('frodo')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('if elsif else', () => {
|
test('if elseif else', () => {
|
||||||
expect(`if false:
|
expect(`if false:
|
||||||
destroyed
|
destroyed
|
||||||
elsif true:
|
elseif true:
|
||||||
fire
|
fire
|
||||||
else:
|
else:
|
||||||
darkness
|
darkness
|
||||||
|
|
@ -131,9 +131,9 @@ describe('compiler', () => {
|
||||||
|
|
||||||
expect(`if false:
|
expect(`if false:
|
||||||
king
|
king
|
||||||
elsif false:
|
elseif false:
|
||||||
elf
|
elf
|
||||||
elsif true:
|
elseif true:
|
||||||
dwarf
|
dwarf
|
||||||
else:
|
else:
|
||||||
scattered
|
scattered
|
||||||
|
|
|
||||||
|
|
@ -140,11 +140,11 @@ export const getIfExprParts = (node: SyntaxNode, input: string) => {
|
||||||
throw new CompilerError(message, child.from, child.to)
|
throw new CompilerError(message, child.from, child.to)
|
||||||
}
|
}
|
||||||
elseThenBlock = parts.at(-1)
|
elseThenBlock = parts.at(-1)
|
||||||
} else if (child.type.id === terms.ElsifExpr) {
|
} else if (child.type.id === terms.ElseIfExpr) {
|
||||||
const [_keyword, conditional, _colon, thenBlock] = parts
|
const [_keyword, conditional, _colon, thenBlock] = parts
|
||||||
if (!conditional || !thenBlock) {
|
if (!conditional || !thenBlock) {
|
||||||
const names = parts.map((p) => p.type.name).join(', ')
|
const names = parts.map((p) => p.type.name).join(', ')
|
||||||
const message = `ElsifExpr expected conditional and thenBlock, got ${names}`
|
const message = `ElseIfExpr expected conditional and thenBlock, got ${names}`
|
||||||
throw new CompilerError(message, child.from, child.to)
|
throw new CompilerError(message, child.from, child.to)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
rightParen { ")" }
|
rightParen { ")" }
|
||||||
colon[closedBy="end", @name="colon"] { ":" }
|
colon[closedBy="end", @name="colon"] { ":" }
|
||||||
Underscore { "_" }
|
Underscore { "_" }
|
||||||
Null { "null" }
|
|
||||||
Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
|
Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
|
||||||
"|"[@name=operator]
|
"|"[@name=operator]
|
||||||
|
|
||||||
|
|
@ -109,11 +108,11 @@ singleLineIf {
|
||||||
}
|
}
|
||||||
|
|
||||||
multilineIf {
|
multilineIf {
|
||||||
@specialize[@name=keyword]<Identifier, "if"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElsifExpr* ElseExpr? @specialize[@name=keyword]<Identifier, "end">
|
@specialize[@name=keyword]<Identifier, "if"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock ElseIfExpr* ElseExpr? @specialize[@name=keyword]<Identifier, "end">
|
||||||
}
|
}
|
||||||
|
|
||||||
ElsifExpr {
|
ElseIfExpr {
|
||||||
@specialize[@name=keyword]<Identifier, "elsif"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock
|
@specialize[@name=keyword]<Identifier, "elseif"> (ConditionalOp | expression) colon newlineOrSemicolon ThenBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
ElseExpr {
|
ElseExpr {
|
||||||
|
|
@ -196,7 +195,7 @@ EscapeSeq {
|
||||||
// to go through ambiguousFunctionCall (which is what we want semantically).
|
// to go through ambiguousFunctionCall (which is what we want semantically).
|
||||||
// Yes, it is annoying and I gave up trying to use GLR to fix it.
|
// Yes, it is annoying and I gave up trying to use GLR to fix it.
|
||||||
expressionWithoutIdentifier {
|
expressionWithoutIdentifier {
|
||||||
ParenExpr | Word | String | Number | Boolean | Regex | Null
|
ParenExpr | Word | String | Number | Boolean | Regex | @specialize[@name=Null]<Identifier, "null">
|
||||||
}
|
}
|
||||||
|
|
||||||
block {
|
block {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
export const
|
export const Star = 1,
|
||||||
Star = 1,
|
|
||||||
Slash = 2,
|
Slash = 2,
|
||||||
Plus = 3,
|
Plus = 3,
|
||||||
Minus = 4,
|
Minus = 4,
|
||||||
|
|
@ -42,6 +41,6 @@ export const
|
||||||
NamedArgPrefix = 41,
|
NamedArgPrefix = 41,
|
||||||
IfExpr = 43,
|
IfExpr = 43,
|
||||||
ThenBlock = 46,
|
ThenBlock = 46,
|
||||||
ElsifExpr = 47,
|
ElseIfExpr = 47,
|
||||||
ElseExpr = 49,
|
ElseExpr = 49,
|
||||||
Assign = 51
|
Assign = 51
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,39 @@
|
||||||
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
// This file was generated by lezer-generator. You probably shouldn't edit it.
|
||||||
import {LRParser, LocalTokenGroup} from "@lezer/lr"
|
import { LRParser, LocalTokenGroup } from '@lezer/lr'
|
||||||
import {operatorTokenizer} from "./operatorTokenizer"
|
import { operatorTokenizer } from './operatorTokenizer'
|
||||||
import {tokenizer} from "./tokenizer"
|
import { tokenizer } from './tokenizer'
|
||||||
import {trackScope} from "./scopeTracker"
|
import { trackScope } from './scopeTracker'
|
||||||
import {highlighting} from "./highlight"
|
import { highlighting } from './highlight'
|
||||||
const spec_Identifier = {__proto__:null,do:70, end:76, if:88, elsif:96, else:100}
|
const spec_Identifier = {
|
||||||
|
__proto__: null,
|
||||||
|
null: 64,
|
||||||
|
do: 70,
|
||||||
|
end: 76,
|
||||||
|
if: 88,
|
||||||
|
elseif: 96,
|
||||||
|
else: 100,
|
||||||
|
}
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: ".jQVQbOOO#XQcO'#CrO$RQRO'#CsO$aQcO'#DmO$xQbO'#CqO%gOSO'#CuOOQa'#Dq'#DqO%uOpO'#C}O%zQcO'#DpO&cQbO'#D|OOQ`'#DO'#DOOOQ`'#Dn'#DnO&kQbO'#DmO&yQbO'#EQOOQ`'#DX'#DXO'hQRO'#DaOOQ`'#Dm'#DmO'mQQO'#DlOOQ`'#Dl'#DlOOQ`'#Db'#DbQVQbOOOOQa'#Dp'#DpOOQ`'#Cp'#CpO'uQbO'#DUOOQ`'#Do'#DoOOQ`'#Dc'#DcO(PQbO,59ZO&yQbO,59_O&yQbO,59_O)XQRO'#CsO)iQRO,59]O)zQRO,59]O)uQQO,59]O*uQQO,59]O*}QbO'#CwO+VQWO'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOSO,59aOOQa,59a,59aO+yO`O,59iOOQ`'#De'#DeO,OQbO'#DQO,WQQO,5:hO,]QbO'#DgO,bQbO,59YO,sQRO,5:lO,zQQO,5:lO-PQbO,59{OOQ`,5:W,5:WOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7a-E7aOOQa1G.y1G.yO-^QcO1G.yO&yQbO,59`O&yQbO,59`OOQa1G.w1G.wOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQa1G.{1G.{OOQa1G/T1G/TOOQ`-E7c-E7cO-xQbO1G0SO!QQbO'#CrOOQ`,5:R,5:ROOQ`-E7e-E7eO.YQbO1G0WOOQ`1G/g1G/gOOQO1G.z1G.zO.jQRO1G.zO.tQbO7+%nO.yQbO7+%oOOQ`'#DZ'#DZOOQ`7+%r7+%rO/ZQbO7+%sOOQ`<<IY<<IYO/qQQO'#DfO/vQbO'#EPO0^QbO<<IZOOQ`'#D['#D[O0cQbO<<I_OOQ`,5:Q,5:QOOQ`-E7d-E7dOOQ`AN>uAN>uO&yQbO'#D]OOQ`'#Dh'#DhO0nQbOAN>yO0yQQO'#D_OOQ`AN>yAN>yO1OQbOAN>yO1TQRO,59wO1[QQO,59wOOQ`-E7f-E7fOOQ`G24eG24eO1aQbOG24eO1fQQO,59yO1kQQO1G/cOOQ`LD*PLD*PO.yQbO1G/eO/ZQbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi",
|
states:
|
||||||
stateData: "1s~O!_OS~O]PO^_O_UO`VOmUOnUOoUOpUOsXO|]O!fSO!hTO!rbO~O]eO_UO`VOmUOnUOoUOpUOsXOwfOygO!fSO!hTOzfX!rfX!vfX!gfXvfX~OP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~P!QOPkOQkORlOSlO~OPkOQkORlOSlO!r!aX!v!aXv!aX~O]PO_UO`VOmUOnUOoUOpUO!fSO!hTO~OjtO!hwO!jrO!ksO~O!oxO~OP!dXQ!dXR!dXS!dX!r!aX!v!aXv!aX~O^yOutP~Oz|O!r!aX!v!aXv!aX~O]eO_UO`VOmUOnUOoUOpUO!fSO!hTO~OV!QO~O!r!RO!v!RO~OsXOw!TO~P&yOsXOwfOygOzca!rca!vca!gcavca~P&yOT!YOU!YOV!XOW!XOX!XOY!XOZ!XO[!XO~OPkOQkORlOSlO~P(mOPkOQkORlOSlO!g!ZO~O!g!ZOP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~Oz|O!g!ZO~O]![O!fSO~O!h!]O!j!]O!k!]O!l!]O!m!]O!n!]O~OjtO!h!_O!jrO!ksO~O]!`O~O^yOutX~Ou!bO~O]!cO~Oz|O!rba!vba!gbavba~Ou!fO~P(mOu!fO~O^_OsXO|]O~P$xOPkOQkORgiSgi!rgi!vgi!ggivgi~O^_OsXO|]O!r!kO~P$xO^_OsXO|]O!r!nO~P$xO!ghiuhi~P(mOv!oO~O^_OsXO|]Ov!sP~P$xO^_OsXO|]Ov!sP!Q!sP!S!sP~P$xO!r!uO~O^_OsXO|]Ov!sX!Q!sX!S!sX~P$xOv!wO~Ov!|O!Q!xO!S!{O~Ov#RO!Q!xO!S!{O~Ou#TO~Ov#RO~Ou#UO~P(mOu#UO~Ov#VO~O!r#WO~O!r#XO~Omo~",
|
".jQVQbOOO#XQcO'#CrO$RQRO'#CsO$aQcO'#DmO$xQbO'#CqO%gOSO'#CuOOQa'#Dq'#DqO%uOpO'#C}O%zQcO'#DpO&cQbO'#D|OOQ`'#DO'#DOOOQ`'#Dn'#DnO&kQbO'#DmO&yQbO'#EQOOQ`'#DX'#DXO'hQRO'#DaOOQ`'#Dm'#DmO'mQQO'#DlOOQ`'#Dl'#DlOOQ`'#Db'#DbQVQbOOOOQa'#Dp'#DpOOQ`'#Cp'#CpO'uQbO'#DUOOQ`'#Do'#DoOOQ`'#Dc'#DcO(PQbO,59ZO&yQbO,59_O&yQbO,59_O)XQRO'#CsO)iQRO,59]O)zQRO,59]O)uQQO,59]O*uQQO,59]O*}QbO'#CwO+VQWO'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOSO,59aOOQa,59a,59aO+yO`O,59iOOQ`'#De'#DeO,OQbO'#DQO,WQQO,5:hO,]QbO'#DgO,bQbO,59YO,sQRO,5:lO,zQQO,5:lO-PQbO,59{OOQ`,5:W,5:WOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7a-E7aOOQa1G.y1G.yO-^QcO1G.yO&yQbO,59`O&yQbO,59`OOQa1G.w1G.wOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQa1G.{1G.{OOQa1G/T1G/TOOQ`-E7c-E7cO-xQbO1G0SO!QQbO'#CrOOQ`,5:R,5:ROOQ`-E7e-E7eO.YQbO1G0WOOQ`1G/g1G/gOOQO1G.z1G.zO.jQRO1G.zO.tQbO7+%nO.yQbO7+%oOOQ`'#DZ'#DZOOQ`7+%r7+%rO/ZQbO7+%sOOQ`<<IY<<IYO/qQQO'#DfO/vQbO'#EPO0^QbO<<IZOOQ`'#D['#D[O0cQbO<<I_OOQ`,5:Q,5:QOOQ`-E7d-E7dOOQ`AN>uAN>uO&yQbO'#D]OOQ`'#Dh'#DhO0nQbOAN>yO0yQQO'#D_OOQ`AN>yAN>yO1OQbOAN>yO1TQRO,59wO1[QQO,59wOOQ`-E7f-E7fOOQ`G24eG24eO1aQbOG24eO1fQQO,59yO1kQQO1G/cOOQ`LD*PLD*PO.yQbO1G/eO/ZQbO7+$}OOQ`7+%P7+%POOQ`<<Hi<<Hi",
|
||||||
|
stateData:
|
||||||
|
'1s~O!_OS~O]PO^_O_UO`VOmUOnUOoUOpUOsXO|]O!fSO!hTO!rbO~O]eO_UO`VOmUOnUOoUOpUOsXOwfOygO!fSO!hTOzfX!rfX!vfX!gfXvfX~OP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~P!QOPkOQkORlOSlO~OPkOQkORlOSlO!r!aX!v!aXv!aX~O]PO_UO`VOmUOnUOoUOpUO!fSO!hTO~OjtO!hwO!jrO!ksO~O!oxO~OP!dXQ!dXR!dXS!dX!r!aX!v!aXv!aX~O^yOutP~Oz|O!r!aX!v!aXv!aX~O]eO_UO`VOmUOnUOoUOpUO!fSO!hTO~OV!QO~O!r!RO!v!RO~OsXOw!TO~P&yOsXOwfOygOzca!rca!vca!gcavca~P&yOT!YOU!YOV!XOW!XOX!XOY!XOZ!XO[!XO~OPkOQkORlOSlO~P(mOPkOQkORlOSlO!g!ZO~O!g!ZOP!dXQ!dXR!dXS!dXT!dXU!dXV!dXW!dXX!dXY!dXZ!dX[!dX~Oz|O!g!ZO~O]![O!fSO~O!h!]O!j!]O!k!]O!l!]O!m!]O!n!]O~OjtO!h!_O!jrO!ksO~O]!`O~O^yOutX~Ou!bO~O]!cO~Oz|O!rba!vba!gbavba~Ou!fO~P(mOu!fO~O^_OsXO|]O~P$xOPkOQkORgiSgi!rgi!vgi!ggivgi~O^_OsXO|]O!r!kO~P$xO^_OsXO|]O!r!nO~P$xO!ghiuhi~P(mOv!oO~O^_OsXO|]Ov!sP~P$xO^_OsXO|]Ov!sP!Q!sP!S!sP~P$xO!r!uO~O^_OsXO|]Ov!sX!Q!sX!S!sX~P$xOv!wO~Ov!|O!Q!xO!S!{O~Ov#RO!Q!xO!S!{O~Ou#TO~Ov#RO~Ou#UO~P(mOu#UO~Ov#VO~O!r#WO~O!r#XO~Omo~',
|
||||||
goto: "+m!vPPPPPPPPPPPPPPPPPP!w#W#f#k#W$V$l$xP%a%aPPPP%e&OP&dPPP#fPP&gP&s&v'PP'TP&g'Z'a'h'n't'}(UPPP([(`(t)W)]*WPPP*sPPPPPP*w*wP+X+a+ad`Od!Q!b!f!k!n!q#W#XRpSiZOSd|!Q!b!f!k!n!q#W#XVhPj!czUOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XR![rdROd!Q!b!f!k!n!q#W#XQnSQ!VkR!WlQpSQ!P]Q!h!YR#P!x{UOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XTtTvdWOd!Q!b!f!k!n!q#W#XgePS]gjkl!X!Y!c!xd`Od!Q!b!f!k!n!q#W#XUfPj!cR!TgR{Xe`Od!Q!b!f!k!n!q#W#XR!m!fQ!t!nQ#Y#WR#Z#XT!y!t!zQ!}!tR#S!zQdOR!SdSjP!cR!UjQvTR!^vQzXR!azW!q!k!n#W#XR!v!qS}[qR!e}Q!z!tR#Q!zTcOdSaOdQ!g!QQ!j!bQ!l!fZ!p!k!n!q#W#Xd[Od!Q!b!f!k!n!q#W#XQqSR!d|ViPj!cdQOd!Q!b!f!k!n!q#W#XUfPj!cQmSQ!O]Q!TgQ!VkQ!WlQ!h!XQ!i!YR#O!xdWOd!Q!b!f!k!n!q#W#XdeP]gjkl!X!Y!c!xRoSTuTvmYOPdgj!Q!b!c!f!k!n!q#W#XQ!r!kV!s!n#W#Xe^Od!Q!b!f!k!n!q#W#X",
|
goto: "+m!vPPPPPPPPPPPPPPPPPP!w#W#f#k#W$V$l$xP%a%aPPPP%e&OP&dPPP#fPP&gP&s&v'PP'TP&g'Z'a'h'n't'}(UPPP([(`(t)W)]*WPPP*sPPPPPP*w*wP+X+a+ad`Od!Q!b!f!k!n!q#W#XRpSiZOSd|!Q!b!f!k!n!q#W#XVhPj!czUOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XR![rdROd!Q!b!f!k!n!q#W#XQnSQ!VkR!WlQpSQ!P]Q!h!YR#P!x{UOPS]dgjkl!Q!X!Y!b!c!f!k!n!q!x#W#XTtTvdWOd!Q!b!f!k!n!q#W#XgePS]gjkl!X!Y!c!xd`Od!Q!b!f!k!n!q#W#XUfPj!cR!TgR{Xe`Od!Q!b!f!k!n!q#W#XR!m!fQ!t!nQ#Y#WR#Z#XT!y!t!zQ!}!tR#S!zQdOR!SdSjP!cR!UjQvTR!^vQzXR!azW!q!k!n#W#XR!v!qS}[qR!e}Q!z!tR#Q!zTcOdSaOdQ!g!QQ!j!bQ!l!fZ!p!k!n!q#W#Xd[Od!Q!b!f!k!n!q#W#XQqSR!d|ViPj!cdQOd!Q!b!f!k!n!q#W#XUfPj!cQmSQ!O]Q!TgQ!VkQ!WlQ!h!XQ!i!YR#O!xdWOd!Q!b!f!k!n!q#W#XdeP]gjkl!X!Y!c!xRoSTuTvmYOPdgj!Q!b!c!f!k!n!q#W#XQ!r!kV!s!n#W#Xe^Od!Q!b!f!k!n!q#W#X",
|
||||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp ConditionalOp String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet FunctionDef keyword Params colon keyword Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
|
nodeNames:
|
||||||
|
'⚠ Star Slash Plus Minus And Or Eq Neq Lt Lte Gt Gte Identifier AssignableIdentifier Word IdentifierBeforeDot Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp ConditionalOp String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet FunctionDef keyword Params colon keyword Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign',
|
||||||
maxTerm: 84,
|
maxTerm: 84,
|
||||||
context: trackScope,
|
context: trackScope,
|
||||||
nodeProps: [
|
nodeProps: [['closedBy', 37, 'end']],
|
||||||
["closedBy", 37,"end"]
|
|
||||||
],
|
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 7,
|
repeatNodeCount: 7,
|
||||||
tokenData: "?p~RyOX#rXY$aYZ$zZp#rpq$aqt#rtu%euw#rwx%jxy%oyz&Yz{#r{|&s|}#r}!O&s!O!P#r!P!Q)g!Q!['b![!]2S!]!^$z!^#O#r#O#P2m#P#R#r#R#S2r#S#T#r#T#Y3]#Y#Z4k#Z#b3]#b#c8y#c#f3]#f#g<c#g#h3]#h#i=Y#i#o3]#o#p#r#p#q?Q#q;'S#r;'S;=`$Z<%l~#r~O#r~~?kS#wUjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rS$^P;=`<%l#r^$hUjS!_YOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU%RUjS!rQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~%jO!j~~%oO!h~U%vUjS!fQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&aUjS!gQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&xWjSOt#ruw#rx!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU'iYjSmQOt#ruw#rx!O#r!O!P(X!P!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU(^WjSOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU(}WjSmQOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU)lWjSOt#ruw#rx!P#r!P!Q*U!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU*Z^jSOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q#r!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VU+^^jSoQOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q.}!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VQ,_XoQOY,YZ!P,Y!P!Q,z!Q!},Y!}#O-i#O#P.h#P;'S,Y;'S;=`.w<%lO,YQ,}P!P!Q-QQ-VUoQ#Z#[-Q#]#^-Q#a#b-Q#g#h-Q#i#j-Q#m#n-QQ-lVOY-iZ#O-i#O#P.R#P#Q,Y#Q;'S-i;'S;=`.b<%lO-iQ.USOY-iZ;'S-i;'S;=`.b<%lO-iQ.eP;=`<%l-iQ.kSOY,YZ;'S,Y;'S;=`.w<%lO,YQ.zP;=`<%l,YU/SWjSOt#ruw#rx!P#r!P!Q/l!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU/sbjSoQOt#ruw#rx#O#r#P#Z#r#Z#[/l#[#]#r#]#^/l#^#a#r#a#b/l#b#g#r#g#h/l#h#i#r#i#j/l#j#m#r#m#n/l#n;'S#r;'S;=`$Z<%lO#rU1Q[jSOY0{YZ#rZt0{tu-iuw0{wx-ix#O0{#O#P.R#P#Q+V#Q;'S0{;'S;=`1v<%lO0{U1yP;=`<%l0{U2PP;=`<%l+VU2ZUjSuQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~2rO!k~U2yUjSwQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU3bYjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#rU4XUyQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU4pZjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#U5c#U#o3]#o;'S#r;'S;=`$Z<%lO#rU5h[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a6^#a#o3]#o;'S#r;'S;=`$Z<%lO#rU6c[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#g3]#g#h7X#h#o3]#o;'S#r;'S;=`$Z<%lO#rU7^[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#X3]#X#Y8S#Y#o3]#o;'S#r;'S;=`$Z<%lO#rU8ZYnQjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^9Q[!lWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#i3]#i#j9v#j#o3]#o;'S#r;'S;=`$Z<%lO#rU9{[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a:q#a#o3]#o;'S#r;'S;=`$Z<%lO#rU:v[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a;l#a#o3]#o;'S#r;'S;=`$Z<%lO#rU;sYpQjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^<jY!nWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^=a[!mWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#f3]#f#g>V#g#o3]#o;'S#r;'S;=`$Z<%lO#rU>[[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#i3]#i#j7X#j#o3]#o;'S#r;'S;=`$Z<%lO#rU?XUzQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~?pO!v~",
|
tokenData:
|
||||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!o~~", 11)],
|
"<}~RyOX#rXY$aYZ$zZp#rpq$aqt#rtu%euw#rwx%jxy%oyz&Yz{#r{|&s|}#r}!O&s!O!P#r!P!Q)g!Q!['b![!]2S!]!^$z!^#O#r#O#P2m#P#R#r#R#S2r#S#T#r#T#Y3]#Y#Z4k#Z#b3]#b#c8y#c#f3]#f#g9p#g#h3]#h#i:g#i#o3]#o#p#r#p#q<_#q;'S#r;'S;=`$Z<%l~#r~O#r~~<xS#wUjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rS$^P;=`<%l#r^$hUjS!_YOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU%RUjS!rQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~%jO!j~~%oO!h~U%vUjS!fQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&aUjS!gQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU&xWjSOt#ruw#rx!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU'iYjSmQOt#ruw#rx!O#r!O!P(X!P!Q#r!Q!['b![#O#r#P;'S#r;'S;=`$Z<%lO#rU(^WjSOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU(}WjSmQOt#ruw#rx!Q#r!Q![(v![#O#r#P;'S#r;'S;=`$Z<%lO#rU)lWjSOt#ruw#rx!P#r!P!Q*U!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU*Z^jSOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q#r!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VU+^^jSoQOY+VYZ#rZt+Vtu,Yuw+Vwx,Yx!P+V!P!Q.}!Q!}+V!}#O0{#O#P.h#P;'S+V;'S;=`1|<%lO+VQ,_XoQOY,YZ!P,Y!P!Q,z!Q!},Y!}#O-i#O#P.h#P;'S,Y;'S;=`.w<%lO,YQ,}P!P!Q-QQ-VUoQ#Z#[-Q#]#^-Q#a#b-Q#g#h-Q#i#j-Q#m#n-QQ-lVOY-iZ#O-i#O#P.R#P#Q,Y#Q;'S-i;'S;=`.b<%lO-iQ.USOY-iZ;'S-i;'S;=`.b<%lO-iQ.eP;=`<%l-iQ.kSOY,YZ;'S,Y;'S;=`.w<%lO,YQ.zP;=`<%l,YU/SWjSOt#ruw#rx!P#r!P!Q/l!Q#O#r#P;'S#r;'S;=`$Z<%lO#rU/sbjSoQOt#ruw#rx#O#r#P#Z#r#Z#[/l#[#]#r#]#^/l#^#a#r#a#b/l#b#g#r#g#h/l#h#i#r#i#j/l#j#m#r#m#n/l#n;'S#r;'S;=`$Z<%lO#rU1Q[jSOY0{YZ#rZt0{tu-iuw0{wx-ix#O0{#O#P.R#P#Q+V#Q;'S0{;'S;=`1v<%lO0{U1yP;=`<%l0{U2PP;=`<%l+VU2ZUjSuQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~2rO!k~U2yUjSwQOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU3bYjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#rU4XUyQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#rU4pZjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#U5c#U#o3]#o;'S#r;'S;=`$Z<%lO#rU5h[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#`3]#`#a6^#a#o3]#o;'S#r;'S;=`$Z<%lO#rU6c[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#g3]#g#h7X#h#o3]#o;'S#r;'S;=`$Z<%lO#rU7^[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#X3]#X#Y8S#Y#o3]#o;'S#r;'S;=`$Z<%lO#rU8ZYnQjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^9QY!lWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^9wY!nWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#o3]#o;'S#r;'S;=`$Z<%lO#r^:n[!mWjSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#f3]#f#g;d#g#o3]#o;'S#r;'S;=`$Z<%lO#rU;i[jSOt#ruw#rx!_#r!_!`4Q!`#O#r#P#T#r#T#i3]#i#j7X#j#o3]#o;'S#r;'S;=`$Z<%lO#rU<fUzQjSOt#ruw#rx#O#r#P;'S#r;'S;=`$Z<%lO#r~<}O!v~",
|
||||||
topRules: {"Program":[0,17]},
|
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup('[~RP!O!PU~ZO!o~~', 11)],
|
||||||
specialized: [{term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
topRules: { Program: [0, 17] },
|
||||||
tokenPrec: 768
|
specialized: [
|
||||||
|
{ term: 13, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1 },
|
||||||
|
],
|
||||||
|
tokenPrec: 768,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,14 @@ describe('null', () => {
|
||||||
Eq =
|
Eq =
|
||||||
Null null`)
|
Null null`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('does not parse null in identifier', () => {
|
||||||
|
expect('null-jk = 5').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier null-jk
|
||||||
|
Eq =
|
||||||
|
Number 5`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Identifier', () => {
|
describe('Identifier', () => {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { expect, describe, test } from 'bun:test'
|
||||||
|
|
||||||
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||||
|
|
||||||
describe('if/elsif/else', () => {
|
describe('if/elseif/else', () => {
|
||||||
test('parses single line if', () => {
|
test('parses single line if', () => {
|
||||||
expect(`if y = 1: 'cool'`).toMatchTree(`
|
expect(`if y = 1: 'cool'`).toMatchTree(`
|
||||||
IfExpr
|
IfExpr
|
||||||
|
|
@ -72,21 +72,21 @@ describe('if/elsif/else', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('parses multiline if with elsif', () => {
|
test('parses multiline if with elseif', () => {
|
||||||
expect(`if with-elsif:
|
expect(`if with-elseif:
|
||||||
x
|
x
|
||||||
elsif another-condition:
|
elseif another-condition:
|
||||||
y
|
y
|
||||||
end`).toMatchTree(`
|
end`).toMatchTree(`
|
||||||
IfExpr
|
IfExpr
|
||||||
keyword if
|
keyword if
|
||||||
Identifier with-elsif
|
Identifier with-elseif
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
FunctionCallOrIdentifier
|
FunctionCallOrIdentifier
|
||||||
Identifier x
|
Identifier x
|
||||||
ElsifExpr
|
ElseIfExpr
|
||||||
keyword elsif
|
keyword elseif
|
||||||
Identifier another-condition
|
Identifier another-condition
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
|
|
@ -96,32 +96,32 @@ describe('if/elsif/else', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('parses multiline if with multiple elsif and else', () => {
|
test('parses multiline if with multiple elseif and else', () => {
|
||||||
expect(`if with-elsif-else:
|
expect(`if with-elseif-else:
|
||||||
x
|
x
|
||||||
elsif another-condition:
|
elseif another-condition:
|
||||||
y
|
y
|
||||||
elsif yet-another-condition:
|
elseif yet-another-condition:
|
||||||
z
|
z
|
||||||
else:
|
else:
|
||||||
oh-no
|
oh-no
|
||||||
end`).toMatchTree(`
|
end`).toMatchTree(`
|
||||||
IfExpr
|
IfExpr
|
||||||
keyword if
|
keyword if
|
||||||
Identifier with-elsif-else
|
Identifier with-elseif-else
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
FunctionCallOrIdentifier
|
FunctionCallOrIdentifier
|
||||||
Identifier x
|
Identifier x
|
||||||
ElsifExpr
|
ElseIfExpr
|
||||||
keyword elsif
|
keyword elseif
|
||||||
Identifier another-condition
|
Identifier another-condition
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
FunctionCallOrIdentifier
|
FunctionCallOrIdentifier
|
||||||
Identifier y
|
Identifier y
|
||||||
ElsifExpr
|
ElseIfExpr
|
||||||
keyword elsif
|
keyword elseif
|
||||||
Identifier yet-another-condition
|
Identifier yet-another-condition
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user