Parsing bindings. adding AST pretty printer
This commit is contained in:
parent
1fc116f2fe
commit
0f0371461d
4 changed files with 64 additions and 42 deletions
36
src/ast.ts
36
src/ast.ts
|
|
@ -122,3 +122,39 @@ export type AST =
|
|||
| Definition
|
||||
| TypeDef
|
||||
| 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
|
||||
| { kind: 'equals' }
|
||||
| { kind: 'colon' }
|
||||
| { kind: 'semicolon' }
|
||||
| { kind: 'backslash' }
|
||||
| { kind: 'pipe' }
|
||||
| { kind: 'greater-than' }
|
||||
|
|
@ -141,6 +142,7 @@ export function tokenize(source: string): Token[] {
|
|||
// Symbols
|
||||
case '=': tokens.push({ kind: 'equals' }); break;
|
||||
case ':': tokens.push({ kind: 'colon' }); break;
|
||||
case ';': tokens.push({ kind: 'semicolon' }); break;
|
||||
case '\\': tokens.push({ kind: 'backslash' }); break;
|
||||
case '|': tokens.push({ kind: 'pipe' }); break;
|
||||
// case '<': tokens.push({ kind: 'less-than' }); break;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
// import type { Env } from './env'
|
||||
import { tokenize } from './lexer'
|
||||
import { Parser } from './parser'
|
||||
import { prettyPrint } from './ast';
|
||||
|
||||
// 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 = 5 in x * 4");
|
||||
// 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 ast3 = p2.parse();
|
||||
console.log(ast3);
|
||||
console.log(prettyPrint(ast3));
|
||||
// const env3: Env = new Map();
|
||||
// console.log(ast3);
|
||||
// console.log(evaluate(ast3, env3));
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ export class Parser {
|
|||
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 {
|
||||
const token = this.current();
|
||||
|
||||
|
|
@ -65,9 +73,24 @@ export class Parser {
|
|||
}
|
||||
|
||||
private parseExpression(): AST {
|
||||
if (this.current().kind === 'ident' && this.peek().kind === 'equals') {
|
||||
return this.parseLet();
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
this.expect('open-paren');
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue