wip
This commit is contained in:
parent
8da3c1674e
commit
82cd199ed8
BIN
assets/C64_Pro-STYLE.woff2
Normal file
BIN
assets/C64_Pro-STYLE.woff2
Normal file
Binary file not shown.
BIN
assets/C64_Pro_Mono-STYLE.woff2
Normal file
BIN
assets/C64_Pro_Mono-STYLE.woff2
Normal file
Binary file not shown.
BIN
assets/PixeloidMono.ttf
Normal file
BIN
assets/PixeloidMono.ttf
Normal file
Binary file not shown.
BIN
assets/PixeloidSans.ttf
Normal file
BIN
assets/PixeloidSans.ttf
Normal file
Binary file not shown.
BIN
assets/PixeloidSansBold.ttf
Normal file
BIN
assets/PixeloidSansBold.ttf
Normal file
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
Subproject commit 1a18a713d7ae86b03a6bef38cc53d12ecfbf9627
|
Subproject commit 47f829fcada71655f0d40ec363b5bcc844af8856
|
||||||
|
|
@ -3,7 +3,7 @@ import { parser } from '#parser/shrimp.ts'
|
||||||
import * as terms from '#parser/shrimp.terms'
|
import * as terms from '#parser/shrimp.terms'
|
||||||
import type { SyntaxNode, Tree } from '@lezer/common'
|
import type { SyntaxNode, Tree } from '@lezer/common'
|
||||||
import { assert, errorMessage } from '#utils/utils'
|
import { assert, errorMessage } from '#utils/utils'
|
||||||
import { toBytecode, type Bytecode, type ProgramItem } from 'reefvm'
|
import { toBytecode, type Bytecode, type ProgramItem, bytecodeToString } from 'reefvm'
|
||||||
import {
|
import {
|
||||||
checkTreeForErrors,
|
checkTreeForErrors,
|
||||||
getAllChildren,
|
getAllChildren,
|
||||||
|
|
@ -72,9 +72,12 @@ export class Compiler {
|
||||||
this.instructions.push(['RETURN'])
|
this.instructions.push(['RETURN'])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) logInstructions(this.instructions)
|
|
||||||
|
|
||||||
this.bytecode = toBytecode(this.instructions)
|
this.bytecode = toBytecode(this.instructions)
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
const bytecodeString = bytecodeToString(this.bytecode)
|
||||||
|
console.log(`\n🤖 bytecode:\n----------------\n${bytecodeString}\n\n`)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof CompilerError) {
|
if (error instanceof CompilerError) {
|
||||||
throw new Error(error.toReadableString(input))
|
throw new Error(error.toReadableString(input))
|
||||||
|
|
@ -475,19 +478,3 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logInstructions = (instructions: ProgramItem[]) => {
|
|
||||||
const instructionsString = instructions
|
|
||||||
.map((parts) => {
|
|
||||||
const isPush = parts[0] === 'PUSH'
|
|
||||||
return parts
|
|
||||||
.map((part, i) => {
|
|
||||||
const partAsString = typeof part == 'string' && isPush ? `'${part}'` : part!.toString()
|
|
||||||
return i > 0 ? partAsString : part
|
|
||||||
})
|
|
||||||
.join(' ')
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
|
|
||||||
console.log(`\n🤖 instructions:\n----------------\n${instructionsString}\n\n`)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -213,7 +213,7 @@ describe('Regex', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe.skip('native functions', () => {
|
describe('native functions', () => {
|
||||||
test('print function', () => {
|
test('print function', () => {
|
||||||
const add = (x: number, y: number) => x + y
|
const add = (x: number, y: number) => x + y
|
||||||
expect(`add 5 9`).toEvaluateTo(14, { add })
|
expect(`add 5 9`).toEvaluateTo(14, { add })
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { shrimpTheme } from '#editor/plugins/theme'
|
||||||
import { shrimpLanguage } from '#/editor/plugins/shrimpLanguage'
|
import { shrimpLanguage } from '#/editor/plugins/shrimpLanguage'
|
||||||
import { shrimpHighlighting } from '#editor/plugins/theme'
|
import { shrimpHighlighting } from '#editor/plugins/theme'
|
||||||
import { shrimpKeymap } from '#editor/plugins/keymap'
|
import { shrimpKeymap } from '#editor/plugins/keymap'
|
||||||
import { asciiEscapeToHtml, log, toElement } from '#utils/utils'
|
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 { shrimpErrors } from '#editor/plugins/errors'
|
||||||
import { debugTags } from '#editor/plugins/debugTags'
|
import { debugTags } from '#editor/plugins/debugTags'
|
||||||
|
|
@ -12,6 +12,11 @@ import { getContent, persistencePlugin } from '#editor/plugins/persistence'
|
||||||
|
|
||||||
import '#editor/editor.css'
|
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 type { Value } from 'reefvm'
|
||||||
|
|
||||||
|
connectToNose()
|
||||||
|
|
||||||
export const Editor = () => {
|
export const Editor = () => {
|
||||||
return (
|
return (
|
||||||
|
|
@ -23,6 +28,7 @@ export const Editor = () => {
|
||||||
parent: ref,
|
parent: ref,
|
||||||
doc: getContent(),
|
doc: getContent(),
|
||||||
extensions: [
|
extensions: [
|
||||||
|
catchErrors,
|
||||||
shrimpKeymap,
|
shrimpKeymap,
|
||||||
basicSetup,
|
basicSetup,
|
||||||
shrimpTheme,
|
shrimpTheme,
|
||||||
|
|
@ -30,7 +36,7 @@ export const Editor = () => {
|
||||||
shrimpHighlighting,
|
shrimpHighlighting,
|
||||||
shrimpErrors,
|
shrimpErrors,
|
||||||
persistencePlugin,
|
persistencePlugin,
|
||||||
debugTags,
|
// debugTags,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -47,23 +53,31 @@ export const Editor = () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const outputSignal = new Signal<{ output: string } | { error: string }>()
|
noseSignals.connect((message) => {
|
||||||
|
if (message.type === 'error') {
|
||||||
|
log.error(`Nose error: ${message.data}`)
|
||||||
|
errorSignal.emit(`Nose error: ${message.data}`)
|
||||||
|
} else if (message.type === 'reef-output') {
|
||||||
|
const x = outputSignal.emit(message.data)
|
||||||
|
} else if (message.type === 'connected') {
|
||||||
|
outputSignal.emit(`╞ Connected to Nose VM`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
let outputTimeout: ReturnType<typeof setTimeout>
|
export const outputSignal = new Signal<Value | string>()
|
||||||
|
export const errorSignal = new Signal<string>()
|
||||||
|
|
||||||
outputSignal.connect((output) => {
|
outputSignal.connect((value) => {
|
||||||
const el = document.querySelector('#output')!
|
const el = document.querySelector('#output')!
|
||||||
el.innerHTML = ''
|
el.innerHTML = ''
|
||||||
let content
|
el.innerHTML = asciiEscapeToHtml(valueToString(value))
|
||||||
if ('error' in output) {
|
})
|
||||||
el.classList.add('error')
|
|
||||||
content = output.error
|
|
||||||
} else {
|
|
||||||
el.classList.remove('error')
|
|
||||||
content = output.output
|
|
||||||
}
|
|
||||||
|
|
||||||
el.innerHTML = asciiEscapeToHtml(content)
|
errorSignal.connect((error) => {
|
||||||
|
const el = document.querySelector('#output')!
|
||||||
|
el.innerHTML = ''
|
||||||
|
el.classList.add('error')
|
||||||
|
el.innerHTML = asciiEscapeToHtml(error)
|
||||||
})
|
})
|
||||||
|
|
||||||
type StatusBarMessage = {
|
type StatusBarMessage = {
|
||||||
|
|
@ -96,3 +110,37 @@ statusBarSignal.connect(async ({ side, message, className, order }) => {
|
||||||
sideEl.insertBefore(toElement(messageEl), nodes[index]!)
|
sideEl.insertBefore(toElement(messageEl), nodes[index]!)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const valueToString = (value: Value | string): string => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (value.type) {
|
||||||
|
case 'null':
|
||||||
|
return 'null'
|
||||||
|
case 'boolean':
|
||||||
|
return value.value ? 'true' : 'false'
|
||||||
|
case 'number':
|
||||||
|
return value.value.toString()
|
||||||
|
case 'string':
|
||||||
|
return value.value
|
||||||
|
case 'array':
|
||||||
|
return `${value.value.map(valueToString).join('\n')}`
|
||||||
|
case 'dict': {
|
||||||
|
const entries = Array.from(value.value.entries()).map(
|
||||||
|
([key, val]) => `"${key}": ${valueToString(val)}`
|
||||||
|
)
|
||||||
|
return `{${entries.join(', ')}}`
|
||||||
|
}
|
||||||
|
case 'regex':
|
||||||
|
return `/${value.value.source}/`
|
||||||
|
case 'function':
|
||||||
|
return `<function>`
|
||||||
|
case 'native':
|
||||||
|
return `<function ${value.fn.name}>`
|
||||||
|
default:
|
||||||
|
assertNever(value)
|
||||||
|
return `<unknown value type: ${(value as any).type}>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
59
src/editor/noseClient.ts
Normal file
59
src/editor/noseClient.ts
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { Signal } from '#utils/signal'
|
||||||
|
import type { Bytecode, Value } from 'reefvm'
|
||||||
|
let ws: WebSocket
|
||||||
|
|
||||||
|
type IncomingMessage =
|
||||||
|
| { type: 'connected' }
|
||||||
|
| { type: 'ping'; data: number }
|
||||||
|
| { type: 'commands'; data: number }
|
||||||
|
| {
|
||||||
|
type: 'apps'
|
||||||
|
data: {
|
||||||
|
name: string
|
||||||
|
type: 'browser' | 'server'
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'session:start'
|
||||||
|
data: {
|
||||||
|
NOSE_DIR: string
|
||||||
|
cwd: string
|
||||||
|
hostname: string
|
||||||
|
mode: string
|
||||||
|
project: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| { type: 'reef-output'; data: Value }
|
||||||
|
| { type: 'error'; data: string }
|
||||||
|
|
||||||
|
export const noseSignals = new Signal<IncomingMessage>()
|
||||||
|
|
||||||
|
export const connectToNose = (url: string = 'ws://localhost:3000/ws') => {
|
||||||
|
ws = new WebSocket(url)
|
||||||
|
ws.onopen = () => noseSignals.emit({ type: 'connected' })
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
const message = JSON.parse(event.data)
|
||||||
|
noseSignals.emit(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onerror = (event) => {
|
||||||
|
console.error(`💥WebSocket error:`, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.onclose = () => {
|
||||||
|
console.log(`🚪 Connection closed`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = 0
|
||||||
|
export const sendToNose = (code: Bytecode) => {
|
||||||
|
if (!ws) {
|
||||||
|
throw new Error('WebSocket is not connected.')
|
||||||
|
} else if (ws.readyState !== WebSocket.OPEN) {
|
||||||
|
throw new Error(`WebSocket is not open, current status is ${ws.readyState}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
id += 1
|
||||||
|
ws.send(JSON.stringify({ type: 'reef-bytecode', data: code, id }))
|
||||||
|
}
|
||||||
9
src/editor/plugins/catchErrors.ts
Normal file
9
src/editor/plugins/catchErrors.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { errorSignal } from '#editor/editor'
|
||||||
|
import { EditorView } from '@codemirror/view'
|
||||||
|
|
||||||
|
export const catchErrors = EditorView.exceptionSink.of((exception) => {
|
||||||
|
console.error('CodeMirror error:', exception)
|
||||||
|
errorSignal.emit(
|
||||||
|
`Editor error: ${exception instanceof Error ? exception.message : String(exception)}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { statusBarSignal } from '#editor/editor'
|
import { printBytecodeOutput, printParserOutput, runCode } from '#editor/runCode'
|
||||||
import { run } from '#editor/runCode'
|
|
||||||
import { EditorState } from '@codemirror/state'
|
import { EditorState } from '@codemirror/state'
|
||||||
import { keymap } from '@codemirror/view'
|
import { keymap } from '@codemirror/view'
|
||||||
|
|
||||||
let multilineMode = false
|
let multilineMode = false
|
||||||
|
|
||||||
const customKeymap = keymap.of([
|
const customKeymap = keymap.of([
|
||||||
{
|
{
|
||||||
key: 'Enter',
|
key: 'Enter',
|
||||||
|
|
@ -11,17 +11,22 @@ const customKeymap = keymap.of([
|
||||||
if (multilineMode) return false
|
if (multilineMode) return false
|
||||||
|
|
||||||
const input = view.state.doc.toString()
|
const input = view.state.doc.toString()
|
||||||
run(input)
|
history.push(input)
|
||||||
|
runCode(input)
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: 0, to: view.state.doc.length, insert: '' },
|
||||||
|
selection: { anchor: 0 },
|
||||||
|
})
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
key: 'Alt-Enter',
|
key: 'Shift-Enter',
|
||||||
run: (view) => {
|
run: (view) => {
|
||||||
if (multilineMode) {
|
if (multilineMode) {
|
||||||
const input = view.state.doc.toString()
|
const input = view.state.doc.toString()
|
||||||
run(input)
|
runCode(input)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,7 +36,62 @@ const customKeymap = keymap.of([
|
||||||
selection: { anchor: view.state.doc.length + 1 },
|
selection: { anchor: view.state.doc.length + 1 },
|
||||||
})
|
})
|
||||||
|
|
||||||
updateStatusMessage()
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'Tab',
|
||||||
|
preventDefault: true,
|
||||||
|
run: (view) => {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'ArrowUp',
|
||||||
|
run: (view) => {
|
||||||
|
const command = history.previous()
|
||||||
|
if (command === undefined) return false
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: 0, to: view.state.doc.length, insert: command },
|
||||||
|
selection: { anchor: command.length },
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'ArrowDown',
|
||||||
|
run: (view) => {
|
||||||
|
const command = history.next()
|
||||||
|
if (command === undefined) return false
|
||||||
|
view.dispatch({
|
||||||
|
changes: { from: 0, to: view.state.doc.length, insert: command },
|
||||||
|
selection: { anchor: command.length },
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'Mod-k 1',
|
||||||
|
preventDefault: true,
|
||||||
|
run: (view) => {
|
||||||
|
const input = view.state.doc.toString()
|
||||||
|
printParserOutput(input)
|
||||||
|
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'Mod-k 2',
|
||||||
|
preventDefault: true,
|
||||||
|
run: (view) => {
|
||||||
|
const input = view.state.doc.toString()
|
||||||
|
printBytecodeOutput(input)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -45,7 +105,6 @@ const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
||||||
firstTime = false
|
firstTime = false
|
||||||
if (transaction.newDoc.toString().includes('\n')) {
|
if (transaction.newDoc.toString().includes('\n')) {
|
||||||
multilineMode = true
|
multilineMode = true
|
||||||
updateStatusMessage()
|
|
||||||
return transaction
|
return transaction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -53,7 +112,6 @@ const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
||||||
transaction.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
transaction.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
||||||
if (inserted.toString().includes('\n')) {
|
if (inserted.toString().includes('\n')) {
|
||||||
multilineMode = true
|
multilineMode = true
|
||||||
updateStatusMessage()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -63,22 +121,38 @@ const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
||||||
|
|
||||||
export const shrimpKeymap = [customKeymap, singleLineFilter]
|
export const shrimpKeymap = [customKeymap, singleLineFilter]
|
||||||
|
|
||||||
const updateStatusMessage = () => {
|
class History {
|
||||||
statusBarSignal.emit({
|
private commands: string[] = []
|
||||||
side: 'left',
|
private index: number | undefined
|
||||||
message: multilineMode ? 'Press Alt-Enter run' : 'Alt-Enter will enter multiline mode',
|
|
||||||
className: 'status',
|
|
||||||
})
|
|
||||||
|
|
||||||
statusBarSignal.emit({
|
push(command: string) {
|
||||||
side: 'right',
|
this.commands.push(command)
|
||||||
message: (
|
this.index = undefined
|
||||||
<div className="multiline">
|
|
||||||
<span className={multilineMode ? 'dot active' : 'dot inactive'}>•</span> multiline
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
className: 'multiline-status',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => updateStatusMessage())
|
previous(): string | undefined {
|
||||||
|
if (this.commands.length === 0) return
|
||||||
|
|
||||||
|
if (this.index === undefined) {
|
||||||
|
this.index = this.commands.length - 1
|
||||||
|
} else if (this.index > 0) {
|
||||||
|
this.index -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.commands[this.index]
|
||||||
|
}
|
||||||
|
|
||||||
|
next(): string | undefined {
|
||||||
|
if (this.commands.length === 0 || this.index === undefined) return
|
||||||
|
|
||||||
|
if (this.index < this.commands.length - 1) {
|
||||||
|
this.index += 1
|
||||||
|
return this.commands[this.index]
|
||||||
|
} else {
|
||||||
|
this.index = undefined
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = new History()
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,36 @@
|
||||||
import { outputSignal } 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 { VM } from 'reefvm'
|
import { bytecodeToString, run } from 'reefvm'
|
||||||
|
import { parser } from '#parser/shrimp'
|
||||||
|
import { sendToNose } from '#editor/noseClient'
|
||||||
|
|
||||||
export const run = async (input: string) => {
|
export const runCode = async (input: string) => {
|
||||||
try {
|
try {
|
||||||
const compiler = new Compiler(input)
|
const compiler = new Compiler(input)
|
||||||
const vm = new VM(compiler.bytecode)
|
sendToNose(compiler.bytecode)
|
||||||
const output = await vm.run()
|
|
||||||
outputSignal.emit({ output: String(output.value) })
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.error(error)
|
log.error(error)
|
||||||
outputSignal.emit({ error: `${errorMessage(error)}` })
|
errorSignal.emit(`${errorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const printParserOutput = (input: string) => {
|
||||||
|
try {
|
||||||
|
const cst = parser.parse(input)
|
||||||
|
outputSignal.emit(cst.toString())
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error)
|
||||||
|
errorSignal.emit(`${errorMessage(error)}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const printBytecodeOutput = (input: string) => {
|
||||||
|
try {
|
||||||
|
const compiler = new Compiler(input)
|
||||||
|
outputSignal.emit(bytecodeToString(compiler.bytecode))
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error)
|
||||||
|
errorSignal.emit(`${errorMessage(error)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
@top Program { item* }
|
@top Program { item* }
|
||||||
|
|
||||||
|
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
@precedence { Number "-" Regex "/"}
|
@precedence { Number "-" Regex "/"}
|
||||||
|
|
||||||
|
|
@ -43,8 +45,6 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@external tokens tokenizer from "./tokenizer" { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot }
|
|
||||||
|
|
||||||
@precedence {
|
@precedence {
|
||||||
pipe @left,
|
pipe @left,
|
||||||
multiplicative @left,
|
multiplicative @left,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ 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: ".jQVQaOOO#XQbO'#CfO$RQPO'#CgO$aQPO'#DmO$xQaO'#CeO%gOSO'#CuOOQ`'#Dq'#DqO%uOPO'#C}O%zQPO'#DpO&cQaO'#D|OOQ`'#DO'#DOOOQO'#Dn'#DnO&kQPO'#DmO&yQaO'#EQOOQO'#DX'#DXO'hQPO'#DaOOQO'#Dm'#DmO'mQPO'#DlOOQ`'#Dl'#DlOOQ`'#Db'#DbQVQaOOOOQ`'#Dp'#DpOOQ`'#Cd'#CdO'uQaO'#DUOOQ`'#Do'#DoOOQ`'#Dc'#DcO(PQbO,58}O&yQaO,59RO&yQaO,59RO)XQPO'#CgO)iQPO,59PO)zQPO,59PO)uQPO,59PO*uQPO,59PO*}QaO'#CwO+VQWO'#CxOOOO'#Du'#DuOOOO'#Dd'#DdO+kOSO,59aOOQ`,59a,59aO+yO`O,59iOOQ`'#De'#DeO,OQaO'#DQO,WQPO,5:hO,]QaO'#DgO,bQPO,58|O,sQPO,5:lO,zQPO,5:lO-PQaO,59{OOQ`,5:W,5:WOOQ`-E7`-E7`OOQ`,59p,59pOOQ`-E7a-E7aOOQO1G.m1G.mO-^QPO1G.mO&yQaO,59WO&yQaO,59WOOQ`1G.k1G.kOOOO,59c,59cOOOO,59d,59dOOOO-E7b-E7bOOQ`1G.{1G.{OOQ`1G/T1G/TOOQ`-E7c-E7cO-xQaO1G0SO!QQbO'#CfOOQO,5:R,5:ROOQO-E7e-E7eO.YQaO1G0WOOQO1G/g1G/gOOQO1G.r1G.rO.jQPO1G.rO.tQPO7+%nO.yQaO7+%oOOQO'#DZ'#DZOOQO7+%r7+%rO/ZQaO7+%sOOQ`<<IY<<IYO/qQPO'#DfO/vQaO'#EPO0^QPO<<IZOOQO'#D['#D[O0cQPO<<I_OOQ`,5:Q,5:QOOQ`-E7d-E7dOOQ`AN>uAN>uO&yQaO'#D]OOQO'#Dh'#DhO0nQPOAN>yO0yQPO'#D_OOQOAN>yAN>yO1OQPOAN>yO1TQPO,59wO1[QPO,59wOOQO-E7f-E7fOOQOG24eG24eO1aQPOG24eO1fQPO,59yO1kQPO1G/cOOQOLD*PLD*PO.yQaO1G/eO/ZQaO7+$}OOQO7+%P7+%POOQO<<Hi<<Hi",
|
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",
|
||||||
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: "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~",
|
||||||
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!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",
|
||||||
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: "⚠ 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",
|
||||||
|
|
@ -19,7 +19,7 @@ export const parser = LRParser.deserialize({
|
||||||
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: "!&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~",
|
||||||
tokenizers: [0, 1, 2, 3, tokenizer],
|
tokenizers: [tokenizer, 0, 1, 2, 3],
|
||||||
topRules: {"Program":[0,5]},
|
topRules: {"Program":[0,5]},
|
||||||
tokenPrec: 768
|
tokenPrec: 768
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,24 @@ describe('Parentheses', () => {
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('a word start with *', () => {
|
||||||
|
expect('find *cool*').toMatchTree(`
|
||||||
|
FunctionCall
|
||||||
|
Identifier find
|
||||||
|
PositionalArg
|
||||||
|
Word *cool*
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('a word can look like a binop', () => {
|
||||||
|
expect('find cool*wow').toMatchTree(`
|
||||||
|
FunctionCall
|
||||||
|
Identifier find
|
||||||
|
PositionalArg
|
||||||
|
Word cool*wow
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
test('nested parentheses', () => {
|
test('nested parentheses', () => {
|
||||||
expect('(2 + (1 * 4))').toMatchTree(`
|
expect('(2 + (1 * 4))').toMatchTree(`
|
||||||
ParenExpr
|
ParenExpr
|
||||||
|
|
@ -131,7 +149,7 @@ describe('Parentheses', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('BinOp', () => {
|
describe.only('BinOp', () => {
|
||||||
test('addition tests', () => {
|
test('addition tests', () => {
|
||||||
expect('2 + 3').toMatchTree(`
|
expect('2 + 3').toMatchTree(`
|
||||||
BinOp
|
BinOp
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,20 @@
|
||||||
--ansi-bright-white: #FFFFFF;
|
--ansi-bright-white: #FFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'C64ProMono';
|
||||||
|
src: url('../../assets/C64_Pro_Mono-STYLE.woff2') format('woff2');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Pixeloid Mono';
|
||||||
|
src: url('../../assets/PixeloidMono.ttf') format('truetype');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import index from './index.html'
|
import index from './index.html'
|
||||||
|
|
||||||
const server = Bun.serve({
|
const server = Bun.serve({
|
||||||
|
port: process.env.PORT ? Number(process.env.PORT) : 3001,
|
||||||
routes: {
|
routes: {
|
||||||
'/*': index,
|
'/*': index,
|
||||||
|
|
||||||
|
|
@ -19,7 +20,6 @@ const server = Bun.serve({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
development: process.env.NODE_ENV !== 'production' && {
|
development: process.env.NODE_ENV !== 'production' && {
|
||||||
hmr: true,
|
hmr: true,
|
||||||
console: true,
|
console: true,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,17 @@
|
||||||
/**
|
/**
|
||||||
* How to use a Signal:
|
* How to use a Signal:
|
||||||
*
|
*
|
||||||
* Create a signal:
|
* Create a signal with primitives:
|
||||||
|
* const nameSignal = new Signal<string>()
|
||||||
|
* const countSignal = new Signal<number>()
|
||||||
|
*
|
||||||
|
* Create a signal with objects:
|
||||||
* const chatSignal = new Signal<{ username: string, message: string }>()
|
* const chatSignal = new Signal<{ username: string, message: string }>()
|
||||||
*
|
*
|
||||||
|
* Create a signal with no data (void):
|
||||||
|
* const clickSignal = new Signal<void>()
|
||||||
|
* const clickSignal2 = new Signal() // Defaults to void
|
||||||
|
*
|
||||||
* Connect to the signal:
|
* Connect to the signal:
|
||||||
* const disconnect = chatSignal.connect((data) => {
|
* const disconnect = chatSignal.connect((data) => {
|
||||||
* const {username, message} = data;
|
* const {username, message} = data;
|
||||||
|
|
@ -11,7 +19,10 @@
|
||||||
* })
|
* })
|
||||||
*
|
*
|
||||||
* Emit a signal:
|
* Emit a signal:
|
||||||
|
* nameSignal.emit("Alice")
|
||||||
|
* countSignal.emit(42)
|
||||||
* chatSignal.emit({ username: "Chad", message: "Hey everyone, how's it going?" });
|
* chatSignal.emit({ username: "Chad", message: "Hey everyone, how's it going?" });
|
||||||
|
* clickSignal.emit() // No argument for void signals
|
||||||
*
|
*
|
||||||
* Forward a signal:
|
* Forward a signal:
|
||||||
* const relaySignal = new Signal<{ username: string, message: string }>()
|
* const relaySignal = new Signal<{ username: string, message: string }>()
|
||||||
|
|
@ -25,7 +36,7 @@
|
||||||
* chatSignal.disconnect()
|
* chatSignal.disconnect()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Signal<T extends object | void> {
|
export class Signal<T = void> {
|
||||||
private listeners: Array<(data: T) => void> = []
|
private listeners: Array<(data: T) => void> = []
|
||||||
|
|
||||||
connect(listenerOrSignal: Signal<T> | ((data: T) => void)) {
|
connect(listenerOrSignal: Signal<T> | ((data: T) => void)) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user