UNPKG

@tsukiroku/tiny

Version:
654 lines (653 loc) 24.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (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 }); const Tiny = __importStar(require("../../index")); class Parser { lexer; option; currToken; peekToken; currLine = 1; currColumn = 1; messages; errors = []; constructor(lexer, option) { this.lexer = lexer; this.option = option; this.messages = Tiny.localization(option); this.nextToken(); this.nextToken(); } parseProgram() { let program = { statements: [], errors: [], }; while (this.currToken.type !== Tiny.TokenType.EOF) { const statement = this.parseStatement(); if (statement) program.statements.push(statement); this.nextToken(); } program.errors = this.errors; return program; } parseStatement() { switch (this.currToken.type) { case Tiny.TokenType.LET: return this.parseLetStatement(); case Tiny.TokenType.RETURN: return this.parseReturnStatement(); case Tiny.TokenType.WHILE: return this.parseWhileStatement(); case Tiny.TokenType.AT: return this.parseDecorator(); case Tiny.TokenType.COMMENT: return null; default: return this.parseExpressionStatement(); } } nextToken() { this.currToken = this.peekToken; this.peekToken = this.lexer.nextToken(); this.currLine = this.peekToken.line; this.currColumn = this.peekToken.column; } currPos() { return { line: this.currLine, column: this.currColumn, }; } expectPeek(tokenType) { if (this.peekTokenIs(tokenType)) { this.nextToken(); return true; } this.pushError(Tiny.errorFormatter(this.messages.parseError.unexpectedToken, tokenType, this.peekToken.type)); return false; } peekTokenIs(tokenType) { if (tokenType === Tiny.TokenType.IDENT) { if (this.peekToken.type === Tiny.TokenType.IDENT) return true; return false; } return this.peekToken.type === tokenType; } currTokenIs(tokenType) { return this.currToken.type === tokenType; } pushError(message) { this.errors.push({ line: this.currLine, column: this.currColumn, message, }); } parseLetStatement() { if (!this.expectPeek(Tiny.TokenType.IDENT)) return null; const ident = { debug: 'parseLetStatement>ident', value: this.currToken.type === Tiny.TokenType.IDENT ? this.currToken.literal : '', kind: Tiny.ExpressionKind.Ident, ...this.currPos(), }; if (!this.expectPeek(Tiny.TokenType.ASSIGN)) return null; this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (!this.expectPeek(Tiny.TokenType.SEMICOLON)) return null; return { debug: 'parseLetStatement>return', ident, value: expression, kind: Tiny.NodeKind.LetStatement, ...this.currPos(), }; } parseReturnStatement() { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (this.peekTokenIs(Tiny.TokenType.SEMICOLON)) this.nextToken(); return { debug: 'parseReturnStatement>return', value: expression, kind: Tiny.NodeKind.ReturnStatement, ...this.currPos(), }; } parseWhileStatement() { if (!this.expectPeek(Tiny.TokenType.LPAREN)) return null; return { debug: 'parseWhileStatement>return', condition: this.parseExpression(Tiny.Priority.LOWEST), body: this.parseBlockStatement(), kind: Tiny.NodeKind.WhileStatement, ...this.currPos(), }; } parseDecorator() { this.nextToken(); const value = this.parseExpression(Tiny.Priority.LOWEST); this.nextToken(); if (this.currTokenIs(Tiny.TokenType.SEMICOLON)) this.nextToken(); const func = this.parseExpression(Tiny.Priority.LOWEST); if (func?.kind !== Tiny.ExpressionKind.Function) { this.pushError(this.messages.parseError.decoratorRequiresFunction); return null; } return { debug: 'parseDecorator>return', value, function: func, kind: Tiny.NodeKind.DecoratorStatement, ...this.currPos(), }; } parseExpression(priority) { let left = this.parsePrefix(); if (!left) if (!this.currTokenIs(Tiny.TokenType.SEMICOLON)) this.pushError(Tiny.errorFormatter(this.messages.parseError.unexpectedExpression, this.currToken.type)); else return null; while (!this.peekTokenIs(Tiny.TokenType.SEMICOLON) && priority < this.peekPriority()) { this.nextToken(); left = this.parseInfixExpression(left); } return left; } parseExpressionStatement() { const expression = this.parseExpression(Tiny.Priority.LOWEST); if (!expression) return null; if (expression.kind !== Tiny.ExpressionKind.If && expression.kind !== Tiny.ExpressionKind.Function && !this.expectPeek(Tiny.TokenType.SEMICOLON)) return null; return { debug: 'parseExpressionStatement>return', expression, kind: Tiny.NodeKind.ExpressionStatement, ...this.currPos(), }; } parsePrefix() { switch (this.currToken.type) { case Tiny.TokenType.IDENT: return { debug: 'parsePrefix>case>ident', value: this.currToken.literal, kind: Tiny.ExpressionKind.Ident, ...this.currPos(), }; case Tiny.TokenType.NUMBER: return { debug: 'parsePrefix>case>number', value: { value: Number(this.currToken.literal), kind: Tiny.LiteralKind.Number, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }; case Tiny.TokenType.STRING: return { debug: 'parsePrefix>case>string', value: { value: this.currToken.literal, kind: Tiny.LiteralKind.String, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }; case Tiny.TokenType.BANG: case Tiny.TokenType.MINUS: return this.prefixParseOps(); case Tiny.TokenType.TRUE: case Tiny.TokenType.FALSE: return { debug: 'parsePrefix>case>true', value: { value: this.currToken.type === Tiny.TokenType.TRUE, kind: Tiny.LiteralKind.Boolean, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }; case Tiny.TokenType.NULL: return { debug: 'parsePrefix>case>null', value: { kind: Tiny.LiteralKind.Null, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }; case Tiny.TokenType.LPAREN: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (!this.expectPeek(Tiny.TokenType.RPAREN)) return null; if (!expression) return null; return expression; } case Tiny.TokenType.IF: { if (!this.expectPeek(Tiny.TokenType.LPAREN)) return null; this.nextToken(); const condition = this.parseExpression(Tiny.Priority.LOWEST); if (!this.expectPeek(Tiny.TokenType.RPAREN)) return null; const consequence = this.parseBlockStatement(); let alternative = null; if (this.peekTokenIs(Tiny.TokenType.ELSE)) { this.nextToken(); alternative = this.parseBlockStatement(); } return { debug: 'parsePrefix>case>if', condition, consequence, alternative, kind: Tiny.ExpressionKind.If, ...this.currPos(), }; } case Tiny.TokenType.FUNCTION: { let name = null; if (!this.peekTokenIs(Tiny.TokenType.IDENT)) name = null; else { this.nextToken(); name = { debug: 'parsePrefix>case>function>name', value: this.currToken.literal, kind: Tiny.ExpressionKind.Ident, ...this.currPos(), }; } if (!this.expectPeek(Tiny.TokenType.LPAREN)) return null; const parameters = this.parseFunctionParameters(); const body = this.parseBlockStatement(); if (!body) return null; return { debug: 'parsePrefix>case>function', function: name, parameters: parameters, body, kind: Tiny.ExpressionKind.Function, ...this.currPos(), }; } case Tiny.TokenType.LBRACKET: return { debug: 'parsePrefix>case>Lbracket', elements: this.parseExpressionParameters(Tiny.TokenType.RBRACKET), kind: Tiny.ExpressionKind.Array, ...this.currPos(), }; case Tiny.TokenType.LBRACE: { const pairs = []; while (!this.peekTokenIs(Tiny.TokenType.RBRACE)) { this.nextToken(); let key = this.parseExpression(Tiny.Priority.LOWEST); if (key?.kind === Tiny.ExpressionKind.Ident) key = { debug: 'parseHash>ident>key', value: { debug: 'parseHash>ident>key>value', value: key.value, kind: Tiny.LiteralKind.String, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }; let value = null; if (!this.peekTokenIs(Tiny.TokenType.COLON)) value = this.parseExpression(Tiny.Priority.LOWEST); else { this.nextToken(); this.nextToken(); value = this.parseExpression(Tiny.Priority.LOWEST); } if (!this.peekTokenIs(Tiny.TokenType.RBRACE) && !this.expectPeek(Tiny.TokenType.COMMA)) return null; if (key === null || value === null) continue; pairs.push({ key, value, ...this.currPos(), }); } if (!this.expectPeek(Tiny.TokenType.RBRACE)) return null; return { debug: 'parseHash>return', pairs, kind: Tiny.ExpressionKind.Object, ...this.currPos(), }; } case Tiny.TokenType.TYPEOF: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) return null; return { debug: 'parsePrefix>case>typeof', value: expression, kind: Tiny.ExpressionKind.Typeof, ...this.currPos(), }; } case Tiny.TokenType.THROW: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) return null; return { debug: 'parsePrefix>case>throw', message: expression, line: this.currPos().line, column: this.currPos().column, kind: Tiny.ExpressionKind.Throw, }; } case Tiny.TokenType.DELETE: { if (!this.expectPeek(Tiny.TokenType.IDENT)) return null; const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) return null; return { debug: 'parsePrefix>case>delete', value: expression, kind: Tiny.ExpressionKind.Delete, ...this.currPos(), }; } case Tiny.TokenType.USE: { if (!this.expectPeek(Tiny.TokenType.STRING)) return null; const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) return null; return { debug: 'parsePrefix>case>use', path: expression, kind: Tiny.ExpressionKind.Use, ...this.currPos(), }; } case Tiny.TokenType.VOID: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) this.pushError(this.messages.parseError.voidRequiresExpression); return { debug: 'parsePrefix>case>void', value: expression, kind: Tiny.ExpressionKind.Void, ...this.currPos(), }; } case Tiny.TokenType.EXPR: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.PREFIX); if (!expression) return null; return { debug: 'parsePrefix>case>expr', value: expression, kind: Tiny.ExpressionKind.Expr, ...this.currPos(), }; } default: return null; } } prefixParseOps() { const operator = this.currToken; this.nextToken(); return { debug: 'prefixParseOps>return', operator: operator.type, right: this.parseExpression(Tiny.Priority.PREFIX), kind: Tiny.ExpressionKind.Prefix, ...this.currPos(), }; } parseInfixExpression(left) { switch (this.currToken.type) { case Tiny.TokenType.LPAREN: return { debug: 'parseInfixExpression>case>Lparen', function: left, parameters: this.parseExpressionParameters(Tiny.TokenType.RPAREN), kind: Tiny.ExpressionKind.Call, ...this.currPos(), }; case Tiny.TokenType.LBRACKET: { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (!this.expectPeek(Tiny.TokenType.RBRACKET)) return { debug: 'parseInfixExpression>case>Lbracket', left, index: { debug: 'parseInfixExpression>case>Lbracket>index', value: { debug: 'parseInfixExpression>case>Lbracket>index>value', value: 0, kind: Tiny.LiteralKind.Number, ...this.currPos(), }, kind: Tiny.ExpressionKind.Literal, ...this.currPos(), }, kind: Tiny.ExpressionKind.Index, ...this.currPos(), }; return { debug: 'parseInfixExpression>case>Lbracket', left, index: expression, kind: Tiny.ExpressionKind.Index, ...this.currPos(), }; } default: { const operator = this.currToken; const priority = this.currPriority(); this.nextToken(); const right = this.parseExpression(priority); if (!right) return null; return { debug: 'parseInfixExpression>case>default', left, operator: operator.type, right, kind: Tiny.ExpressionKind.Infix, ...this.currPos(), }; } } } parseBlockStatement() { if (!this.peekTokenIs(Tiny.TokenType.LBRACE)) { this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (!expression) return null; return { debug: 'parseBlockStatement>return', statements: [ { debug: 'parseBlockStatement>return>statement', expression, kind: Tiny.NodeKind.ExpressionStatement, ...this.currPos(), }, ], returnFinal: true, kind: Tiny.ExpressionKind.Block, ...this.currPos(), }; } this.nextToken(); let statements = []; this.nextToken(); while (!this.currTokenIs(Tiny.TokenType.RBRACE) && !this.currTokenIs(Tiny.TokenType.EOF)) { const statement = this.parseStatement(); if (statement) statements.push(statement); this.nextToken(); } if (!this.currTokenIs(Tiny.TokenType.RBRACE)) { this.pushError(Tiny.errorFormatter(this.messages.parseError.unexpectedToken, Tiny.TokenType.RPAREN, this.peekToken.type)); return null; } return { debug: 'parseBlockStatement>return', statements, returnFinal: false, kind: Tiny.ExpressionKind.Block, ...this.currPos(), }; } parseFunctionParameters() { let parameters = []; if (this.peekTokenIs(Tiny.TokenType.RPAREN)) { this.nextToken(); return []; } this.nextToken(); parameters.push({ value: this.currToken.literal, kind: Tiny.ExpressionKind.Ident, ...this.currPos(), }); while (this.peekTokenIs(Tiny.TokenType.COMMA)) { this.nextToken(); this.nextToken(); parameters.push({ value: this.currToken.literal, kind: Tiny.ExpressionKind.Ident, ...this.currPos(), }); } if (this.expectPeek(Tiny.TokenType.RPAREN)) return parameters; return parameters; } parseExpressionParameters(end) { const parameters = []; if (this.peekTokenIs(end)) { this.nextToken(); return parameters; } this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (expression) parameters.push(expression); while (this.peekTokenIs(Tiny.TokenType.COMMA)) { this.nextToken(); this.nextToken(); const expression = this.parseExpression(Tiny.Priority.LOWEST); if (expression) parameters.push(expression); } if (!this.expectPeek(end)) return parameters; return parameters; } peekPriority() { return this.getPriority(this.peekToken); } currPriority() { return this.getPriority(this.currToken); } getPriority(token) { switch (token.type) { case Tiny.TokenType.ASSIGN: return Tiny.Priority.ASSIGN; case Tiny.TokenType.AND: case Tiny.TokenType.OR: return Tiny.Priority.AND_OR; case Tiny.TokenType.EQUAL: case Tiny.TokenType.NOT_EQUAL: return Tiny.Priority.EQUAL; case Tiny.TokenType.LT: case Tiny.TokenType.GT: case Tiny.TokenType.LTE: case Tiny.TokenType.GTE: case Tiny.TokenType.IN: return Tiny.Priority.LESS_GREATER; case Tiny.TokenType.PLUS: case Tiny.TokenType.MINUS: return Tiny.Priority.SUM; case Tiny.TokenType.SLASH: case Tiny.TokenType.ASTERISK: case Tiny.TokenType.PERCENT: return Tiny.Priority.PRODUCT; case Tiny.TokenType.TYPEOF: case Tiny.TokenType.DELETE: case Tiny.TokenType.THROW: case Tiny.TokenType.USE: case Tiny.TokenType.VOID: case Tiny.TokenType.EXPR: return Tiny.Priority.PREFIX; case Tiny.TokenType.LPAREN: return Tiny.Priority.CALL; case Tiny.TokenType.LBRACKET: case Tiny.TokenType.ELEMENT: case Tiny.TokenType.IN: case Tiny.TokenType.NULLISH: return Tiny.Priority.INDEX; default: return Tiny.Priority.LOWEST; } } } exports.default = Parser;