Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fontes/analisador-semantico/analisador-semantico-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import {
TextoDocumentacao,
} from '../declaracoes';
import {
CorrecaoSugeridaInterface,
DiagnosticoAnalisadorSemantico,
DiagnosticoSeveridade,
SimboloInterface,
Expand Down Expand Up @@ -122,7 +123,7 @@ export abstract class AnalisadorSemanticoBase implements AnalisadorSemanticoInte
sugestao(
simbolo: SimboloInterface,
mensagem: string,
correcoes: import('../interfaces/erros').CorrecaoSugeridaInterface[]
correcoes: CorrecaoSugeridaInterface[]
): void {
if (this.diagnosticoJaExiste(simbolo, mensagem)) {
return;
Expand Down
4 changes: 2 additions & 2 deletions fontes/analisador-semantico/analisador-semantico.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1120,8 +1120,8 @@ export class AnalisadorSemantico extends AnalisadorSemanticoBase {
textoOriginal: 'qualquer',
textoSubstituto: tipoMelhor,
linha: declaracao.simbolo.linha,
colunaInicio: 0,
colunaFim: 0,
colunaInicio: declaracao.simbolo.colunaInicio,
colunaFim: declaracao.simbolo.colunaFim,
Comment on lines +1123 to +1124
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the Simbolo class always provides colunaInicio and colunaFim values (defaulting to 0), the SimboloInterface declares them as optional. This creates a type mismatch when assigning these potentially undefined values to CorrecaoSugeridaInterface, which requires non-optional number values. Consider using the nullish coalescing operator to provide a fallback value: colunaInicio: declaracao.simbolo.colunaInicio ?? 0 and colunaFim: declaracao.simbolo.colunaFim ?? 0.

Suggested change
colunaInicio: declaracao.simbolo.colunaInicio,
colunaFim: declaracao.simbolo.colunaFim,
colunaInicio: declaracao.simbolo.colunaInicio ?? 0,
colunaFim: declaracao.simbolo.colunaFim ?? 0,

Copilot uses AI. Check for mistakes.
}]
);
}
Expand Down
2 changes: 2 additions & 0 deletions fontes/interfaces/simbolo-interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ export interface SimboloInterface<TTipo = string> {
literal: string;
linha: number;
hashArquivo: number;
colunaInicio?: number;
colunaFim?: number;
}
5 changes: 4 additions & 1 deletion fontes/lexador/dialetos/lexador-calango.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,10 @@ export class LexadorCalango implements LexadorInterface<SimboloInterface> {
}
adicionarSimbolo(tipo: any, literal?: any): void {
const texto = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha, -1));
const comprimento = Math.max(texto.length, 1);
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha, -1, colunaInicio, colunaFim));
Comment on lines 86 to +91
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lexer has methods (analisarTexto, analisarNumero, identificarPalavraChave) that directly create Simbolo instances without calling adicionarSimbolo. These methods bypass the column tracking logic added here, meaning text, number, and keyword tokens won't have proper colunaInicio and colunaFim values. Consider refactoring those methods to use adicionarSimbolo or to manually calculate and include column information when creating Simbolo instances.

Copilot uses AI. Check for mistakes.
}

simboloAtual(): string {
Expand Down
5 changes: 4 additions & 1 deletion fontes/lexador/dialetos/lexador-egua-classico.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,10 @@ export class LexadorEguaClassico implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: any, literal: any = null) {
const texto = this.codigo.substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha, -1));
const comprimento = Math.max(texto.length, 1);
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha, -1, colunaInicio, colunaFim));
}

proximoIgualA(esperado: any) {
Expand Down
5 changes: 4 additions & 1 deletion fontes/lexador/dialetos/lexador-pitugues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,10 @@ export class LexadorPitugues implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: any, literal: any = null, linha: number = null): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, texto, literal, linha || this.linha + 1, this.hashArquivo));
const comprimento = Math.max(texto.length, 1);
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, texto, literal, linha || this.linha + 1, this.hashArquivo, colunaInicio, colunaFim));
}

simboloAtual(): string {
Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/dialetos/lexador-portugol-ipt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@ export class LexadorPortugolIpt implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: any, literal?: any): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(
new Simbolo(tipo, literal || texto, literal, this.linha + 1, this.hashArquivo)
new Simbolo(tipo, lexema, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim)
);
Comment on lines 89 to 98
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lexer has methods (analisarTexto, analisarNumero, identificarPalavraChave) that directly create Simbolo instances without calling adicionarSimbolo. These methods bypass the column tracking logic added here, meaning text, number, and keyword tokens won't have proper colunaInicio and colunaFim values. Consider refactoring those methods to use adicionarSimbolo or to manually calculate and include column information when creating Simbolo instances.

Copilot uses AI. Check for mistakes.
}

Expand Down
5 changes: 4 additions & 1 deletion fontes/lexador/dialetos/lexador-prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ export class LexadorPrisma implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: string, literal: any = null): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha + 1, this.hashArquivo));
const comprimento = Math.max(texto.length, 1);
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim));
Comment on lines 111 to +116
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identificarPalavraChave method directly creates a Simbolo instance without calling adicionarSimbolo. This method bypasses the column tracking logic added here, meaning keyword tokens won't have proper colunaInicio and colunaFim values. Consider refactoring this method to use adicionarSimbolo or to manually calculate and include column information when creating the Simbolo instance.

Copilot uses AI. Check for mistakes.
}

analisarTexto(delimitador: string = '"'): void {
Expand Down
5 changes: 4 additions & 1 deletion fontes/lexador/dialetos/lexador-tenda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,10 @@ export class LexadorTenda implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: any, literal: any = null): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha + 1, this.hashArquivo));
const comprimento = Math.max(texto.length, 1);
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, texto, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim));
}
Comment on lines 122 to 128
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lexer has methods (analisarTexto, analisarNumero, identificarPalavraChave) that directly create Simbolo instances without calling adicionarSimbolo. These methods bypass the column tracking logic added here, meaning text, number, and keyword tokens won't have proper colunaInicio and colunaFim values. Consider refactoring those methods to use adicionarSimbolo or to manually calculate and include column information when creating Simbolo instances.

Copilot uses AI. Check for mistakes.

simboloAtual(): string {
Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/lexador-base-linha-unica.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,13 @@ export abstract class LexadorBaseLinhaUnica implements LexadorInterface<SimboloI

adicionarSimbolo(tipo: any, literal?: any): void {
const texto: string = this.codigo.substring(this.inicioSimbolo, this.atual);
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(
new Simbolo(tipo, literal || texto, literal, this.linha + 1, this.hashArquivo)
new Simbolo(tipo, lexema, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim)
);
}

Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/lexador-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,13 @@ export abstract class LexadorBase implements LexadorInterface<SimboloInterface>

adicionarSimbolo(tipo: any, literal?: any): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(
new Simbolo(tipo, literal || texto, literal, this.linha + 1, this.hashArquivo)
new Simbolo(tipo, lexema, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim)
);
}

Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/lexador.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,13 @@ export class Lexador implements LexadorInterface<SimboloInterface> {

adicionarSimbolo(tipo: string, literal: any = null): void {
const texto: string = this.codigo[this.linha].substring(this.inicioSimbolo, this.atual);
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(
new Simbolo(tipo, literal || texto, literal, this.linha + 1, this.hashArquivo)
new Simbolo(tipo, lexema, literal, this.linha + 1, this.hashArquivo, colunaInicio, colunaFim)
);
}

Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/micro-lexador-pitugues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ export class MicroLexadorPitugues {

adicionarSimbolo(tipo: string, literal: any = null): void {
const texto: string = this.codigo.substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, literal || texto, literal, 1, -1));
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, lexema, literal, 1, -1, colunaInicio, colunaFim));
}

analisarTexto(delimitador = '"'): void {
Expand Down
7 changes: 6 additions & 1 deletion fontes/lexador/micro-lexador.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,12 @@ export class MicroLexador {

adicionarSimbolo(tipo: string, literal: any = null): void {
const texto: string = this.codigo.substring(this.inicioSimbolo, this.atual);
this.simbolos.push(new Simbolo(tipo, literal || texto, literal, 1, -1));
const lexema = literal || texto;
const comprimentoLexema = typeof lexema === 'string' ? lexema.length : 0;
const comprimento = Math.max(comprimentoLexema, texto.length) || 1;
const colunaInicio = this.inicioSimbolo + 1;
const colunaFim = this.inicioSimbolo + comprimento;
this.simbolos.push(new Simbolo(tipo, lexema, literal, 1, -1, colunaInicio, colunaFim));
}

analisarTexto(delimitador = '"'): void {
Expand Down
6 changes: 5 additions & 1 deletion fontes/lexador/simbolo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ export class Simbolo implements SimboloInterface {
literal: any;
linha: number;
hashArquivo: number;
colunaInicio: number;
colunaFim: number;
Comment on lines +9 to +10
Copy link

Copilot AI Feb 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an inconsistency between the interface and implementation. The SimboloInterface defines colunaInicio and colunaFim as optional fields (with ?), but the Simbolo class implementation declares them as required (non-optional). While the constructor provides default values (0), this discrepancy could cause confusion. Consider making these fields optional in the class as well (colunaInicio?: number) to match the interface, or remove the optional marker from the interface if these fields should always be present.

Copilot uses AI. Check for mistakes.

constructor(tipo: string, lexema: string, literal: any, linha: number, hashArquivo: number) {
constructor(tipo: string, lexema: string, literal: any, linha: number, hashArquivo: number, colunaInicio: number = 0, colunaFim: number = 0) {
this.tipo = tipo;
this.lexema = lexema;
this.literal = literal;
this.linha = linha;
this.hashArquivo = hashArquivo;
this.colunaInicio = colunaInicio;
this.colunaFim = colunaFim;
}

paraTexto(): string {
Expand Down
20 changes: 15 additions & 5 deletions testes/analisador-semantico/analisador-semantico.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1965,9 +1965,15 @@ describe('Analisador semântico', () => {
expect(sugestao.mensagem).toBe('Um tipo melhor pode ser inferido.');
expect(sugestao.correcoes).toBeDefined();
expect(sugestao.correcoes).toHaveLength(1);
expect(sugestao.correcoes[0].titulo).toBe("Alterar tipo para 'número'");
expect(sugestao.correcoes[0].textoOriginal).toBe('qualquer');
expect(sugestao.correcoes[0].textoSubstituto).toBe('número');
const primeiraCorrecao = sugestao.correcoes![0];
expect(primeiraCorrecao.titulo).toBe("Alterar tipo para 'número'");
expect(primeiraCorrecao.textoOriginal).toBe('qualquer');
expect(primeiraCorrecao.textoSubstituto).toBe('número');
// 'a' está na coluna 5
expect(primeiraCorrecao.colunaInicio).toBe(5);
expect(primeiraCorrecao.colunaFim).toBe(5);
expect(sugestao.colunaInicio).toBe(5);
expect(sugestao.colunaFim).toBe(5);
});

it('Sugestão - tipo qualquer pode ser inferido para texto', async () => {
Expand All @@ -1985,8 +1991,12 @@ describe('Analisador semântico', () => {

const sugestao = retornoAnalisadorSemantico.diagnosticos[0];
expect(sugestao.severidade).toBe(DiagnosticoSeveridade.SUGESTAO);
expect(sugestao.correcoes[0].titulo).toBe("Alterar tipo para 'texto'");
expect(sugestao.correcoes[0].textoSubstituto).toBe('texto');
const primeiraCorrecao = sugestao.correcoes![0];
expect(primeiraCorrecao.titulo).toBe("Alterar tipo para 'texto'");
expect(primeiraCorrecao.textoSubstituto).toBe('texto');
// 'b' está na coluna 5
expect(primeiraCorrecao.colunaInicio).toBe(5);
expect(primeiraCorrecao.colunaFim).toBe(5);
});

it('Sem sugestão - tipo já é específico', async () => {
Expand Down
122 changes: 122 additions & 0 deletions testes/lexador/lexador.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,128 @@ describe('Lexador', () => {
});
});

describe('Informações de coluna (colunaInicio e colunaFim)', () => {
it('Identificador simples - colunaInicio e colunaFim corretas', () => {
const resultado = lexador.mapear(['var x = 10'], -1);

expect(resultado).toBeTruthy();
// 'var' na coluna 1-3
expect(resultado.simbolos[0]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.VARIAVEL,
colunaInicio: 1,
colunaFim: 3,
})
);
// 'x' na coluna 5
expect(resultado.simbolos[1]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.IDENTIFICADOR,
colunaInicio: 5,
colunaFim: 5,
})
);
// '=' na coluna 7
expect(resultado.simbolos[2]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.IGUAL,
colunaInicio: 7,
colunaFim: 7,
})
);
// '10' na coluna 9-10
expect(resultado.simbolos[3]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.NUMERO,
colunaInicio: 9,
colunaFim: 10,
})
);
});

it('Operadores compostos - colunaInicio e colunaFim corretas', () => {
const resultado = lexador.mapear(['a >= b'], -1);

expect(resultado).toBeTruthy();
// 'a' na coluna 1
expect(resultado.simbolos[0]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.IDENTIFICADOR,
colunaInicio: 1,
colunaFim: 1,
})
);
// '>=' na coluna 3-4
expect(resultado.simbolos[1]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.MAIOR_IGUAL,
colunaInicio: 3,
colunaFim: 4,
})
);
// 'b' na coluna 6
expect(resultado.simbolos[2]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.IDENTIFICADOR,
colunaInicio: 6,
colunaFim: 6,
})
);
});

it('Texto (string) - colunaInicio e colunaFim corretas', () => {
const resultado = lexador.mapear(['"ola"'], -1);

expect(resultado).toBeTruthy();
// '"ola"' abrange colunas 1-5
expect(resultado.simbolos[0]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.TEXTO,
colunaInicio: 1,
colunaFim: 5,
})
);
});

it('Múltiplas linhas - cada linha tem colunas independentes', () => {
const resultado = lexador.mapear(['var a = 1', 'var b = 2'], -1);

expect(resultado).toBeTruthy();
// Primeira linha: 'var' col 1-3, 'a' col 5, '=' col 7, '1' col 9
expect(resultado.simbolos[0]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.VARIAVEL,
linha: 1,
colunaInicio: 1,
colunaFim: 3,
})
);
// Segunda linha: 'var' col 1-3 (coluna reinicia)
expect(resultado.simbolos[4]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.VARIAVEL,
linha: 2,
colunaInicio: 1,
colunaFim: 3,
})
);
});

it('Número decimal - colunaInicio e colunaFim corretas', () => {
const resultado = lexador.mapear(['3.14'], -1);

expect(resultado).toBeTruthy();
// '3.14' na coluna 1-4
expect(resultado.simbolos[0]).toEqual(
expect.objectContaining({
tipo: tiposDeSimbolos.NUMERO,
colunaInicio: 1,
colunaFim: 4,
})
);
});
});

describe('Cenários de falha', () => {
it('Falha léxica - texto sem fim', () => {
const resultado = lexador.mapear(['"texto sem fim'], -1);
Expand Down
Loading