Parsing bindings. adding AST pretty printer

master
Dustin Swan 6 days ago
parent 1fc116f2fe
commit 0f0371461d
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -122,3 +122,39 @@ export type AST =
| Definition | Definition
| TypeDef | TypeDef
| Import | Import
export function prettyPrint(ast: AST, indent = 0): string {
const i = ' '.repeat(indent);
switch (ast.kind) {
case 'literal':
return `${i}${ast.value.value}`;
case 'variable':
return `${i}${ast.name}`;
case 'constructor':
return `${i}${ast.name}`;
case 'apply':
const func = prettyPrint(ast.func, 0);
const args = ast.args.map(a => prettyPrint(a, 0)).join(' ');
return `${i}(${func} ${args})`
case 'let':
return `${i}let ${ast.name} = \n${prettyPrint(ast.value, indent + 1)}\n${i}in\n${prettyPrint(ast.body, indent + 1)}`
case 'list':
const elems = ast.elements.map(e => prettyPrint(e, 0)).join(', ');
return `${i}[${elems}]`;
case 'record':
const fields = Object.entries(ast.fields)
.map(([k, v]) => `${k} = ${prettyPrint(v, 0)}`)
.join(', ');
return `${i}{${fields}}`;
default:
return `${i}${ast.kind}`
}
}

@ -17,6 +17,7 @@ export type Token =
// Symbols // Symbols
| { kind: 'equals' } | { kind: 'equals' }
| { kind: 'colon' } | { kind: 'colon' }
| { kind: 'semicolon' }
| { kind: 'backslash' } | { kind: 'backslash' }
| { kind: 'pipe' } | { kind: 'pipe' }
| { kind: 'greater-than' } | { kind: 'greater-than' }
@ -141,6 +142,7 @@ export function tokenize(source: string): Token[] {
// Symbols // Symbols
case '=': tokens.push({ kind: 'equals' }); break; case '=': tokens.push({ kind: 'equals' }); break;
case ':': tokens.push({ kind: 'colon' }); break; case ':': tokens.push({ kind: 'colon' }); break;
case ';': tokens.push({ kind: 'semicolon' }); break;
case '\\': tokens.push({ kind: 'backslash' }); break; case '\\': tokens.push({ kind: 'backslash' }); break;
case '|': tokens.push({ kind: 'pipe' }); break; case '|': tokens.push({ kind: 'pipe' }); break;
// case '<': tokens.push({ kind: 'less-than' }); break; // case '<': tokens.push({ kind: 'less-than' }); break;

@ -3,6 +3,7 @@
// import type { Env } from './env' // import type { Env } from './env'
import { tokenize } from './lexer' import { tokenize } from './lexer'
import { Parser } from './parser' import { Parser } from './parser'
import { prettyPrint } from './ast';
// const env: Env = new Map(); // const env: Env = new Map();
@ -24,10 +25,11 @@ console.log(tokenize(str));
// const tokens2 = tokenize("let x = (y) => 5 + y in x(3)"); // const tokens2 = tokenize("let x = (y) => 5 + y in x(3)");
// const tokens2 = tokenize("let x = 5 in x * 4"); // const tokens2 = tokenize("let x = 5 in x * 4");
// const tokens2 = tokenize("(x, y) => x + y"); // const tokens2 = tokenize("(x, y) => x + y");
const tokens2 = tokenize('map double [1, 2, 3]'); const tokens2 = tokenize('x = (y = 1; y + 1); x * 2');
const p2 = new Parser(tokens2); const p2 = new Parser(tokens2);
const ast3 = p2.parse(); const ast3 = p2.parse();
console.log(ast3); console.log(ast3);
console.log(prettyPrint(ast3));
// const env3: Env = new Map(); // const env3: Env = new Map();
// console.log(ast3); // console.log(ast3);
// console.log(evaluate(ast3, env3)); // console.log(evaluate(ast3, env3));

@ -22,6 +22,14 @@ export class Parser {
return this.tokens[this.pos++]; return this.tokens[this.pos++];
} }
private peek(offset = 1): Token {
const pos = this.pos + offset;
if (pos >= this.tokens.length) {
return { kind: 'eof' } as Token;
}
return this.tokens[pos];
}
private expect(kind: Token['kind']): Token { private expect(kind: Token['kind']): Token {
const token = this.current(); const token = this.current();
@ -65,9 +73,24 @@ export class Parser {
} }
private parseExpression(): AST { private parseExpression(): AST {
if (this.current().kind === 'ident' && this.peek().kind === 'equals') {
return this.parseLet();
}
return this.parseInfix(); return this.parseInfix();
} }
private parseLet(): AST {
const nameToken = this.expect('ident');
const name = (nameToken as { value: string }).value;
this.expect('equals');
const value = this.parseInfix();
this.expect('semicolon');
const body = this.parseExpression();
return { kind: 'let', name, value, body };
}
private parseInfix(): AST { private parseInfix(): AST {
let left = this.parseApplication(); let left = this.parseApplication();
@ -177,47 +200,6 @@ export class Parser {
} }
/* /*
private parseMultiplicative(): AST {
let left = this.parsePostfix();
while (this.current().kind === 'star' || this.current().kind === 'slash') {
const op = this.current().kind === 'star' ? '*' : '/';
this.advance();
const right = this.parsePostfix();
left = { kind: 'binaryop', operator: op, left, right };
}
return left;
}
private parseAdditive(): AST {
let left = this.parseMultiplicative();
while (this.current().kind === 'plus' || this.current().kind === 'minus') {
const op = this.current().kind === 'plus' ? '+' : '-';
this.advance();
const right = this.parseMultiplicative();
left = { kind: 'binaryop', operator: op, left, right };
}
return left;
}
private parseLet(): AST {
this.expect('let');
const nameToken = this.expect('ident');
const name = (nameToken as { value: string }).value;
this.expect('equals');
const value = this.parseExpression();
this.expect('in');
const body = this.parseExpression();
return { kind: 'let', name, value, body };
}
private parseLambda(): AST { private parseLambda(): AST {
this.expect('open-paren'); this.expect('open-paren');

Loading…
Cancel
Save