UNPKG

@designliquido/delegua

Version:

Linguagem de programação simples e moderna usando português estruturado.

542 lines 20.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LexadorPitugues = void 0; const browser_process_hrtime_1 = __importDefault(require("browser-process-hrtime")); const simbolo_1 = require("../simbolo"); const pitugues_1 = require("./palavras-reservadas/pitugues"); const pitugues_2 = __importDefault(require("../../tipos-de-simbolos/pitugues")); /** * 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. * * Este lexador é diferente dos demais, porque também produz uma estrutura de dados de pragmas, que explica, * por exemplo quantos espaços há na frente de cada linha. Assim como a linguagem Python, os blocos de * escopo são definidos pelo número de espaços à frente do código. */ class LexadorPitugues { constructor(performance = false) { this.performance = performance; this.simbolos = []; this.erros = []; this.localizacoes = {}; this.inicioSimbolo = 0; this.atual = 0; this.linha = 0; } eDigito(caractere) { return caractere >= '0' && caractere <= '9'; } eAlfabeto(caractere) { const acentuacoes = [ 'á', 'Á', 'ã', 'Ã', 'â', 'Â', 'à', 'À', 'é', 'É', 'ê', 'Ê', 'í', 'Í', 'ó', 'Ó', 'õ', 'Õ', 'ô', 'Ô', 'ú', 'Ú', 'ç', 'Ç', '_', ]; return ((caractere >= 'a' && caractere <= 'z') || (caractere >= 'A' && caractere <= 'Z') || acentuacoes.includes(caractere)); } eAlfabetoOuDigito(caractere) { return this.eDigito(caractere) || this.eAlfabeto(caractere); } /** * Indica se o código está na última linha. * @returns Verdadeiro se contador de linhas está na última linha. * Falso caso contrário. */ eUltimaLinha() { return this.linha >= this.codigo.length - 1; } eFinalDaLinha() { return this.atual >= this.codigo[this.linha].length; } eFinalDoCodigo() { if (this.linha > this.codigo.length - 1) return true; return (this.linha == this.codigo.length - 1 && this.codigo[this.codigo.length - 1].length <= this.atual); } avancar() { this.atual += 1; if (this.eFinalDaLinha() && !this.eUltimaLinha()) { this.linha++; this.atual = 0; this.analisarIndentacao(); } } adicionarSimbolo(tipo, literal = null, linha = null) { const texto = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual); const comprimento = Math.max(texto.length, 1); const colunaInicio = this.inicioSimbolo + 1; const colunaFim = this.inicioSimbolo + comprimento; this.simbolos.push(new simbolo_1.Simbolo(tipo, texto, literal, linha || this.linha + 1, this.hashArquivo, colunaInicio, colunaFim)); } simboloAtual() { if (this.eFinalDaLinha()) return '\0'; if (this.linha > this.codigo.length - 1) return '\0'; return this.codigo[this.linha].charAt(this.atual); } proximoSimbolo() { if (this.atual + 1 >= this.codigo[this.linha].length) return '\0'; return this.codigo[this.linha].charAt(this.atual + 1); } simboloAnterior() { return this.codigo[this.linha].charAt(this.atual - 1); } analisarTextoMultilinha(delimitador) { const inicioLinha = this.linha; let contagemDelimitadores = 0; while (!this.eFinalDoCodigo() && contagemDelimitadores < 3) { this.avancar(); if (this.simboloAtual() === delimitador) { contagemDelimitadores++; } else { contagemDelimitadores = 0; } } if (contagemDelimitadores === 3) { const linhas = this.codigo.slice(inicioLinha, this.linha + 1); let conteudo = linhas.join('\n'); // Remove delimitadores inicial e final const larguraDelimitadoresFim = delimitador.repeat(3); conteudo = conteudo.substring(conteudo.indexOf(larguraDelimitadoresFim) + 3); conteudo = conteudo.substring(0, conteudo.lastIndexOf(larguraDelimitadoresFim)); this.simbolos.push(new simbolo_1.Simbolo(pitugues_2.default.TEXTO_MULTILINHAS, conteudo, conteudo, inicioLinha + 1, this.hashArquivo)); return; } this.erros.push({ linha: this.linha + 1, caractere: this.simboloAnterior(), mensagem: 'Texto multilinha não finalizado.', }); } analisarTexto(delimitador = '"', ehFString = false) { const linhaPrimeiroCaracter = this.linha; let valor = ''; while (this.simboloAtual() !== delimitador && !this.eFinalDoCodigo()) { const caractereAtual = this.simboloAtual(); if (caractereAtual === '\\') { this.avancar(); const proximoCaractere = this.simboloAtual(); switch (proximoCaractere) { case 'r': valor += '\r'; break; case 'b': valor += '\b'; break; case 'n': valor += '\n'; break; case 't': valor += '\t'; break; case "'": valor += "'"; break; case '"': valor += '"'; break; case '\\': valor += '\\'; break; default: valor += '\\' + proximoCaractere; break; } } else { valor += caractereAtual; } this.avancar(); } if (this.eFinalDoCodigo()) { this.erros.push({ linha: this.linha + 1, caractere: this.simboloAnterior(), mensagem: 'Texto não finalizado.', }); return; } const deslocamento = ehFString ? 2 : 1; const textoCompleto = this.codigo[this.linha].substring(this.inicioSimbolo + deslocamento, this.atual); if (textoCompleto.length === 0 && !this.eFinalDoCodigo() && this.codigo[this.linha].charAt(this.atual + 1) === delimitador) { this.avancar(); // Avança para o próximo delimitador this.analisarTextoMultilinha(delimitador); return; } const tipoSimbolo = ehFString ? pitugues_2.default.INTERPOLACAO : pitugues_2.default.TEXTO; this.simbolos.push(new simbolo_1.Simbolo(tipoSimbolo, textoCompleto, valor, linhaPrimeiroCaracter + 1, this.hashArquivo)); } analisarNumero() { const linhaInicial = this.linha; while (this.linha === linhaInicial && this.eDigito(this.simboloAtual())) this.avancar(); const temPonto = this.simboloAtual() === '.'; const proximoEhDigito = this.eDigito(this.proximoSimbolo()); if (this.linha === linhaInicial && temPonto && proximoEhDigito) { this.avancar(); while (this.linha === linhaInicial && this.eDigito(this.simboloAtual())) this.avancar(); } let numeroCompleto; if (linhaInicial < this.linha) { numeroCompleto = this.codigo[linhaInicial].substring(this.inicioSimbolo, this.codigo[linhaInicial].length); } else { numeroCompleto = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual); } this.simbolos.push(new simbolo_1.Simbolo(pitugues_2.default.NUMERO, numeroCompleto, parseFloat(numeroCompleto), linhaInicial + 1, this.hashArquivo)); } identificarPalavraChave() { const linhaPrimeiroCaracter = this.linha; while (this.eAlfabetoOuDigito(this.simboloAtual()) && this.linha === linhaPrimeiroCaracter) { this.avancar(); } let textoPalavraChave; if (linhaPrimeiroCaracter < this.linha) { const linhaPalavraChave = this.codigo[linhaPrimeiroCaracter]; textoPalavraChave = linhaPalavraChave.substring(this.inicioSimbolo, linhaPalavraChave.length); } else { textoPalavraChave = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual); } const tipo = textoPalavraChave in pitugues_1.palavrasReservadasPitugues ? pitugues_1.palavrasReservadasPitugues[textoPalavraChave] : pitugues_2.default.IDENTIFICADOR; this.simbolos.push(new simbolo_1.Simbolo(tipo, textoPalavraChave, null, linhaPrimeiroCaracter + 1, this.hashArquivo)); } analisarIndentacao() { let espacos = 0; while (['\t', ' '].includes(this.simboloAtual()) && !this.eFinalDoCodigo()) { espacos++; this.avancar(); } this.localizacoes[this.linha + 1] = { linha: this.linha + 1, espacosIndentacao: espacos, }; } comentarioUmaLinha() { this.avancar(); const linhaAtual = this.linha; let ultimoAtual = this.atual; while (linhaAtual === this.linha && !this.eFinalDoCodigo()) { ultimoAtual = this.atual; this.avancar(); } const conteudo = this.codigo[linhaAtual].substring(this.inicioSimbolo + 2, ultimoAtual + 1); this.adicionarSimbolo(pitugues_2.default.COMENTARIO, conteudo.trim(), linhaAtual + 1); } avancarParaProximaLinha() { this.linha++; this.atual = 0; this.analisarIndentacao(); } analisarToken() { const caractere = this.simboloAtual(); switch (caractere) { case ' ': case '\t': case '\r': case '\n': case '\0': this.avancar(); break; case ';': this.adicionarSimbolo(pitugues_2.default.PONTO_E_VIRGULA); this.avancar(); break; case '@': this.adicionarSimbolo(pitugues_2.default.ARROBA); this.avancar(); break; case '=': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.IGUAL_IGUAL); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.IGUAL); } break; case '#': this.comentarioUmaLinha(); break; case '[': this.adicionarSimbolo(pitugues_2.default.COLCHETE_ESQUERDO); this.avancar(); break; case ']': this.adicionarSimbolo(pitugues_2.default.COLCHETE_DIREITO); this.avancar(); break; case '(': this.adicionarSimbolo(pitugues_2.default.PARENTESE_ESQUERDO); this.avancar(); break; case ')': this.adicionarSimbolo(pitugues_2.default.PARENTESE_DIREITO); this.avancar(); break; case '{': this.adicionarSimbolo(pitugues_2.default.CHAVE_ESQUERDA); this.avancar(); break; case '}': this.adicionarSimbolo(pitugues_2.default.CHAVE_DIREITA); this.avancar(); break; case ',': this.adicionarSimbolo(pitugues_2.default.VIRGULA); this.avancar(); break; case '.': if (this.eDigito(this.proximoSimbolo())) { this.analisarNumero(); } else { this.adicionarSimbolo(pitugues_2.default.PONTO); this.avancar(); } break; case '-': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.MENOS_IGUAL); this.avancar(); } else if (this.simboloAtual() === '>') { this.adicionarSimbolo(pitugues_2.default.SETA); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.SUBTRACAO); } break; case '+': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.MAIS_IGUAL); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.ADICAO); } break; case '/': this.avancar(); switch (this.simboloAtual()) { case '/': this.adicionarSimbolo(pitugues_2.default.DIVISAO_INTEIRA); this.avancar(); break; case '=': this.adicionarSimbolo(pitugues_2.default.DIVISAO_IGUAL); this.avancar(); break; default: this.adicionarSimbolo(pitugues_2.default.DIVISAO); break; } break; case ':': if (this.proximoSimbolo() === '=') { this.avancar(); // consome ':' this.avancar(); // consome '=' this.adicionarSimbolo(pitugues_2.default.MORSA); } else { this.adicionarSimbolo(pitugues_2.default.DOIS_PONTOS); this.avancar(); } break; case '%': this.adicionarSimbolo(pitugues_2.default.MODULO); this.avancar(); break; case '*': this.inicioSimbolo = this.atual; this.avancar(); if (this.simboloAtual() === '*') { this.avancar(); this.adicionarSimbolo(pitugues_2.default.EXPONENCIACAO); } else if (this.simboloAtual() === '=') { this.avancar(); this.adicionarSimbolo(pitugues_2.default.MULTIPLICACAO_IGUAL); } else { this.adicionarSimbolo(pitugues_2.default.MULTIPLICACAO); } break; case '?': this.adicionarSimbolo(pitugues_2.default.INTERROGACAO); this.avancar(); break; case '!': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.DIFERENTE, '!='); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.NEGACAO); } break; case '&': this.adicionarSimbolo(pitugues_2.default.BIT_AND); this.avancar(); break; case '~': if (this.proximoSimbolo() === '>') { this.avancar(); this.avancar(); this.adicionarSimbolo(pitugues_2.default.BOTE); } else { this.adicionarSimbolo(pitugues_2.default.BIT_NOT); this.avancar(); } break; case '|': this.adicionarSimbolo(pitugues_2.default.BIT_OR); this.avancar(); break; case '^': this.adicionarSimbolo(pitugues_2.default.BIT_XOR); this.avancar(); break; case '<': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.MENOR_IGUAL); this.avancar(); } else if (this.simboloAtual() === '<') { this.adicionarSimbolo(pitugues_2.default.MENOR_MENOR); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.MENOR); } break; case '>': this.avancar(); if (this.simboloAtual() === '=') { this.adicionarSimbolo(pitugues_2.default.MAIOR_IGUAL); this.avancar(); } else if (this.simboloAtual() === '>') { this.adicionarSimbolo(pitugues_2.default.MAIOR_MAIOR); this.avancar(); } else { this.adicionarSimbolo(pitugues_2.default.MAIOR); } break; case 'f': case 'F': const proximoChar = this.proximoSimbolo(); if (proximoChar == '"' || proximoChar == "'") { this.avancar(); // consome o 'f' const delimitador = this.simboloAtual(); this.avancar(); // consome a aspa inicial // avisa que é F-STRING this.analisarTexto(delimitador, true); this.avancar(); // Consome a aspa final break; } // Se não tiver aspa depois, é uma palavra comum (ex: "faca", "f") this.identificarPalavraChave(); break; case '"': this.avancar(); this.analisarTexto('"'); this.avancar(); break; case "'": this.avancar(); this.analisarTexto("'"); this.avancar(); break; default: if (this.eDigito(caractere)) this.analisarNumero(); else if (this.eAlfabeto(caractere)) this.identificarPalavraChave(); else { this.erros.push({ linha: this.linha + 1, caractere: caractere, mensagem: 'Caractere inesperado.', }); this.avancar(); } } } mapear(codigo, hashArquivo) { const inicioMapeamento = (0, browser_process_hrtime_1.default)(); this.simbolos = []; this.erros = []; this.localizacoes = {}; this.inicioSimbolo = 0; this.atual = 0; this.linha = 0; this.codigo = codigo || ['']; this.hashArquivo = hashArquivo; // Análise de indentação da primeira linha. this.analisarIndentacao(); while (!this.eFinalDoCodigo()) { this.inicioSimbolo = this.atual; this.analisarToken(); } if (this.performance) { const deltaMapeamento = (0, browser_process_hrtime_1.default)(inicioMapeamento); // eslint-disable-next-line no-undef console.log(`[Lexador] Tempo para mapeamento: ${deltaMapeamento[0] * 1e9 + deltaMapeamento[1]}ns`); } return { simbolos: this.simbolos, erros: this.erros, pragmas: this.localizacoes, }; } } exports.LexadorPitugues = LexadorPitugues; //# sourceMappingURL=lexador-pitugues.js.map