regex and null
This commit is contained in:
parent
80e489f55d
commit
e8a1befdcc
|
|
@ -164,7 +164,11 @@ export class Compiler {
|
||||||
return [[`PUSH`, value === 'true']]
|
return [[`PUSH`, value === 'true']]
|
||||||
}
|
}
|
||||||
|
|
||||||
case terms.RegExp: {
|
case terms.Null: {
|
||||||
|
return [[`PUSH`, null]]
|
||||||
|
}
|
||||||
|
|
||||||
|
case terms.Regex: {
|
||||||
// remove the surrounding slashes and any flags
|
// remove the surrounding slashes and any flags
|
||||||
const [_, pattern, flags] = value.match(/^\/\/(.*)\/\/([gimsuy]*)$/) || []
|
const [_, pattern, flags] = value.match(/^\/\/(.*)\/\/([gimsuy]*)$/) || []
|
||||||
if (!pattern) {
|
if (!pattern) {
|
||||||
|
|
@ -216,6 +220,7 @@ export class Compiler {
|
||||||
const { identifier, right } = getAssignmentParts(node)
|
const { identifier, right } = getAssignmentParts(node)
|
||||||
const instructions: ProgramItem[] = []
|
const instructions: ProgramItem[] = []
|
||||||
instructions.push(...this.#compileNode(right, input))
|
instructions.push(...this.#compileNode(right, input))
|
||||||
|
instructions.push(['DUP']) // Keep a copy on the stack after storing
|
||||||
const identifierName = input.slice(identifier.from, identifier.to)
|
const identifierName = input.slice(identifier.from, identifier.to)
|
||||||
instructions.push(['STORE', identifierName])
|
instructions.push(['STORE', identifierName])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ describe('compiler', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
test('assign number', () => {
|
test('assign number', () => {
|
||||||
expect('x = 5; x').toEvaluateTo(5)
|
expect('x = 5').toEvaluateTo(5)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('emoji assignment to number', () => {
|
test('emoji assignment to number', () => {
|
||||||
|
|
@ -182,7 +182,7 @@ describe('string interpolation', () => {
|
||||||
expect(`'price is \\$10'`).toEvaluateTo('price is $10')
|
expect(`'price is \\$10'`).toEvaluateTo('price is $10')
|
||||||
})
|
})
|
||||||
|
|
||||||
test.only('string with mixed interpolation and escapes', () => {
|
test('string with mixed interpolation and escapes', () => {
|
||||||
expect(`x = 5; 'value: $x\\ntotal: $(x * 2)'`).toEvaluateTo('value: 5\ntotal: 10')
|
expect(`x = 5; 'value: $x\\ntotal: $(x * 2)'`).toEvaluateTo('value: 5\ntotal: 10')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ describe('string interpolation', () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('RegExp', () => {
|
describe('Regex', () => {
|
||||||
test('simple regex', () => {
|
test('simple regex', () => {
|
||||||
expect('//hello//').toEvaluateTo(/hello/)
|
expect('//hello//').toEvaluateTo(/hello/)
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#output {
|
#output {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
background: #40318D;
|
background: var(--bg-output);
|
||||||
color: #7C70DA;
|
color: var(--text-output);
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
|
@ -9,20 +9,16 @@
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#output.error {
|
|
||||||
color: #FF6E6E;
|
|
||||||
}
|
|
||||||
|
|
||||||
#status-bar {
|
#status-bar {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background: #1E2A4A;
|
background: var(--bg-status-bar);
|
||||||
color: #B3A9FF55;
|
color: var(--text-status);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
border-top: 3px solid #0E1A3A;
|
border-top: 3px solid var(--bg-status-border);
|
||||||
border-bottom: 3px solid #0E1A3A;
|
border-bottom: 3px solid var(--bg-status-border);
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
@ -44,7 +40,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
color: #C3E88D;
|
color: var(--color-string);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inactive {
|
.inactive {
|
||||||
|
|
@ -53,5 +49,5 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.syntax-error {
|
.syntax-error {
|
||||||
text-decoration: underline dotted #FF6E6E;
|
text-decoration: underline dotted var(--color-error);
|
||||||
}
|
}
|
||||||
|
|
@ -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 { log, toElement } from '#utils/utils'
|
import { asciiEscapeToHtml, 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'
|
||||||
|
|
@ -53,7 +53,7 @@ let outputTimeout: ReturnType<typeof setTimeout>
|
||||||
|
|
||||||
outputSignal.connect((output) => {
|
outputSignal.connect((output) => {
|
||||||
const el = document.querySelector('#output')!
|
const el = document.querySelector('#output')!
|
||||||
el.textContent = ''
|
el.innerHTML = ''
|
||||||
let content
|
let content
|
||||||
if ('error' in output) {
|
if ('error' in output) {
|
||||||
el.classList.add('error')
|
el.classList.add('error')
|
||||||
|
|
@ -63,15 +63,7 @@ outputSignal.connect((output) => {
|
||||||
content = output.output
|
content = output.output
|
||||||
}
|
}
|
||||||
|
|
||||||
clearInterval(outputTimeout)
|
el.innerHTML = asciiEscapeToHtml(content)
|
||||||
const totalTime = 100
|
|
||||||
const speed = totalTime / content.length
|
|
||||||
let i = 0
|
|
||||||
outputTimeout = setInterval(() => {
|
|
||||||
el.textContent += content[i]
|
|
||||||
i++
|
|
||||||
if (i >= content.length) clearInterval(outputTimeout)
|
|
||||||
}, speed)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
type StatusBarMessage = {
|
type StatusBarMessage = {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ export const shrimpErrors = ViewPlugin.fromClass(
|
||||||
tree.iterate({
|
tree.iterate({
|
||||||
enter: (node) => {
|
enter: (node) => {
|
||||||
if (!node.type.isError) return
|
if (!node.type.isError) return
|
||||||
|
|
||||||
// Skip empty error nodes
|
// Skip empty error nodes
|
||||||
if (node.from === node.to) return
|
if (node.from === node.to) return
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ export const shrimpErrors = ViewPlugin.fromClass(
|
||||||
})
|
})
|
||||||
|
|
||||||
this.decorations = Decoration.set(decorations)
|
this.decorations = Decoration.set(decorations)
|
||||||
requestAnimationFrame(() => view.dispatch({}))
|
// requestAnimationFrame(() => view.dispatch({}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('🙈 Error parsing document', e)
|
console.error('🙈 Error parsing document', e)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
import { outputSignal, statusBarSignal } from '#editor/editor'
|
import { statusBarSignal } from '#editor/editor'
|
||||||
|
import { run } from '#editor/runCode'
|
||||||
import { EditorState } from '@codemirror/state'
|
import { EditorState } from '@codemirror/state'
|
||||||
import { Compiler } from '#compiler/compiler'
|
|
||||||
import { errorMessage, log } from '#utils/utils'
|
|
||||||
import { keymap } from '@codemirror/view'
|
import { keymap } from '@codemirror/view'
|
||||||
import { VM } from 'reefvm'
|
|
||||||
|
|
||||||
let multilineMode = false
|
let multilineMode = false
|
||||||
const customKeymap = keymap.of([
|
const customKeymap = keymap.of([
|
||||||
|
|
@ -39,9 +37,19 @@ const customKeymap = keymap.of([
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
let firstTime = true
|
||||||
const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
const singleLineFilter = EditorState.transactionFilter.of((transaction) => {
|
||||||
if (multilineMode) return transaction // Allow everything in multiline mode
|
if (multilineMode) return transaction // Allow everything in multiline mode
|
||||||
|
|
||||||
|
if (firstTime) {
|
||||||
|
firstTime = false
|
||||||
|
if (transaction.newDoc.toString().includes('\n')) {
|
||||||
|
multilineMode = true
|
||||||
|
updateStatusMessage()
|
||||||
|
return 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
|
||||||
|
|
@ -74,15 +82,3 @@ const updateStatusMessage = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(() => updateStatusMessage())
|
requestAnimationFrame(() => updateStatusMessage())
|
||||||
|
|
||||||
const run = async (input: string) => {
|
|
||||||
try {
|
|
||||||
const compiler = new Compiler(input)
|
|
||||||
const vm = new VM(compiler.bytecode)
|
|
||||||
const output = await vm.run()
|
|
||||||
outputSignal.emit({ output: String(output.value) })
|
|
||||||
} catch (error) {
|
|
||||||
log.error(error)
|
|
||||||
outputSignal.emit({ error: `${errorMessage(error)}` })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,20 @@ import { HighlightStyle, syntaxHighlighting } from '@codemirror/language'
|
||||||
import { tags } from '@lezer/highlight'
|
import { tags } from '@lezer/highlight'
|
||||||
|
|
||||||
const highlightStyle = HighlightStyle.define([
|
const highlightStyle = HighlightStyle.define([
|
||||||
{ tag: tags.keyword, color: '#C792EA' },
|
{ tag: tags.keyword, color: 'var(--color-keyword)' },
|
||||||
{ tag: tags.name, color: '#82AAFF' },
|
{ tag: tags.name, color: 'var(--color-function)' },
|
||||||
{ tag: tags.string, color: '#C3E88D' },
|
{ tag: tags.string, color: 'var(--color-string)' },
|
||||||
{ tag: tags.number, color: '#F78C6C' },
|
{ tag: tags.number, color: 'var(--color-number)' },
|
||||||
{ tag: tags.bool, color: '#FF5370' },
|
{ tag: tags.bool, color: 'var(--color-bool)' },
|
||||||
{ tag: tags.operator, color: '#89DDFF' },
|
{ tag: tags.operator, color: 'var(--color-operator)' },
|
||||||
{ tag: tags.paren, color: '#676E95' },
|
{ tag: tags.paren, color: 'var(--color-paren)' },
|
||||||
{ tag: tags.function(tags.variableName), color: '#FF9CAC' },
|
{ tag: tags.regexp, color: 'var(--color-regex)' },
|
||||||
|
{ tag: tags.function(tags.variableName), color: 'var(--color-function-call)' },
|
||||||
{ tag: tags.function(tags.invalid), color: 'white' },
|
{ tag: tags.function(tags.invalid), color: 'white' },
|
||||||
{
|
{
|
||||||
tag: tags.definition(tags.variableName),
|
tag: tags.definition(tags.variableName),
|
||||||
color: '#FFCB6B',
|
color: 'var(--color-variable-def)',
|
||||||
backgroundColor: '#1E2A4A',
|
backgroundColor: 'var(--bg-variable-def)',
|
||||||
padding: '1px 2px',
|
padding: '1px 2px',
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
fontWeight: '500',
|
fontWeight: '500',
|
||||||
|
|
@ -27,24 +28,24 @@ export const shrimpHighlighting = syntaxHighlighting(highlightStyle)
|
||||||
export const shrimpTheme = EditorView.theme(
|
export const shrimpTheme = EditorView.theme(
|
||||||
{
|
{
|
||||||
'&': {
|
'&': {
|
||||||
color: '#D6DEEB', // Night Owl text color
|
color: 'var(--text-editor)',
|
||||||
backgroundColor: '#011627', // Night Owl dark blue
|
backgroundColor: 'var(--bg-editor)',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
fontSize: '18px',
|
fontSize: '18px',
|
||||||
},
|
},
|
||||||
'.cm-content': {
|
'.cm-content': {
|
||||||
fontFamily: '"Pixeloid Mono", "Courier New", monospace',
|
fontFamily: '"Pixeloid Mono", "Courier New", monospace',
|
||||||
caretColor: '#80A4C2', // soft blue caret
|
caretColor: 'var(--caret)',
|
||||||
padding: '0px',
|
padding: '0px',
|
||||||
},
|
},
|
||||||
'.cm-activeLine': {
|
'.cm-activeLine': {
|
||||||
backgroundColor: 'transparent',
|
backgroundColor: 'transparent',
|
||||||
},
|
},
|
||||||
'&.cm-focused .cm-cursor': {
|
'&.cm-focused .cm-cursor': {
|
||||||
borderLeftColor: '#80A4C2',
|
borderLeftColor: 'var(--caret)',
|
||||||
},
|
},
|
||||||
'&.cm-focused .cm-selectionBackground, ::selection': {
|
'&.cm-focused .cm-selectionBackground, ::selection': {
|
||||||
backgroundColor: '#1D3B53', // darker blue selection
|
backgroundColor: 'var(--bg-selection)',
|
||||||
},
|
},
|
||||||
'.cm-gutters': {
|
'.cm-gutters': {
|
||||||
display: 'none',
|
display: 'none',
|
||||||
|
|
@ -55,10 +56,10 @@ export const shrimpTheme = EditorView.theme(
|
||||||
height: '100%',
|
height: '100%',
|
||||||
},
|
},
|
||||||
'.cm-matchingBracket': {
|
'.cm-matchingBracket': {
|
||||||
backgroundColor: '#FF5370',
|
backgroundColor: 'var(--color-bool)',
|
||||||
},
|
},
|
||||||
'.cm-nonmatchingBracket': {
|
'.cm-nonmatchingBracket': {
|
||||||
backgroundColor: '#C3E88D',
|
backgroundColor: 'var(--color-string)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ dark: true }
|
{ dark: true }
|
||||||
|
|
|
||||||
16
src/editor/runCode.tsx
Normal file
16
src/editor/runCode.tsx
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { outputSignal } from '#editor/editor'
|
||||||
|
import { Compiler } from '#compiler/compiler'
|
||||||
|
import { errorMessage, log } from '#utils/utils'
|
||||||
|
import { VM } from 'reefvm'
|
||||||
|
|
||||||
|
export const run = async (input: string) => {
|
||||||
|
try {
|
||||||
|
const compiler = new Compiler(input)
|
||||||
|
const vm = new VM(compiler.bytecode)
|
||||||
|
const output = await vm.run()
|
||||||
|
outputSignal.emit({ output: String(output.value) })
|
||||||
|
} catch (error) {
|
||||||
|
log.error(error)
|
||||||
|
outputSignal.emit({ error: `${errorMessage(error)}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ export const highlighting = styleTags({
|
||||||
fn: tags.keyword,
|
fn: tags.keyword,
|
||||||
end: tags.keyword,
|
end: tags.keyword,
|
||||||
':': tags.keyword,
|
':': tags.keyword,
|
||||||
|
Regex: tags.regexp,
|
||||||
Operator: tags.operator,
|
Operator: tags.operator,
|
||||||
Command: tags.function(tags.variableName),
|
Command: tags.function(tags.variableName),
|
||||||
'Params/Identifier': tags.definition(tags.variableName),
|
'Params/Identifier': tags.definition(tags.variableName),
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
@top Program { item* }
|
@top Program { item* }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
@precedence { Number "-" RegExp "/"}
|
@precedence { Number "-" Regex "/"}
|
||||||
|
|
||||||
StringFragment { !['\\$]+ }
|
StringFragment { !['\\$]+ }
|
||||||
NamedArgPrefix { $[a-z]+ "=" }
|
NamedArgPrefix { $[a-z]+ "=" }
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
colon[closedBy="end", @name="colon"] { ":" }
|
colon[closedBy="end", @name="colon"] { ":" }
|
||||||
end[openedBy="colon", @name="end"] { "end" }
|
end[openedBy="colon", @name="end"] { "end" }
|
||||||
Underscore { "_" }
|
Underscore { "_" }
|
||||||
RegExp { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
|
Null { "null" }
|
||||||
|
Regex { "//" (![/\\\n[] | "\\" ![\n] | "[" (![\n\\\]] | "\\" ![\n])* "]")+ ("//" $[gimsuy]*)? } // Stolen from the lezer JavaScript grammar
|
||||||
"fn" [@name=keyword]
|
"fn" [@name=keyword]
|
||||||
"if" [@name=keyword]
|
"if" [@name=keyword]
|
||||||
"elsif" [@name=keyword]
|
"elsif" [@name=keyword]
|
||||||
|
|
@ -198,7 +199,7 @@ EscapeSeq {
|
||||||
// to go through ambiguousFunctionCall (which is what we want semantically).
|
// to go through ambiguousFunctionCall (which is what we want semantically).
|
||||||
// Yes, it is annoying and I gave up trying to use GLR to fix it.
|
// Yes, it is annoying and I gave up trying to use GLR to fix it.
|
||||||
expressionWithoutIdentifier {
|
expressionWithoutIdentifier {
|
||||||
ParenExpr | Word | String | Number | Boolean | RegExp
|
ParenExpr | Word | String | Number | Boolean | Regex | Null
|
||||||
}
|
}
|
||||||
|
|
||||||
block {
|
block {
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,17 @@ export const
|
||||||
EscapeSeq = 26,
|
EscapeSeq = 26,
|
||||||
Number = 27,
|
Number = 27,
|
||||||
Boolean = 28,
|
Boolean = 28,
|
||||||
RegExp = 29,
|
Regex = 29,
|
||||||
FunctionDef = 30,
|
Null = 30,
|
||||||
Params = 32,
|
FunctionDef = 31,
|
||||||
colon = 33,
|
Params = 33,
|
||||||
end = 34,
|
colon = 34,
|
||||||
Underscore = 35,
|
end = 35,
|
||||||
NamedArg = 36,
|
Underscore = 36,
|
||||||
NamedArgPrefix = 37,
|
NamedArg = 37,
|
||||||
IfExpr = 39,
|
NamedArgPrefix = 38,
|
||||||
ThenBlock = 42,
|
IfExpr = 40,
|
||||||
ElsifExpr = 43,
|
ThenBlock = 43,
|
||||||
ElseExpr = 45,
|
ElsifExpr = 44,
|
||||||
Assign = 47
|
ElseExpr = 46,
|
||||||
|
Assign = 48
|
||||||
|
|
|
||||||
|
|
@ -4,20 +4,20 @@ import {tokenizer} from "./tokenizer"
|
||||||
import {highlighting} from "./highlight"
|
import {highlighting} from "./highlight"
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: ".WQVQaOOO!xQbO'#CdO#YQPO'#CeO#hQPO'#DiO$eQaO'#CcO$lOSO'#CsOOQ`'#Dm'#DmO$zQPO'#DlO%cQaO'#DwOOQ`'#Cz'#CzOOQO'#Dj'#DjO%kQPO'#DiO%yQaO'#D{OOQO'#DT'#DTOOQO'#Di'#DiO&QQPO'#DhOOQ`'#Dh'#DhOOQ`'#D^'#D^QVQaOOOOQ`'#Dl'#DlOOQ`'#Cb'#CbO&YQaO'#DQOOQ`'#Dk'#DkOOQ`'#D_'#D_O&gQbO,58{O'WQaO,59wO%yQaO,59PO%yQaO,59PO'eQbO'#CdO(pQPO'#CeO)QQPO,58}O)cQPO,58}O)^QPO,58}O*^QPO,58}O*fQaO'#CuO*nQWO'#CvOOOO'#Dq'#DqOOOO'#D`'#D`O+SOSO,59_OOQ`,59_,59_OOQ`'#Da'#DaO+bQaO'#C|O+jQPO,5:cO+oQaO'#DcO+tQPO,58zO,VQPO,5:gO,^QPO,5:gOOQ`,5:S,5:SOOQ`-E7[-E7[OOQ`,59l,59lOOQ`-E7]-E7]OOQO1G/c1G/cOOQO1G.k1G.kO,cQPO1G.kO%yQaO,59UO%yQaO,59UOOQ`1G.i1G.iOOOO,59a,59aOOOO,59b,59bOOOO-E7^-E7^OOQ`1G.y1G.yOOQ`-E7_-E7_O,}QaO1G/}O-_QbO'#CdOOQO,59},59}OOQO-E7a-E7aO.OQaO1G0ROOQO1G.p1G.pO.`QPO1G.pO.jQPO7+%iO.oQaO7+%jOOQO'#DV'#DVOOQO7+%m7+%mO/PQaO7+%nOOQ`<<IT<<ITO/gQPO'#DbO/lQaO'#DzO0SQPO<<IUOOQO'#DW'#DWO0XQPO<<IYOOQ`,59|,59|OOQ`-E7`-E7`OOQ`AN>pAN>pO%yQaO'#DXOOQO'#Dd'#DdO0dQPOAN>tO0oQPO'#DZOOQOAN>tAN>tO0tQPOAN>tO0yQPO,59sO1QQPO,59sOOQO-E7b-E7bOOQOG24`G24`O1VQPOG24`O1[QPO,59uO1aQPO1G/_OOQOLD)zLD)zO.oQaO1G/aO/PQaO7+$yOOQO7+${7+${OOQO<<He<<He",
|
states: ".WQVQaOOO#OQbO'#CdO#`QPO'#CeO#nQPO'#DjO$nQaO'#CcO$uOSO'#CsOOQ`'#Dn'#DnO%TQPO'#DmO%lQaO'#DxOOQ`'#C{'#C{OOQO'#Dk'#DkO%tQPO'#DjO&SQaO'#D|OOQO'#DU'#DUOOQO'#Dj'#DjO&ZQPO'#DiOOQ`'#Di'#DiOOQ`'#D_'#D_QVQaOOOOQ`'#Dm'#DmOOQ`'#Cb'#CbO&cQaO'#DROOQ`'#Dl'#DlOOQ`'#D`'#D`O&pQbO,58{O'aQaO,59xO&SQaO,59PO&SQaO,59PO'nQbO'#CdO(yQPO'#CeO)ZQPO,58}O)lQPO,58}O)gQPO,58}O*gQPO,58}O*oQaO'#CuO*wQWO'#CvOOOO'#Dr'#DrOOOO'#Da'#DaO+]OSO,59_OOQ`,59_,59_OOQ`'#Db'#DbO+kQaO'#C}O+sQPO,5:dO+xQaO'#DdO+}QPO,58zO,`QPO,5:hO,gQPO,5:hOOQ`,5:T,5:TOOQ`-E7]-E7]OOQ`,59m,59mOOQ`-E7^-E7^OOQO1G/d1G/dOOQO1G.k1G.kO,lQPO1G.kO&SQaO,59UO&SQaO,59UOOQ`1G.i1G.iOOOO,59a,59aOOOO,59b,59bOOOO-E7_-E7_OOQ`1G.y1G.yOOQ`-E7`-E7`O-WQaO1G0OO-hQbO'#CdOOQO,5:O,5:OOOQO-E7b-E7bO.XQaO1G0SOOQO1G.p1G.pO.iQPO1G.pO.sQPO7+%jO.xQaO7+%kOOQO'#DW'#DWOOQO7+%n7+%nO/YQaO7+%oOOQ`<<IU<<IUO/pQPO'#DcO/uQaO'#D{O0]QPO<<IVOOQO'#DX'#DXO0bQPO<<IZOOQ`,59},59}OOQ`-E7a-E7aOOQ`AN>qAN>qO&SQaO'#DYOOQO'#De'#DeO0mQPOAN>uO0xQPO'#D[OOQOAN>uAN>uO0}QPOAN>uO1SQPO,59tO1ZQPO,59tOOQO-E7c-E7cOOQOG24aG24aO1`QPOG24aO1eQPO,59vO1jQPO1G/`OOQOLD){LD){O.xQaO1G/bO/YQaO7+$zOOQO7+$|7+$|OOQO<<Hf<<Hf",
|
||||||
stateData: "1l~O!ZOS~OPPOQUOkUOlUOmUOoWOx[O!bSO!dTO!m`O~OPcOQUOkUOlUOmUOoWOsdOueO!bSO!dTOY!`XZ!`X[!`X]!`XvWX~O_iO!mWX!qWXrWX~PwOYjOZjO[kO]kO~OYjOZjO[kO]kO!m!]X!q!]Xr!]X~OQUOkUOlUOmUO!bSO!dTO~OPlO~P$POhtO!dwO!frO!gsO~OY!`XZ!`X[!`X]!`X!m!]X!q!]Xr!]X~OPxOqpP~Ov{O!m!]X!q!]Xr!]X~OPcO~P$PO!m!PO!q!PO~OPcOoWOs!RO~P$POPcOoWOsdOueOvTa!mTa!qTa!cTarTa~P$POPPOoWOx[O~P$PO_!`X`!`Xa!`Xb!`Xc!`Xd!`Xe!`Xf!`X!cWX~PwO_!WO`!WOa!WOb!WOc!WOd!WOe!XOf!XO~OYjOZjO[kO]kO~P(UOYjOZjO[kO]kO!c!YO~O!c!YOY!`XZ!`X[!`X]!`X_!`X`!`Xa!`Xb!`Xc!`Xd!`Xe!`Xf!`X~Ov{O!c!YO~OP!ZO!bSO~O!d![O!f![O!g![O!h![O!i![O!j![O~OhtO!d!^O!frO!gsO~OPxOqpX~Oq!`O~OP!aO~Ov{O!mSa!qSa!cSarSa~Oq!dO~P(UOq!dO~OYjOZjO[Xi]Xi!mXi!qXi!cXirXi~OPPOoWOx[O!m!hO~P$POPcOoWOsdOueOvWX!mWX!qWX!cWXrWX~P$POPPOoWOx[O!m!kO~P$PO!c^iq^i~P(UOr!lO~OPPOoWOx[Or!nP~P$POPPOoWOx[Or!nP|!nP!O!nP~P$PO!m!rO~OPPOoWOx[Or!nX|!nX!O!nX~P$POr!tO~Or!yO|!uO!O!xO~Or#OO|!uO!O!xO~Oq#QO~Or#OO~Oq#RO~P(UOq#RO~Or#SO~O!m#TO~O!m#UO~Ok]mZm~",
|
stateData: "1u~O![OS~OPPOQUOkUOlUOmUOnUOpWOy[O!cSO!eTO!n`O~OPcOQUOkUOlUOmUOnUOpWOtdOveO!cSO!eTOY!aXZ!aX[!aX]!aXwWX~O_iO!nWX!rWXsWX~PzOYjOZjO[kO]kO~OYjOZjO[kO]kO!n!^X!r!^Xs!^X~OQUOkUOlUOmUOnUO!cSO!eTO~OPlO~P$VOhtO!ewO!grO!hsO~OY!aXZ!aX[!aX]!aX!n!^X!r!^Xs!^X~OPxOrqP~Ow{O!n!^X!r!^Xs!^X~OPcO~P$VO!n!PO!r!PO~OPcOpWOt!RO~P$VOPcOpWOtdOveOwTa!nTa!rTa!dTasTa~P$VOPPOpWOy[O~P$VO_!aX`!aXa!aXb!aXc!aXd!aXe!aXf!aX!dWX~PzO_!WO`!WOa!WOb!WOc!WOd!WOe!XOf!XO~OYjOZjO[kO]kO~P(_OYjOZjO[kO]kO!d!YO~O!d!YOY!aXZ!aX[!aX]!aX_!aX`!aXa!aXb!aXc!aXd!aXe!aXf!aX~Ow{O!d!YO~OP!ZO!cSO~O!e![O!g![O!h![O!i![O!j![O!k![O~OhtO!e!^O!grO!hsO~OPxOrqX~Or!`O~OP!aO~Ow{O!nSa!rSa!dSasSa~Or!dO~P(_Or!dO~OYjOZjO[Xi]Xi!nXi!rXi!dXisXi~OPPOpWOy[O!n!hO~P$VOPcOpWOtdOveOwWX!nWX!rWX!dWXsWX~P$VOPPOpWOy[O!n!kO~P$VO!d^ir^i~P(_Os!lO~OPPOpWOy[Os!oP~P$VOPPOpWOy[Os!oP}!oP!P!oP~P$VO!n!rO~OPPOpWOy[Os!oX}!oX!P!oX~P$VOs!tO~Os!yO}!uO!P!xO~Os#OO}!uO!P!xO~Or#QO~Os#OO~Or#RO~P(_Or#RO~Os#SO~O!n#TO~O!n#UO~Ok]mZm~",
|
||||||
goto: "+W!qPPPP!r#R#a#g#R$SPPPP$iPPPPPPPP$uP%_%_PPP%cP%xPPP#aPP%{P&X&[&eP&iP%{&o&u&}'T'Z'd'kPPP'q'u(Z(m(s)oPPP*]PPPPP*a*aP*r*z*zd^Obi!`!d!h!k!n#T#URpSiYOSbi{!`!d!h!k!n#T#UXfPhl!a|UOPS[behijkl!W!X!`!a!d!h!k!n!u#T#UR!ZrdRObi!`!d!h!k!n#T#UQnSQ!UjR!VkQpSQ!O[Q!e!XR!|!u}UOPS[behijkl!W!X!`!a!d!h!k!n!u#T#UTtTvd^Obi!`!d!h!k!n#T#UWdPhl!aR!ReRzWe^Obi!`!d!h!k!n#T#UR!j!dQ!q!kQ#V#TR#W#UT!v!q!wQ!z!qR#P!wQbOR!QbUhPl!aR!ShQvTR!]vQyWR!_yW!n!h!k#T#UR!s!nS|ZqR!c|Q!w!qR!}!wTaObS_ObQ!TiQ!g!`Q!i!dZ!m!h!k!n#T#UdZObi!`!d!h!k!n#T#UQqSR!b{XgPhl!adQObi!`!d!h!k!n#T#UWdPhl!aQmSQ}[Q!ReQ!UjQ!VkQ!e!WQ!f!XR!{!udVObi!`!d!h!k!n#T#UfcP[ehjkl!W!X!a!uRoSTuTvoXOPbehil!`!a!d!h!k!n#T#UQ!o!hV!p!k#T#Ue]Obi!`!d!h!k!n#T#U",
|
goto: "+X!rPPPP!s#S#b#h#S$TPPPP$jPPPPPPPP$vP%`%`PPPP%dP%yPPP#bPP%|P&Y&]&fP&jP%|&p&v'O'U'['e'lPPP'r'v([(n(t)pPPP*^PPPPP*b*bP*s*{*{d^Obi!`!d!h!k!n#T#URpSiYOSbi{!`!d!h!k!n#T#UXfPhl!a|UOPS[behijkl!W!X!`!a!d!h!k!n!u#T#UR!ZrdRObi!`!d!h!k!n#T#UQnSQ!UjR!VkQpSQ!O[Q!e!XR!|!u}UOPS[behijkl!W!X!`!a!d!h!k!n!u#T#UTtTvd^Obi!`!d!h!k!n#T#UWdPhl!aR!ReRzWe^Obi!`!d!h!k!n#T#UR!j!dQ!q!kQ#V#TR#W#UT!v!q!wQ!z!qR#P!wQbOR!QbUhPl!aR!ShQvTR!]vQyWR!_yW!n!h!k#T#UR!s!nS|ZqR!c|Q!w!qR!}!wTaObS_ObQ!TiQ!g!`Q!i!dZ!m!h!k!n#T#UdZObi!`!d!h!k!n#T#UQqSR!b{XgPhl!adQObi!`!d!h!k!n#T#UWdPhl!aQmSQ}[Q!ReQ!UjQ!VkQ!e!WQ!f!XR!{!udVObi!`!d!h!k!n#T#UfcP[ehjkl!W!X!a!uRoSTuTvoXOPbehil!`!a!d!h!k!n#T#UQ!o!hV!p!k#T#Ue]Obi!`!d!h!k!n#T#U",
|
||||||
nodeNames: "⚠ Identifier Word 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 RegExp FunctionDef keyword Params colon end Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
|
nodeNames: "⚠ Identifier Word 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 FunctionDef keyword Params colon end Underscore NamedArg NamedArgPrefix operator IfExpr keyword ThenBlock ThenBlock ElsifExpr keyword ElseExpr keyword Assign",
|
||||||
maxTerm: 79,
|
maxTerm: 80,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
["closedBy", 33,"end"],
|
["closedBy", 34,"end"],
|
||||||
["openedBy", 34,"colon"]
|
["openedBy", 35,"colon"]
|
||||||
],
|
],
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0],
|
skippedNodes: [0],
|
||||||
repeatNodeCount: 7,
|
repeatNodeCount: 7,
|
||||||
tokenData: "!!{~R!SOX$_XY$|YZ%gZp$_pq$|qr&Qrt$_tu'Yuw$_wx'_xy'dyz'}z{(h{|)R|}$_}!O)l!O!P$_!P!Q,b!Q![*]![!]5P!]!^%g!^!_5j!_!`6t!`!a7_!a#O$_#O#P8i#P#R$_#R#S8n#S#T$_#T#U9X#U#X:m#X#Y=S#Y#ZDY#Z#]:m#]#^Ie#^#b:m#b#cKV#c#dK|#d#f:m#f#gMn#g#h:m#h#iNe#i#o:m#o#p$_#p#q!!]#q;'S$_;'S;=`$v<%l~$_~O$_~~!!vS$dUhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_S$yP;=`<%l$__%TUhS!ZZOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V%nUhS!mROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V&VWhSOt$_uw$_x!_$_!_!`&o!`#O$_#P;'S$_;'S;=`$v<%lO$_V&vU`RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~'_O!f~~'dO!d~V'kUhS!bROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(UUhS!cROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(oUYRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)YU[RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)sWhS]ROt$_uw$_x!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V*dYhSkROt$_uw$_x!O$_!O!P+S!P!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V+XWhSOt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V+xWhSkROt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V,iWhSZROt$_uw$_x!P$_!P!Q-R!Q#O$_#P;'S$_;'S;=`$v<%lO$_V-W^hSOY.SYZ$_Zt.Stu/Vuw.Swx/Vx!P.S!P!Q$_!Q!}.S!}#O3x#O#P1e#P;'S.S;'S;=`4y<%lO.SV.Z^hSmROY.SYZ$_Zt.Stu/Vuw.Swx/Vx!P.S!P!Q1z!Q!}.S!}#O3x#O#P1e#P;'S.S;'S;=`4y<%lO.SR/[XmROY/VZ!P/V!P!Q/w!Q!}/V!}#O0f#O#P1e#P;'S/V;'S;=`1t<%lO/VR/zP!P!Q/}R0SUmR#Z#[/}#]#^/}#a#b/}#g#h/}#i#j/}#m#n/}R0iVOY0fZ#O0f#O#P1O#P#Q/V#Q;'S0f;'S;=`1_<%lO0fR1RSOY0fZ;'S0f;'S;=`1_<%lO0fR1bP;=`<%l0fR1hSOY/VZ;'S/V;'S;=`1t<%lO/VR1wP;=`<%l/VV2PWhSOt$_uw$_x!P$_!P!Q2i!Q#O$_#P;'S$_;'S;=`$v<%lO$_V2pbhSmROt$_uw$_x#O$_#P#Z$_#Z#[2i#[#]$_#]#^2i#^#a$_#a#b2i#b#g$_#g#h2i#h#i$_#i#j2i#j#m$_#m#n2i#n;'S$_;'S;=`$v<%lO$_V3}[hSOY3xYZ$_Zt3xtu0fuw3xwx0fx#O3x#O#P1O#P#Q.S#Q;'S3x;'S;=`4s<%lO3xV4vP;=`<%l3xV4|P;=`<%l.ST5WUhSqPOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V5qWaRhSOt$_uw$_x!_$_!_!`6Z!`#O$_#P;'S$_;'S;=`$v<%lO$_V6bUbRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V6{U_RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V7fWcRhSOt$_uw$_x!_$_!_!`8O!`#O$_#P;'S$_;'S;=`$v<%lO$_V8VUdRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~8nO!g~V8uUhSsROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V9^[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#b:m#b#c;b#c#o:m#o;'S$_;'S;=`$v<%lO$_U:ZUuQhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_U:rYhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V;g[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#W:m#W#X<]#X#o:m#o;'S$_;'S;=`$v<%lO$_V<dYeRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V=X^hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#a>T#a#b:m#b#cBh#c#o:m#o;'S$_;'S;=`$v<%lO$_V>Y[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#g:m#g#h?O#h#o:m#o;'S$_;'S;=`$v<%lO$_V?T^hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#X:m#X#Y@P#Y#]:m#]#^@v#^#o:m#o;'S$_;'S;=`$v<%lO$_V@WY!OPhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V@{[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#Y:m#Y#ZAq#Z#o:m#o;'S$_;'S;=`$v<%lO$_VAxY|PhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VBm[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#W:m#W#XCc#X#o:m#o;'S$_;'S;=`$v<%lO$_VCjYhSrROt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VD_]hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#UEW#U#b:m#b#cHn#c#o:m#o;'S$_;'S;=`$v<%lO$_VE][hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#aFR#a#o:m#o;'S$_;'S;=`$v<%lO$_VFW[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#g:m#g#hF|#h#o:m#o;'S$_;'S;=`$v<%lO$_VGR[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#X:m#X#YGw#Y#o:m#o;'S$_;'S;=`$v<%lO$_VHOYlRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VHuYoRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VIj[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#Y:m#Y#ZJ`#Z#o:m#o;'S$_;'S;=`$v<%lO$_VJgYxPhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_^K^Y!hWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VLR[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#f:m#f#gLw#g#o:m#o;'S$_;'S;=`$v<%lO$_VMOYfRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_^MuY!jWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$__Nl[!iWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#f:m#f#g! b#g#o:m#o;'S$_;'S;=`$v<%lO$_V! g[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#i:m#i#jF|#j#o:m#o;'S$_;'S;=`$v<%lO$_V!!dUvRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~!!{O!q~",
|
tokenData: "!%n~R!SOX$_XY$|YZ%gZp$_pq$|qr&Qrt$_tu'Yuw$_wx'_xy'dyz'}z{(h{|)R|}$_}!O)l!O!P$_!P!Q,b!Q![*]![!]5P!]!^%g!^!_5j!_!`6t!`!a7_!a#O$_#O#P8i#P#R$_#R#S8n#S#T$_#T#U9X#U#X:m#X#Y=S#Y#ZDY#Z#]:m#]#^Ie#^#b:m#b#cKV#c#dNo#d#f:m#f#g!!a#g#h:m#h#i!#W#i#o:m#o#p$_#p#q!%O#q;'S$_;'S;=`$v<%l~$_~O$_~~!%iS$dUhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_S$yP;=`<%l$__%TUhS![ZOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V%nUhS!nROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V&VWhSOt$_uw$_x!_$_!_!`&o!`#O$_#P;'S$_;'S;=`$v<%lO$_V&vU`RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~'_O!g~~'dO!e~V'kUhS!cROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(UUhS!dROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V(oUYRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)YU[RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V)sWhS]ROt$_uw$_x!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V*dYhSkROt$_uw$_x!O$_!O!P+S!P!Q$_!Q![*]![#O$_#P;'S$_;'S;=`$v<%lO$_V+XWhSOt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V+xWhSkROt$_uw$_x!Q$_!Q![+q![#O$_#P;'S$_;'S;=`$v<%lO$_V,iWhSZROt$_uw$_x!P$_!P!Q-R!Q#O$_#P;'S$_;'S;=`$v<%lO$_V-W^hSOY.SYZ$_Zt.Stu/Vuw.Swx/Vx!P.S!P!Q$_!Q!}.S!}#O3x#O#P1e#P;'S.S;'S;=`4y<%lO.SV.Z^hSmROY.SYZ$_Zt.Stu/Vuw.Swx/Vx!P.S!P!Q1z!Q!}.S!}#O3x#O#P1e#P;'S.S;'S;=`4y<%lO.SR/[XmROY/VZ!P/V!P!Q/w!Q!}/V!}#O0f#O#P1e#P;'S/V;'S;=`1t<%lO/VR/zP!P!Q/}R0SUmR#Z#[/}#]#^/}#a#b/}#g#h/}#i#j/}#m#n/}R0iVOY0fZ#O0f#O#P1O#P#Q/V#Q;'S0f;'S;=`1_<%lO0fR1RSOY0fZ;'S0f;'S;=`1_<%lO0fR1bP;=`<%l0fR1hSOY/VZ;'S/V;'S;=`1t<%lO/VR1wP;=`<%l/VV2PWhSOt$_uw$_x!P$_!P!Q2i!Q#O$_#P;'S$_;'S;=`$v<%lO$_V2pbhSmROt$_uw$_x#O$_#P#Z$_#Z#[2i#[#]$_#]#^2i#^#a$_#a#b2i#b#g$_#g#h2i#h#i$_#i#j2i#j#m$_#m#n2i#n;'S$_;'S;=`$v<%lO$_V3}[hSOY3xYZ$_Zt3xtu0fuw3xwx0fx#O3x#O#P1O#P#Q.S#Q;'S3x;'S;=`4s<%lO3xV4vP;=`<%l3xV4|P;=`<%l.ST5WUhSrPOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V5qWaRhSOt$_uw$_x!_$_!_!`6Z!`#O$_#P;'S$_;'S;=`$v<%lO$_V6bUbRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V6{U_RhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V7fWcRhSOt$_uw$_x!_$_!_!`8O!`#O$_#P;'S$_;'S;=`$v<%lO$_V8VUdRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~8nO!h~V8uUhStROt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_V9^[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#b:m#b#c;b#c#o:m#o;'S$_;'S;=`$v<%lO$_U:ZUvQhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_U:rYhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V;g[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#W:m#W#X<]#X#o:m#o;'S$_;'S;=`$v<%lO$_V<dYeRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V=X^hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#a>T#a#b:m#b#cBh#c#o:m#o;'S$_;'S;=`$v<%lO$_V>Y[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#g:m#g#h?O#h#o:m#o;'S$_;'S;=`$v<%lO$_V?T^hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#X:m#X#Y@P#Y#]:m#]#^@v#^#o:m#o;'S$_;'S;=`$v<%lO$_V@WY!PPhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_V@{[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#Y:m#Y#ZAq#Z#o:m#o;'S$_;'S;=`$v<%lO$_VAxY}PhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VBm[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#W:m#W#XCc#X#o:m#o;'S$_;'S;=`$v<%lO$_VCjYhSsROt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VD_]hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#UEW#U#b:m#b#cHn#c#o:m#o;'S$_;'S;=`$v<%lO$_VE][hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#aFR#a#o:m#o;'S$_;'S;=`$v<%lO$_VFW[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#g:m#g#hF|#h#o:m#o;'S$_;'S;=`$v<%lO$_VGR[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#X:m#X#YGw#Y#o:m#o;'S$_;'S;=`$v<%lO$_VHOYlRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VHuYpRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VIj[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#Y:m#Y#ZJ`#Z#o:m#o;'S$_;'S;=`$v<%lO$_VJgYyPhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$__K^[!iWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#i:m#i#jLS#j#o:m#o;'S$_;'S;=`$v<%lO$_VLX[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#aL}#a#o:m#o;'S$_;'S;=`$v<%lO$_VMS[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#`:m#`#aMx#a#o:m#o;'S$_;'S;=`$v<%lO$_VNPYnRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_VNt[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#f:m#f#g! j#g#o:m#o;'S$_;'S;=`$v<%lO$_V! qYfRhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$_^!!hY!kWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#o:m#o;'S$_;'S;=`$v<%lO$__!#_[!jWhSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#f:m#f#g!$T#g#o:m#o;'S$_;'S;=`$v<%lO$_V!$Y[hSOt$_uw$_x!_$_!_!`:S!`#O$_#P#T$_#T#i:m#i#jF|#j#o:m#o;'S$_;'S;=`$v<%lO$_V!%VUwRhSOt$_uw$_x#O$_#P;'S$_;'S;=`$v<%lO$_~!%nO!r~",
|
||||||
tokenizers: [0, 1, 2, 3, tokenizer],
|
tokenizers: [0, 1, 2, 3, tokenizer],
|
||||||
topRules: {"Program":[0,3]},
|
topRules: {"Program":[0,3]},
|
||||||
tokenPrec: 758
|
tokenPrec: 767
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,20 @@ import { expect, describe, test } from 'bun:test'
|
||||||
|
|
||||||
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
import '../shrimp.grammar' // Importing this so changes cause it to retest!
|
||||||
|
|
||||||
|
describe('null', () => {
|
||||||
|
test('parses null', () => {
|
||||||
|
expect('null').toMatchTree(`Null null`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses null in assignments', () => {
|
||||||
|
expect('a = null').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
Identifier a
|
||||||
|
operator =
|
||||||
|
Null null`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('Identifier', () => {
|
describe('Identifier', () => {
|
||||||
test('parses identifiers with emojis and dashes', () => {
|
test('parses identifiers with emojis and dashes', () => {
|
||||||
expect('moo-😊-34').toMatchTree(`
|
expect('moo-😊-34').toMatchTree(`
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,52 @@
|
||||||
|
:root {
|
||||||
|
/* Background colors */
|
||||||
|
--bg-editor: #011627;
|
||||||
|
--bg-output: #40318D;
|
||||||
|
--bg-status-bar: #1E2A4A;
|
||||||
|
--bg-status-border: #0E1A3A;
|
||||||
|
--bg-selection: #1D3B53;
|
||||||
|
--bg-variable-def: #1E2A4A;
|
||||||
|
|
||||||
|
/* Text colors */
|
||||||
|
--text-editor: #D6DEEB;
|
||||||
|
--text-output: #7C70DA;
|
||||||
|
--text-status: #B3A9FF55;
|
||||||
|
--caret: #80A4C2;
|
||||||
|
|
||||||
|
/* Syntax highlighting colors */
|
||||||
|
--color-keyword: #C792EA;
|
||||||
|
--color-function: #82AAFF;
|
||||||
|
--color-string: #C3E88D;
|
||||||
|
--color-number: #F78C6C;
|
||||||
|
--color-bool: #FF5370;
|
||||||
|
--color-operator: #89DDFF;
|
||||||
|
--color-paren: #676E95;
|
||||||
|
--color-function-call: #FF9CAC;
|
||||||
|
--color-variable-def: #FFCB6B;
|
||||||
|
--color-error: #FF6E6E;
|
||||||
|
--color-regex: #E1ACFF;
|
||||||
|
|
||||||
|
/* ANSI terminal colors */
|
||||||
|
--ansi-black: #011627;
|
||||||
|
--ansi-red: #FF5370;
|
||||||
|
--ansi-green: #C3E88D;
|
||||||
|
--ansi-yellow: #FFCB6B;
|
||||||
|
--ansi-blue: #82AAFF;
|
||||||
|
--ansi-magenta: #C792EA;
|
||||||
|
--ansi-cyan: #89DDFF;
|
||||||
|
--ansi-white: #D6DEEB;
|
||||||
|
|
||||||
|
/* ANSI bright colors (slightly more vibrant) */
|
||||||
|
--ansi-bright-black: #676E95;
|
||||||
|
--ansi-bright-red: #FF6E90;
|
||||||
|
--ansi-bright-green: #D4F6A8;
|
||||||
|
--ansi-bright-yellow: #FFE082;
|
||||||
|
--ansi-bright-blue: #A8C7FA;
|
||||||
|
--ansi-bright-magenta: #E1ACFF;
|
||||||
|
--ansi-bright-cyan: #A8F5FF;
|
||||||
|
--ansi-bright-white: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
@ -5,8 +54,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #40318D;
|
background: var(--bg-output);
|
||||||
color: #7C70DA;
|
color: var(--text-output);
|
||||||
font-family: 'Pixeloid Mono', 'Courier New', monospace;
|
font-family: 'Pixeloid Mono', 'Courier New', monospace;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
@ -15,7 +64,7 @@ body {
|
||||||
|
|
||||||
#root {
|
#root {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
background: #40318D;
|
background: var(--bg-output);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
@ -27,3 +27,112 @@ export const toElement = (node: any): HTMLElement => {
|
||||||
export const assertNever = (x: never): never => {
|
export const assertNever = (x: never): never => {
|
||||||
throw new Error(`Unexpected object: ${x}`)
|
throw new Error(`Unexpected object: ${x}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HtmlEscapedString = string & { __htmlEscaped: true }
|
||||||
|
|
||||||
|
const ansiCodeToCssVar = (code: number): string | null => {
|
||||||
|
// Foreground colors (30-37)
|
||||||
|
if (code === 30) return '--ansi-black'
|
||||||
|
if (code === 31) return '--ansi-red'
|
||||||
|
if (code === 32) return '--ansi-green'
|
||||||
|
if (code === 33) return '--ansi-yellow'
|
||||||
|
if (code === 34) return '--ansi-blue'
|
||||||
|
if (code === 35) return '--ansi-magenta'
|
||||||
|
if (code === 36) return '--ansi-cyan'
|
||||||
|
if (code === 37) return '--ansi-white'
|
||||||
|
|
||||||
|
// Background colors (40-47)
|
||||||
|
if (code === 40) return '--ansi-black'
|
||||||
|
if (code === 41) return '--ansi-red'
|
||||||
|
if (code === 42) return '--ansi-green'
|
||||||
|
if (code === 43) return '--ansi-yellow'
|
||||||
|
if (code === 44) return '--ansi-blue'
|
||||||
|
if (code === 45) return '--ansi-magenta'
|
||||||
|
if (code === 46) return '--ansi-cyan'
|
||||||
|
if (code === 47) return '--ansi-white'
|
||||||
|
|
||||||
|
// Bright foreground colors (90-97)
|
||||||
|
if (code === 90) return '--ansi-bright-black'
|
||||||
|
if (code === 91) return '--ansi-bright-red'
|
||||||
|
if (code === 92) return '--ansi-bright-green'
|
||||||
|
if (code === 93) return '--ansi-bright-yellow'
|
||||||
|
if (code === 94) return '--ansi-bright-blue'
|
||||||
|
if (code === 95) return '--ansi-bright-magenta'
|
||||||
|
if (code === 96) return '--ansi-bright-cyan'
|
||||||
|
if (code === 97) return '--ansi-bright-white'
|
||||||
|
|
||||||
|
// Bright background colors (100-107)
|
||||||
|
if (code === 100) return '--ansi-bright-black'
|
||||||
|
if (code === 101) return '--ansi-bright-red'
|
||||||
|
if (code === 102) return '--ansi-bright-green'
|
||||||
|
if (code === 103) return '--ansi-bright-yellow'
|
||||||
|
if (code === 104) return '--ansi-bright-blue'
|
||||||
|
if (code === 105) return '--ansi-bright-magenta'
|
||||||
|
if (code === 106) return '--ansi-bright-cyan'
|
||||||
|
if (code === 107) return '--ansi-bright-white'
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const asciiEscapeToHtml = (str: string): HtmlEscapedString => {
|
||||||
|
let result = ''
|
||||||
|
let openSpans = 0
|
||||||
|
|
||||||
|
const parts = str.split(/\x1b\[(.*?)m/)
|
||||||
|
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
if (i % 2 === 0) {
|
||||||
|
// Regular text
|
||||||
|
result += parts[i]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ANSI escape code
|
||||||
|
const codes = parts[i]!.split(';').map((code) => parseInt(code, 10))
|
||||||
|
|
||||||
|
for (const code of codes) {
|
||||||
|
if (code === 0) {
|
||||||
|
// Reset - close all open spans
|
||||||
|
result += '</span>'.repeat(openSpans)
|
||||||
|
openSpans = 0
|
||||||
|
} else if (code === 1) {
|
||||||
|
// Bold
|
||||||
|
result += '<span style="font-weight: bold;">'
|
||||||
|
openSpans++
|
||||||
|
} else if (code >= 30 && code <= 37) {
|
||||||
|
// Foreground color
|
||||||
|
const cssVar = ansiCodeToCssVar(code)
|
||||||
|
if (cssVar) {
|
||||||
|
result += `<span style="color: var(${cssVar});">`
|
||||||
|
openSpans++
|
||||||
|
}
|
||||||
|
} else if (code >= 40 && code <= 47) {
|
||||||
|
// Background color
|
||||||
|
const cssVar = ansiCodeToCssVar(code)
|
||||||
|
if (cssVar) {
|
||||||
|
result += `<span style="background-color: var(${cssVar});">`
|
||||||
|
openSpans++
|
||||||
|
}
|
||||||
|
} else if (code >= 90 && code <= 97) {
|
||||||
|
// Bright foreground color
|
||||||
|
const cssVar = ansiCodeToCssVar(code)
|
||||||
|
if (cssVar) {
|
||||||
|
result += `<span style="color: var(${cssVar});">`
|
||||||
|
openSpans++
|
||||||
|
}
|
||||||
|
} else if (code >= 100 && code <= 107) {
|
||||||
|
// Bright background color
|
||||||
|
const cssVar = ansiCodeToCssVar(code)
|
||||||
|
if (cssVar) {
|
||||||
|
result += `<span style="background-color: var(${cssVar});">`
|
||||||
|
openSpans++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any remaining spans
|
||||||
|
result += '</span>'.repeat(openSpans)
|
||||||
|
|
||||||
|
return result as HtmlEscapedString
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user