@designliquido/delegua
Version:
Linguagem de programação simples e moderna usando português estruturado.
542 lines • 20.9 kB
JavaScript
"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