-
Notifications
You must be signed in to change notification settings - Fork 61
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Melhorias no verificador de erros com escopo e tipos
- Loading branch information
Showing
11 changed files
with
1,065 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,164 +1,116 @@ | ||
import { PortugolCodeError } from "@portugol-webstudio/antlr"; | ||
import { PortugolCodeError } from "packages/antlr/lib/PortugolErrorListener.js"; | ||
|
||
import { getAllChildrenFromNode } from "../helpers/nodes.js"; | ||
import { Tipo } from "../helpers/Tipo.js"; | ||
import { ResultadoCompatibilidade, TabelaCompatibilidadeAtribuição } from "../helpers/compatibilidade.js"; | ||
import { Escopo } from "../helpers/Escopo.js"; | ||
import { resolverResultadoExpressão } from "../helpers/expressões.js"; | ||
import { Arquivo } from "../nodes/Arquivo.js"; | ||
import { AtribuiçãoCmd } from "../nodes/AtribuiçãoCmd.js"; | ||
import { CadeiaExpr } from "../nodes/CadeiaExpr.js"; | ||
import { CaractereExpr } from "../nodes/CaractereExpr.js"; | ||
import { ChamadaFunçãoExpr } from "../nodes/ChamadaFunçãoExpr.js"; | ||
import { Comando } from "../nodes/Comando.js"; | ||
import { DeclaraçãoCmd } from "../nodes/DeclaraçãoCmd.js"; | ||
import { Expressão } from "../nodes/Expressão.js"; | ||
import { InteiroExpr } from "../nodes/InteiroExpr.js"; | ||
import { LógicoExpr } from "../nodes/LógicoExpr.js"; | ||
import { RealExpr } from "../nodes/RealExpr.js"; | ||
import { EnquantoCmd } from "../nodes/EnquantoCmd.js"; | ||
import { EscolhaCmd } from "../nodes/EscolhaCmd.js"; | ||
import { FaçaEnquantoCmd } from "../nodes/FaçaEnquantoCmd.js"; | ||
import { Função } from "../nodes/Função.js"; | ||
import { Node } from "../nodes/Node.js"; | ||
import { ParaCmd } from "../nodes/ParaCmd.js"; | ||
import { Parâmetro } from "../nodes/Parâmetro.js"; | ||
import { ReferênciaVarExpr } from "../nodes/ReferênciaVarExpr.js"; | ||
import { SeCmd } from "../nodes/SeCmd.js"; | ||
|
||
interface Escopo { | ||
variáveis: Map<string, Tipo>; | ||
funções: Map<string, Tipo>; | ||
} | ||
export function* checarUsoEscopo(arquivo: Arquivo): Generator<PortugolCodeError> { | ||
const escopo = new Escopo(); | ||
|
||
export function* checarUsoEscopo(arquivo: Arquivo) { | ||
const escopo: Escopo = { | ||
variáveis: new Map<string, Tipo>(), | ||
funções: new Map<string, Tipo>([ | ||
["escreva", { primitivo: "vazio" }], | ||
["leia", { primitivo: "cadeia" }], | ||
["limpa", { primitivo: "vazio" }], | ||
]), | ||
}; | ||
|
||
for (const declr of arquivo.declarações) { | ||
if (escopo.variáveis.has(declr.nome)) { | ||
yield PortugolCodeError.fromContext(declr.ctx, `A variável '${declr.nome}' foi declarada múltiplas vezes`); | ||
function* varrerNós(nós: Node[]) { | ||
for (const nó of nós) { | ||
yield* varrerNó(nó); | ||
} | ||
|
||
escopo.variáveis.set(declr.nome, declr.tipo); | ||
} | ||
|
||
for (const func of arquivo.funções) { | ||
if (escopo.funções.has(func.nome)) { | ||
yield PortugolCodeError.fromContext(func.ctx, `A função '${func.nome}' foi declarada múltiplas vezes`); | ||
} | ||
function* varrerNó(nó: Node): Generator<PortugolCodeError> { | ||
switch (nó.constructor) { | ||
case DeclaraçãoCmd: | ||
case Parâmetro: | ||
const declr = nó as DeclaraçãoCmd | Parâmetro; | ||
|
||
escopo.funções.set(func.nome, func.retorno); | ||
} | ||
escopo.variáveis.set(declr.nome, declr.tipo); | ||
break; | ||
|
||
for (const func of arquivo.funções) { | ||
const escopoFunção: Escopo = { | ||
variáveis: new Map(escopo.variáveis), | ||
funções: new Map(escopo.funções), | ||
}; | ||
case ReferênciaVarExpr: | ||
const ref = nó as ReferênciaVarExpr; | ||
|
||
for (const param of func.parâmetros) { | ||
if (escopoFunção.variáveis.has(param.nome)) { | ||
yield PortugolCodeError.fromContext(param.ctx, `O parâmetro '${param.nome}' foi declarado múltiplas vezes`); | ||
} | ||
if (!escopo.hasVariável(ref.nome)) { | ||
yield PortugolCodeError.fromContext(ref.ctx, `Variável não declarada: ${ref.nome}`); | ||
} | ||
|
||
escopoFunção.variáveis.set(param.nome, param.tipo); | ||
} | ||
break; | ||
|
||
const instruções: Array<Comando | Expressão> = func.instruções.concat( | ||
func.instruções.flatMap(getAllChildrenFromNode) as Array<Comando | Expressão>, | ||
); | ||
case AtribuiçãoCmd: | ||
const attr = nó as AtribuiçãoCmd; | ||
|
||
for (const expr of instruções) { | ||
if (expr instanceof DeclaraçãoCmd) { | ||
if (escopoFunção.variáveis.has(expr.nome)) { | ||
yield PortugolCodeError.fromContext(expr.ctx, `A variável '${expr.nome}' foi declarada múltiplas vezes`); | ||
} | ||
yield* varrerNós(attr.children); | ||
|
||
escopoFunção.variáveis.set(expr.nome, expr.tipo); | ||
} else if (expr instanceof AtribuiçãoCmd) { | ||
const nome = expr.variável instanceof ReferênciaVarExpr ? expr.variável.nome : expr.variável.variável.nome; | ||
// TODO: bibliotecas | ||
if (attr.variável instanceof ReferênciaVarExpr && !attr.variável.escopoBiblioteca) { | ||
const svar = escopo.variáveis.get(attr.variável.nome); | ||
|
||
if (!escopoFunção.variáveis.has(nome)) { | ||
yield PortugolCodeError.fromContext(expr.ctx, `A variável '${nome}' não foi declarada`); | ||
} | ||
|
||
const variável = escopoFunção.variáveis.get(nome); | ||
|
||
if (expr.expressão instanceof ChamadaFunçãoExpr) { | ||
if ( | ||
!expr.expressão.escopoBiblioteca && | ||
expr.expressão.nome !== "leia" && | ||
escopoFunção.funções.get(expr.expressão.nome)?.primitivo !== variável?.primitivo | ||
) { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A função '${expr.expressão.nome}' não retorna um valor do tipo '${variável?.primitivo}'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof ReferênciaVarExpr) { | ||
if (escopoFunção.variáveis.get(expr.expressão.nome)?.primitivo !== variável?.primitivo) { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${expr.expressão.nome}' não é do tipo '${variável?.primitivo}'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof InteiroExpr) { | ||
if (variável?.primitivo !== "inteiro") { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${nome}' esperava ser atribuída com um valor do tipo '${variável?.primitivo}', mas recebeu um valor do tipo 'inteiro'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof RealExpr) { | ||
if (variável?.primitivo !== "real") { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${nome}' esperava ser atribuída com um valor do tipo '${variável?.primitivo}', mas recebeu um valor do tipo 'real'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof CadeiaExpr) { | ||
if (variável?.primitivo !== "cadeia") { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${nome}' esperava ser atribuída com um valor do tipo '${variável?.primitivo}', mas recebeu um valor do tipo 'cadeia'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof CaractereExpr) { | ||
if (variável?.primitivo !== "caracter") { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${nome}' esperava ser atribuída com um valor do tipo '${variável?.primitivo}', mas recebeu um valor do tipo 'caracter'`, | ||
); | ||
} | ||
} else if (expr.expressão instanceof LógicoExpr) { | ||
if (variável?.primitivo !== "logico") { | ||
yield PortugolCodeError.fromContext( | ||
expr.ctx, | ||
`A variável '${nome}' esperava ser atribuída com um valor do tipo '${variável?.primitivo}', mas recebeu um valor do tipo 'logico'`, | ||
); | ||
if (!svar) { | ||
break; | ||
} | ||
} | ||
} else if (expr instanceof ReferênciaVarExpr) { | ||
if (!escopoFunção.variáveis.has(expr.nome)) { | ||
yield PortugolCodeError.fromContext(expr.ctx, `A variável '${expr.nome}' não foi declarada`); | ||
} | ||
} else if (expr instanceof ChamadaFunçãoExpr) { | ||
if (!expr.escopoBiblioteca && !escopoFunção.funções.has(expr.nome)) { | ||
yield PortugolCodeError.fromContext(expr.ctx, `A função '${expr.nome}' não foi declarada`); | ||
} | ||
|
||
const args = expr.argumentos; | ||
try { | ||
const tret = resolverResultadoExpressão(attr.expressão, escopo); | ||
|
||
for (const arg of args) { | ||
if (arg instanceof ChamadaFunçãoExpr) { | ||
if (!arg.escopoBiblioteca && !escopoFunção.funções.has(arg.nome)) { | ||
yield PortugolCodeError.fromContext(arg.ctx, `A função '${arg.nome}' não foi declarada`); | ||
if (TabelaCompatibilidadeAtribuição[svar.primitivo][tret] === ResultadoCompatibilidade.INCOMPATÍVEL) { | ||
yield PortugolCodeError.fromContext( | ||
attr.ctx, | ||
`Não é possível atribuir um valor do tipo '${tret}' a uma variável do tipo '${svar.primitivo}'`, | ||
); | ||
} | ||
} catch (e) { | ||
const message = e instanceof Error ? e.message : "Não foi possível resolver o tipo da expressão"; | ||
|
||
const tipo = escopoFunção.funções.get(arg.nome); | ||
|
||
if (tipo?.primitivo === "vazio") { | ||
yield PortugolCodeError.fromContext(arg.ctx, `A função '${arg.nome}' não retorna um valor`); | ||
if (message === "TODO") { | ||
break; | ||
} | ||
|
||
yield PortugolCodeError.fromContext(attr.ctx, message); | ||
} | ||
} | ||
} | ||
|
||
// TODO: array e matriz | ||
|
||
break; | ||
|
||
case EnquantoCmd: | ||
case EscolhaCmd: | ||
case FaçaEnquantoCmd: | ||
case Função: | ||
case ParaCmd: | ||
escopo.push(); | ||
yield* varrerNós(nó.children); | ||
escopo.pop(); | ||
break; | ||
|
||
case SeCmd: | ||
const se = nó as SeCmd; | ||
|
||
escopo.push(); | ||
yield* varrerNó(se.condição); | ||
yield* varrerNós(se.instruções); | ||
escopo.pop(); | ||
|
||
if (se.senão) { | ||
escopo.push(); | ||
yield* varrerNós(se.senão.instruções); | ||
escopo.pop(); | ||
} | ||
|
||
break; | ||
|
||
default: | ||
yield* varrerNós(nó.children); | ||
break; | ||
} | ||
} | ||
|
||
yield* varrerNó(arquivo); | ||
} |
Oops, something went wrong.