more CG syntax. parsing the easy stuff

master
Dustin Swan 6 days ago
parent 71237d0307
commit 1ed325e98b
Signed by: dustinswan
GPG Key ID: 30D46587E2100467

@ -1,35 +1,24 @@
import type { Value } from './types';
// Literals and Variables
export type Literal = {
kind: 'literal'
value: Value
}
export type BinaryOp = {
kind: 'binaryop'
operator: string
left: AST
right: AST
}
export type Variable = {
kind: 'variable'
name: string
}
export type Let = {
kind: 'let'
export type Constructor = {
kind: 'constructor'
name: string
value: AST
body: AST
}
export type If = {
kind: 'if'
condition: AST
then: AST
else: AST
}
// Functions
export type Lambda = {
kind: 'lambda'
@ -43,4 +32,93 @@ export type Apply = {
args: AST[]
}
export type AST = Literal | BinaryOp | Variable | Let | If | Lambda | Apply
// Bindings
export type Let = {
kind: 'let'
name: string
value: AST
body: AST
}
// Matching
export type Match = {
kind: 'match'
expr: AST
cases: MatchCase[]
}
export type MatchCase = {
pattern: Pattern
result: AST
}
export type Pattern =
| { kind: 'wildcard' }
| { kind: 'var', name: string }
| { kind: 'literal', value: number | string }
| { kind: 'constructor', name: string, args: Pattern[] }
| { kind: 'list', elements: Pattern[] }
| { kind: 'record', fields: { [key: string]: Pattern } }
// Data Structures
export type Record = {
kind: 'record'
fields: { [key: string]: AST }
}
export type RecordAccess = {
kind: 'record-access'
record: AST
field: string
}
export type RecordUpdate = {
kind: 'record-update'
record: AST
updates: { [key: string]: AST }
}
export type List = {
kind: 'list'
elements: AST[]
}
// Top-level constructs
export type Definition = {
kind: 'definition'
name: string
body: AST
// type?: string // TODO
}
export type TypeDef = {
kind: 'typedef'
name: string
variants: Array<{ name: string, args: string[] }>
}
export type Import = {
kind: 'import'
module: string
items: string[] | 'all'
}
export type AST =
| Literal
| Variable
| Constructor
| Lambda
| Apply
| Let
| Match
| Record
| RecordAccess
| RecordUpdate
| List
| Definition
| TypeDef
| Import

@ -1,31 +1,10 @@
import { evaluate } from './interpreter'
import type { AST } from './ast'
import type { Env } from './env'
// import { evaluate } from './interpreter'
// import type { AST } from './ast'
// import type { Env } from './env'
import { tokenize } from './lexer'
import { Parser } from './parser'
const ast: AST = {
kind: 'binaryop',
operator: '+',
left: {
kind: 'literal',
value: { kind: 'int', value: 1 }
},
right: {
kind: 'binaryop',
operator: '*',
left: {
kind: 'literal',
value: { kind: 'int', value: 2 }
},
right: {
kind: 'literal',
value: { kind: 'int', value: 3 }
}
}
};
const env: Env = new Map();
// const env: Env = new Map();
// const res = evaluate(ast, env);
@ -42,13 +21,13 @@ console.log(tokenize(str));
// const p = new Parser(tokens);
// console.log(p.parse());
/*
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("(x, y) => x + y");
const tokens2 = tokenize('[1, 2, 3]');
const p2 = new Parser(tokens2);
const ast3 = p2.parse();
const env3: Env = new Map();
console.log(ast3);
console.log(evaluate(ast3, env3));
*/
// const env3: Env = new Map();
// console.log(ast3);
// console.log(evaluate(ast3, env3));

@ -14,6 +14,7 @@ export class Parser {
if (this.pos >= this.tokens.length) {
return { kind: 'eof' } as Token;
}
return this.tokens[this.pos];
}
@ -21,63 +22,134 @@ export class Parser {
return this.tokens[this.pos++];
}
private expect(kind: Token['kind']): Token {
const token = this.current();
if (token.kind !== kind) {
throw new Error(`Expected ${kind}, got ${token.kind}`);
}
return this.advance();
}
private isInfixOp(): boolean {
const kind = this.current().kind;
return kind === 'plus' || kind === 'minus' ||
kind === 'star' || kind === 'slash' ||
kind === 'greater-than' || kind === 'ampersand';
}
private tokenToOpName(token: Token): string {
switch (token.kind) {
case 'plus': return '+';
case 'minus': return '-';
case 'star': return '*';
case 'slash': return '/';
case 'greater-than': return '>';
case 'ampersand': return '&';
default: throw new Error(`Not an operator: ${token.kind}`);
}
}
parse(): AST {
return this.parseExpression();
}
private parseExpression(): AST {
if (this.current().kind === 'let') {
return this.parseLet();
let left = this.parsePrimary();
while (this.isInfixOp()) {
const opToken = this.advance();
const opName = this.tokenToOpName(opToken);
const right = this.parsePrimary();
left = {
kind: 'apply',
func: { kind: 'variable', name: opName },
args: [left, right]
}
}
return this.parseAdditive();
return left;
}
private parsePrimary(): AST {
const token = this.current();
if (token.kind === 'open-paren') {
const savedPos = this.pos;
this.advance();
const expr = this.parseExpression();
this.expect('close-paren');
return expr;
}
let isLambda = false;
if (this.current().kind === 'close-paren') {
isLambda = true;
} else if (this.current().kind === 'ident') {
let tempPos = this.pos;
if (token.kind === 'open-bracket') {
this.advance();
if (this.current().kind === 'comma' || (this.current().kind === 'close-paren' && this.tokens[this.pos + 1]?.kind === 'arrow')) {
isLambda = true;
const items: AST[] = [];
let first = true;
while (this.current().kind !== 'close-bracket') {
if (!first) {
this.expect('comma');
}
this.pos = tempPos;
first = false;
items.push(this.parseExpression());
}
this.expect('close-bracket');
return { kind: 'list', elements: items };
}
if (token.kind === 'open-brace') {
this.advance();
const fields: { [key: string]: AST } = {};
let first = true;
while (this.current().kind !== 'close-brace') {
if (!first) {
this.expect('comma');
}
first = false;
this.pos = savedPos;
const keyToken = this.expect('ident');
const key = (keyToken as { value: string }).value;
this.expect('equals');
fields[key] = this.parseExpression();
}
if (isLambda) {
return this.parseLambda();
this.expect('close-brace');
return { kind: 'record', fields };
}
if (token.kind === 'int') {
this.advance();
const expr = this.parseExpression();
this.expect('close-paren');
return expr;
return { kind: 'literal', value: { kind: 'int', value: token.value } };
}
if (token.kind === 'number') {
if (token.kind === 'float') {
this.advance();
return { kind: 'literal', value: { kind: 'float', value: token.value } };
}
return { kind: 'literal', value: { kind: 'int', value: token.value } };
if (token.kind === 'string') {
this.advance();
return { kind: 'literal', value: { kind: 'string', value: token.value } };
}
if (token.kind === 'ident') {
this.advance();
return { kind: 'variable', name: token.value };
}
throw new Error(`Unexpected token: ${token.kind}`);
}
/*
private parseMultiplicative(): AST {
let left = this.parsePostfix();
@ -165,13 +237,6 @@ export class Parser {
return expr;
}
*/
private expect(kind: Token['kind']): Token {
const token = this.current();
if (token.kind !== kind) {
throw new Error(`Expected ${kind}, got ${token.kind}`);
}
return this.advance();
}
}

Loading…
Cancel
Save