Started working on CG UI primitives. rendering to a canvas

master
Dustin Swan 5 days ago
parent 232d9351c1
commit 52647a9ce1
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -1,47 +1,30 @@
import { evaluate } from './interpreter' import type { UIValue } from './types';
import type { Env } from './env' import { render } from './ui';
import { tokenize } from './lexer'
import { Parser } from './parser'
import { prettyPrint } from './ast';
function e(str: string) { const canvas = document.createElement('canvas');
console.log(str); canvas.width = 800;
const tokens = tokenize(str); canvas.height = 600;
console.log(tokens); document.body.appendChild(canvas);
const p = new Parser(tokens);
const ast = p.parse();
console.log(ast);
console.log(prettyPrint(ast));
const env: Env = new Map();
console.log(evaluate(ast, env));
}
e('add1 = (x \\ x + 1); add1 3'); const ui: UIValue = {
e('sum = x y \\ x + y; sum 5 3'); kind: 'column',
e('[1, 2, 3]'); gap: 10,
e('c = 5; { a = 3, b = c }'); children: [
e('rec = { a = 3, b = 5 }; rec.a'); { kind: 'text', content: "Hello CG World", x: 0, y: 20 },
e('rec = { a = 3, b = 5 }; rec{ a = 10 }'); { kind: 'rect', w: 200, h: 50, color: 'blue' },
e('add1 = (x \\ x + 1); 3 > add1'); { kind: 'text', content: "YESS", x: 0, y: 20 },
e('[1, 2] & [3, 4]'); { kind: 'row', gap: 13, children:
e('"abc" & "def"'); [
// e('n | 0 \\ 1 | _ \\ 99'); { kind: 'text', content: "In a row", x: 0, y: 10 },
// e('m | Some x \\ 1 | None \\ 0'); { kind: 'clickable', event: "test", child: {
// e('head = list \\ list | [x, _] \\ Some x | [] \\ None; head'); kind: 'padding', amount: 10, child: {
e('n = 5; n | 5 \\ "five" | _ \\ "other"'); kind: 'rect', w: 80, h: 30, color: '#4a90e2'
e('list = [1, 2, 3]; list | [x, y, z] \\ x + y + z'); }
e('point = {x = 5, y = 10}; point | {x = px, y = py} \\ px + py'); }
}
]
}
]
};
e(`factorial = n \\ n render(ui, canvas);
| 0 \\ 1
| n \\ n * factorial (n - 1);
factorial 5`)
e(`factorial = n \\ n
| 0 \\ 1
| n \\ n * factorial (n - 1);
factorial 5`)
e('some5 = Some 5; some5');

@ -0,0 +1,52 @@
import { evaluate } from './interpreter'
import type { Env } from './env'
import { tokenize } from './lexer'
import { Parser } from './parser'
import { prettyPrint } from './ast';
function e(str: string) {
console.log(str);
const tokens = tokenize(str);
console.log(tokens);
const p = new Parser(tokens);
const ast = p.parse();
console.log(ast);
console.log(prettyPrint(ast));
const env: Env = new Map();
console.log(evaluate(ast, env));
}
e('add1 = (x \\ x + 1); add1 3');
e('sum = x y \\ x + y; sum 5 3');
e('[1, 2, 3]');
e('c = 5; { a = 3, b = c }');
e('rec = { a = 3, b = 5 }; rec.a');
e('rec = { a = 3, b = 5 }; rec{ a = 10 }');
e('add1 = (x \\ x + 1); 3 > add1');
e('[1, 2] & [3, 4]');
e('"abc" & "def"');
e('head = list \\ list | [x, _] \\ Some x | [] \\ None; head');
e('n = 5; n | 5 \\ "five" | _ \\ "other"');
e('list = [1, 2, 3]; list | [x, y, z] \\ x + y + z');
e('point = {x = 5, y = 10}; point | {x = px, y = py} \\ px + py');
e('some5 = Some 5; some5');
e(`factorial = n \\ n
| 0 \\ 1
| n \\ n * factorial (n - 1);
factorial 5`)
e(`
factorial = n \\ n
| 0 \\ 1
| n \\ n * factorial (n - 1);
fib = n \\ n
| 0 \\ 0
| 1 \\ 1
| n \\ fib (n - 1) + fib (n - 2);
[ fib 10, factorial 5 ];
`);

@ -39,4 +39,12 @@ export type ConstructorValue = {
args: Value[] args: Value[]
} }
export type Value = IntValue | FloatValue | StringValue | Closure | ListValue | RecordValue | ConstructorValue; export type UIValue =
| { kind: 'rect', w: number, h: number, color: string }
| { kind: 'text', content: string, x: number, y: number }
| { kind: 'row', children: UIValue[], gap: number }
| { kind: 'column', children: UIValue[], gap: number }
| { kind: 'clickable', child: UIValue, event: string }
| { kind: 'padding', child: UIValue, amount: number }
export type Value = IntValue | FloatValue | StringValue | Closure | ListValue | RecordValue | ConstructorValue | UIValue;

@ -0,0 +1,90 @@
import type { UIValue } from './types';
export function render(ui: UIValue, canvas: HTMLCanvasElement) {
const ctx = canvas.getContext('2d');
if (ctx) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
renderUI(ui, ctx, 0, 0);
}
}
function renderUI(ui: UIValue, ctx: CanvasRenderingContext2D, x: number, y: number) {
switch (ui.kind) {
case 'rect':
ctx.fillStyle = ui.color;
ctx.fillRect(x, y, ui.w, ui.h);
break;
case 'text':
ctx.fillStyle = 'black';
ctx.font = '16px monospace';
ctx.fillText(ui.content, x + ui.x, y + ui.y);
break;
case 'row': {
let offsetX = 0;
for (const child of ui.children) {
renderUI(child, ctx, x + offsetX, y);
offsetX += measure(child).width + ui.gap;
}
break;
}
case 'column': {
let offsetY = 0;
for (const child of ui.children) {
renderUI(child, ctx, x, y + offsetY);
offsetY += measure(child).height + ui.gap;
}
break;
}
case 'clickable': {
renderUI(ui.child, ctx, x, y);
break;
}
case 'padding':
renderUI(ui.child, ctx, x + ui.amount, y + ui.amount);
break;
}
}
function measure(ui: UIValue): { width: number, height: number } {
switch (ui.kind) {
case 'rect': return { width: ui.w, height: ui.h };
case 'text': return { width: ui.content.length * 10, height: 20 }; // TODO
case 'row': {
let totalWidth = 0;
let maxHeight = 0;
for (const child of ui.children) {
const size = measure(child);
totalWidth += size.width;
maxHeight = Math.max(maxHeight, size.height);
}
totalWidth += ui.gap * (ui.children.length - 1);
return { width: totalWidth, height: maxHeight };
}
case 'column': {
let totalHeight = 0;
let maxWidth = 0;
for (const child of ui.children) {
const size = measure(child);
totalHeight += size.height;
maxWidth = Math.max(maxWidth, size.width);
}
totalHeight += ui.gap * (ui.children.length - 1);
return { width: maxWidth, height: totalHeight };
}
case 'clickable':
return measure(ui.child);
case 'padding': {
const childSize = measure(ui.child);
return {
width: childSize.width + ui.amount * 2,
height: childSize.height + ui.amount * 2,
}
}
}
return { width: 0, height: 0 };
}
Loading…
Cancel
Save