Fixing too much. also, the OS is actually spawning and managing windows
This commit is contained in:
parent
62f6e202b0
commit
db4abfa450
5 changed files with 168 additions and 130 deletions
137
src/cg/10-os.cg
137
src/cg/10-os.cg
|
|
@ -2,68 +2,107 @@ osState = {
|
|||
palette = {
|
||||
visible = True
|
||||
},
|
||||
inspector = {
|
||||
visible = False,
|
||||
name = ""
|
||||
}
|
||||
windows = [],
|
||||
wm = {
|
||||
focusedIndex = 0,
|
||||
scrollOffset = 0
|
||||
},
|
||||
nextId = 0
|
||||
};
|
||||
|
||||
run = cmd \
|
||||
_ = debug "run" cmd;
|
||||
result = eval cmd;
|
||||
_ = debug "result" result;
|
||||
result
|
||||
| Defined name \ debug "defined" name
|
||||
| Value v \ debug "result" v
|
||||
| Err msg \ debug "error" msg
|
||||
| _ \ noOp;
|
||||
|
||||
onSelect = item \
|
||||
# for now, if it matches a store item, inspect it
|
||||
# if not, eval it
|
||||
res = storeSearch item;
|
||||
zeroth = nth 0 res;
|
||||
(and (len res > 0) (nth 0 res == Some item))
|
||||
| True \ inspect item
|
||||
| _ \ run item;
|
||||
|
||||
inspect = item \
|
||||
openWindow = title content \
|
||||
id = osState.nextId;
|
||||
batch [
|
||||
osState.palette.visible := False,
|
||||
osState.inspector.visible := True,
|
||||
osState.inspector.name := item
|
||||
osState.nextId := id + 1,
|
||||
osState.windows := osState.windows & [{ id = id, title = title, content = content, width = 400 }],
|
||||
osState.palette.visible := False
|
||||
];
|
||||
|
||||
init = {};
|
||||
closeFocused = _ \
|
||||
i = osState.wm.focusedIndex;
|
||||
batch [
|
||||
osState.windows := (take i osState.windows) & (drop (i + 1) osState.windows),
|
||||
osState.wm.focusedIndex := max 0 (osState.wm.focusedIndex - 1)
|
||||
];
|
||||
|
||||
update = state event \ event
|
||||
| Key { key = "p", meta = True } \
|
||||
{ state = state, emit = [osState.palette.visible := not (osState.palette.visible)] }
|
||||
| _ \ { state = state, emit = [] };
|
||||
onSelect = input \
|
||||
result = eval input;
|
||||
result
|
||||
| Value v \ openWindow input (ui.text { content = show v, color = "white" })
|
||||
| Defined name \ noOp
|
||||
| Err msg \ openWindow "Error" (ui.text { content = msg, color = "red" });
|
||||
|
||||
view = state viewport \
|
||||
renderWindow = window isActive \
|
||||
_ = debug "renderWindow" window.title;
|
||||
titleBarHeight = 30;
|
||||
ui.stack {
|
||||
children = [
|
||||
ui.rect { w = viewport.width, h = viewport.height, color = "#012" },
|
||||
# background
|
||||
ui.rect { w = window.width, h = viewport.height, color = (isActive | True \ "#0a2a3a" | False \ "#061820")},
|
||||
|
||||
osState.inspector.visible
|
||||
| True \ inspector {
|
||||
name = osState.inspector.name,
|
||||
viewport = viewport,
|
||||
# title bar
|
||||
ui.positioned {
|
||||
x = 0, y = 0,
|
||||
child = ui.stack {
|
||||
children = [
|
||||
ui.rect { w = window.width, h = titleBarHeight, color = (isActive | True \ "#a15f80" | False \ "#0f3348") },
|
||||
ui.positioned { x = 8, y = 8, child = ui.text { content = window.title, color = "white" } },
|
||||
]
|
||||
}
|
||||
| False \ empty,
|
||||
},
|
||||
|
||||
# keep palette at the end so it's on top
|
||||
osState.palette.visible
|
||||
| True \ palette {
|
||||
# state = osState.palette,
|
||||
# query = osState.palette.query,
|
||||
search = storeSearch,
|
||||
onSelect = onSelect,
|
||||
viewport = viewport,
|
||||
# content
|
||||
ui.positioned {
|
||||
x = 0, y = titleBarHeight,
|
||||
child = ui.clip {
|
||||
w = window.width,
|
||||
h = viewport.height - titleBarHeight,
|
||||
child = window.content
|
||||
}
|
||||
| False \ empty,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
os = { init = init, update = update, view = view }
|
||||
renderWindows = _ \
|
||||
windows = osState.windows;
|
||||
focused = osState.wm.focusedIndex;
|
||||
ui.row {
|
||||
gap = 1,
|
||||
children = mapWithIndex (w i \ renderWindow w (i == focused)) windows
|
||||
};
|
||||
|
||||
os = ui.stateful {
|
||||
key = "os",
|
||||
autoFocus = True,
|
||||
|
||||
init = {},
|
||||
|
||||
update = state event \ event
|
||||
| Key { key = "p", meta = True } \
|
||||
{ state = state, emit = [osState.palette.visible := not (osState.palette.visible)] }
|
||||
| Key { key = "ArrowLeft", meta = True } \
|
||||
{ state = state, emit = [osState.wm.focusedIndex := max 0 (osState.wm.focusedIndex - 1)] }
|
||||
| Key { key = "ArrowRight", meta = True } \
|
||||
{ state = state, emit = [osState.wm.focusedIndex := min (len osState.windows - 1) (osState.wm.focusedIndex + 1)] }
|
||||
| Key { key = "d", meta = True } \
|
||||
{ state = state, emit = [closeFocused 0] }
|
||||
| _ \ _ = debug "key" []; { state = state, emit = [] },
|
||||
|
||||
view = state \
|
||||
ui.stack {
|
||||
children = [
|
||||
ui.rect { w = viewport.width, h = viewport.height, color = "#012" },
|
||||
|
||||
renderWindows 0,
|
||||
|
||||
# keep palette at the end so it's on top
|
||||
osState.palette.visible
|
||||
| True \ palette {
|
||||
search = storeSearch,
|
||||
onSelect = onSelect,
|
||||
viewport = viewport,
|
||||
}
|
||||
| False \ empty,
|
||||
]
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import type { AST, Pattern, Definition } from './ast';
|
||||
import { _rt, store } from './runtime-js';
|
||||
import { store } from './runtime-js';
|
||||
|
||||
let matchCounter = 0;
|
||||
|
||||
export const definitions: Map<string, AST> = new Map();
|
||||
export const dependencies: Map<string, Set<string>> = new Map();
|
||||
export const dependents: Map<string, Set<string>> = new Map();
|
||||
export const astRegistry = new Map<number, AST>();
|
||||
let astIdCounter = 0;
|
||||
|
||||
export function compile(ast: AST, useStore = true, bound = new Set<string>()): string {
|
||||
export function compile(ast: AST, useStore = true, bound = new Set<string>(), topLevel = new Set<string>()): string {
|
||||
switch (ast.kind) {
|
||||
case 'literal':
|
||||
if (ast.value.kind === 'string')
|
||||
|
|
@ -18,61 +20,62 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
|
|||
|
||||
case 'variable': {
|
||||
if (bound.has(ast.name)) {
|
||||
return sanitizeName(ast.name);
|
||||
return sanitizeName(ast.name);
|
||||
}
|
||||
return sanitize(ast.name, useStore);
|
||||
return sanitize(ast.name, useStore, topLevel);
|
||||
}
|
||||
|
||||
case 'lambda': {
|
||||
const newBound = new Set([...bound, ...ast.params]);
|
||||
const params = ast.params.map(sanitizeName).join(') => (');
|
||||
return `Object.assign((${params}) => ${compile(ast.body, useStore, newBound)}, { _ast: (${JSON.stringify(ast)}) })`;
|
||||
const id = astIdCounter++;
|
||||
return `Object.assign((${params}) => ${compile(ast.body, useStore, newBound, topLevel)}, { _astId: (${id}) })`;
|
||||
}
|
||||
|
||||
case 'apply':
|
||||
// Constructor
|
||||
if (ast.func.kind === 'constructor') {
|
||||
const ctorName = ast.func.name;
|
||||
const arg = compile(ast.args[0], useStore, bound);
|
||||
const arg = compile(ast.args[0], useStore, bound, topLevel);
|
||||
return `((_a) => _a && typeof _a === 'object' && !Array.isArray(_a) && !_a._tag
|
||||
? { _tag: "${ctorName}", ..._a }
|
||||
: { _tag: "${ctorName}", _0: _a })(${arg})`;
|
||||
}
|
||||
|
||||
const args = ast.args.map(a => compile(a, useStore, bound)).join(')(');
|
||||
return `${compile(ast.func, useStore, bound)}(${args})`;
|
||||
const args = ast.args.map(a => compile(a, useStore, bound, topLevel)).join(')(');
|
||||
return `${compile(ast.func, useStore, bound, topLevel)}(${args})`;
|
||||
|
||||
case 'record': {
|
||||
const parts = ast.entries.map(entry =>
|
||||
entry.kind === 'spread'
|
||||
? `...${compile(entry.expr, useStore, bound)}`
|
||||
: `${sanitizeName(entry.key)}: ${compile(entry.value, useStore, bound)}`
|
||||
? `...${compile(entry.expr, useStore, bound, topLevel)}`
|
||||
: `${sanitizeName(entry.key)}: ${compile(entry.value, useStore, bound, topLevel)}`
|
||||
)
|
||||
return `({${parts.join(', ')}})`;
|
||||
}
|
||||
|
||||
case 'list': {
|
||||
const elements = ast.elements.map(e =>
|
||||
'spread' in e ? `...${compile(e.spread, useStore, bound)}` : compile(e, useStore, bound)
|
||||
'spread' in e ? `...${compile(e.spread, useStore, bound, topLevel)}` : compile(e, useStore, bound, topLevel)
|
||||
);
|
||||
return `[${elements.join(', ')}]`;
|
||||
}
|
||||
|
||||
case 'record-access':
|
||||
return `${compile(ast.record, useStore, bound)}.${sanitizeName(ast.field)}`;
|
||||
return `${compile(ast.record, useStore, bound, topLevel)}.${sanitizeName(ast.field)}`;
|
||||
|
||||
case 'record-update':
|
||||
const updates = Object.entries(ast.updates)
|
||||
.map(([k, v]) => `${sanitizeName(k)}: ${compile(v, useStore, bound)}`);
|
||||
return `({...${compile(ast.record, useStore, bound)}, ${updates.join(', ')}})`;
|
||||
.map(([k, v]) => `${sanitizeName(k)}: ${compile(v, useStore, bound, topLevel)}`);
|
||||
return `({...${compile(ast.record, useStore, bound, topLevel)}, ${updates.join(', ')}})`;
|
||||
|
||||
case 'let':
|
||||
const newBound = new Set([...bound, ast.name]);
|
||||
return `((${sanitizeName(ast.name)}) =>
|
||||
${compile(ast.body, useStore, newBound)})(${compile(ast.value, useStore, bound)})`;
|
||||
${compile(ast.body, useStore, newBound, topLevel)})(${compile(ast.value, useStore, bound, topLevel)})`;
|
||||
|
||||
case 'match':
|
||||
return compileMatch(ast, useStore, bound);
|
||||
return compileMatch(ast, useStore, bound, topLevel);
|
||||
|
||||
case 'constructor':
|
||||
return `({ _tag: "${ast.name}" })`;
|
||||
|
|
@ -85,10 +88,15 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
|
|||
case 'rebind': {
|
||||
const rootName = getRootName(ast.target);
|
||||
const path = getPath(ast.target);
|
||||
const value = compile(ast.value, useStore, bound);
|
||||
const value = compile(ast.value, useStore, bound, topLevel);
|
||||
|
||||
if (!rootName) throw new Error('Rebind target must be a variable');
|
||||
|
||||
if (bound.has(rootName)) {
|
||||
const target = compile(ast.target, useStore, bound, topLevel);
|
||||
return `(() => { ${target} = ${value}; return { _tag: "NoOp" }; })()`;
|
||||
}
|
||||
|
||||
if (path.length === 0) {
|
||||
return `({ _tag: "Rebind", _0: "${rootName}", _1: ${value} })`;
|
||||
} else {
|
||||
|
|
@ -102,23 +110,9 @@ export function compile(ast: AST, useStore = true, bound = new Set<string>()): s
|
|||
}
|
||||
}
|
||||
|
||||
function sanitize(name: string, useStore = true): string {
|
||||
const ops: Record<string, string> = {
|
||||
'add': '_rt.add', 'sub': '_rt.sub', 'mul': '_rt.mul', 'div': '_rt.div',
|
||||
'mod': '_rt.mod', 'pow': '_rt.pow',
|
||||
'eq': '_rt.eq', 'gt': '_rt.gt', 'lt': '_rt.lt', 'gte': '_rt.gte', 'lte': '_rt.lte',
|
||||
'cat': '_rt.cat', 'max': '_rt.max', 'min': '_rt.min',
|
||||
};
|
||||
|
||||
if (ops[name]) return ops[name];
|
||||
|
||||
const natives = ['eval', 'measure', 'measureText', 'storeSearch', 'getSource', 'debug', 'nth', 'len', 'slice', 'join', 'chars', 'split', 'str', 'redefine', 'undefine', 'batch', 'noOp', 'focus', 'ui'];
|
||||
if (natives.includes(name)) return `_rt.${name}`;
|
||||
|
||||
if (useStore) {
|
||||
return `store.${sanitizeName(name)}`;
|
||||
}
|
||||
return sanitizeName(name);
|
||||
function sanitize(name: string, useStore = true, topLevel: Set<string>): string {
|
||||
if (!useStore && topLevel.has(name)) return sanitizeName(name);
|
||||
return `store[${JSON.stringify(name)}]`
|
||||
}
|
||||
|
||||
function sanitizeName(name: string): string {
|
||||
|
|
@ -135,8 +129,8 @@ function sanitizeName(name: string): string {
|
|||
return name.replace(/-/g, '_');
|
||||
}
|
||||
|
||||
function compileMatch(ast: AST & { kind: 'match'}, useStore = true, bound = new Set<string>()): string {
|
||||
const expr = compile(ast.expr, useStore, bound);
|
||||
function compileMatch(ast: AST & { kind: 'match'}, useStore = true, bound = new Set<string>(), topLevel = new Set<string>()): string {
|
||||
const expr = compile(ast.expr, useStore, bound, topLevel);
|
||||
const tmpVar = `_m${matchCounter++}`;
|
||||
|
||||
let code = `((${tmpVar}) => { `;
|
||||
|
|
@ -149,7 +143,7 @@ function compileMatch(ast: AST & { kind: 'match'}, useStore = true, bound = new
|
|||
if (bindings.length > 0) {
|
||||
code += `const ${bindings.join(', ')}; `;
|
||||
}
|
||||
code += `return ${compile(c.result, useStore, newBound)}; }`;
|
||||
code += `return ${compile(c.result, useStore, newBound, topLevel)}; }`;
|
||||
}
|
||||
|
||||
code += `console.error("No match for:", ${tmpVar}); throw new Error("No match"); })(${expr})`;
|
||||
|
|
@ -245,11 +239,11 @@ export function compileAndRun(defs: Definition[]) {
|
|||
}
|
||||
|
||||
for (const def of defs) {
|
||||
const compiled = `const ${sanitizeName(def.name)} = ${compile(def.body, false)};`;
|
||||
const compiled = `const ${sanitizeName(def.name)} = ${compile(def.body, false, new Set(), topLevel)};`;
|
||||
compiledDefs.push(compiled);
|
||||
|
||||
try {
|
||||
new Function('_rt', compiled);
|
||||
new Function('store', compiled);
|
||||
} catch (e) {
|
||||
console.error(`=== BROKEN: ${def.name} ===`);
|
||||
console.error(compiled);
|
||||
|
|
@ -263,8 +257,8 @@ export function compileAndRun(defs: Definition[]) {
|
|||
const code = `${compiledDefs.join('\n')}
|
||||
return { ${defNames}, __result: ${sanitizeName(lastName)} };`;
|
||||
|
||||
const fn = new Function('_rt', 'store', code);
|
||||
const allDefs = fn(_rt, store);
|
||||
const fn = new Function('store', code);
|
||||
const allDefs = fn(store);
|
||||
|
||||
// Populate store
|
||||
for (const [name, value] of Object.entries(allDefs)) {
|
||||
|
|
@ -397,8 +391,8 @@ export function recompile(name: string, newAst: AST) {
|
|||
const ast = definitions.get(defName)!;
|
||||
const compiled = compile(ast);
|
||||
|
||||
const fn = new Function('_rt', 'store', `return ${compiled}`);
|
||||
store[defName] = fn(_rt, store);
|
||||
const fn = new Function('store', `return ${compiled}`);
|
||||
store[defName] = fn(store);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
18
src/main.ts
18
src/main.ts
|
|
@ -2,7 +2,7 @@ import { compileAndRun } from './compiler'
|
|||
import { tokenize } from './lexer'
|
||||
import { Parser } from './parser'
|
||||
import { runAppCompiled } from './runtime-compiled'
|
||||
import { _rt, loadDefinitions, saveDefinitions } from './runtime-js'
|
||||
import { _rt, store, loadDefinitions, saveDefinitions } from './runtime-js'
|
||||
|
||||
const modules = import.meta.glob('./cg/*.cg', { query: 'raw', import: 'default', eager: true });
|
||||
const cgCode = Object.keys(modules)
|
||||
|
|
@ -13,20 +13,22 @@ const cgCode = Object.keys(modules)
|
|||
const canvas = document.createElement('canvas') as HTMLCanvasElement;
|
||||
document.body.appendChild(canvas);
|
||||
|
||||
// Populate store with natives
|
||||
for (const [name, value] of Object.entries(_rt)) {
|
||||
store[name] = value;
|
||||
}
|
||||
|
||||
store.viewport = { width: window.innerWidth, height: window.innerHeight };
|
||||
|
||||
try {
|
||||
const tokens = tokenize(cgCode);
|
||||
const parser = new Parser(tokens, cgCode);
|
||||
const defs = parser.parse();
|
||||
loadDefinitions();
|
||||
const os = compileAndRun(defs);
|
||||
compileAndRun(defs);
|
||||
saveDefinitions();
|
||||
|
||||
runAppCompiled(
|
||||
{ init: os.init, update: os.update, view: os.view },
|
||||
canvas,
|
||||
_rt
|
||||
);
|
||||
|
||||
runAppCompiled(canvas, store);
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,20 +2,13 @@ import { render, hitTest } from './ui';
|
|||
|
||||
type UIValue = any;
|
||||
|
||||
type App = {
|
||||
init: any;
|
||||
update: (state: any) => (event: any) => any;
|
||||
view: (state: any) => (viewport: any) => any;
|
||||
}
|
||||
|
||||
type ComponentInstance = {
|
||||
state: any;
|
||||
update: (state: any) => (event: any) => any;
|
||||
view: (state: any) => any;
|
||||
};
|
||||
|
||||
export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
||||
let state = app.init;
|
||||
export function runAppCompiled(canvas: HTMLCanvasElement, store: any) {
|
||||
const componentInstances = new Map<string, ComponentInstance>();
|
||||
let focusedComponentKey: string | null = null;
|
||||
|
||||
|
|
@ -132,13 +125,10 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
}
|
||||
|
||||
function rerender() {
|
||||
const viewport = { width: window.innerWidth, height: window.innerHeight };
|
||||
const renderedKeys = new Set<string>();
|
||||
|
||||
try {
|
||||
const uiValue = app.view(state)(viewport);
|
||||
const ui = uiValue;
|
||||
const expandedUI = expandStateful(ui, [], renderedKeys);
|
||||
const expandedUI = expandStateful(store.os, [], renderedKeys);
|
||||
|
||||
// clean up unrendered instances
|
||||
for (const key of componentInstances.keys()) {
|
||||
|
|
@ -148,7 +138,16 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
}
|
||||
|
||||
if (focusedComponentKey && !renderedKeys.has(focusedComponentKey)) {
|
||||
const parts = focusedComponentKey.split('.');
|
||||
focusedComponentKey = null;
|
||||
while (parts.length > 0) {
|
||||
parts.pop();
|
||||
const ancestor = parts.join('.');
|
||||
if (ancestor && renderedKeys.has(ancestor)) {
|
||||
focusedComponentKey = ancestor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render(expandedUI, canvas);
|
||||
|
|
@ -182,7 +181,7 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
}
|
||||
|
||||
if (event._tag === 'Rebind') {
|
||||
rt.rebind(event._0, event._1, event._2);
|
||||
store.rebind(event._0, event._1, event._2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -193,14 +192,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
|
||||
if (event._tag === 'NoOp')
|
||||
return;
|
||||
|
||||
const result = app.update(state)(event);
|
||||
state = result.state;
|
||||
if (result.emit && Array.isArray(result.emit)) {
|
||||
for (const e of result.emit) {
|
||||
handleEvent(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas.addEventListener('click', (e) => {
|
||||
|
|
@ -251,9 +242,6 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
}
|
||||
}
|
||||
|
||||
// OS root
|
||||
handleEvent(event);
|
||||
|
||||
e.preventDefault();
|
||||
rerender();
|
||||
});
|
||||
|
|
@ -263,6 +251,7 @@ export function runAppCompiled(app: App, canvas: HTMLCanvasElement, rt: any) {
|
|||
cancelAnimationFrame(resizeRAF);
|
||||
resizeRAF = requestAnimationFrame(() => {
|
||||
setupCanvas();
|
||||
store.viewport = { width: window.innerWidth, height: window.innerHeight };
|
||||
rerender();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { tokenize } from './lexer'
|
||||
import { Parser } from './parser'
|
||||
import { compile, recompile, definitions, freeVars, dependencies, dependents } from './compiler'
|
||||
import { compile, recompile, definitions, freeVars, dependencies, dependents, astRegistry } from './compiler'
|
||||
import { prettyPrint } from './ast'
|
||||
import type { AST } from './ast'
|
||||
import { measure } from './ui';
|
||||
|
|
@ -61,7 +61,21 @@ export const _rt = {
|
|||
? { _tag: 'Some', _0: xs[i] }
|
||||
: { _tag: 'None' },
|
||||
len: (xs: any[] | string) => xs.length,
|
||||
str: (x: any) => String(x),
|
||||
// str: (x: any) => String(x),
|
||||
show: (value: any): string => {
|
||||
if (value === null || value === undefined) return "None";
|
||||
if (typeof value === 'string') return value;
|
||||
if (typeof value === 'number') return String(value);
|
||||
if (typeof value === 'boolean') return value ? "True" : "False";
|
||||
if (value._tag) return value._0 !== undefined ? `${value._tag} ${_rt.show(value._0)}` : value._tag;
|
||||
if (Array.isArray(value)) return `[${value.map(_rt.show).join(", ")}]`;
|
||||
if (typeof value === 'function') return "<function>";
|
||||
if (typeof value === 'object') {
|
||||
const entries = Object.entries(value).map(([k, v]) => `${k} = ${_rt.show(v)}`);
|
||||
return `{ ${entries.join(", ")} }`;
|
||||
}
|
||||
return String(value);
|
||||
},
|
||||
chars: (s: string) => s.split(''),
|
||||
join: (delim: string) => (xs: string[]) => xs.join(delim),
|
||||
split: (delim: string) => (xs: string) => xs.split(delim),
|
||||
|
|
@ -183,7 +197,7 @@ export function loadDefinitions() {
|
|||
|
||||
try {
|
||||
const saved = JSON.parse(data);
|
||||
for (const [name, source] of Object.entries(saved)) {
|
||||
for (const [_, source] of Object.entries(saved)) {
|
||||
const tokens = tokenize(source as string);
|
||||
const parser = new Parser(tokens, source as string);
|
||||
const defs = parser.parse();
|
||||
|
|
@ -245,11 +259,11 @@ function valueToAst(value: any): AST {
|
|||
|
||||
// Functions
|
||||
if (typeof value === 'function') {
|
||||
if (value._ast) {
|
||||
return value._ast;
|
||||
if (value._astId !== undefined) {
|
||||
return astRegistry.get(value._astId)!;
|
||||
}
|
||||
|
||||
throw new Error('Cannot serialize function without _ast');
|
||||
throw new Error('Cannot serialize function without _astId');
|
||||
}
|
||||
|
||||
throw new Error(`Cannot convert to AST: ${typeof value}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue