UNPKG

cookeylang-ts

Version:
473 lines (472 loc) 18.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Parser = void 0; const base_1 = require("./expr/base"); const Stmt = __importStar(require("./expr/stmt")); const Expr = __importStar(require("./expr/expr")); const token_1 = require("./token"); class Parser { constructor(tokens, file) { this.i = 0; this.tokens = []; this.hasError = false; this.file = file; this.tokens = tokens; } init() { let stmts = []; while (this.isValid()) { stmts.push(this.decl()); } return stmts; } decl() { if (this.match(token_1.TType.CLASS)) return this.classDecl(); if (this.match(token_1.TType.FUNCTION)) return this.funcDecl("function"); if (this.match(token_1.TType.VAR, token_1.TType.FINAL)) return this.varDecl(); return this.stmt(); } classDecl() { let name = this.consume(token_1.TType.IDENTIFIER, "Expected class name after 'class' keyword."); this.consume(token_1.TType.LEFT_BRACE, "Expected '{' after class name."); let methods = []; while (this.peek().type != token_1.TType.RIGHT_BRACE && this.isValid()) { methods.push(this.funcDecl("method")); } this.consume(token_1.TType.RIGHT_BRACE, "Expected '}' after class body."); return new Stmt.ClassDecl(name, methods); } funcDecl(type) { let name = this.consume(token_1.TType.IDENTIFIER, `Expected ${type} name.`); this.consume(token_1.TType.LEFT_PAREN, `Expected '(' after ${type} name.`); let params = []; if (this.peek().type != token_1.TType.RIGHT_PAREN) { do { params.push(this.consume(token_1.TType.IDENTIFIER, "Expected parameter name.")); } while (this.match(token_1.TType.COMMA)); } this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after parameters."); let body; if (this.match(token_1.TType.LEFT_BRACE)) body = this.block(); else body = [this.stmt()]; return new Stmt.FuncDecl(name, params, body); } varDecl() { let mut = this.previous(); let name = this.consume(token_1.TType.IDENTIFIER, "Expected a variable name."); let value = null; if (this.match(token_1.TType.EQ)) value = this.expression(); this.consume(token_1.TType.SEMI, "Expected a ';' after variable declaration."); if (value == null && mut.type == token_1.TType.FINAL) this.error("Constants require a value"); return new Stmt.VarDecl(mut, name, value); } stmt() { if (this.match(token_1.TType.IF)) return this.ifStmt(); if (this.match(token_1.TType.WHILE)) return this.whileStmt(); if (this.match(token_1.TType.DO)) return this.doWhileStmt(); if (this.match(token_1.TType.FOR)) return this.forStmt(); if (this.match(token_1.TType.EXIT)) return this.exitStmt(); if (this.match(token_1.TType.RET)) return this.retStmt(); if (this.match(token_1.TType.LEFT_BRACE)) return new Stmt.Block(this.block()); return this.exprStmt(); } exprStmt() { let expr = this.expression(); this.consume(token_1.TType.SEMI, "Expected ';' after expression"); return new Stmt.ExprStmt(expr); } ifStmt() { this.consume(token_1.TType.LEFT_PAREN, "Expected '(' after if keyword."); let condition = this.expression(); this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after if condition."); let thenBr = this.stmt(); let elseBr = null; if (this.match(token_1.TType.EL)) elseBr = this.stmt(); return new Stmt.IfStmt(condition, thenBr, elseBr); } whileStmt() { this.consume(token_1.TType.LEFT_PAREN, "Expected '(' after while keyword."); let condition = this.expression(); this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after condition."); let body = this.stmt(); return new Stmt.WhileStmt(condition, body); } doWhileStmt() { let body = this.stmt(); this.consume(token_1.TType.WHILE, "Expected 'while' after statement."); this.consume(token_1.TType.LEFT_PAREN, "Expected '(' after 'while' keyword."); let condition = this.expression(); this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after expression."); this.consume(token_1.TType.SEMI, "Expected ';' after ')'"); return new Stmt.Block([body, new Stmt.WhileStmt(condition, body)]); } forStmt() { this.consume(token_1.TType.LEFT_PAREN, "Expected '(' after 'for' keyword."); let initializer; if (this.match(token_1.TType.SEMI)) initializer = null; else if (this.match(token_1.TType.VAR)) initializer = this.varDecl(); else initializer = this.exprStmt(); let condition = null; if (!this.match(token_1.TType.SEMI)) condition = this.expression(); this.consume(token_1.TType.SEMI, "Expected ';' after for loop condition."); let increment = null; if (!this.match(token_1.TType.SEMI)) increment = this.expression(); this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after for loop clauses."); let body = this.stmt(); if (increment) body = new Stmt.Block([body, new Stmt.ExprStmt(increment)]); if (condition == null) condition = new Expr.Literal(body.lineData, true); body = new Stmt.WhileStmt(condition, body); if (initializer) body = new Stmt.Block([initializer, body]); return body; } exitStmt() { let num = new Expr.Literal(this.previous(), 0); if (!this.match(token_1.TType.SEMI)) num = this.expression(); this.consume(token_1.TType.SEMI, "Expected ';' after exit."); return new Stmt.ExitStmt(num); } retStmt() { let ret = this.previous(); let value = new Expr.Literal(ret, null); if (this.peek().type != token_1.TType.SEMI) { value = this.expression(); } this.consume(token_1.TType.SEMI, "Expected ';' after ret statement."); return new Stmt.RetStmt(ret, value); } block() { let stmts = []; while (this.isValid() && this.peek().type != token_1.TType.RIGHT_BRACE) { stmts.push(this.decl()); } this.consume(token_1.TType.RIGHT_BRACE, "Expected '}' after block."); return stmts; } expression() { return this.lambda(); } lambda() { if (this.match(token_1.TType.LAMBDA)) { let lineData = this.previous(); let params = []; if (this.match(token_1.TType.LEFT_PAREN)) { let params = []; if (this.peek().type != token_1.TType.RIGHT_PAREN) { do { params.push(this.consume(token_1.TType.IDENTIFIER, "Expected parameter name.")); } while (this.match(token_1.TType.COMMA)); } this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after lamda parameters."); this.consume(token_1.TType.COL, "Expected ':' after parameters."); } else this.consume(token_1.TType.COL, "Expected ':' after lambda keyword."); let body; if (this.match(token_1.TType.LEFT_BRACE)) body = this.block(); else body = [new Stmt.RetStmt(lineData, this.expression())]; return new Expr.Lambda(lineData, params, body); } return this.ternary(); } ternary() { let expr = this.assignment(); if (this.match(token_1.TType.QUE)) { let thenBr = this.expression(); this.consume(token_1.TType.COL, "Expected ':' after ternary branch."); let elseBr = this.ternary(); expr = new Stmt.IfStmt(expr, thenBr, elseBr); } return expr; } assignment() { let expr = this.logicOr(); if (this.match(token_1.TType.EQ)) { let value = this.assignment(); if (expr instanceof Expr.Variable) { let name = expr.name; return new Expr.Assign(name, value); } this.error("Invalid assignment target."); } if (this.match(token_1.TType.PLUS_EQ, token_1.TType.MINUS_EQ, token_1.TType.TIMES_EQ, token_1.TType.DIVIDE_EQ, token_1.TType.POWER_EQ, token_1.TType.MODULO_EQ)) { let op = this.previous(); switch (op.type) { case token_1.TType.PLUS_EQ: op.type = token_1.TType.PLUS; break; case token_1.TType.MINUS_EQ: op.type = token_1.TType.MINUS; break; case token_1.TType.TIMES_EQ: op.type = token_1.TType.TIMES; break; case token_1.TType.DIVIDE_EQ: op.type = token_1.TType.DIVIDE; break; case token_1.TType.POWER_EQ: op.type = token_1.TType.POWER; break; case token_1.TType.MODULO_EQ: op.type = token_1.TType.MODULO; break; } let value = this.assignment(); return this.desugarAssign(op, expr, op.type, value); } if (this.match(token_1.TType.PLUS_PLUS, token_1.TType.MINUS_MINUS)) { let op = this.previous(); switch (op.type) { case token_1.TType.PLUS_PLUS: op.type = token_1.TType.PLUS; break; case token_1.TType.MINUS_MINUS: op.type = token_1.TType.MINUS; break; } return this.desugarAssign(op, expr, op.type, 1); } return expr; } logicOr() { let expr = this.logicAnd(); while (this.match(token_1.TType.OR)) { let op = this.previous(); let right = this.logicAnd(); expr = new Expr.Logic(expr, op, right); } return expr; } logicAnd() { let expr = this.equality(); while (this.match(token_1.TType.AND)) { let op = this.previous(); let right = this.equality(); expr = new Expr.Logic(expr, op, right); } return expr; } equality() { let expr = this.comparison(); while (this.match(token_1.TType.EQ_EQ, token_1.TType.BANG_EQ)) { let op = this.previous(); let right = this.comparison(); expr = new Expr.Binary(expr, op, right); } return expr; } comparison() { let expr = this.addition(); while (this.match(token_1.TType.GREATER, token_1.TType.GREATER_EQ, token_1.TType.LESS, token_1.TType.LESS_EQ)) { let op = this.previous(); let right = this.addition(); expr = new Expr.Binary(expr, op, right); } return expr; } addition() { let expr = this.multiplication(); while (this.match(token_1.TType.PLUS, token_1.TType.MINUS)) { let op = this.previous(); let right = this.multiplication(); expr = new Expr.Binary(expr, op, right); } return expr; } multiplication() { let expr = this.power(); while (this.match(token_1.TType.DIVIDE, token_1.TType.TIMES, token_1.TType.MODULO)) { let op = this.previous(); let right = this.power(); expr = new Expr.Binary(expr, op, right); } return expr; } power() { let expr = this.unary(); while (this.match(token_1.TType.POWER)) { let op = this.previous(); let right = this.unary(); expr = new Expr.Binary(expr, op, right); } return expr; } unary() { if (this.match(token_1.TType.BANG_EQ, token_1.TType.PLUS, token_1.TType.MINUS)) { let op = this.previous(); let right = this.unary(); return new Expr.Unary(op, right); } if (this.match(token_1.TType.PLUS_PLUS, token_1.TType.MINUS_MINUS)) { let lineData = this.previous(); let right = this.unary(); let op = lineData.type; switch (op) { case token_1.TType.PLUS_PLUS: op = token_1.TType.PLUS; break; case token_1.TType.MINUS_MINUS: op = token_1.TType.MINUS; break; } return this.desugarAssign(lineData, right, op, 1); } return this.call(); } call() { let expr = this.primary(); while (true) { if (this.match(token_1.TType.LEFT_PAREN)) { expr = this.finishCall(expr); } else { break; } } return expr; } finishCall(callee) { let args = []; if (this.peek().type != token_1.TType.RIGHT_PAREN) { do { args.push(this.expression()); } while (this.match(token_1.TType.COMMA)); } let paren = this.consume(token_1.TType.RIGHT_PAREN, "Expected ')' after arguments."); return new Expr.Call(callee, paren, args); } primary() { if (this.match(token_1.TType.FALSE)) return new Expr.Literal(this.previous(), false); if (this.match(token_1.TType.TRUE)) return new Expr.Literal(this.previous(), true); if (this.match(token_1.TType.NAV)) return new Expr.Literal(this.previous(), null); if (this.match(token_1.TType.NUMBER, token_1.TType.STRING)) { return new Expr.Literal(this.previous(), this.previous().value); } if (this.match(token_1.TType.LEFT_PAREN)) { let expr = this.expression(); this.consume(token_1.TType.RIGHT_PAREN, "Expected a ')' after expression."); return new Expr.Grouping(expr); } if (this.match(token_1.TType.IDENTIFIER)) return new Expr.Variable(this.previous()); this.error(`Unexpected token ${token_1.TType[this.peek().type]}.`); return new base_1.Base(token_1.defualtToken); } isValid() { return this.i < this.tokens.length && this.peek().type != token_1.TType.END; } match(...tokens) { for (const token of tokens) { if (this.isValid() && this.peek().type == token) { this.advance(); return true; } } return false; } advance() { if (this.isValid()) this.i++; return this.previous(); } previous() { return this.tokens[this.i - 1]; } peek() { return this.tokens[this.i]; } consume(type, message) { if (this.match(type)) return this.previous(); this.error(message); } error(message) { this.hasError = true; console.log(`<${this.file}> [ ${this.tokens[this.i].line} : ${this.tokens[this.i].col} ] ${message}`); this.synchronize(); } synchronize() { this.advance(); while (this.isValid()) { if (this.previous().type == token_1.TType.SEMI) return; switch (this.peek().type) { case token_1.TType.CLASS: case token_1.TType.FUNCTION: case token_1.TType.VAR: case token_1.TType.FINAL: case token_1.TType.DELETEVARIABLE: case token_1.TType.FOR: case token_1.TType.WHILE: case token_1.TType.FOREACH: case token_1.TType.FORREP: case token_1.TType.SWITCH: case token_1.TType.IF: case token_1.TType.RET: case token_1.TType.EXIT: case token_1.TType.BREAK: return; } this.advance(); } } desugarAssign(lineData, name, type, value) { let op = new token_1.Token(lineData.line, lineData.col, lineData.file, type, ""); let val; if (!(value instanceof Expr.Literal)) val = new Expr.Literal(lineData, value); else val = value; if (name instanceof Expr.Variable) return new Expr.Assign(name.name, new Expr.Binary(name, op, val)); this.error("Invalid assignment target."); } } exports.Parser = Parser;