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
43 changes: 43 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Tests

on:
pull_request:
branches:
- principal

jobs:
test:
name: Run Unit Tests
runs-on: ubuntu-latest

strategy:
matrix:
node-version: [18.x, 20.x]

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'

- name: Install LLVM 14
run: |
sudo apt-get update
sudo apt-get install -y llvm-14 llvm-14-dev clang-14

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Run tests
run: yarn testes-unitarios

- name: Upload coverage reports
if: matrix.node-version == '20.x'
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: false
continue-on-error: true
71 changes: 60 additions & 11 deletions fontes/compilador-llvm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,23 @@ export class CompiladorLLVM implements VisitanteComumInterface {

const tipoLlvm = this.obterTipoLlvm(tipoVariavel);
const inicializacaoVariavel = this.montador.CreateAlloca(tipoLlvm, null, declaracao.simbolo.lexema);
const valorOuReferenciaVariavel = await declaracao.inicializador.aceitar(this);
let valorOuReferenciaVariavel = await declaracao.inicializador.aceitar(this);

const tipoInicializador = this.resolverTipoConstruto(declaracao.inicializador);
if (tipoVariavel === 'inteiro' && tipoInicializador === 'número') {
valorOuReferenciaVariavel = this.montador.CreateFPToSI(
valorOuReferenciaVariavel,
this.montador.getInt32Ty(),
'double_para_int'
);
} else if (tipoVariavel === 'número' && tipoInicializador === 'inteiro') {
valorOuReferenciaVariavel = this.montador.CreateSIToFP(
valorOuReferenciaVariavel,
this.montador.getDoubleTy(),
'int_para_double'
);
}

this.montador.CreateStore(valorOuReferenciaVariavel, inicializacaoVariavel);

const topoDaPilha = this.pilhaVariaveisEscopo.topoDaPilha();
Expand Down Expand Up @@ -352,6 +368,12 @@ export class CompiladorLLVM implements VisitanteComumInterface {

protected resolverOperando(operando: llvm.Value | VariavelEscopo, tipo: string): OperandoInterface {
switch (operando.constructor.name) {
case 'ConstantFP':
case 'SIToFPInst':
return {
valor: operando as llvm.Value,
tipo: 'número'
};
case 'VariavelEscopo':
const variavelEscopo = operando as VariavelEscopo;
const tipoVariavel = variavelEscopo.variavelLlvm.getType();
Expand Down Expand Up @@ -461,23 +483,26 @@ export class CompiladorLLVM implements VisitanteComumInterface {
expressao.direita.aceitar(this)
]);

let operandoEsquerdo: llvm.Value | VariavelEscopo = promises[0],
operandoDireito: llvm.Value | VariavelEscopo = promises[1];
let operandoEsquerdo: llvm.Value | VariavelEscopo | ConstantFP = promises[0],
operandoDireito: llvm.Value | VariavelEscopo | ConstantFP = promises[1];

let tipoEsquerdo = this.resolverTipoConstruto(expressao.esquerda);
let tipoDireito = this.resolverTipoConstruto(expressao.direita);

const tipoPrevalente = this.definirTipoPrevalente(tipoEsquerdo, tipoDireito);
if (tipoEsquerdo != tipoPrevalente) {
operandoEsquerdo = this.montador.CreateSIToFP((operandoEsquerdo as VariavelEscopo).variavelLlvm, llvm.Type.getDoubleTy(this.contexto));
}
const operandoEsquerdoResolvido: OperandoInterface = this.resolverOperando(operandoEsquerdo, tipoEsquerdo);
const operandoDireitoResolvido: OperandoInterface = this.resolverOperando(operandoDireito, tipoDireito);

if (tipoDireito != tipoPrevalente) {
operandoDireito = this.montador.CreateSIToFP((operandoDireito as VariavelEscopo).variavelLlvm, llvm.Type.getDoubleTy(this.contexto));
const tipoPrevalente = this.definirTipoPrevalente(operandoEsquerdoResolvido.tipo, operandoDireitoResolvido.tipo);

if (operandoEsquerdoResolvido.tipo !== tipoPrevalente) {
operandoEsquerdoResolvido.valor = this.montador.CreateSIToFP(operandoEsquerdoResolvido.valor, llvm.Type.getDoubleTy(this.contexto));
operandoEsquerdoResolvido.tipo = 'número';
}

const operandoEsquerdoResolvido: OperandoInterface = this.resolverOperando(operandoEsquerdo, tipoEsquerdo);
const operandoDireitoResolvido: OperandoInterface = this.resolverOperando(operandoDireito, tipoDireito);
if (operandoDireitoResolvido.tipo !== tipoPrevalente) {
operandoDireitoResolvido.valor = this.montador.CreateSIToFP(operandoDireitoResolvido.valor, llvm.Type.getDoubleTy(this.contexto));
operandoDireitoResolvido.tipo = 'número';
}

switch (expressao.operador.tipo) {
case 'MULTIPLICACAO':
Expand All @@ -492,14 +517,38 @@ export class CompiladorLLVM implements VisitanteComumInterface {
return Promise.resolve(this.montador.CreateSDiv((operandoEsquerdo as VariavelEscopo).variavelLlvm, (operandoDireito as VariavelEscopo).variavelLlvm));
case 'MODULO':
return this.resolverModulo(operandoEsquerdoResolvido, operandoDireitoResolvido);
case 'MENOR':
if (tipoPrevalente === 'inteiro') {
return Promise.resolve(this.montador.CreateICmpSLT(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
} else {
return Promise.resolve(this.montador.CreateFCmpOLT(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
}
case 'MENOR_IGUAL':
if (tipoPrevalente === 'inteiro') {
return Promise.resolve(this.montador.CreateICmpSLE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
} else {
return Promise.resolve(this.montador.CreateFCmpOLE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
}
case 'MAIOR':
if (tipoPrevalente === 'inteiro') {
return Promise.resolve(this.montador.CreateICmpSGT(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
} else {
return Promise.resolve(this.montador.CreateFCmpOGT(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
}
case 'MAIOR_IGUAL':
if (tipoPrevalente === 'inteiro') {
return Promise.resolve(this.montador.CreateICmpSGE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
} else {
return Promise.resolve(this.montador.CreateFCmpOGE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
}
case 'IGUAL_IGUAL':
return this.resolverIgualdade(operandoEsquerdoResolvido, operandoDireitoResolvido);
case 'DIFERENTE':
if (tipoPrevalente === 'inteiro') {
return Promise.resolve(this.montador.CreateICmpNE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
} else {
return Promise.resolve(this.montador.CreateFCmpONE(operandoEsquerdoResolvido.valor, operandoDireitoResolvido.valor));
}
}
}

Expand Down
200 changes: 200 additions & 0 deletions testes/compilador-llvm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,206 @@ describe('Compilador', () => {
});
});

describe('Se', () => {
it('Simples com senão', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var idade: inteiro = 18',
'se (idade >= 18) {',
' escreva("maior de idade");',
'} senao {',
' escreva("menor de idade");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oge double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
expect(resultado).toContain('br i1 %1, label %se_entao, label %se_senao');
expect(resultado).toContain('call i32 (i8*, ...) @printf');
});

it('Sem senão', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var nota: inteiro = 7',
'se (nota >= 6) {',
' escreva("Aprovado");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oge double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
expect(resultado).toContain('br i1');
expect(resultado).toContain('call i32 (i8*, ...) @printf');
});

it('Com senão se', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var nota: inteiro = 7',
'se (nota >= 9) {',
' escreva("Excelente");',
'} senao se (nota >= 7) {',
' escreva("Bom");',
'} senao {',
' escreva("Insuficiente");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oge double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
expect(resultado).toContain('call i32 (i8*, ...) @printf');
});

it('Múltiplos senão se', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var nota: inteiro = 8',
'se (nota >= 9) {',
' escreva("A");',
'} senao se (nota >= 8) {',
' escreva("B");',
'} senao se (nota >= 7) {',
' escreva("C");',
'} senao se (nota >= 6) {',
' escreva("D");',
'} senao {',
' escreva("F");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oge double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
expect(resultado).toContain('call i32 (i8*, ...) @printf');
});

it('Operador menor que', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var temperatura: inteiro = 15',
'se (temperatura < 20) {',
' escreva("Frio");',
'} senao {',
' escreva("Quente");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp olt double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});

it('Operador igual', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var x: inteiro = 10',
'se (x == 10) {',
' escreva("Igual a dez");',
'} senao {',
' escreva("Diferente de dez");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oeq double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});

it('Operador menor ou igual', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var saldo: inteiro = 100',
'se (saldo <= 100) {',
' escreva("Saldo baixo");',
'} senao {',
' escreva("Saldo alto");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp ole double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});

it('Operador maior que', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var pontuacao: inteiro = 85',
'se (pontuacao > 80) {',
' escreva("Pontuação alta");',
'} senao {',
' escreva("Pontuação baixa");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp ogt double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});

it('Operador diferente', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var status: inteiro = 1',
'se (status != 0) {',
' escreva("Ativo");',
'} senao {',
' escreva("Inativo");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp one double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});

it('Se aninhado', async () => {
const compilador = new CompiladorLLVM();
const resultado = await compilador.compilar([
'var idade: inteiro = 20',
'var estudante: inteiro = 1',
'se (idade >= 18) {',
' se (estudante == 1) {',
' escreva("Adulto estudante");',
' } senao {',
' escreva("Adulto não estudante");',
' }',
'} senao {',
' escreva("Menor de idade");',
'}'
]);

expect(resultado).toBeTruthy();
expect(resultado).toContain('fcmp oge double');
expect(resultado).toContain('fcmp oeq double');
expect(resultado).toContain('se_entao:');
expect(resultado).toContain('se_senao:');
expect(resultado).toContain('se_apos:');
});
})

describe('Para', () => {
it('Laço simples', async () => {
const compilador = new CompiladorLLVM();
Expand Down