i did things
This commit is contained in:
parent
82cd199ed8
commit
66671970e0
|
|
@ -1 +1 @@
|
||||||
Subproject commit 47f829fcada71655f0d40ec363b5bcc844af8856
|
Subproject commit 995487f2d5d8bb260e223ca402220c51ceba1c4a
|
||||||
|
|
@ -209,7 +209,7 @@ describe('Regex', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('invalid regex pattern', () => {
|
test('invalid regex pattern', () => {
|
||||||
expect('//[unclosed//').toFailEvaluation()
|
expect('//[unclosed//').toEvaluateTo('//[unclosed//')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,24 @@
|
||||||
import { basicSetup } from 'codemirror'
|
|
||||||
import { EditorView } from '@codemirror/view'
|
import { EditorView } from '@codemirror/view'
|
||||||
import { shrimpTheme } from '#editor/plugins/theme'
|
import { asciiEscapeToHtml, assertNever, log, toElement } from '#utils/utils'
|
||||||
import { shrimpLanguage } from '#/editor/plugins/shrimpLanguage'
|
|
||||||
import { shrimpHighlighting } from '#editor/plugins/theme'
|
|
||||||
import { shrimpKeymap } from '#editor/plugins/keymap'
|
|
||||||
import { asciiEscapeToHtml, assert, assertNever, log, toElement } from '#utils/utils'
|
|
||||||
import { Signal } from '#utils/signal'
|
import { Signal } from '#utils/signal'
|
||||||
import { shrimpErrors } from '#editor/plugins/errors'
|
import { getContent } from '#editor/plugins/persistence'
|
||||||
import { debugTags } from '#editor/plugins/debugTags'
|
|
||||||
import { getContent, persistencePlugin } from '#editor/plugins/persistence'
|
|
||||||
|
|
||||||
import '#editor/editor.css'
|
|
||||||
import type { HtmlEscapedString } from 'hono/utils/html'
|
import type { HtmlEscapedString } from 'hono/utils/html'
|
||||||
import { catchErrors } from '#editor/plugins/catchErrors'
|
|
||||||
import { connectToNose, noseSignals } from '#editor/noseClient'
|
import { connectToNose, noseSignals } from '#editor/noseClient'
|
||||||
import type { Value } from 'reefvm'
|
import type { Value } from 'reefvm'
|
||||||
|
import { Compartment } from '@codemirror/state'
|
||||||
|
import { lineNumbers } from '@codemirror/view'
|
||||||
|
import { shrimpSetup } from '#editor/plugins/shrimpSetup'
|
||||||
|
|
||||||
|
import '#editor/editor.css'
|
||||||
|
|
||||||
|
const lineNumbersCompartment = new Compartment()
|
||||||
|
|
||||||
connectToNose()
|
connectToNose()
|
||||||
|
|
||||||
|
export const outputSignal = new Signal<Value | string>()
|
||||||
|
export const errorSignal = new Signal<string>()
|
||||||
|
export const multilineModeSignal = new Signal<boolean>()
|
||||||
|
|
||||||
export const Editor = () => {
|
export const Editor = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -27,17 +28,14 @@ export const Editor = () => {
|
||||||
const view = new EditorView({
|
const view = new EditorView({
|
||||||
parent: ref,
|
parent: ref,
|
||||||
doc: getContent(),
|
doc: getContent(),
|
||||||
extensions: [
|
extensions: shrimpSetup(lineNumbersCompartment),
|
||||||
catchErrors,
|
})
|
||||||
shrimpKeymap,
|
|
||||||
basicSetup,
|
multilineModeSignal.connect((isMultiline) => {
|
||||||
shrimpTheme,
|
console.log(`🌭 hey babe`, isMultiline)
|
||||||
shrimpLanguage,
|
view.dispatch({
|
||||||
shrimpHighlighting,
|
effects: lineNumbersCompartment.reconfigure(isMultiline ? lineNumbers() : []),
|
||||||
shrimpErrors,
|
})
|
||||||
persistencePlugin,
|
|
||||||
// debugTags,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
requestAnimationFrame(() => view.focus())
|
requestAnimationFrame(() => view.focus())
|
||||||
|
|
@ -64,9 +62,6 @@ noseSignals.connect((message) => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const outputSignal = new Signal<Value | string>()
|
|
||||||
export const errorSignal = new Signal<string>()
|
|
||||||
|
|
||||||
outputSignal.connect((value) => {
|
outputSignal.connect((value) => {
|
||||||
const el = document.querySelector('#output')!
|
const el = document.querySelector('#output')!
|
||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { multilineModeSignal, outputSignal } from '#editor/editor'
|
||||||
import { printBytecodeOutput, printParserOutput, runCode } from '#editor/runCode'
|
import { printBytecodeOutput, printParserOutput, runCode } from '#editor/runCode'
|
||||||
import { EditorState } from '@codemirror/state'
|
import { EditorState } from '@codemirror/state'
|
||||||
import { keymap } from '@codemirror/view'
|
import { keymap } from '@codemirror/view'
|
||||||
|
|
@ -27,9 +28,13 @@ const customKeymap = keymap.of([
|
||||||
if (multilineMode) {
|
if (multilineMode) {
|
||||||
const input = view.state.doc.toString()
|
const input = view.state.doc.toString()
|
||||||
runCode(input)
|
runCode(input)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
} else {
|
||||||
|
outputSignal.emit('Press Shift+Enter to insert run the code.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
multilineModeSignal.emit(true)
|
||||||
multilineMode = true
|
multilineMode = true
|
||||||
view.dispatch({
|
view.dispatch({
|
||||||
changes: { from: view.state.doc.length, insert: '\n' },
|
changes: { from: view.state.doc.length, insert: '\n' },
|
||||||
|
|
@ -44,6 +49,10 @@ const customKeymap = keymap.of([
|
||||||
key: 'Tab',
|
key: 'Tab',
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: view.state.selection.main.from, insert: ' ' },
|
||||||
|
selection: { anchor: view.state.selection.main.from + 2 },
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -51,6 +60,8 @@ const customKeymap = keymap.of([
|
||||||
{
|
{
|
||||||
key: 'ArrowUp',
|
key: 'ArrowUp',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
|
if (multilineMode) return false
|
||||||
|
|
||||||
const command = history.previous()
|
const command = history.previous()
|
||||||
if (command === undefined) return false
|
if (command === undefined) return false
|
||||||
view.dispatch({
|
view.dispatch({
|
||||||
|
|
@ -64,6 +75,8 @@ const customKeymap = keymap.of([
|
||||||
{
|
{
|
||||||
key: 'ArrowDown',
|
key: 'ArrowDown',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
|
if (multilineMode) return false
|
||||||
|
|
||||||
const command = history.next()
|
const command = history.next()
|
||||||
if (command === undefined) return false
|
if (command === undefined) return false
|
||||||
view.dispatch({
|
view.dispatch({
|
||||||
|
|
@ -124,9 +137,22 @@ export const shrimpKeymap = [customKeymap, singleLineFilter]
|
||||||
class History {
|
class History {
|
||||||
private commands: string[] = []
|
private commands: string[] = []
|
||||||
private index: number | undefined
|
private index: number | undefined
|
||||||
|
private storageKey = 'shrimp-command-history'
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
try {
|
||||||
|
this.commands = JSON.parse(localStorage.getItem(this.storageKey) || '[]')
|
||||||
|
} catch {
|
||||||
|
console.warn('Failed to load command history from localStorage')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
push(command: string) {
|
push(command: string) {
|
||||||
this.commands.push(command)
|
this.commands.push(command)
|
||||||
|
|
||||||
|
// Limit to last 50 commands
|
||||||
|
this.commands = this.commands.slice(-50)
|
||||||
|
localStorage.setItem(this.storageKey, JSON.stringify(this.commands))
|
||||||
this.index = undefined
|
this.index = undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
35
src/editor/plugins/shrimpSetup.ts
Normal file
35
src/editor/plugins/shrimpSetup.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands'
|
||||||
|
import { bracketMatching, indentOnInput } from '@codemirror/language'
|
||||||
|
import { highlightSpecialChars, drawSelection, dropCursor, keymap } from '@codemirror/view'
|
||||||
|
import { closeBrackets, autocompletion, completionKeymap } from '@codemirror/autocomplete'
|
||||||
|
import { EditorState, Compartment } from '@codemirror/state'
|
||||||
|
import { searchKeymap } from '@codemirror/search'
|
||||||
|
import { shrimpKeymap } from './keymap'
|
||||||
|
import { shrimpTheme, shrimpHighlighting } from './theme'
|
||||||
|
import { shrimpLanguage } from './shrimpLanguage'
|
||||||
|
import { shrimpErrors } from './errors'
|
||||||
|
import { persistencePlugin } from './persistence'
|
||||||
|
import { catchErrors } from './catchErrors'
|
||||||
|
|
||||||
|
export const shrimpSetup = (lineNumbersCompartment: Compartment) => {
|
||||||
|
return [
|
||||||
|
catchErrors,
|
||||||
|
shrimpKeymap,
|
||||||
|
highlightSpecialChars(),
|
||||||
|
history(),
|
||||||
|
drawSelection(),
|
||||||
|
dropCursor(),
|
||||||
|
EditorState.allowMultipleSelections.of(true),
|
||||||
|
bracketMatching(),
|
||||||
|
closeBrackets(),
|
||||||
|
autocompletion(),
|
||||||
|
indentOnInput(),
|
||||||
|
keymap.of([...defaultKeymap, ...historyKeymap, ...searchKeymap, ...completionKeymap]),
|
||||||
|
lineNumbersCompartment.of([]),
|
||||||
|
shrimpTheme,
|
||||||
|
shrimpLanguage,
|
||||||
|
shrimpHighlighting,
|
||||||
|
shrimpErrors,
|
||||||
|
persistencePlugin,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -38,18 +38,12 @@ export const shrimpTheme = EditorView.theme(
|
||||||
caretColor: 'var(--caret)',
|
caretColor: 'var(--caret)',
|
||||||
padding: '0px',
|
padding: '0px',
|
||||||
},
|
},
|
||||||
'.cm-activeLine': {
|
|
||||||
backgroundColor: 'transparent',
|
|
||||||
},
|
|
||||||
'&.cm-focused .cm-cursor': {
|
'&.cm-focused .cm-cursor': {
|
||||||
borderLeftColor: 'var(--caret)',
|
borderLeftColor: 'var(--caret)',
|
||||||
},
|
},
|
||||||
'&.cm-focused .cm-selectionBackground, ::selection': {
|
'&.cm-focused .cm-selectionBackground, ::selection': {
|
||||||
backgroundColor: 'var(--bg-selection)',
|
backgroundColor: 'var(--bg-selection)',
|
||||||
},
|
},
|
||||||
'.cm-gutters': {
|
|
||||||
display: 'none',
|
|
||||||
},
|
|
||||||
'.cm-editor': {
|
'.cm-editor': {
|
||||||
border: 'none',
|
border: 'none',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
import { outputSignal, errorSignal } from '#editor/editor'
|
import { outputSignal, errorSignal } from '#editor/editor'
|
||||||
import { Compiler } from '#compiler/compiler'
|
import { Compiler } from '#compiler/compiler'
|
||||||
import { errorMessage, log } from '#utils/utils'
|
import { errorMessage, log } from '#utils/utils'
|
||||||
import { bytecodeToString, run } from 'reefvm'
|
import { bytecodeToString } from 'reefvm'
|
||||||
import { parser } from '#parser/shrimp'
|
import { parser } from '#parser/shrimp'
|
||||||
import { sendToNose } from '#editor/noseClient'
|
import { sendToNose } from '#editor/noseClient'
|
||||||
|
import { treeToString } from '#utils/tree'
|
||||||
|
|
||||||
export const runCode = async (input: string) => {
|
export const runCode = async (input: string) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -18,7 +19,8 @@ export const runCode = async (input: string) => {
|
||||||
export const printParserOutput = (input: string) => {
|
export const printParserOutput = (input: string) => {
|
||||||
try {
|
try {
|
||||||
const cst = parser.parse(input)
|
const cst = parser.parse(input)
|
||||||
outputSignal.emit(cst.toString())
|
const string = treeToString(cst, input)
|
||||||
|
outputSignal.emit(string)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
errorSignal.emit(`${errorMessage(error)}`)
|
errorSignal.emit(`${errorMessage(error)}`)
|
||||||
|
|
|
||||||
80
src/parser/operatorTokenizer.ts
Normal file
80
src/parser/operatorTokenizer.ts
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { ExternalTokenizer, InputStream } from '@lezer/lr'
|
||||||
|
import * as terms from './shrimp.terms'
|
||||||
|
|
||||||
|
type Operator = { str: string; tokenName: keyof typeof terms }
|
||||||
|
const operators: Array<Operator> = [
|
||||||
|
{ str: 'and', tokenName: 'And' },
|
||||||
|
{ str: 'or', tokenName: 'Or' },
|
||||||
|
{ str: '>=', tokenName: 'Gte' },
|
||||||
|
{ str: '<=', tokenName: 'Lte' },
|
||||||
|
{ str: '!=', tokenName: 'Neq' },
|
||||||
|
|
||||||
|
// // Single-char operators
|
||||||
|
{ str: '*', tokenName: 'Star' },
|
||||||
|
{ str: '=', tokenName: 'Eq' },
|
||||||
|
{ str: '/', tokenName: 'Slash' },
|
||||||
|
{ str: '+', tokenName: 'Plus' },
|
||||||
|
{ str: '-', tokenName: 'Minus' },
|
||||||
|
{ str: '>', tokenName: 'Gt' },
|
||||||
|
{ str: '<', tokenName: 'Lt' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const operatorTokenizer = new ExternalTokenizer((input: InputStream) => {
|
||||||
|
for (let operator of operators) {
|
||||||
|
if (!matchesString(input, 0, operator.str)) continue
|
||||||
|
const afterOpPos = operator.str.length
|
||||||
|
const charAfterOp = input.peek(afterOpPos)
|
||||||
|
if (!isWhitespace(charAfterOp)) continue
|
||||||
|
|
||||||
|
// Accept the operator token
|
||||||
|
const token = terms[operator.tokenName]
|
||||||
|
if (token === undefined) {
|
||||||
|
throw new Error(`Unknown token name: ${operator.tokenName}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
input.advance(afterOpPos)
|
||||||
|
input.acceptToken(token)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const isWhitespace = (ch: number): boolean => {
|
||||||
|
return matchesChar(ch, [' ', '\t', '\n'])
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesChar = (ch: number, chars: (string | number)[]): boolean => {
|
||||||
|
for (const c of chars) {
|
||||||
|
if (typeof c === 'number') {
|
||||||
|
if (ch === c) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if (ch === c.charCodeAt(0)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchesString = (input: InputStream, pos: number, str: string): boolean => {
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
if (input.peek(pos + i) !== str.charCodeAt(i)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const peek = (numChars: number, input: InputStream): string => {
|
||||||
|
let result = ''
|
||||||
|
for (let i = 0; i < numChars; i++) {
|
||||||
|
const ch = input.peek(i)
|
||||||
|
if (ch === -1) {
|
||||||
|
result += 'EOF'
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
result += String.fromCharCode(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -6,14 +6,14 @@
|
||||||
|
|
||||||
@top Program { item* }
|
@top Program { item* }
|
||||||
|
|
||||||
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
|
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, Neq, Lt, Lte, Gt, Gte }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
@precedence { Number "-" Regex "/"}
|
@precedence { Number Regex }
|
||||||
|
|
||||||
StringFragment { !['\\$]+ }
|
StringFragment { !['\\$]+ }
|
||||||
NamedArgPrefix { $[a-z]+ "=" }
|
NamedArgPrefix { $[a-z]+ "=" }
|
||||||
Number { "-"? $[0-9]+ ('.' $[0-9]+)? }
|
Number { ("-" | "+")? $[0-9]+ ('.' $[0-9]+)? }
|
||||||
Boolean { "true" | "false" }
|
Boolean { "true" | "false" }
|
||||||
newlineOrSemicolon { "\n" | ";" }
|
newlineOrSemicolon { "\n" | ";" }
|
||||||
eof { @eof }
|
eof { @eof }
|
||||||
|
|
@ -29,22 +29,12 @@
|
||||||
"if" [@name=keyword]
|
"if" [@name=keyword]
|
||||||
"elsif" [@name=keyword]
|
"elsif" [@name=keyword]
|
||||||
"else" [@name=keyword]
|
"else" [@name=keyword]
|
||||||
"and" [@name=operator]
|
|
||||||
"or" [@name=operator]
|
|
||||||
"!=" [@name=operator]
|
|
||||||
"<" [@name=operator]
|
|
||||||
"<=" [@name=operator]
|
|
||||||
">" [@name=operator]
|
|
||||||
">=" [@name=operator]
|
|
||||||
"=" [@name=operator]
|
|
||||||
"+"[@name=operator]
|
|
||||||
"-"[@name=operator]
|
|
||||||
"*"[@name=operator]
|
|
||||||
"/"[@name=operator]
|
|
||||||
"|"[@name=operator]
|
"|"[@name=operator]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
|
||||||
|
|
||||||
@precedence {
|
@precedence {
|
||||||
pipe @left,
|
pipe @left,
|
||||||
multiplicative @left,
|
multiplicative @left,
|
||||||
|
|
@ -140,14 +130,14 @@ ThenBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConditionalOp {
|
ConditionalOp {
|
||||||
expression "=" expression |
|
expression Eq expression |
|
||||||
expression "!=" expression |
|
expression Neq expression |
|
||||||
expression "<" expression |
|
expression Lt expression |
|
||||||
expression "<=" expression |
|
expression Lte expression |
|
||||||
expression ">" expression |
|
expression Gt expression |
|
||||||
expression ">=" expression |
|
expression Gte expression |
|
||||||
expression "and" (expression | ConditionalOp) |
|
expression And (expression | ConditionalOp) |
|
||||||
expression "or" (expression | ConditionalOp)
|
expression Or (expression | ConditionalOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
Params {
|
Params {
|
||||||
|
|
@ -155,14 +145,14 @@ Params {
|
||||||
}
|
}
|
||||||
|
|
||||||
Assign {
|
Assign {
|
||||||
AssignableIdentifier "=" consumeToTerminator
|
AssignableIdentifier Eq consumeToTerminator
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOp {
|
BinOp {
|
||||||
(expression | BinOp) !multiplicative "*" (expression | BinOp) |
|
(expression | BinOp) !multiplicative Star (expression | BinOp) |
|
||||||
(expression | BinOp) !multiplicative "/" (expression | BinOp) |
|
(expression | BinOp) !multiplicative Slash (expression | BinOp) |
|
||||||
(expression | BinOp) !additive "+" (expression | BinOp) |
|
(expression | BinOp) !additive Plus (expression | BinOp) |
|
||||||
(expression | BinOp) !additive "-" (expression | BinOp)
|
(expression | BinOp) !additive Minus (expression | BinOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParenExpr {
|
ParenExpr {
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,29 @@
|
||||||
// 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
|
||||||
Identifier = 1,
|
Star = 1,
|
||||||
AssignableIdentifier = 2,
|
Slash = 2,
|
||||||
Word = 3,
|
Plus = 3,
|
||||||
IdentifierBeforeDot = 4,
|
Minus = 4,
|
||||||
Program = 5,
|
And = 5,
|
||||||
PipeExpr = 6,
|
Or = 6,
|
||||||
FunctionCall = 7,
|
Eq = 7,
|
||||||
PositionalArg = 8,
|
Neq = 8,
|
||||||
ParenExpr = 9,
|
Lt = 9,
|
||||||
FunctionCallOrIdentifier = 10,
|
Lte = 10,
|
||||||
BinOp = 11,
|
Gt = 11,
|
||||||
ConditionalOp = 16,
|
Gte = 12,
|
||||||
|
Identifier = 13,
|
||||||
|
AssignableIdentifier = 14,
|
||||||
|
Word = 15,
|
||||||
|
IdentifierBeforeDot = 16,
|
||||||
|
Program = 17,
|
||||||
|
PipeExpr = 18,
|
||||||
|
FunctionCall = 19,
|
||||||
|
PositionalArg = 20,
|
||||||
|
ParenExpr = 21,
|
||||||
|
FunctionCallOrIdentifier = 22,
|
||||||
|
BinOp = 23,
|
||||||
|
ConditionalOp = 24,
|
||||||
String = 25,
|
String = 25,
|
||||||
StringFragment = 26,
|
StringFragment = 26,
|
||||||
Interpolation = 27,
|
Interpolation = 27,
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,15 @@
|
||||||
// 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} from "@lezer/lr"
|
import {LRParser} from "@lezer/lr"
|
||||||
|
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"
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: ".jQVQROOO#XQTO'#CfO$RQQO'#CgO$aQQO'#DmO$xQRO'#CeO%gOWO'#CuOOQP'#Dq'#DqO%uOQO'#C}O%zQQO'#DpO&cQRO'#D|OOQP'#DO'#DOOOQO'#Dn'#DnO&kQQO'#DmO&yQRO'#EQOOQO'#DX'#DXO'hQQO'#DaOOQO'#Dm'#DmO'mQQO'#DlOOQP'#Dl'#DlOOQP'#Db'#DbQVQROOOOQP'#Dp'#DpOOQP'#Cd'#CdO'uQRO'#DUOOQP'#Do'#DoOOQP'#Dc'#DcO(PQTO,58}O&yQRO,59RO&yQRO,59RO)XQQO'#CgO)iQQO,59PO)zQQO,59PO)uQQO,59PO*uQQO,59PO*}QRO'#CwO+VQ`O'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOWO,59aOOQP,59a,59aO+yOPO,59iOOQP'#De'#DeO,OQRO'#DQO,WQQO,5:hO,]QRO'#DgO,bQQO,58|O,sQQO,5:lO,zQQO,5:lO-PQRO,59{OOQP,5:W,5:WOOQP-E7`-E7`OOQP,59p,59pOOQP-E7a-E7aOOQO1G.m1G.mO-^QQO1G.mO&yQRO,59WO&yQRO,59WOOQP1G.k1G.kOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQP1G.{1G.{OOQP1G/T1G/TOOQP-E7c-E7cO-xQRO1G0SO!QQTO'#CfOOQO,5:R,5:ROOQO-E7e-E7eO.YQRO1G0WOOQO1G/g1G/gOOQO1G.r1G.rO.jQQO1G.rO.tQQO7+%nO.yQRO7+%oOOQO'#DZ'#DZOOQO7+%r7+%rO/ZQRO7+%sOOQP<<IY<<IYO/qQQO'#DfO/vQRO'#EPO0^QQO<<IZOOQO'#D['#D[O0cQQO<<I_OOQP,5:Q,5:QOOQP-E7d-E7dOOQPAN>uAN>uO&yQRO'#D]OOQO'#Dh'#DhO0nQQOAN>yO0yQQO'#D_OOQOAN>yAN>yO1OQQOAN>yO1TQQO,59wO1[QQO,59wOOQO-E7f-E7fOOQOG24eG24eO1aQQOG24eO1fQQO,59yO1kQQO1G/cOOQOLD*PLD*PO.yQRO1G/eO/ZQRO7+$}OOQO7+%P7+%POOQO<<Hi<<Hi",
|
states: ".jQVQrOOO#XQuO'#CrO$RQRO'#CsO$aQRO'#DmO$xQrO'#CqO%gOWO'#CuOOQq'#Dq'#DqO%uOQO'#C}O%zQRO'#DpO&cQrO'#D|OOQp'#DO'#DOOOQO'#Dn'#DnO&kQQO'#DmO&yQrO'#EQOOQO'#DX'#DXO'hQRO'#DaOOQO'#Dm'#DmO'mQQO'#DlOOQp'#Dl'#DlOOQp'#Db'#DbQVQrOOOOQq'#Dp'#DpOOQp'#Cp'#CpO'uQrO'#DUOOQp'#Do'#DoOOQp'#Dc'#DcO(PQtO,59ZO&yQrO,59_O&yQrO,59_O)XQRO'#CsO)iQRO,59]O)zQRO,59]O)uQQO,59]O*uQQO,59]O*}QrO'#CwO+VQ`O'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOWO,59aOOQq,59a,59aO+yOpO,59iOOQp'#De'#DeO,OQrO'#DQO,WQQO,5:hO,]QrO'#DgO,bQQO,59YO,sQRO,5:lO,zQQO,5:lO-PQrO,59{OOQp,5:W,5:WOOQp-E7`-E7`OOQp,59p,59pOOQp-E7a-E7aOOQP1G.y1G.yO-^QRO1G.yO&yQrO,59`O&yQrO,59`OOQq1G.w1G.wOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQq1G.{1G.{OOQq1G/T1G/TOOQp-E7c-E7cO-xQrO1G0SO!QQtO'#CrOOQO,5:R,5:ROOQO-E7e-E7eO.YQrO1G0WOOQO1G/g1G/gOOQO1G.z1G.zO.jQRO1G.zO.tQQO7+%nO.yQrO7+%oOOQO'#DZ'#DZOOQO7+%r7+%rO/ZQrO7+%sOOQp<<IY<<IYO/qQQO'#DfO/vQrO'#EPO0^QQO<<IZOOQO'#D['#D[O0cQQO<<I_OOQp,5:Q,5:QOOQp-E7d-E7dOOQpAN>uAN>uO&yQrO'#D]OOQO'#Dh'#DhO0nQQOAN>yO0yQQO'#D_OOQOAN>yAN>yO1OQQOAN>yO1TQRO,59wO1[QQO,59wOOQO-E7f-E7fOOQOG24eG24eO1aQQOG24eO1fQQO,59yO1kQQO1G/cOOQOLD*PLD*PO.yQrO1G/eO/ZQrO7+$}OOQO7+%P7+%POOQO<<Hi<<Hi",
|
||||||
stateData: "1v~O!_OS~OPPOQ_ORUOSVOmUOnUOoUOpUOsXO|]O!fSO!hTO!rbO~OPeORUOSVOmUOnUOoUOpUOsXOwfOygO!fSO!hTOzYX!rYX!vYX!gYXvYX~O[!dX]!dX^!dX_!dXa!dXb!dXc!dXd!dXe!dXf!dXg!dXh!dX~P!QO[kO]kO^lO_lO~O[kO]kO^lO_lO!r!aX!v!aXv!aX~OPPORUOSVOmUOnUOoUOpUO!fSO!hTO~OjtO!hwO!jrO!ksO~O!oxO~O[!dX]!dX^!dX_!dX!r!aX!v!aXv!aX~OQyOutP~Oz|O!r!aX!v!aXv!aX~OPeORUOSVOmUOnUOoUOpUO!fSO!hTO~Oa!QO~O!r!RO!v!RO~OsXOw!TO~P&yOsXOwfOygOzVa!rVa!vVa!gVavVa~P&yOa!XOb!XOc!XOd!XOe!XOf!XOg!YOh!YO~O[kO]kO^lO_lO~P(mO[kO]kO^lO_lO!g!ZO~O!g!ZO[!dX]!dX^!dX_!dXa!dXb!dXc!dXd!dXe!dXf!dXg!dXh!dX~Oz|O!g!ZO~OP![O!fSO~O!h!]O!j!]O!k!]O!l!]O!m!]O!n!]O~OjtO!h!_O!jrO!ksO~OP!`O~OQyOutX~Ou!bO~OP!cO~Oz|O!rUa!vUa!gUavUa~Ou!fO~P(mOu!fO~OQ_OsXO|]O~P$xO[kO]kO^Zi_Zi!rZi!vZi!gZivZi~OQ_OsXO|]O!r!kO~P$xOQ_OsXO|]O!r!nO~P$xO!g`iu`i~P(mOv!oO~OQ_OsXO|]Ov!sP~P$xOQ_OsXO|]Ov!sP!Q!sP!S!sP~P$xO!r!uO~OQ_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~Om_o]o~",
|
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!vPPPPPP!w#W#f#k#W$VPPPP$lPPPPPPPP$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: "⚠ Identifier AssignableIdentifier Word IdentifierBeforeDot Program PipeExpr FunctionCall PositionalArg ParenExpr FunctionCallOrIdentifier BinOp operator operator operator operator ConditionalOp operator operator operator operator operator operator operator operator String StringFragment Interpolation EscapeSeq Number Boolean Regex Null DotGet FunctionDef keyword Params colon end 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 end Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
|
||||||
maxTerm: 84,
|
maxTerm: 84,
|
||||||
context: trackScope,
|
context: trackScope,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
|
|
@ -18,8 +19,8 @@ export const parser = LRParser.deserialize({
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 7,
|
repeatNodeCount: 7,
|
||||||
tokenData: "!&X~R!SOX$_XY$|YZ%gZp$_pq$|qr&Qrt$_tu'Yuw$_wx'_xy'dyz'}z{(h{|)R|}$_}!O)l!O!P,b!P!Q,{!Q![*]![!]5j!]!^%g!^!_6T!_!`7_!`!a7x!a#O$_#O#P9S#P#R$_#R#S9X#S#T$_#T#U9r#U#X;W#X#Y=m#Y#ZDs#Z#];W#]#^JO#^#b;W#b#cKp#c#d! Y#d#f;W#f#g!!z#g#h;W#h#i!#q#i#o;W#o#p$_#p#q!%i#q;'S$_;'S;=`$v<%l~$_~O$_~~!&SS$dUjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_S$yP;=`<%l$__%TUjS!_ZOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V%nUjS!rROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V&VWjSOt$_uw$_x!_$_!_!`&o!`#O$_#P;'S$_;'S;=`$v<%lO$_V&vUbRjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~'_O!j~~'dO!h~V'kUjS!fROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(UUjS!gROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(oU[RjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)YU^RjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)sWjS_ROt$_uw$_x!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V*dYjSmROt$_uw$_x!O$_!O!P+S!P!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V+XWjSOt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V+xWjSmROt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_T,iU!oPjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V-SWjS]ROt$_uw$_x!P$_!P!Q-l!Q#O$_#P;'S$_;'S;=`$v<%lO$_V-q^jSOY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q$_!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mV.t^jSoROY.mYZ$_Zt.mtu/puw.mwx/px!P.m!P!Q2e!Q!}.m!}#O4c#O#P2O#P;'S.m;'S;=`5d<%lO.mR/uXoROY/pZ!P/p!P!Q0b!Q!}/p!}#O1P#O#P2O#P;'S/p;'S;=`2_<%lO/pR0eP!P!Q0hR0mUoR#Z#[0h#]#^0h#a#b0h#g#h0h#i#j0h#m#n0hR1SVOY1PZ#O1P#O#P1i#P#Q/p#Q;'S1P;'S;=`1x<%lO1PR1lSOY1PZ;'S1P;'S;=`1x<%lO1PR1{P;=`<%l1PR2RSOY/pZ;'S/p;'S;=`2_<%lO/pR2bP;=`<%l/pV2jWjSOt$_uw$_x!P$_!P!Q3S!Q#O$_#P;'S$_;'S;=`$v<%lO$_V3ZbjSoROt$_uw$_x#O$_#P#Z$_#Z#[3S#[#]$_#]#^3S#^#a$_#a#b3S#b#g$_#g#h3S#h#i$_#i#j3S#j#m$_#m#n3S#n;'S$_;'S;=`$v<%lO$_V4h[jSOY4cYZ$_Zt4ctu1Puw4cwx1Px#O4c#O#P1i#P#Q.m#Q;'S4c;'S;=`5^<%lO4cV5aP;=`<%l4cV5gP;=`<%l.mT5qUjSuPOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V6[WcRjSOt$_uw$_x!_$_!_!`6t!`#O$_#P;'S$_;'S;=`$v<%lO$_V6{UdRjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V7fUaRjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V8PWeRjSOt$_uw$_x!_$_!_!`8i!`#O$_#P;'S$_;'S;=`$v<%lO$_V8pUfRjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~9XO!k~V9`UjSwROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V9w[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#b;W#b#c;{#c#o;W#o;'S$_;'S;=`$v<%lO$_U:tUyQjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_U;]YjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V<Q[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#X<v#X#o;W#o;'S$_;'S;=`$v<%lO$_V<}YgRjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V=r^jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#a>n#a#b;W#b#cCR#c#o;W#o;'S$_;'S;=`$v<%lO$_V>s[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#h?i#h#o;W#o;'S$_;'S;=`$v<%lO$_V?n^jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#Y@j#Y#];W#]#^Aa#^#o;W#o;'S$_;'S;=`$v<%lO$_V@qY!SPjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VAf[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZB[#Z#o;W#o;'S$_;'S;=`$v<%lO$_VBcY!QPjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VCW[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#W;W#W#XC|#X#o;W#o;'S$_;'S;=`$v<%lO$_VDTYjSvROt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VDx]jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#UEq#U#b;W#b#cIX#c#o;W#o;'S$_;'S;=`$v<%lO$_VEv[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aFl#a#o;W#o;'S$_;'S;=`$v<%lO$_VFq[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#g;W#g#hGg#h#o;W#o;'S$_;'S;=`$v<%lO$_VGl[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#X;W#X#YHb#Y#o;W#o;'S$_;'S;=`$v<%lO$_VHiYnRjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VI`YsRjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_VJT[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#Y;W#Y#ZJy#Z#o;W#o;'S$_;'S;=`$v<%lO$_VKQY|PjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__Kw[!lWjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jLm#j#o;W#o;'S$_;'S;=`$v<%lO$_VLr[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aMh#a#o;W#o;'S$_;'S;=`$v<%lO$_VMm[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#`;W#`#aNc#a#o;W#o;'S$_;'S;=`$v<%lO$_VNjYpRjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_V! _[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!!T#g#o;W#o;'S$_;'S;=`$v<%lO$_V!![YhRjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$_^!#RY!nWjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#o;W#o;'S$_;'S;=`$v<%lO$__!#x[!mWjSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#f;W#f#g!$n#g#o;W#o;'S$_;'S;=`$v<%lO$_V!$s[jSOt$_uw$_x!_$_!_!`:m!`#O$_#P#T$_#T#i;W#i#jGg#j#o;W#o;'S$_;'S;=`$v<%lO$_V!%pUzRjSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~!&XO!v~",
|
tokenData: "JX~R|OX#{XY$jYZ%TZp#{pq$jqt#{tu%nuw#{wx%sxy%xyz&cz{#{{|&||}#{}!O&|!O!P)p!P!Q*Z!Q!['k![!]2v!]!^%T!^#O#{#O#P3a#P#R#{#R#S3f#S#T#{#T#X4P#X#Y5_#Y#Z<e#Z#]4P#]#^Ap#^#b4P#b#cCb#c#f4P#f#gFz#g#h4P#h#iGq#i#o4P#o#p#{#p#qIi#q;'S#{;'S;=`$d<%l~#{~O#{~~JSS$QUjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{_$qUjS!_ZOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V%[UjS!rROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~%sO!j~~%xO!h~V&PUjS!fROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V&jUjS!gROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V'RWjSOt#{uw#{x!Q#{!Q!['k![#O#{#P;'S#{;'S;=`$d<%lO#{V'rYjSmROt#{uw#{x!O#{!O!P(b!P!Q#{!Q!['k![#O#{#P;'S#{;'S;=`$d<%lO#{V(gWjSOt#{uw#{x!Q#{!Q![)P![#O#{#P;'S#{;'S;=`$d<%lO#{V)WWjSmROt#{uw#{x!Q#{!Q![)P![#O#{#P;'S#{;'S;=`$d<%lO#{T)wU!oPjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V*`WjSOt#{uw#{x!P#{!P!Q*x!Q#O#{#P;'S#{;'S;=`$d<%lO#{V*}^jSOY+yYZ#{Zt+ytu,|uw+ywx,|x!P+y!P!Q#{!Q!}+y!}#O1o#O#P/[#P;'S+y;'S;=`2p<%lO+yV,Q^jSoROY+yYZ#{Zt+ytu,|uw+ywx,|x!P+y!P!Q/q!Q!}+y!}#O1o#O#P/[#P;'S+y;'S;=`2p<%lO+yR-RXoROY,|Z!P,|!P!Q-n!Q!},|!}#O.]#O#P/[#P;'S,|;'S;=`/k<%lO,|R-qP!P!Q-tR-yUoR#Z#[-t#]#^-t#a#b-t#g#h-t#i#j-t#m#n-tR.`VOY.]Z#O.]#O#P.u#P#Q,|#Q;'S.];'S;=`/U<%lO.]R.xSOY.]Z;'S.];'S;=`/U<%lO.]R/XP;=`<%l.]R/_SOY,|Z;'S,|;'S;=`/k<%lO,|R/nP;=`<%l,|V/vWjSOt#{uw#{x!P#{!P!Q0`!Q#O#{#P;'S#{;'S;=`$d<%lO#{V0gbjSoROt#{uw#{x#O#{#P#Z#{#Z#[0`#[#]#{#]#^0`#^#a#{#a#b0`#b#g#{#g#h0`#h#i#{#i#j0`#j#m#{#m#n0`#n;'S#{;'S;=`$d<%lO#{V1t[jSOY1oYZ#{Zt1otu.]uw1owx.]x#O1o#O#P.u#P#Q+y#Q;'S1o;'S;=`2j<%lO1oV2mP;=`<%l1oV2sP;=`<%l+yT2}UjSuPOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~3fO!k~V3mUjSwROt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U4UYjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{U4{UyQjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{V5d^jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#a6`#a#b4P#b#c:s#c#o4P#o;'S#{;'S;=`$d<%lO#{V6e[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#g4P#g#h7Z#h#o4P#o;'S#{;'S;=`$d<%lO#{V7`^jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#X4P#X#Y8[#Y#]4P#]#^9R#^#o4P#o;'S#{;'S;=`$d<%lO#{V8cY!SPjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V9W[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#Y4P#Y#Z9|#Z#o4P#o;'S#{;'S;=`$d<%lO#{V:TY!QPjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V:x[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#W4P#W#X;n#X#o4P#o;'S#{;'S;=`$d<%lO#{V;uYjSvROt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{V<j]jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#U=c#U#b4P#b#c@y#c#o4P#o;'S#{;'S;=`$d<%lO#{V=h[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#a>^#a#o4P#o;'S#{;'S;=`$d<%lO#{V>c[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#g4P#g#h?X#h#o4P#o;'S#{;'S;=`$d<%lO#{V?^[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#X4P#X#Y@S#Y#o4P#o;'S#{;'S;=`$d<%lO#{V@ZYnRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{VAQYsRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{VAu[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#Y4P#Y#ZBk#Z#o4P#o;'S#{;'S;=`$d<%lO#{VBrY|PjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{_Ci[!lWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#i4P#i#jD_#j#o4P#o;'S#{;'S;=`$d<%lO#{VDd[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#aEY#a#o4P#o;'S#{;'S;=`$d<%lO#{VE_[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#`4P#`#aFT#a#o4P#o;'S#{;'S;=`$d<%lO#{VF[YpRjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{^GRY!nWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#o4P#o;'S#{;'S;=`$d<%lO#{_Gx[!mWjSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#f4P#f#gHn#g#o4P#o;'S#{;'S;=`$d<%lO#{VHs[jSOt#{uw#{x!_#{!_!`4t!`#O#{#P#T#{#T#i4P#i#j?X#j#o4P#o;'S#{;'S;=`$d<%lO#{VIpUzRjSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~JXO!v~",
|
||||||
tokenizers: [tokenizer, 0, 1, 2, 3],
|
tokenizers: [operatorTokenizer, 0, 1, 2, 3, tokenizer],
|
||||||
topRules: {"Program":[0,5]},
|
topRules: {"Program":[0,17]},
|
||||||
tokenPrec: 768
|
tokenPrec: 768
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ describe('null', () => {
|
||||||
expect('a = null').toMatchTree(`
|
expect('a = null').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier a
|
AssignableIdentifier a
|
||||||
operator =
|
Eq =
|
||||||
Null null`)
|
Null null`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -30,7 +30,7 @@ describe('Parentheses', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Number 2
|
Number 2
|
||||||
operator +
|
Plus +
|
||||||
Number 3`)
|
Number 3`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -72,14 +72,14 @@ describe('Parentheses', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
ConditionalOp
|
ConditionalOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator >
|
Gt >
|
||||||
Identifier b`)
|
Identifier b`)
|
||||||
|
|
||||||
expect('(a and b)').toMatchTree(`
|
expect('(a and b)').toMatchTree(`
|
||||||
ParenExpr
|
ParenExpr
|
||||||
ConditionalOp
|
ConditionalOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator and
|
And and
|
||||||
Identifier b`)
|
Identifier b`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ describe('Parentheses', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Number 3
|
Number 3
|
||||||
operator +
|
Plus +
|
||||||
Number 3`)
|
Number 3`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -105,13 +105,16 @@ describe('Parentheses', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('a word start with *', () => {
|
test('a word start with an operator', () => {
|
||||||
expect('find *cool*').toMatchTree(`
|
const operators = ['*', '/', '+', '-', 'and', 'or', '=', '!=', '>=', '<=', '>', '<']
|
||||||
|
for (const operator of operators) {
|
||||||
|
expect(`find ${operator}cool*`).toMatchTree(`
|
||||||
FunctionCall
|
FunctionCall
|
||||||
Identifier find
|
Identifier find
|
||||||
PositionalArg
|
PositionalArg
|
||||||
Word *cool*
|
Word ${operator}cool*
|
||||||
`)
|
`)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
test('a word can look like a binop', () => {
|
test('a word can look like a binop', () => {
|
||||||
|
|
@ -128,11 +131,11 @@ describe('Parentheses', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Number 2
|
Number 2
|
||||||
operator +
|
Plus +
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Number 1
|
Number 1
|
||||||
operator *
|
Star *
|
||||||
Number 4`)
|
Number 4`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -140,7 +143,7 @@ describe('Parentheses', () => {
|
||||||
expect('4 + (echo 3)').toMatchTree(`
|
expect('4 + (echo 3)').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Number 4
|
Number 4
|
||||||
operator +
|
Plus +
|
||||||
ParenExpr
|
ParenExpr
|
||||||
FunctionCall
|
FunctionCall
|
||||||
Identifier echo
|
Identifier echo
|
||||||
|
|
@ -149,12 +152,12 @@ describe('Parentheses', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.only('BinOp', () => {
|
describe('BinOp', () => {
|
||||||
test('addition tests', () => {
|
test('addition tests', () => {
|
||||||
expect('2 + 3').toMatchTree(`
|
expect('2 + 3').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Number 2
|
Number 2
|
||||||
operator +
|
Plus +
|
||||||
Number 3
|
Number 3
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -163,7 +166,7 @@ describe.only('BinOp', () => {
|
||||||
expect('5 - 2').toMatchTree(`
|
expect('5 - 2').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Number 5
|
Number 5
|
||||||
operator -
|
Minus -
|
||||||
Number 2
|
Number 2
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -172,7 +175,7 @@ describe.only('BinOp', () => {
|
||||||
expect('4 * 3').toMatchTree(`
|
expect('4 * 3').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Number 4
|
Number 4
|
||||||
operator *
|
Star *
|
||||||
Number 3
|
Number 3
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -181,7 +184,7 @@ describe.only('BinOp', () => {
|
||||||
expect('8 / 2').toMatchTree(`
|
expect('8 / 2').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Number 8
|
Number 8
|
||||||
operator /
|
Slash /
|
||||||
Number 2
|
Number 2
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -191,15 +194,15 @@ describe.only('BinOp', () => {
|
||||||
BinOp
|
BinOp
|
||||||
BinOp
|
BinOp
|
||||||
Number 2
|
Number 2
|
||||||
operator +
|
Plus +
|
||||||
BinOp
|
BinOp
|
||||||
Number 3
|
Number 3
|
||||||
operator *
|
Star *
|
||||||
Number 4
|
Number 4
|
||||||
operator -
|
Minus -
|
||||||
BinOp
|
BinOp
|
||||||
Number 5
|
Number 5
|
||||||
operator /
|
Slash /
|
||||||
Number 1
|
Number 1
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -210,7 +213,7 @@ describe('ambiguity', () => {
|
||||||
expect('a + -3').toMatchTree(`
|
expect('a + -3').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator +
|
Plus +
|
||||||
Number -3
|
Number -3
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -219,7 +222,7 @@ describe('ambiguity', () => {
|
||||||
expect('a-var + a-thing').toMatchTree(`
|
expect('a-var + a-thing').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a-var
|
Identifier a-var
|
||||||
operator +
|
Plus +
|
||||||
Identifier a-thing
|
Identifier a-thing
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
@ -231,11 +234,11 @@ describe('newlines', () => {
|
||||||
y = 2`).toMatchTree(`
|
y = 2`).toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier x
|
AssignableIdentifier x
|
||||||
operator =
|
Eq =
|
||||||
Number 5
|
Number 5
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier y
|
AssignableIdentifier y
|
||||||
operator =
|
Eq =
|
||||||
Number 2`)
|
Number 2`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -243,11 +246,11 @@ y = 2`).toMatchTree(`
|
||||||
expect(`x = 5; y = 2`).toMatchTree(`
|
expect(`x = 5; y = 2`).toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier x
|
AssignableIdentifier x
|
||||||
operator =
|
Eq =
|
||||||
Number 5
|
Number 5
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier y
|
AssignableIdentifier y
|
||||||
operator =
|
Eq =
|
||||||
Number 2`)
|
Number 2`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -255,7 +258,7 @@ y = 2`).toMatchTree(`
|
||||||
expect(`a = hello; 2`).toMatchTree(`
|
expect(`a = hello; 2`).toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier a
|
AssignableIdentifier a
|
||||||
operator =
|
Eq =
|
||||||
FunctionCallOrIdentifier
|
FunctionCallOrIdentifier
|
||||||
Identifier hello
|
Identifier hello
|
||||||
Number 2`)
|
Number 2`)
|
||||||
|
|
@ -267,7 +270,7 @@ describe('Assign', () => {
|
||||||
expect('x = 5').toMatchTree(`
|
expect('x = 5').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier x
|
AssignableIdentifier x
|
||||||
operator =
|
Eq =
|
||||||
Number 5`)
|
Number 5`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -275,10 +278,10 @@ describe('Assign', () => {
|
||||||
expect('x = 5 + 3').toMatchTree(`
|
expect('x = 5 + 3').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier x
|
AssignableIdentifier x
|
||||||
operator =
|
Eq =
|
||||||
BinOp
|
BinOp
|
||||||
Number 5
|
Number 5
|
||||||
operator +
|
Plus +
|
||||||
Number 3`)
|
Number 3`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -286,7 +289,7 @@ describe('Assign', () => {
|
||||||
expect('add = fn a b: a + b end').toMatchTree(`
|
expect('add = fn a b: a + b end').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier add
|
AssignableIdentifier add
|
||||||
operator =
|
Eq =
|
||||||
FunctionDef
|
FunctionDef
|
||||||
keyword fn
|
keyword fn
|
||||||
Params
|
Params
|
||||||
|
|
@ -295,7 +298,7 @@ describe('Assign', () => {
|
||||||
colon :
|
colon :
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator +
|
Plus +
|
||||||
Identifier b
|
Identifier b
|
||||||
end end`)
|
end end`)
|
||||||
})
|
})
|
||||||
|
|
@ -306,7 +309,7 @@ describe('DotGet whitespace sensitivity', () => {
|
||||||
expect('basename = 5; basename.prop').toMatchTree(`
|
expect('basename = 5; basename.prop').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier basename
|
AssignableIdentifier basename
|
||||||
operator =
|
Eq =
|
||||||
Number 5
|
Number 5
|
||||||
DotGet
|
DotGet
|
||||||
IdentifierBeforeDot basename
|
IdentifierBeforeDot basename
|
||||||
|
|
@ -317,11 +320,11 @@ describe('DotGet whitespace sensitivity', () => {
|
||||||
expect('basename = 5; basename / prop').toMatchTree(`
|
expect('basename = 5; basename / prop').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier basename
|
AssignableIdentifier basename
|
||||||
operator =
|
Eq =
|
||||||
Number 5
|
Number 5
|
||||||
BinOp
|
BinOp
|
||||||
Identifier basename
|
Identifier basename
|
||||||
operator /
|
Slash /
|
||||||
Identifier prop`)
|
Identifier prop`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ describe('if/elsif/else', () => {
|
||||||
keyword if
|
keyword if
|
||||||
ConditionalOp
|
ConditionalOp
|
||||||
Identifier y
|
Identifier y
|
||||||
operator =
|
Eq =
|
||||||
Number 1
|
Number 1
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
|
|
@ -20,7 +20,7 @@ describe('if/elsif/else', () => {
|
||||||
expect('a = if x: 2').toMatchTree(`
|
expect('a = if x: 2').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier a
|
AssignableIdentifier a
|
||||||
operator =
|
Eq =
|
||||||
IfExpr
|
IfExpr
|
||||||
keyword if
|
keyword if
|
||||||
Identifier x
|
Identifier x
|
||||||
|
|
@ -39,7 +39,7 @@ describe('if/elsif/else', () => {
|
||||||
keyword if
|
keyword if
|
||||||
ConditionalOp
|
ConditionalOp
|
||||||
Identifier x
|
Identifier x
|
||||||
operator <
|
Lt <
|
||||||
Number 9
|
Number 9
|
||||||
colon :
|
colon :
|
||||||
ThenBlock
|
ThenBlock
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ describe('DotGet', () => {
|
||||||
expect('obj = 5; obj.prop').toMatchTree(`
|
expect('obj = 5; obj.prop').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier obj
|
AssignableIdentifier obj
|
||||||
operator =
|
Eq =
|
||||||
Number 5
|
Number 5
|
||||||
DotGet
|
DotGet
|
||||||
IdentifierBeforeDot obj
|
IdentifierBeforeDot obj
|
||||||
|
|
@ -106,7 +106,7 @@ end`).toMatchTree(`
|
||||||
expect('config = 42; echo config.path').toMatchTree(`
|
expect('config = 42; echo config.path').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier config
|
AssignableIdentifier config
|
||||||
operator =
|
Eq =
|
||||||
Number 42
|
Number 42
|
||||||
FunctionCall
|
FunctionCall
|
||||||
Identifier echo
|
Identifier echo
|
||||||
|
|
@ -121,7 +121,7 @@ end`).toMatchTree(`
|
||||||
expect('config = 42; cat readme.txt; echo config.path').toMatchTree(`
|
expect('config = 42; cat readme.txt; echo config.path').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier config
|
AssignableIdentifier config
|
||||||
operator =
|
Eq =
|
||||||
Number 42
|
Number 42
|
||||||
FunctionCall
|
FunctionCall
|
||||||
Identifier cat
|
Identifier cat
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ describe('Fn', () => {
|
||||||
colon :
|
colon :
|
||||||
BinOp
|
BinOp
|
||||||
Identifier x
|
Identifier x
|
||||||
operator +
|
Plus +
|
||||||
Number 1
|
Number 1
|
||||||
end end`)
|
end end`)
|
||||||
})
|
})
|
||||||
|
|
@ -91,7 +91,7 @@ describe('Fn', () => {
|
||||||
colon :
|
colon :
|
||||||
BinOp
|
BinOp
|
||||||
Identifier x
|
Identifier x
|
||||||
operator *
|
Star *
|
||||||
Identifier y
|
Identifier y
|
||||||
end end`)
|
end end`)
|
||||||
})
|
})
|
||||||
|
|
@ -109,11 +109,11 @@ end`).toMatchTree(`
|
||||||
colon :
|
colon :
|
||||||
BinOp
|
BinOp
|
||||||
Identifier x
|
Identifier x
|
||||||
operator *
|
Star *
|
||||||
Identifier y
|
Identifier y
|
||||||
BinOp
|
BinOp
|
||||||
Identifier x
|
Identifier x
|
||||||
operator +
|
Plus +
|
||||||
Number 9
|
Number 9
|
||||||
end end`)
|
end end`)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ describe('multiline', () => {
|
||||||
`).toMatchTree(`
|
`).toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier add
|
AssignableIdentifier add
|
||||||
operator =
|
Eq =
|
||||||
FunctionDef
|
FunctionDef
|
||||||
keyword fn
|
keyword fn
|
||||||
Params
|
Params
|
||||||
|
|
@ -31,10 +31,10 @@ describe('multiline', () => {
|
||||||
colon :
|
colon :
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier result
|
AssignableIdentifier result
|
||||||
operator =
|
Eq =
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator +
|
Plus +
|
||||||
Identifier b
|
Identifier b
|
||||||
FunctionCallOrIdentifier
|
FunctionCallOrIdentifier
|
||||||
Identifier result
|
Identifier result
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ describe('pipe expressions', () => {
|
||||||
expect('result = echo hello | grep h').toMatchTree(`
|
expect('result = echo hello | grep h').toMatchTree(`
|
||||||
Assign
|
Assign
|
||||||
AssignableIdentifier result
|
AssignableIdentifier result
|
||||||
operator =
|
Eq =
|
||||||
PipeExpr
|
PipeExpr
|
||||||
FunctionCall
|
FunctionCall
|
||||||
Identifier echo
|
Identifier echo
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ describe('string interpolation', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator +
|
Plus +
|
||||||
Identifier b
|
Identifier b
|
||||||
StringFragment !
|
StringFragment !
|
||||||
`)
|
`)
|
||||||
|
|
@ -34,7 +34,7 @@ describe('string interpolation', () => {
|
||||||
ParenExpr
|
ParenExpr
|
||||||
BinOp
|
BinOp
|
||||||
Identifier a
|
Identifier a
|
||||||
operator +
|
Plus +
|
||||||
Identifier b
|
Identifier b
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,12 @@ export const tokenizer = new ExternalTokenizer(
|
||||||
const ch = getFullCodePoint(input, 0)
|
const ch = getFullCodePoint(input, 0)
|
||||||
if (!isWordChar(ch)) return
|
if (!isWordChar(ch)) return
|
||||||
|
|
||||||
|
// Don't consume things that start with digits - let Number token handle it
|
||||||
|
if (isDigit(ch)) return
|
||||||
|
|
||||||
|
// Don't consume things that start with - or + followed by a digit (negative/positive numbers)
|
||||||
|
if ((ch === 45 /* - */ || ch === 43) /* + */ && isDigit(input.peek(1))) return
|
||||||
|
|
||||||
const isValidStart = isLowercaseLetter(ch) || isEmoji(ch)
|
const isValidStart = isLowercaseLetter(ch) || isEmoji(ch)
|
||||||
const canBeWord = stack.canShift(Word)
|
const canBeWord = stack.canShift(Word)
|
||||||
|
|
||||||
|
|
@ -166,7 +172,16 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextCh = getFullCodePoint(input, peekPos)
|
const nextCh = getFullCodePoint(input, peekPos)
|
||||||
return nextCh === 61 /* = */ ? AssignableIdentifier : Identifier
|
if (nextCh === 61 /* = */) {
|
||||||
|
// Found '=', but check if it's followed by whitespace
|
||||||
|
// If '=' is followed by non-whitespace (like '=cool*'), it won't be tokenized as Eq
|
||||||
|
// In that case, this should be Identifier (for function call), not AssignableIdentifier
|
||||||
|
const charAfterEquals = getFullCodePoint(input, peekPos + 1)
|
||||||
|
if (isWhiteSpace(charAfterEquals) || charAfterEquals === -1 /* EOF */) {
|
||||||
|
return AssignableIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Identifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// Character classification helpers
|
// Character classification helpers
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { expect } from 'bun:test'
|
import { expect } from 'bun:test'
|
||||||
import { Tree, TreeCursor } from '@lezer/common'
|
|
||||||
import { parser } from '#parser/shrimp'
|
import { parser } from '#parser/shrimp'
|
||||||
import { $ } from 'bun'
|
import { $ } from 'bun'
|
||||||
import { assert, assertNever, errorMessage } from '#utils/utils'
|
import { assert, errorMessage } from '#utils/utils'
|
||||||
import { Compiler } from '#compiler/compiler'
|
import { Compiler } from '#compiler/compiler'
|
||||||
import { run, VM, type Value } from 'reefvm'
|
import { run, VM } from 'reefvm'
|
||||||
|
import { treeToString, VMResultToValue } from '#utils/tree'
|
||||||
|
|
||||||
const regenerateParser = async () => {
|
const regenerateParser = async () => {
|
||||||
let generate = true
|
let generate = true
|
||||||
|
|
@ -131,10 +131,11 @@ expect.extend({
|
||||||
try {
|
try {
|
||||||
const compiler = new Compiler(received)
|
const compiler = new Compiler(received)
|
||||||
const vm = new VM(compiler.bytecode)
|
const vm = new VM(compiler.bytecode)
|
||||||
await vm.run()
|
const value = await vm.run()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
message: () => `Expected evaluation to fail, but it succeeded.`,
|
message: () =>
|
||||||
|
`Expected evaluation to fail, but it succeeded with ${JSON.stringify(value)}`,
|
||||||
pass: false,
|
pass: false,
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -146,38 +147,6 @@ expect.extend({
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const treeToString = (tree: Tree, input: string): string => {
|
|
||||||
const lines: string[] = []
|
|
||||||
|
|
||||||
const addNode = (cursor: TreeCursor, depth: number) => {
|
|
||||||
if (!cursor.name) return
|
|
||||||
|
|
||||||
const indent = ' '.repeat(depth)
|
|
||||||
const text = input.slice(cursor.from, cursor.to)
|
|
||||||
const nodeName = cursor.name // Save the node name before moving cursor
|
|
||||||
|
|
||||||
if (cursor.firstChild()) {
|
|
||||||
lines.push(`${indent}${nodeName}`)
|
|
||||||
do {
|
|
||||||
addNode(cursor, depth + 1)
|
|
||||||
} while (cursor.nextSibling())
|
|
||||||
cursor.parent()
|
|
||||||
} else {
|
|
||||||
const cleanText = nodeName === 'String' ? text.slice(1, -1) : text
|
|
||||||
lines.push(`${indent}${nodeName} ${cleanText}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cursor = tree.cursor()
|
|
||||||
if (cursor.firstChild()) {
|
|
||||||
do {
|
|
||||||
addNode(cursor, 0)
|
|
||||||
} while (cursor.nextSibling())
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines.join('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
const trimWhitespace = (str: string): string => {
|
const trimWhitespace = (str: string): string => {
|
||||||
const lines = str.split('\n').filter((line) => line.trim().length > 0)
|
const lines = str.split('\n').filter((line) => line.trim().length > 0)
|
||||||
const firstLine = lines[0]
|
const firstLine = lines[0]
|
||||||
|
|
@ -196,29 +165,3 @@ const trimWhitespace = (str: string): string => {
|
||||||
})
|
})
|
||||||
.join('\n')
|
.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
const VMResultToValue = (result: Value): unknown => {
|
|
||||||
if (
|
|
||||||
result.type === 'number' ||
|
|
||||||
result.type === 'boolean' ||
|
|
||||||
result.type === 'string' ||
|
|
||||||
result.type === 'regex'
|
|
||||||
) {
|
|
||||||
return result.value
|
|
||||||
} else if (result.type === 'null') {
|
|
||||||
return null
|
|
||||||
} else if (result.type === 'array') {
|
|
||||||
return result.value.map(VMResultToValue)
|
|
||||||
} else if (result.type === 'dict') {
|
|
||||||
const obj: Record<string, unknown> = {}
|
|
||||||
for (const [key, val] of Object.entries(result.value)) {
|
|
||||||
obj[key] = VMResultToValue(val)
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj
|
|
||||||
} else if (result.type === 'function') {
|
|
||||||
return Function
|
|
||||||
} else {
|
|
||||||
assertNever(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
61
src/utils/tree.ts
Normal file
61
src/utils/tree.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { Tree, TreeCursor } from '@lezer/common'
|
||||||
|
import { assertNever } from '#utils/utils'
|
||||||
|
import { type Value } from 'reefvm'
|
||||||
|
|
||||||
|
export const treeToString = (tree: Tree, input: string): string => {
|
||||||
|
const lines: string[] = []
|
||||||
|
|
||||||
|
const addNode = (cursor: TreeCursor, depth: number) => {
|
||||||
|
if (!cursor.name) return
|
||||||
|
|
||||||
|
const indent = ' '.repeat(depth)
|
||||||
|
const text = input.slice(cursor.from, cursor.to)
|
||||||
|
const nodeName = cursor.name // Save the node name before moving cursor
|
||||||
|
|
||||||
|
if (cursor.firstChild()) {
|
||||||
|
lines.push(`${indent}${nodeName}`)
|
||||||
|
do {
|
||||||
|
addNode(cursor, depth + 1)
|
||||||
|
} while (cursor.nextSibling())
|
||||||
|
cursor.parent()
|
||||||
|
} else {
|
||||||
|
const cleanText = nodeName === 'String' ? text.slice(1, -1) : text
|
||||||
|
lines.push(`${indent}${nodeName} ${cleanText}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cursor = tree.cursor()
|
||||||
|
if (cursor.firstChild()) {
|
||||||
|
do {
|
||||||
|
addNode(cursor, 0)
|
||||||
|
} while (cursor.nextSibling())
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines.join('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const VMResultToValue = (result: Value): unknown => {
|
||||||
|
if (
|
||||||
|
result.type === 'number' ||
|
||||||
|
result.type === 'boolean' ||
|
||||||
|
result.type === 'string' ||
|
||||||
|
result.type === 'regex'
|
||||||
|
) {
|
||||||
|
return result.value
|
||||||
|
} else if (result.type === 'null') {
|
||||||
|
return null
|
||||||
|
} else if (result.type === 'array') {
|
||||||
|
return result.value.map(VMResultToValue)
|
||||||
|
} else if (result.type === 'dict') {
|
||||||
|
const obj: Record<string, unknown> = {}
|
||||||
|
for (const [key, val] of Object.entries(result.value)) {
|
||||||
|
obj[key] = VMResultToValue(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj
|
||||||
|
} else if (result.type === 'function') {
|
||||||
|
return Function
|
||||||
|
} else {
|
||||||
|
assertNever(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user