UNPKG

egua

Version:

Linguagem de programação simples e moderna em português

310 lines (265 loc) 8.85 kB
const tokenTypes = require("./tokenTypes.js"); const reservedWords = { e: tokenTypes.E, em: tokenTypes.EM, classe: tokenTypes.CLASSE, senão: tokenTypes.SENÃO, falso: tokenTypes.FALSO, para: tokenTypes.PARA, função: tokenTypes.FUNÇÃO, se: tokenTypes.SE, senãose: tokenTypes.SENÃOSE, nulo: tokenTypes.NULO, ou: tokenTypes.OU, escreva: tokenTypes.ESCREVA, retorna: tokenTypes.RETORNA, super: tokenTypes.SUPER, isto: tokenTypes.ISTO, verdadeiro: tokenTypes.VERDADEIRO, var: tokenTypes.VAR, fazer: tokenTypes.FAZER, enquanto: tokenTypes.ENQUANTO, pausa: tokenTypes.PAUSA, continua: tokenTypes.CONTINUA, escolha: tokenTypes.ESCOLHA, caso: tokenTypes.CASO, padrão: tokenTypes.PADRÃO, importar: tokenTypes.IMPORTAR, tente: tokenTypes.TENTE, pegue: tokenTypes.PEGUE, finalmente: tokenTypes.FINALMENTE, herda: tokenTypes.HERDA }; class Token { constructor(type, lexeme, literal, line) { this.type = type; this.lexeme = lexeme; this.literal = literal; this.line = line; } toString() { return this.type + " " + this.lexeme + " " + this.literal; } } /** * O Lexador é responsável por transformar o código em uma coleção de tokens de linguagem. * Cada token de linguagem é representado por um tipo, um lexema e informações da linha de código em que foi expresso. * Também é responsável por mapear as palavras reservadas da linguagem, que não podem ser usadas por outras * estruturas, tais como nomes de variáveis, funções, literais, classes e assim por diante. */ module.exports = class Lexer { constructor(code, Egua) { this.Egua = Egua; this.code = code; this.tokens = []; this.start = 0; this.current = 0; this.line = 1; } isDigit(c) { return c >= "0" && c <= "9"; } isAlpha(c) { let specialAlphabet = ["_", "á", "Á", "ã", "Ã", "â", "Â", "à", "À", "é", "É", "ê", "Ê", "í", "Í", "ó", "Ó", "õ", "Õ", "ô", "Ô", "ú", "Ú", "ç", "Ç"]; return (c >= "a" && c <= "z") || (c >= "A" && c <= "Z") || specialAlphabet.includes(c); } isAlphaNumeric(c) { return this.isDigit(c) || this.isAlpha(c); } endOfCode() { return this.current >= this.code.length; } advance() { this.current += 1; return this.code[this.current - 1]; } addToken(type, literal = null) { const text = this.code.substring(this.start, this.current); this.tokens.push(new Token(type, text, literal, this.line)); } match(expected) { if (this.endOfCode()) { return false; } if (this.code[this.current] !== expected) { return false; } this.current += 1; return true; } peek() { if (this.endOfCode()) return "\0"; return this.code.charAt(this.current); } peekNext() { if (this.current + 1 >= this.code.length) return "\0"; return this.code.charAt(this.current + 1); } previous() { return this.code.charAt(this.current - 1); } parseString(stringChar = '"') { while (this.peek() !== stringChar && !this.endOfCode()) { if (this.peek() === "\n") this.line = +1; this.advance(); } if (this.endOfCode()) { this.Egua.lexerError( this.line, this.previous(), "Texto não finalizado." ); return; } this.advance(); let value = this.code.substring(this.start + 1, this.current - 1); this.addToken(tokenTypes.STRING, value); } parseNumber() { while (this.isDigit(this.peek())) { this.advance(); } if (this.peek() == "." && this.isDigit(this.peekNext())) { this.advance(); while (this.isDigit(this.peek())) { this.advance(); } } const fullNumber = this.code.substring(this.start, this.current); this.addToken(tokenTypes.NUMBER, parseFloat(fullNumber)); } identifyKeyword() { while (this.isAlphaNumeric(this.peek())) { this.advance(); } const c = this.code.substring(this.start, this.current); const type = c in reservedWords ? reservedWords[c] : tokenTypes.IDENTIFIER; this.addToken(type); } scanToken() { const char = this.advance(); switch (char) { case "[": this.addToken(tokenTypes.LEFT_SQUARE_BRACKET); break; case "]": this.addToken(tokenTypes.RIGHT_SQUARE_BRACKET); break; case "(": this.addToken(tokenTypes.LEFT_PAREN); break; case ")": this.addToken(tokenTypes.RIGHT_PAREN); break; case "{": this.addToken(tokenTypes.LEFT_BRACE); break; case "}": this.addToken(tokenTypes.RIGHT_BRACE); break; case ",": this.addToken(tokenTypes.COMMA); break; case ".": this.addToken(tokenTypes.DOT); break; case "-": this.addToken(tokenTypes.MINUS); break; case "+": this.addToken(tokenTypes.PLUS); break; case ":": this.addToken(tokenTypes.COLON); break; case ";": this.addToken(tokenTypes.SEMICOLON); break; case "%": this.addToken(tokenTypes.MODULUS); break; case "*": if (this.peek() === "*") { this.advance(); this.addToken(tokenTypes.STAR_STAR); break; } this.addToken(tokenTypes.STAR); break; case "!": this.addToken( this.match("=") ? tokenTypes.BANG_EQUAL : tokenTypes.BANG ); break; case "=": this.addToken( this.match("=") ? tokenTypes.EQUAL_EQUAL : tokenTypes.EQUAL ); break; case "&": this.addToken(tokenTypes.BIT_AND); break; case "~": this.addToken(tokenTypes.BIT_NOT); break; case "|": this.addToken(tokenTypes.BIT_OR); break; case "^": this.addToken(tokenTypes.BIT_XOR); break; case "<": if (this.match("=")) { this.addToken(tokenTypes.LESS_EQUAL); } else if (this.match("<")) { this.addToken(tokenTypes.LESSER_LESSER); } else { this.addToken(tokenTypes.LESS); } break; case ">": if (this.match("=")) { this.addToken(tokenTypes.GREATER_EQUAL); } else if (this.match(">")) { this.addToken(tokenTypes.GREATER_GREATER); } else { this.addToken(tokenTypes.GREATER); } break; case "/": if (this.match("/")) { while (this.peek() != "\n" && !this.endOfCode()) this.advance(); } else { this.addToken(tokenTypes.SLASH); } break; // Esta sessão ignora espaços em branco na tokenização case " ": case "\r": case "\t": break; // tentativa de pulhar linha com \n que ainda não funciona case "\n": this.line += 1; break; case '"': this.parseString('"'); break; case "'": this.parseString("'"); break; default: if (this.isDigit(char)) this.parseNumber(); else if (this.isAlpha(char)) this.identifyKeyword(); else this.Egua.lexerError(this.line, char, "Caractere inesperado."); } } scan() { while (!this.endOfCode()) { this.start = this.current; this.scanToken(); } this.tokens.push(new Token(tokenTypes.EOF, "", null, this.line)); return this.tokens; } };