Switching to ML style type annotations. not separate statement from the expression

This commit is contained in:
Dustin Swan 2026-03-26 16:05:22 -06:00
parent 6acec5641c
commit f3c3a76671
No known key found for this signature in database
GPG key ID: 30D46587E2100467
6 changed files with 35 additions and 36 deletions

View file

@ -325,9 +325,9 @@ export function prettyPrint(ast: AST, indent = 0): string {
case 'definition': case 'definition':
const ann = ast.annotation const ann = ast.annotation
? `${ast.name} : ${prettyPrintType(ast.annotation.type)};\n` ? ` : ${prettyPrintType(ast.annotation.type)}`
: ''; : '';
return `${ann}${ast.name} = ${prettyPrint(ast.body, indent)};`; return `${ast.name}${ann} = ${prettyPrint(ast.body, indent)};`;
default: default:
return `Unknown AST kind: ${i}${(ast as any).kind}` return `Unknown AST kind: ${i}${(ast as any).kind}`
@ -373,7 +373,7 @@ function prettyPrintPattern(pattern: Pattern): string {
} }
} }
function prettyPrintType(type: TypeAST): string { export function prettyPrintType(type: TypeAST): string {
switch (type.kind) { switch (type.kind) {
case 'type-name': case 'type-name':
case 'type-var': case 'type-var':

View file

@ -8,8 +8,7 @@ Maybe a = None | Some a;
# | [0, [x, ...xs]] \ (Some x) # | [0, [x, ...xs]] \ (Some x)
# | [n, [x, ...xs]] \ nth (n - 1) xs; # | [n, [x, ...xs]] \ nth (n - 1) xs;
map : (a \ b) \ List a \ List b; map : (a \ b) \ List a \ List b = f list \ list
map = f list \ list
| [] \ [] | [] \ []
| [x, ...xs] \ [f x, ...map f xs]; | [x, ...xs] \ [f x, ...map f xs];

View file

@ -1,4 +1,4 @@
myFontBackup = { glyphs = { myFont = { glyphs = {
"0" = { "0" = {
w = 7, w = 7,
h = 12, h = 12,
@ -5836,7 +5836,7 @@ myFontBackup = { glyphs = {
} }
} }; } };
myFont2Backup = { glyphs = { myFont2 = { glyphs = {
"0" = { "0" = {
w = 5, w = 5,
h = 12, h = 12,

View file

@ -10,7 +10,7 @@ type CompileCtx = {
}; };
const defaultCtx: CompileCtx = { useStore: true, bound: new Set(), topLevel: new Set() }; const defaultCtx: CompileCtx = { useStore: true, bound: new Set(), topLevel: new Set() };
export const definitions: Map<string, AST> = new Map(); export const definitions: Map<string, Definition> = new Map();
export const dependencies: Map<string, Set<string>> = new Map(); export const dependencies: Map<string, Set<string>> = new Map();
export const dependents: Map<string, Set<string>> = new Map(); export const dependents: Map<string, Set<string>> = new Map();
export const astRegistry = new Map<number, AST>(); export const astRegistry = new Map<number, AST>();
@ -226,7 +226,7 @@ export function compileAndRun(defs: Definition[]) {
const topLevel = new Set(defs.map(d => d.name)); const topLevel = new Set(defs.map(d => d.name));
for (const def of defs) { for (const def of defs) {
definitions.set(def.name, def.body); definitions.set(def.name, def);
const free = freeVars(def.body); const free = freeVars(def.body);
const deps = new Set([...free].filter(v => topLevel.has(v))); const deps = new Set([...free].filter(v => topLevel.has(v)));
dependencies.set(def.name, deps); dependencies.set(def.name, deps);
@ -364,7 +364,9 @@ function patternVars(pattern: Pattern): string[] {
} }
export function recompile(name: string, newAst: AST) { export function recompile(name: string, newAst: AST) {
definitions.set(name, newAst); const existing = definitions.get(name);
definitions.set(name, { kind: 'definition', name, body: newAst, annotation: existing?.annotation });;
// definitions.set(name, newAst);
const topLevel = new Set(definitions.keys()); const topLevel = new Set(definitions.keys());
const free = freeVars(newAst); const free = freeVars(newAst);
@ -394,7 +396,7 @@ export function recompile(name: string, newAst: AST) {
collectDependents(name); collectDependents(name);
for (const defName of toRecompile) { for (const defName of toRecompile) {
const ast = definitions.get(defName)!; const ast = definitions.get(defName)!.body;
const compiled = compile(ast); const compiled = compile(ast);
const fn = new Function('store', `return ${compiled}`); const fn = new Function('store', `return ${compiled}`);

View file

@ -160,21 +160,6 @@ export class Parser {
continue; continue;
} }
// type annotation
if (this.current().kind === 'ident' && this.peek().kind === 'colon') {
this.advance(); // eat ident
this.advance();
const type = this.parseType();
this.expect('semicolon');
// parse definition
const def = this.parseDefinition();
def.annotation = { constraints: [], type };
definitions.push(def);
continue;
}
definitions.push(this.parseDefinition()); definitions.push(this.parseDefinition());
} }
@ -185,6 +170,12 @@ export class Parser {
const nameToken = this.expect('ident'); const nameToken = this.expect('ident');
const name = (nameToken as { value: string }).value; const name = (nameToken as { value: string }).value;
let annotation: Annotation | undefined;
if (this.current().kind === 'colon') {
this.advance();
annotation = { constraints: [], type: this.parseType() };
}
this.expect('equals'); this.expect('equals');
const body = this.parseExpression(); const body = this.parseExpression();
@ -193,7 +184,7 @@ export class Parser {
this.expect('semicolon'); this.expect('semicolon');
} }
return { kind: 'definition', name, body, ...this.getPos(nameToken) }; return { kind: 'definition', name, body, annotation, ...this.getPos(nameToken) };
} }
private parseExpression(): AST { private parseExpression(): AST {

View file

@ -1,7 +1,7 @@
import { tokenize } from './lexer' import { tokenize } from './lexer'
import { Parser } from './parser' import { Parser } from './parser'
import { compile, recompile, definitions, freeVars, dependencies, dependents, astRegistry } from './compiler' import { compile, recompile, definitions, freeVars, dependencies, dependents, astRegistry } from './compiler'
import { prettyPrint } from './ast' import { prettyPrint, prettyPrintType } from './ast'
import type { AST } from './ast' import type { AST } from './ast'
import { measure } from './ui'; import { measure } from './ui';
@ -118,10 +118,9 @@ export const _rt = {
return obj === undefined ? { _tag: 'None' } : { _tag: 'Some', _0: obj }; return obj === undefined ? { _tag: 'None' } : { _tag: 'Some', _0: obj };
}, },
getSource: (name: string) => { getSource: (name: string) => {
const ast = definitions.get(name); const def = definitions.get(name);
if (!ast) return ""; if (!def) return "";
const printed = prettyPrint(ast); return prettyPrint(def);
return printed;
}, },
"saveImage!": () => { "saveImage!": () => {
const saved: Record<string, string> = {}; const saved: Record<string, string> = {};
@ -259,8 +258,8 @@ export const _rt = {
export function saveDefinitions() { export function saveDefinitions() {
const saved: Record<string, string> = {}; const saved: Record<string, string> = {};
for (const [name, ast] of definitions) { for (const [name, def] of definitions) {
const source = prettyPrint({ kind: 'definition', name, body: ast }); const source = prettyPrint(def);
saved[name] = source; saved[name] = source;
} }
localStorage.setItem(STORAGE_KEY, JSON.stringify(saved)); localStorage.setItem(STORAGE_KEY, JSON.stringify(saved));
@ -356,8 +355,16 @@ function valueToAst(value: any): AST {
export function syncToAst(name: string) { export function syncToAst(name: string) {
// if (definitions.has(name)) { // if (definitions.has(name)) {
if (name in store) { if (name in store) {
definitions.set(name, valueToAst(store[name])); const existing = definitions.get(name);
const source = prettyPrint({ kind: 'definition', name, body: definitions.get(name)! }); const newDef: Definition = {
kind: 'definition',
name,
body: valueToAst(store[name]),
annotation: existing?.annotation,
};
definitions.set(name, newDef); // valueToAst(store[name]));
// const source = prettyPrint({ kind: 'definition', name, body: definitions.get(name)! });
const source = prettyPrint(definitions.get(name)!);
appendChangeLog(name, source); appendChangeLog(name, source);
saveDefinitions(); saveDefinitions();
} }