diff --git a/src/ast.ts b/src/ast.ts index 0031515..376c587 100644 --- a/src/ast.ts +++ b/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}` + } +} diff --git a/src/lexer.ts b/src/lexer.ts index 73261b0..f8fe039 100644 --- a/src/lexer.ts +++ b/src/lexer.ts @@ -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; diff --git a/src/main.ts b/src/main.ts index 66d3ac1..91f2902 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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)); diff --git a/src/parser.ts b/src/parser.ts index bc955ef..dd843c1 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -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');