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
|
| 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…
Add table
Add a link
Reference in a new issue