diff options
Diffstat (limited to 'pc-thing/the_e_programming_language/ast.ts')
-rw-r--r-- | pc-thing/the_e_programming_language/ast.ts | 357 |
1 files changed, 0 insertions, 357 deletions
diff --git a/pc-thing/the_e_programming_language/ast.ts b/pc-thing/the_e_programming_language/ast.ts deleted file mode 100644 index 80210d8..0000000 --- a/pc-thing/the_e_programming_language/ast.ts +++ /dev/null @@ -1,357 +0,0 @@ -import { Token, TokenType } from "./tokenizer.ts"; - -export interface ASTNode { - type: string; -} - -export interface VariableDeclarationNode extends ASTNode { - type: "VariableDeclaration"; - identifier: string; - value: ASTNode; - vtype: string; - length: number; -} - -export interface FunctionDeclarationNode extends ASTNode { - type: "FunctionDeclaration"; - name: string; - // params: string[]; - body: ASTNode[]; -} - -export interface AssignmentNode extends ASTNode { - type: "Assignment"; - identifier: IdentifierNode; - value: ASTNode; -} - -export interface BinaryExpressionNode extends ASTNode { - type: "BinaryExpression"; - operator: string; - left: ASTNode; - right: ASTNode; -} - -export interface LiteralNode extends ASTNode { - type: "Literal"; - value: string; -} - -export interface NumberNode extends ASTNode { - type: "Number"; - value: number; -} - -export interface IdentifierNode extends ASTNode { - type: "Identifier"; - name: string; - offset?: ASTNode; -} - -export interface FunctionCallNode extends ASTNode { - type: "FunctionCall"; - identifier: string; - args: ASTNode[]; -} - -// export interface BranchFunctionCallNode extends ASTNode { -// type: "BranchFunctionCall"; -// identifier: string; -// args: ASTNode[]; -// branches: ASTNode[][]; -// } - -// export interface StartBlockNode extends ASTNode { -// type: "StartBlock"; -// body: ASTNode[]; -// } - -export interface IfNode extends ASTNode { - type: "If"; - condition: ASTNode; - thenBranch: ASTNode[]; - elseBranch?: ASTNode[]; -} - -export interface WhileNode extends ASTNode { - type: "While"; - condition: ASTNode; - branch: ASTNode[]; -} - -// export interface ForNode extends ASTNode { -// type: "For"; -// times: ASTNode; -// varname: ASTNode; -// branch: ASTNode[]; -// } - -// export interface GreenFlagNode extends ASTNode { -// type: "GreenFlag"; -// branch: ASTNode[]; -// } - -// use 1 or 0 for boolean -// export interface BooleanNode extends ASTNode { -// type: "Boolean"; -// value: boolean; -// } - -// export interface IncludeNode extends ASTNode { -// type: "Include"; -// itype: string; -// path: string; -// } - -// export interface ListDeclarationNode extends ASTNode { -// type: "ListDeclaration"; -// identifier: string; -// value: ASTNode[]; -// vtype: 'list' | 'global' -// } - -export default class AST { - private tokens: Token[]; - position: number = 0; - - constructor(tokens: Token[]) { - this.tokens = tokens; - } - - private peek(ahead = 0): Token { - return this.tokens[this.position + ahead]; - } - - private advance(): Token { - return this.tokens[this.position++]; - } - - private match(...types: TokenType[]): boolean { - if (types.includes(this.peek().type)) { - this.advance(); - return true; - } - return false; - } - - private matchTk(types: TokenType[], token = this.peek()): boolean { - if (types.includes(token.type)) { - return true; - } - return false; - } - - private expect(type: TokenType, errorMessage: string): Token { - if (this.peek().type === type) { - return this.advance(); - } - console.error('trace: tokens', this.tokens, '\nIDX:', this.position); - throw new Error(errorMessage); - } - - parse(): ASTNode[] { - const nodes: ASTNode[] = []; - while (this.peek().type !== TokenType.EOF) { - nodes.push(this.parseStatement()); - } - return nodes; - } - - private parseStatement(): ASTNode { - if (this.matchTk([TokenType.TYPE])) { - const type = this.advance().value - let len = 1; - if (this.match(TokenType.LBRACKET)) { - len = Number(this.expect(TokenType.NUMBER, 'expected number after [').value); - this.expect(TokenType.RBRACKET, 'expected ] after length') - } - const identifier = this.expect(TokenType.IDENTIFIER, "expected var name after type (hint: functions dont have return types yet").value; - this.expect(TokenType.ASSIGN, "expected = after var name"); - const value = this.parseAssignment(false); - return { type: "VariableDeclaration", identifier, value, vtype: type, length: len } as VariableDeclarationNode; - } - - if (this.match(TokenType.FN_DECL)) { - const name = this.expect(TokenType.IDENTIFIER, "expected function name after fn").value; - // this.expect(TokenType.LPAREN, "Expected '(' after function name"); - // const params: string[] = []; - // if (!this.match(TokenType.RPAREN)) { - // do { - // params.push(this.expect(TokenType.IDENTIFIER, "Expected parameter name").value); - // } while (this.match(TokenType.COMMA)); - // this.expect(TokenType.RPAREN, "Expected ')' after parameters"); - // } - this.expect(TokenType.LBRACE, "expected '{' before function body"); - const body = this.parseBlock(); - return { type: "FunctionDeclaration", name, body } as FunctionDeclarationNode; - } - - if (this.match(TokenType.IF)) { - this.expect(TokenType.LPAREN, "Expected '(' after 'if'"); - const condition = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after if condition"); - this.expect(TokenType.LBRACE, "Expected '{' after if condition"); - const thenBranch = this.parseBlock(); - let elseBranch: ASTNode[] | undefined; - if (this.match(TokenType.ELSE)) { - this.expect(TokenType.LBRACE, "Expected '{' after 'else'"); - elseBranch = this.parseBlock(); - } - return { type: "If", condition, thenBranch, elseBranch } as IfNode; - } - - if (this.match(TokenType.WHILE)) { - this.expect(TokenType.LPAREN, "Expected '(' after 'while'"); - const condition = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after while condition"); - this.expect(TokenType.LBRACE, "Expected '{' after while condition"); - const branch = this.parseBlock(); - return { type: "While", condition, branch } as WhileNode; - } - - // if (this.match(TokenType.FOR)) { - // this.expect(TokenType.LPAREN, "Expected '(' after 'for'"); - // const varname = this.parseAssignment(); - // const of = this.expect(TokenType.IDENTIFIER, 'expected of'); - // if (of.value !== 'of') throw new Error('expected of'); - // const times = this.parseAssignment(); - // this.expect(TokenType.RPAREN, "Expected ')' after for"); - // this.expect(TokenType.LBRACE, "Expected '{' after for"); - // const branch = this.parseBlock(); - - // return { type: "For", varname, times, branch } as ForNode; - // } - - // if (this.match(TokenType.GREENFLAG)) { - // this.expect(TokenType.LBRACE, "Expected '{' after greenflag"); - // const branch = this.parseBlock(); - - // return { type: "GreenFlag", branch } as GreenFlagNode; - // } - - return this.parseAssignment(); - } - - private parseBlock(): ASTNode[] { - const nodes: ASTNode[] = []; - - while (!this.match(TokenType.RBRACE)) { - nodes.push(this.parseStatement()); - } - - return nodes; - } - - private parseAssignment(allowStuff = true): ASTNode { - - const expr = this.parseBinaryExpression(allowStuff); - if (this.match(TokenType.ASSIGN)) { - if (expr.type !== "Identifier") - throw new Error("invalid assignment target; expected an identifier"); - const value = allowStuff ? this.parseAssignment() : this.parsePrimary(false); - // let offset = undefined; - // if (this.match(TokenType.LBRACKET)) { - // offset = this.parseAssignment(); - // this.expect(TokenType.RBRACKET, 'expected ]') - // } - return { type: "Assignment", identifier: (expr as IdentifierNode), value } as AssignmentNode; - } - return expr; - } - - private parseBinaryExpression(allowStuff = false): ASTNode { - let left = this.parseCall(allowStuff); - - while (this.peek().type === TokenType.BINOP) { - const operator = this.advance().value; - const right = this.parseCall(); - left = { type: "BinaryExpression", operator, left, right } as BinaryExpressionNode; - } - return left; - } - - private parseCall(allowStuff = false): ASTNode { - let expr = this.parsePrimary(allowStuff); - - while (this.peek().type === TokenType.LPAREN) { - expr = this.finishCall(expr); - } - return expr; - } - - private finishCall(callee: ASTNode): ASTNode { - this.expect(TokenType.LPAREN, "Expected '(' after function name"); - //TODO - arguments - // const args: ASTNode[] = []; - // if (this.peek().type !== TokenType.RPAREN) { - // do { - // args.push(this.parseAssignment()); - // } while (this.match(TokenType.COMMA)); - // } - this.expect(TokenType.RPAREN, "Expected ')' after arguments"); - - - // if (this.peek().type === TokenType.LBRACE) { - // const branches: ASTNode[][] = []; - // do { - // this.expect(TokenType.LBRACE, "Expected '{' for branch block"); - // branches.push(this.parseBlock()); - // } while (this.peek().type === TokenType.LBRACE); - - // if (callee.type !== "Identifier") - // throw new Error("Branch function call expects an identifier"); - // return { - // type: "BranchFunctionCall", - // identifier: (callee as IdentifierNode).name, - // args, - // branches, - // } as BranchFunctionCallNode; - // } - - - if (callee.type !== "Identifier") - throw new Error("Function call expects an identifier"); - return { - type: "FunctionCall", - identifier: (callee as IdentifierNode).name, - // args, - } as FunctionCallNode; - } - - private parsePrimary(allowOther = true): ASTNode { - const token = this.peek(); - - if (this.match(TokenType.NUMBER)) { - return { type: "Number", value: Number(token.value) } as NumberNode; - } - - if (this.match(TokenType.LITERAL)) { - return { type: "Literal", value: token.value } as LiteralNode; - } - - if (this.match(TokenType.IDENTIFIER) && allowOther) { - - // if (["True", "true", "False", "false"].includes(token.value)) { - // return { - // type: "Boolean", - // value: token.value === "True" || token.value === "true" - // } as BooleanNode; - // } - let offset = undefined; - if (this.match(TokenType.LBRACKET)) { - offset = this.parseAssignment(); - this.expect(TokenType.RBRACKET, 'expected ]') - } - return { type: "Identifier", name: token.value, offset } as IdentifierNode; - } - - if (this.match(TokenType.LPAREN) && allowOther) { - const expr = this.parseAssignment(); - this.expect(TokenType.RPAREN, "Expected ')' after expression"); - return expr; - } - - throw new Error(`Unexpected token: ${token.type}`); - } - -} \ No newline at end of file |