records in the store

master
Dustin Swan 5 hours ago
parent 70569dfe48
commit 6652c0f970
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -1,4 +1,4 @@
import type { Value, NativeFunction } from './types'
import type { Value } from './types'
const measureCanvas = document.createElement('canvas');
const measureCtx = measureCanvas.getContext('2d')!;
@ -468,7 +468,7 @@ export const builtins: { [name: string]: Value } = {
name: 'debug',
arity: 2,
fn: (label, value) => {
const l = expectString(label, 'debug');
expectString(label, 'debug');
return value;
}
}

@ -202,17 +202,49 @@ export function evaluate(ast: AST, env: Env, source: string): Value {
}
case 'rebind': {
if (ast.target.kind !== 'variable')
throw new Error('Rebind target must be a variable');
const name = ast.target.name;
const value = evaluate(ast.value, env, source);
return {
kind: 'constructor',
name: 'Rebind',
args: [{ kind: 'string', value: name }, value]
};
if (ast.target.kind === 'variable') {
const name = ast.target.name;
return {
kind: 'constructor',
name: 'Rebind',
args: [{ kind: 'string', value: name }, value]
};
}
if (ast.target.kind === 'record-access') {
let current: AST = ast.target;
const path: string[] = [];
while (current.kind === 'record-access') {
path.unshift(current.field);
current = current.record;
}
if (current.kind !== 'variable')
throw RuntimeError('Rebind target must be a variable or field access', ast.line, ast.column, source);
const rootName = current.name;
const rootValue = env.get(rootName);
if (!rootValue)
throw RuntimeError(`Unknown variable: ${rootName}`, ast.line, ast.column, source);
// const newRoot = updatePath(rootValue, path, value, ast.line, ast.column, source);
return {
kind: 'constructor',
name: 'Rebind',
args: [
{ kind: 'string', value: rootName },
{ kind: 'list', elements: path.map(p => ({ kind: 'string', value: p })) },
value
]
};
}
throw RuntimeError('Rebind target must be a variable or field access', ast.line, ast.column, source);
}
default:
@ -297,5 +329,4 @@ function matchPattern(value: Value, pattern: Pattern): Bindings | null {
return bindings;
}
}
}

@ -187,7 +187,7 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string, env:
};
try {
const callEnv = new Map(app.view.env);
const callEnv = new Map(env);
callEnv.set(app.view.params[0], state);
callEnv.set(app.view.params[1], viewport);
const uiValue = evaluate(app.view.body, callEnv, source);
@ -223,13 +223,25 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string, env:
}
if (event.kind === 'constructor' && event.name === 'Rebind') {
if (event.args.length === 2 && event.args[0].kind === 'string') {
const name = event.args[0].value;
const value = event.args[1];
env.set(name, value);
rerender();
return;
if (event.args[0].kind !== 'string') return;
const name = event.args[0].value;
if (event.args.length === 2) {
// Rebind "name" value
env.set(name, event.args[1]);
} else if (event.args.length === 3 && event.args[1].kind === 'list') {
// Rebind "name" ["path"]
const pathList = (event.args[1] as { elements: Value[] });
const path = pathList.elements.map((e: Value) => e.kind === 'string' ? e.value : '');
const currentValue = env.get(name);
if (currentValue) {
const newValue = updatePath(currentValue, path, event.args[2]);
env.set(name, newValue);
}
}
rerender();
return;
}
if (app.update.kind !== 'closure')
@ -321,3 +333,19 @@ export function runApp(app: App, canvas: HTMLCanvasElement, source: string, env:
rerender();
}
function updatePath(obj: Value, path: string[], value: Value): Value {
if (path.length === 0) return value;
if (obj.kind !== 'record')
throw new Error('Cannot access field on non-record');
const [field, ...rest] = path;
return {
kind: 'record',
fields: {
...obj.fields,
[field]: updatePath(obj.fields[field], rest, value)
}
};
}

@ -1,7 +1,9 @@
init = {};
email = "";
password = "";
testApp = {
email = "",
password = ""
};
update = state event \ event
| _ \ state;
@ -19,7 +21,7 @@ view = state viewport \
initialFocus = True,
w = 300,
h = 40,
onChange = text \ email := text
onChange = text \ testApp.email := text
},
textInput {
key = "password",
@ -27,10 +29,10 @@ view = state viewport \
initialFocus = False,
w = 300,
h = 40,
onChange = text \ password := text
onChange = text \ testApp.password := text
},
Text { content = "Username: " & email, x = 8, y = 16 },
Text { content = "Password: " & password, x = 8, y = 16 }
Text { content = "Username: " & testApp.email, x = 8, y = 16 },
Text { content = "Password: " & testApp.password, x = 8, y = 16 }
]
}
};

Loading…
Cancel
Save