SemAn - LVal
This commit is contained in:
parent
b688385835
commit
e868f3030c
605
src/pins25/phase/SemAn.java
Normal file
605
src/pins25/phase/SemAn.java
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
package pins25.phase;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import pins25.common.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Semanticni analizator.
|
||||||
|
*/
|
||||||
|
public class SemAn {
|
||||||
|
|
||||||
|
@SuppressWarnings({"doclint:missing"})
|
||||||
|
public SemAn() {
|
||||||
|
throw new Report.InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
* <p>
|
||||||
|
* Dodani atributi:
|
||||||
|
* <ol>
|
||||||
|
* <li>({@link Abstr}) lokacija kode, ki pripada posameznemu vozliscu;</li>
|
||||||
|
* <li>({@link SemAn}) definicija uporabljenega imena;</li>
|
||||||
|
* <li>({@link SemAn}) ali je dani izraz levi izraz.</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
public static class AttrAST extends Abstr.AttrAST {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atribut: definicija uporabljenega imena.
|
||||||
|
*/
|
||||||
|
public final Map<AST.NameExpr, AST.Def> attrDef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Atribut: ali je dani izraz levi izraz.
|
||||||
|
*/
|
||||||
|
public final Map<AST.Expr, Boolean> attrLVal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari novo abstraktno sintaksno drevo z dodanim atributi semanticne
|
||||||
|
* analize.
|
||||||
|
*
|
||||||
|
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi abstraktne
|
||||||
|
* sintakse.
|
||||||
|
* @param attrDef Atribut: definicija uporabljenega imena.
|
||||||
|
* @param attrLVal Atribut: ali je dani izraz levi izraz.
|
||||||
|
*/
|
||||||
|
public AttrAST(final Abstr.AttrAST attrAST, final Map<AST.NameExpr, AST.Def> attrDef,
|
||||||
|
final Map<AST.Expr, Boolean> attrLVal) {
|
||||||
|
super(attrAST);
|
||||||
|
this.attrDef = attrDef;
|
||||||
|
this.attrLVal = attrLVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari novo abstraktno sintaksno drevo z dodanimi atributi semanticne
|
||||||
|
* analize.
|
||||||
|
*
|
||||||
|
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi semanticne
|
||||||
|
* analize.
|
||||||
|
*/
|
||||||
|
public AttrAST(final AttrAST attrAST) {
|
||||||
|
super(attrAST);
|
||||||
|
this.attrDef = attrAST.attrDef;
|
||||||
|
this.attrLVal = attrAST.attrLVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String head(final AST.Node node, final boolean highlighted) {
|
||||||
|
final StringBuffer head = new StringBuffer();
|
||||||
|
head.append(super.head(node, false));
|
||||||
|
switch (node) {
|
||||||
|
case final AST.NameExpr nameExpr:
|
||||||
|
final AST.Def def = attrDef.get(nameExpr);
|
||||||
|
if (def == null)
|
||||||
|
break;
|
||||||
|
final Report.Locatable loc = attrLoc.get(def);
|
||||||
|
if (loc == null)
|
||||||
|
break;
|
||||||
|
head.append((" ") + (highlighted ? "\033[31m" : "") + "def@" + loc.location().toString()
|
||||||
|
+ (highlighted ? "\033[30m" : ""));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (node) {
|
||||||
|
case final AST.Expr expr:
|
||||||
|
final Boolean lval = attrLVal.get(expr);
|
||||||
|
if (lval == null)
|
||||||
|
break;
|
||||||
|
if (lval)
|
||||||
|
head.append((" ") + (highlighted ? "\033[31m" : "") + "lval" + (highlighted ? "\033[30m" : ""));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return head.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opravi semanticno analizo.
|
||||||
|
*
|
||||||
|
* @param abstrAttrAST Abstraktno sintaksno drevo z dodanimi atributi abstraktne
|
||||||
|
* sintakse.
|
||||||
|
* @return Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
*/
|
||||||
|
public static AttrAST analyze(Abstr.AttrAST abstrAttrAST) {
|
||||||
|
AttrAST attrAST = new AttrAST(abstrAttrAST, new HashMap<AST.NameExpr, AST.Def>(),
|
||||||
|
new HashMap<AST.Expr, Boolean>());
|
||||||
|
attrAST = new NameResolver(attrAST).resolve();
|
||||||
|
attrAST = new TypeResolver(attrAST).resolve();
|
||||||
|
attrAST = new LValResolver(attrAST).resolve();
|
||||||
|
return attrAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Razresevanje imen.
|
||||||
|
*/
|
||||||
|
private static class NameResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
*/
|
||||||
|
private final AttrAST attrAST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari nov razresevalnik imen.
|
||||||
|
*
|
||||||
|
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi semanticne
|
||||||
|
* analize.
|
||||||
|
*/
|
||||||
|
public NameResolver(final AttrAST attrAST) {
|
||||||
|
this.attrAST = attrAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprozi razresevanje imen.
|
||||||
|
*
|
||||||
|
* @return Abstraktno sintaksno drevo z dodanimi atributi semanticne analize
|
||||||
|
* ({@link AttrAST#attrDef} izracunan in nespremenljiv).
|
||||||
|
*/
|
||||||
|
public AttrAST resolve() {
|
||||||
|
attrAST.ast.accept(new ResolverVisitor(), null);
|
||||||
|
return new AttrAST(attrAST, Collections.unmodifiableMap(attrAST.attrDef), attrAST.attrLVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simbolna tabela, ki se uporablja med razresevanjem imen.
|
||||||
|
*/
|
||||||
|
private final SymbolTable symbolTable = new SymbolTable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simbolna tabela.
|
||||||
|
*/
|
||||||
|
private class SymbolTable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Definicija v trenutnem dosega na dani staticni globini.
|
||||||
|
*
|
||||||
|
* @param depth Staticna globina definicije.
|
||||||
|
* @param def Definicija.
|
||||||
|
*/
|
||||||
|
private record ScopedDef(int depth, AST.Def def) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preslikava imena v seznam definicij tega imena na razlicnih staticnih
|
||||||
|
* globinah.
|
||||||
|
*/
|
||||||
|
private final HashMap<String, LinkedList<ScopedDef>> namesToDefs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seznami imen definiranih na posameznih staticnih globinah.
|
||||||
|
*/
|
||||||
|
private final LinkedList<LinkedList<String>> namesToDefsByDepth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trenutna staticna globina.
|
||||||
|
*/
|
||||||
|
private int depth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari novo simbolno tabelo.
|
||||||
|
*/
|
||||||
|
public SymbolTable() {
|
||||||
|
namesToDefs = new HashMap<String, LinkedList<ScopedDef>>();
|
||||||
|
namesToDefsByDepth = new LinkedList<LinkedList<String>>();
|
||||||
|
depth = -1;
|
||||||
|
newScope();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pripravi simbolno tabelo za vstavljanje definicij imen v novem dosegu.
|
||||||
|
*/
|
||||||
|
public void newScope() {
|
||||||
|
depth++;
|
||||||
|
namesToDefsByDepth.addFirst(new LinkedList<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Razveljavi trenutni doseg.
|
||||||
|
*/
|
||||||
|
public void oldScope() {
|
||||||
|
for (final String name : namesToDefsByDepth.getFirst()) {
|
||||||
|
final LinkedList<ScopedDef> defsOfName = namesToDefs.get(name);
|
||||||
|
if (defsOfName.size() == 1)
|
||||||
|
namesToDefs.remove(name);
|
||||||
|
else
|
||||||
|
defsOfName.removeFirst();
|
||||||
|
}
|
||||||
|
namesToDefsByDepth.removeFirst();
|
||||||
|
depth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vstavi novo definicijo imena v trenutni doseg.
|
||||||
|
*
|
||||||
|
* @param def Definicija imena.
|
||||||
|
* @return {@code true}, ce je vstavitev mozna (pred to vstavitvijo v tem dosegu
|
||||||
|
* se ni definicije tega imena), ali {@code false}, ce vstavitev ni
|
||||||
|
* mozna (pred to vstavitvijo je v tem dosegu ze definicija tega imena).
|
||||||
|
*/
|
||||||
|
public boolean ins(final AST.Def def) {
|
||||||
|
final LinkedList<ScopedDef> defsOfOldName = namesToDefs.get(def.name);
|
||||||
|
if (defsOfOldName == null) {
|
||||||
|
final LinkedList<ScopedDef> defsOfNewName = new LinkedList<ScopedDef>();
|
||||||
|
defsOfNewName.addFirst(new ScopedDef(depth, def));
|
||||||
|
namesToDefs.put(def.name, defsOfNewName);
|
||||||
|
namesToDefsByDepth.getFirst().add(def.name);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (defsOfOldName.getFirst().depth == depth)
|
||||||
|
return false;
|
||||||
|
defsOfOldName.addFirst(new ScopedDef(depth, def));
|
||||||
|
namesToDefsByDepth.getFirst().add(def.name);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vrne definicijo imena.
|
||||||
|
*
|
||||||
|
* @param name Ime.
|
||||||
|
* @return Definicija imena ali {@code null}, ce ime ni definirano v tem in
|
||||||
|
* obsegajocih dosegih.
|
||||||
|
*/
|
||||||
|
public AST.Def fnd(final String name) {
|
||||||
|
final LinkedList<ScopedDef> defsOfName = namesToDefs.get(name);
|
||||||
|
return (defsOfName == null) ? null : defsOfName.getFirst().def();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obiskovalec za razresevanje imen.
|
||||||
|
*/
|
||||||
|
private class ResolverVisitor implements AST.FullVisitor<Object, ResolverVisitor.Pass> {
|
||||||
|
|
||||||
|
@SuppressWarnings({"doclint:missing"})
|
||||||
|
public ResolverVisitor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dva preleta abstraktnega sintaksnega drevesa med razresevanjem imen.
|
||||||
|
* <p>
|
||||||
|
* Med prvim preletom se obdelajo definicije funkcij in spremenljivk (ne pa tudi
|
||||||
|
* telesa funkcij), med drugim preletom se obdela vse ostalo (tudi telesa
|
||||||
|
* funkcij). Oba preleta se prepletata in se razcepita le pri obdelavi zaporedja
|
||||||
|
* vozlisc.
|
||||||
|
*/
|
||||||
|
private enum Pass {
|
||||||
|
/**
|
||||||
|
* Prelet definicij funkcij in spremenljivk.
|
||||||
|
*/
|
||||||
|
Defs,
|
||||||
|
/**
|
||||||
|
* Prelet vsega razen definicij funkcij in spremenljivk.
|
||||||
|
*/
|
||||||
|
Rest,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.Nodes<? extends AST.Node> nodes, final Pass pass) {
|
||||||
|
for (final AST.Node node : nodes) {
|
||||||
|
switch (node) {
|
||||||
|
case final AST.FunDef funDef:
|
||||||
|
funDef.accept(this, Pass.Defs);
|
||||||
|
break;
|
||||||
|
case final AST.VarDef varDef:
|
||||||
|
varDef.accept(this, Pass.Defs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (final AST.Node node : nodes) {
|
||||||
|
switch (node) {
|
||||||
|
case final AST.FunDef funDef:
|
||||||
|
funDef.accept(this, Pass.Rest);
|
||||||
|
break;
|
||||||
|
case final AST.VarDef varDef:
|
||||||
|
varDef.accept(this, Pass.Rest);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
node.accept(this, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.FunDef funDef, final Pass pass) {
|
||||||
|
switch (pass) {
|
||||||
|
case Defs: {
|
||||||
|
if (!symbolTable.ins(funDef))
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(funDef),
|
||||||
|
"Illegal definition of function '" + funDef.name + "'.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Rest: {
|
||||||
|
symbolTable.newScope();
|
||||||
|
funDef.pars.accept(this, null);
|
||||||
|
funDef.stmts.accept(this, null);
|
||||||
|
symbolTable.oldScope();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Report.InternalError();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.ParDef parDef, final Pass pass) {
|
||||||
|
if (!symbolTable.ins(parDef))
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(parDef),
|
||||||
|
"Illegal definition of parameter '" + parDef.name + "'.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.VarDef varDef, final Pass pass) {
|
||||||
|
switch (pass) {
|
||||||
|
case Defs: {
|
||||||
|
if (!symbolTable.ins(varDef))
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(varDef),
|
||||||
|
"Illegal definition of variable '" + varDef.name + "'.");
|
||||||
|
varDef.inits.accept(this, null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Rest: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Report.InternalError();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.LetStmt letStmt, final Pass pass) {
|
||||||
|
symbolTable.newScope();
|
||||||
|
letStmt.defs.accept(this, null);
|
||||||
|
letStmt.stmts.accept(this, null);
|
||||||
|
symbolTable.oldScope();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.VarExpr varExpr, final Pass pass) {
|
||||||
|
final AST.Def def = symbolTable.fnd(varExpr.name);
|
||||||
|
if (def == null)
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(varExpr), "Undefined name '" + varExpr.name + "'.");
|
||||||
|
attrAST.attrDef.put(varExpr, def);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.CallExpr callExpr, final Pass pass) {
|
||||||
|
final AST.Def def = symbolTable.fnd(callExpr.name);
|
||||||
|
if (def == null)
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(callExpr), "Undefined name '" + callExpr.name + "'.");
|
||||||
|
attrAST.attrDef.put(callExpr, def);
|
||||||
|
callExpr.args.accept(this, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preverjanje tipov.
|
||||||
|
*/
|
||||||
|
private static class TypeResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
*/
|
||||||
|
private final AttrAST attrAST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari nov razresevalnik imen.
|
||||||
|
*
|
||||||
|
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi semanticne
|
||||||
|
* analize
|
||||||
|
*/
|
||||||
|
public TypeResolver(final AttrAST attrAST) {
|
||||||
|
this.attrAST = attrAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprozi razresevanje imen.
|
||||||
|
*
|
||||||
|
* @return Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
*/
|
||||||
|
public AttrAST resolve() {
|
||||||
|
attrAST.ast.accept(new ResolverVisitor(), null);
|
||||||
|
return new AttrAST(attrAST, attrAST.attrDef, attrAST.attrLVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obiskovalec za preverjanje tipov.
|
||||||
|
*/
|
||||||
|
private class ResolverVisitor implements AST.FullVisitor<Object, ResolverVisitor.Pass> {
|
||||||
|
|
||||||
|
@SuppressWarnings({"doclint:missing"})
|
||||||
|
public ResolverVisitor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dva preleta abstraktnega sintaksnega drevesa med razresevanjem imen.
|
||||||
|
* <p>
|
||||||
|
* Med prvim preletom se obdelajo definicije funkcij in spremenljivk (ne pa tudi
|
||||||
|
* telesa funkcij), med drugim preletom se obdela vse ostalo (tudi telesa
|
||||||
|
* funkcij). Oba preleta se prepletata in se razcepita le pri obdelavi zaporedja
|
||||||
|
* vozlisc.
|
||||||
|
*/
|
||||||
|
private enum Pass {
|
||||||
|
/**
|
||||||
|
* Prelet definicij funkcij in spremenljivk.
|
||||||
|
*/
|
||||||
|
Defs,
|
||||||
|
/**
|
||||||
|
* Prelet vsega razen definicij funkcij in spremenljivk.
|
||||||
|
*/
|
||||||
|
Rest,
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.FunDef funDef, final Pass pass) {
|
||||||
|
funDef.pars.accept(this, pass);
|
||||||
|
funDef.stmts.accept(this, pass);
|
||||||
|
if (funDef.stmts.size() != 0) {
|
||||||
|
AST.Stmt lastStmt = funDef.stmts.getAll().getLast();
|
||||||
|
loop:
|
||||||
|
while (true) {
|
||||||
|
switch (lastStmt) {
|
||||||
|
case AST.ExprStmt exprStmt:
|
||||||
|
break loop;
|
||||||
|
case AST.LetStmt letStmt:
|
||||||
|
if (letStmt.stmts.size() != 0) {
|
||||||
|
lastStmt = letStmt.stmts.getAll().getLast();
|
||||||
|
} else
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(funDef),
|
||||||
|
"Function '" + funDef.name + "' does not return any value.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(funDef),
|
||||||
|
"Function '" + funDef.name + "' does not return any value.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.VarExpr varExpr, final Pass pass) {
|
||||||
|
switch (attrAST.attrDef.get(varExpr)) {
|
||||||
|
case final AST.VarDef varDef:
|
||||||
|
break;
|
||||||
|
case final AST.ParDef parDef:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(varExpr),
|
||||||
|
"'" + varExpr.name + "' is not a variable or a parameter.");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visit(final AST.CallExpr callExpr, final Pass pass) {
|
||||||
|
switch (attrAST.attrDef.get(callExpr)) {
|
||||||
|
case final AST.FunDef funDef: {
|
||||||
|
if (funDef.pars.size() != callExpr.args.size())
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(callExpr),
|
||||||
|
"Illegal number of arguments in a call of function '" + callExpr.name + "'.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(callExpr), "'" + callExpr.name + "' is not a function.");
|
||||||
|
}
|
||||||
|
callExpr.args.accept(this, null);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preverjanje levih vrednosti.
|
||||||
|
*/
|
||||||
|
private static class LValResolver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraktno sintaksno drevo z dodanimi atributi semanticne analize.
|
||||||
|
*/
|
||||||
|
private final AttrAST attrAST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ustvari nov razresevalnik levih vrednosti.
|
||||||
|
*
|
||||||
|
* @param attrAST Abstraktno sintaksno drevo z dodanimi atributi semanticne
|
||||||
|
* analize.
|
||||||
|
*/
|
||||||
|
public LValResolver(final AttrAST attrAST) {
|
||||||
|
this.attrAST = attrAST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprozi preverjanje levih vrednosti.
|
||||||
|
*
|
||||||
|
* @return Abstraktno sintaksno drevo z dodanimi atributi semanticne analize
|
||||||
|
* ({@link AttrAST#attrLVal} izracunan in nespremenljiv).
|
||||||
|
*/
|
||||||
|
public AttrAST resolve() {
|
||||||
|
attrAST.ast.accept(new ResolverVisitor(), null);
|
||||||
|
return new AttrAST(attrAST, attrAST.attrDef, Collections.unmodifiableMap(attrAST.attrLVal));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obiskovalec za preverjanje levih vrednosti.
|
||||||
|
*/
|
||||||
|
private class ResolverVisitor implements AST.FullVisitor<Object, Object> {
|
||||||
|
@Override
|
||||||
|
public Object visit(AST.AssignStmt assignStmt, Object arg) {
|
||||||
|
switch (assignStmt.dstExpr) {
|
||||||
|
case AST.VarExpr varExpr:
|
||||||
|
attrAST.attrLVal.put(varExpr, true);
|
||||||
|
return null;
|
||||||
|
case AST.UnExpr unExpr:
|
||||||
|
if (unExpr.oper == AST.UnExpr.Oper.VALUEAT) {
|
||||||
|
attrAST.attrLVal.put(unExpr, true);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Report.Error(attrAST.attrLoc.get(assignStmt.dstExpr), "Illegal assignment target");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ZAGON ---
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zagon semanticne analize kot samostojnega programa.
|
||||||
|
*
|
||||||
|
* @param cmdLineArgs Argumenti v ukazni vrstici.
|
||||||
|
*/
|
||||||
|
public static void main(final String[] cmdLineArgs) {
|
||||||
|
System.out.println("This is PINS'25 compiler (semantic analysis):");
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (cmdLineArgs.length == 0)
|
||||||
|
throw new Report.Error("No source file specified in the command line.");
|
||||||
|
if (cmdLineArgs.length > 1)
|
||||||
|
Report.warning("Unused arguments in the command line.");
|
||||||
|
|
||||||
|
try (SynAn synAn = new SynAn(cmdLineArgs[0])) {
|
||||||
|
// abstraktna sintaksa:
|
||||||
|
final Abstr.AttrAST abstrAttrAST = Abstr.constructAST(synAn);
|
||||||
|
// semanticna analiza:
|
||||||
|
final SemAn.AttrAST semanAttrAST = SemAn.analyze(abstrAttrAST);
|
||||||
|
|
||||||
|
(new AST.Logger(semanAttrAST)).log();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upajmo, da kdaj pridemo to te tocke.
|
||||||
|
// A zavedajmo se sledecega:
|
||||||
|
// 1. Prevod je zaradi napak v programu lahko napacen :-o
|
||||||
|
// 2. Izvorni program se zdalec ni tisto, kar je programer hotel, da bi bil ;-)
|
||||||
|
Report.info("Done.");
|
||||||
|
} catch (Report.Error error) {
|
||||||
|
// Izpis opisa napake.
|
||||||
|
System.err.println(error.getMessage());
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user