package pins25.phase; import java.util.*; import pins25.common.*; /** * Generiranje kode. */ public class CodeGen { @SuppressWarnings({"doclint:missing"}) public CodeGen() { throw new Report.InternalError(); } /** * Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. *

* Atributi: *

    *
  1. ({@link Abstr}) lokacija kode, ki pripada posameznemu vozliscu;
  2. *
  3. ({@link SemAn}) definicija uporabljenega imena;
  4. *
  5. ({@link SemAn}) ali je dani izraz levi izraz;
  6. *
  7. ({@link Memory}) klicni zapis funkcije;
  8. *
  9. ({@link Memory}) dostop do parametra;
  10. *
  11. ({@link Memory}) dostop do spremenljivke;
  12. *
  13. ({@link CodeGen}) seznam ukazov, ki predstavljajo kodo programa;
  14. *
  15. ({@link CodeGen}) seznam ukazov, ki predstavljajo podatke programa.
  16. *
*/ public static class AttrAST extends Memory.AttrAST { /** * Atribut: seznam ukazov, ki predstavljajo kodo programa. */ public final Map> attrCode; /** * Atribut: seznam ukazov, ki predstavljajo podatke programa. */ public final Map> attrData; /** * Ustvari novo abstraktno sintaksno drevo z dodanimi atributi generiranja kode. * * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi pomnilniske * predstavitve. * @param attrCode Attribut: seznam ukazov, ki predstavljajo kodo programa. * @param attrData Attribut: seznam ukazov, ki predstavljajo podatke programa. */ public AttrAST(final Memory.AttrAST attrAST, final Map> attrCode, final Map> attrData) { super(attrAST); this.attrCode = attrCode; this.attrData = attrData; } /** * Ustvari novo abstraktno sintaksno drevo z dodanimi atributi generiranja kode. * * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi generiranja * kode. */ public AttrAST(final AttrAST attrAST) { super(attrAST); this.attrCode = attrAST.attrCode; this.attrData = attrAST.attrData; } @Override public String head(final AST.Node node, final boolean highlighted) { final StringBuffer head = new StringBuffer(); head.append(super.head(node, false)); return head.toString(); } @Override public void desc(final int indent, final AST.Node node, final boolean highlighted) { super.desc(indent, node, false); System.out.print(highlighted ? "\033[31m" : ""); if (attrCode.get(node) != null) { List instrs = attrCode.get(node); if (instrs != null) { if (indent > 0) System.out.printf("%" + indent + "c", ' '); System.out.printf("--- Code: ---\n"); for (final PDM.CodeInstr instr : instrs) { if (indent > 0) System.out.printf("%" + indent + "c", ' '); System.out.println((instr instanceof PDM.LABEL ? "" : " ") + instr.toString()); } } } if (attrData.get(node) != null) { List instrs = attrData.get(node); if (instrs != null) { if (indent > 0) System.out.printf("%" + indent + "c", ' '); System.out.printf("--- Data: ---\n"); for (final PDM.DataInstr instr : instrs) { if (indent > 0) System.out.printf("%" + indent + "c", ' '); System.out.println((instr instanceof PDM.LABEL ? "" : " ") + instr.toString()); } } } System.out.print(highlighted ? "\033[30m" : ""); return; } } /** * Izracuna kodo programa * * @param memoryAttrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna * pomnilniske predstavitve. * @return Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. */ public static AttrAST generate(final Memory.AttrAST memoryAttrAST) { AttrAST attrAST = new AttrAST(memoryAttrAST, new HashMap>(), new HashMap>()); (new CodeGenerator(attrAST)).generate(); return attrAST; } /** * Generiranje kode v abstraktnem sintaksnem drevesu. */ private static class CodeGenerator { /** * Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. */ private final AttrAST attrAST; /** * Stevec anonimnih label. */ private int labelCounter = 0; /** * Ustvari nov generator kode v abstraktnem sintaksnem drevesu. * * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna * pomnilniske predstavitve. */ public CodeGenerator(final AttrAST attrAST) { this.attrAST = attrAST; } /** * Sprozi generiranje kode v abstraktnem sintaksnem drevesu. * * @return Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. */ public AttrAST generate() { attrAST.ast.accept(new Generator(), null); return new AttrAST(attrAST, Collections.unmodifiableMap(attrAST.attrCode), Collections.unmodifiableMap(attrAST.attrData)); } /** * Obiskovalec, ki generira kodo v abstraktnem sintaksnem drevesu. */ private class Generator implements AST.FullVisitor, Mem.Frame> { @SuppressWarnings({"doclint:missing"}) public Generator() { } private String funLabel(AST.FunDef funDef) { if (funDef.name.equals("main") || funDef.stmts.size() == 0) { return funDef.name; } Report.Location loc = attrAST.attrLoc.get(funDef).location(); return "$fun:" + funDef.name + "@" + loc.begLine() + ":" + loc.begColumn(); } @Override public List visit(AST.FunDef funDef, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(funDef); List code = new ArrayList<>(); frame = attrAST.attrFrame.get(funDef); code.add(new PDM.LABEL(funLabel(funDef), loc)); code.addAll(funDef.stmts.accept(this, frame)); code.add(new PDM.PUSH(attrAST.attrFrame.get(funDef).parsSize, loc)); code.add(new PDM.RETN(attrAST.attrFrame.get(funDef), loc)); attrAST.attrCode.put(funDef, code); return new ArrayList<>(); } @Override public List visit(AST.ParDef parDef, Mem.Frame frame) { return new ArrayList<>(); } @Override public List visit(AST.AtomExpr atomExpr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(atomExpr); List code = new ArrayList<>(); switch (atomExpr.type) { case INTCONST: code.add(new PDM.PUSH(Memory.decodeIntConst(atomExpr, loc), loc)); break; case CHRCONST: code.add(new PDM.PUSH(Memory.decodeChrConst(atomExpr, loc), loc)); break; case STRCONST: List data = new ArrayList<>(); String label = "$str@" + loc.location().begLine() + ":" + loc.location().begColumn(); data.add(new PDM.LABEL(label, loc)); for (Integer value : Memory.decodeStrConst(atomExpr, loc)) { data.add(new PDM.DATA(value, loc)); } attrAST.attrData.put(atomExpr, data); code.add(new PDM.NAME(label, loc)); break; } attrAST.attrCode.put(atomExpr, code); return code; } @Override public List visit(AST.BinExpr binExpr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(binExpr); List code = new ArrayList<>(); code.addAll(binExpr.fstExpr.accept(this, frame)); code.addAll(binExpr.sndExpr.accept(this, frame)); PDM.OPER.Oper oper = switch (binExpr.oper) { case OR -> PDM.OPER.Oper.OR; case AND -> PDM.OPER.Oper.AND; case EQU -> PDM.OPER.Oper.EQU; case NEQ -> PDM.OPER.Oper.NEQ; case GTH -> PDM.OPER.Oper.GTH; case LTH -> PDM.OPER.Oper.LTH; case GEQ -> PDM.OPER.Oper.GEQ; case LEQ -> PDM.OPER.Oper.LEQ; case ADD -> PDM.OPER.Oper.ADD; case SUB -> PDM.OPER.Oper.SUB; case MUL -> PDM.OPER.Oper.MUL; case DIV -> PDM.OPER.Oper.DIV; case MOD -> PDM.OPER.Oper.MOD; }; code.add(new PDM.OPER(oper, loc)); attrAST.attrCode.put(binExpr, code); return code; } @Override public List visit(AST.Nodes nodes, Mem.Frame frame) { List code = new ArrayList<>(); for (AST.Node node : nodes) { code.addAll(node.accept(this, frame)); } attrAST.attrCode.put(nodes, code); return code; } @Override public List visit(AST.ExprStmt exprStmt, Mem.Frame frame) { return exprStmt.expr.accept(this, frame); } private List resolveAddr(AST.Expr expr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(expr); List code = new ArrayList<>(); switch (expr) { case AST.UnExpr unExpr: if (unExpr.oper != AST.UnExpr.Oper.VALUEAT) { return code; } code.addAll(unExpr.expr.accept(this, frame)); break; case AST.VarExpr varExpr: AST.Def def = attrAST.attrDef.get(varExpr); Mem.Access access = switch (def) { case AST.VarDef varDef -> attrAST.attrVarAccess.get(varDef); case AST.ParDef parDef -> attrAST.attrParAccess.get(parDef); default -> null; }; if (access == null) { return code; } switch (access) { case Mem.AbsAccess absAccess: code.add(new PDM.NAME("$var:" + absAccess.name, loc)); break; case Mem.RelAccess relAccess: code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc)); for (int i = 0; i < frame.depth - relAccess.depth; i++) { code.add(new PDM.LOAD(loc)); } code.add(new PDM.PUSH(relAccess.offset, loc)); code.add(new PDM.OPER(PDM.OPER.Oper.ADD, loc)); break; default: } break; default: break; } return code; } @Override public List visit(AST.VarExpr varExpr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(varExpr); List code = resolveAddr(varExpr, frame); code.add(new PDM.LOAD(loc)); attrAST.attrCode.put(varExpr, code); return code; } @Override public List visit(AST.UnExpr unExpr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(unExpr); List code = new ArrayList<>(); if (unExpr.oper != AST.UnExpr.Oper.MEMADDR) { code.addAll(unExpr.expr.accept(this, frame)); } switch (unExpr.oper) { case NOT -> code.add(new PDM.OPER(PDM.OPER.Oper.NOT, loc)); case SUB -> code.add(new PDM.OPER(PDM.OPER.Oper.NEG, loc)); case VALUEAT -> code.add(new PDM.LOAD(loc)); case MEMADDR -> { if (unExpr.expr instanceof AST.VarExpr varExpr) { code.addAll(resolveAddr(varExpr, frame)); } else { throw new Report.Error(loc, "Expression not a variable"); } } case ADD -> { } } attrAST.attrCode.put(unExpr, code); return code; } @Override public List visit(AST.CallExpr callExpr, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(callExpr); List code = new ArrayList<>(); AST.FunDef funDef = (AST.FunDef) attrAST.attrDef.get(callExpr); for (int i = callExpr.args.size() - 1; i >= 0; i--) { AST.Expr expr = (AST.Expr) callExpr.args.get(i); code.addAll(expr.accept(this, frame)); } // SL code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc)); if (attrAST.attrFrame.get(funDef).depth.equals(frame.depth)) { code.add(new PDM.LOAD(loc)); } code.add(new PDM.NAME(funLabel(funDef), loc)); code.add(new PDM.CALL(frame, loc)); attrAST.attrCode.put(callExpr, code); return code; } @Override public List visit(AST.VarDef varDef, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(varDef); List code = new ArrayList<>(); List data = new ArrayList<>(); Mem.Access access = attrAST.attrVarAccess.get(varDef); switch (access) { case Mem.AbsAccess absAccess -> { String varLabel = "$var:" + absAccess.name; data.add(new PDM.LABEL(varLabel, loc)); for (int i = 0; i < absAccess.size / 4; i++) { data.add(new PDM.DATA(0, loc)); } String initLabel = "$init:" + absAccess.name; data.add(new PDM.LABEL(initLabel, loc)); for (Integer init : absAccess.inits) { data.add(new PDM.DATA(init, loc)); } code.add(new PDM.NAME(varLabel, loc)); code.add(new PDM.NAME(initLabel, loc)); code.add(new PDM.INIT(loc)); } case Mem.RelAccess relAccess -> { String initLabel = "$init:" + varDef.name + "@" + loc.location().begLine() + ":" + loc.location().begColumn(); data.add(new PDM.LABEL(initLabel, loc)); for (Integer init : relAccess.inits) { data.add(new PDM.DATA(init, loc)); } code.add(new PDM.PUSH(-relAccess.size, loc)); code.add(new PDM.POPN(loc)); code.add(new PDM.REGN(PDM.REGN.Reg.SP, loc)); code.add(new PDM.NAME(initLabel, loc)); code.add(new PDM.INIT(loc)); } default -> { } } attrAST.attrCode.put(varDef, code); attrAST.attrData.put(varDef, data); return code; } @Override public List visit(AST.Init init, Mem.Frame frame) { return new ArrayList<>(); } @Override public List visit(AST.AssignStmt assignStmt, Mem.Frame frame) { Report.Locatable loc = attrAST.attrLoc.get(assignStmt); List code = new ArrayList<>(); code.addAll(assignStmt.srcExpr.accept(this, frame)); code.addAll(resolveAddr(assignStmt.dstExpr, frame)); code.add(new PDM.SAVE(loc)); attrAST.attrCode.put(assignStmt, code); return code; } @Override public List visit(AST.LetStmt letStmt, Mem.Frame frame) { List code = new ArrayList<>(); for (AST.MainDef def : letStmt.defs) { code.addAll(def.accept(this, frame)); } for (AST.Stmt stmt : letStmt.stmts) { code.addAll(stmt.accept(this, frame)); } attrAST.attrCode.put(letStmt, code); return code; } @Override public List visit(AST.IfStmt ifStmt, Mem.Frame frame) { Report.Location loc = attrAST.attrLoc.get(ifStmt).location(); String locStr = loc.begLine() + ":" + loc.begColumn(); String thenLabel = "$if:then@" + locStr; String elseLabel = "$if:else@" + locStr; String endLabel = "$if:end@" + locStr; List code = ifStmt.cond.accept(this, frame); code.add(new PDM.NAME(thenLabel, loc)); code.add(new PDM.NAME(elseLabel, loc)); code.add(new PDM.CJMP(loc)); code.add(new PDM.LABEL(thenLabel, loc)); code.addAll(ifStmt.thenStmts.accept(this, frame)); code.add(new PDM.NAME(endLabel, loc)); code.add(new PDM.UJMP(loc)); code.add(new PDM.LABEL(elseLabel, loc)); code.addAll(ifStmt.elseStmts.accept(this, frame)); code.add(new PDM.LABEL(endLabel, loc)); attrAST.attrCode.put(ifStmt, code); return code; } @Override public List visit(AST.WhileStmt whileStmt, Mem.Frame frame) { Report.Location loc = attrAST.attrLoc.get(whileStmt).location(); List code = new ArrayList<>(); String locStr = loc.begLine() + ":" + loc.begColumn(); String condLabel = "$while:cond@" + locStr; String begLabel = "$while:beg@" + locStr; String endLabel = "$while:end@" + locStr; code.add(new PDM.LABEL(condLabel, loc)); code.addAll(whileStmt.cond.accept(this, frame)); code.add(new PDM.NAME(begLabel, loc)); code.add(new PDM.NAME(endLabel, loc)); code.add(new PDM.CJMP(loc)); code.add(new PDM.LABEL(begLabel, loc)); code.addAll(whileStmt.stmts.accept(this, frame)); code.add(new PDM.NAME(condLabel, loc)); code.add(new PDM.UJMP(loc)); code.add(new PDM.LABEL(endLabel, loc)); attrAST.attrCode.put(whileStmt, code); return code; } } } /** * Generator seznama ukazov, ki predstavljajo kodo programa. */ public static class CodeSegmentGenerator { /** * Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. */ private final AttrAST attrAST; /** * Seznam ukazov za inicializacijo staticnih spremenljivk. */ private final Vector codeInitSegment = new Vector(); /** * Seznam ukazov funkcij. */ private final Vector codeFunsSegment = new Vector(); /** * Klicni zapis funkcije {@code main}. */ private Mem.Frame main = null; /** * Ustvari nov generator seznama ukazov, ki predstavljajo kodo programa. * * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna * pomnilniske predstavitve. */ public CodeSegmentGenerator(final AttrAST attrAST) { this.attrAST = attrAST; } /** * Izracuna seznam ukazov, ki predstavljajo kodo programa. * * @return Seznam ukazov, ki predstavljajo kodo programa. */ public List codeSegment() { attrAST.ast.accept(new Generator(), null); codeInitSegment.addLast(new PDM.PUSH(0, null)); codeInitSegment.addLast(new PDM.NAME("main", null)); codeInitSegment.addLast(new PDM.CALL(main, null)); codeInitSegment.addLast(new PDM.PUSH(0, null)); codeInitSegment.addLast(new PDM.NAME("exit", null)); codeInitSegment.addLast(new PDM.CALL(null, null)); final Vector codeSegment = new Vector(); codeSegment.addAll(codeInitSegment); codeSegment.addAll(codeFunsSegment); return Collections.unmodifiableList(codeSegment); } /** * Obiskovalec, ki izracuna seznam ukazov, ki predstavljajo kodo programa. */ private class Generator implements AST.FullVisitor { @SuppressWarnings({"doclint:missing"}) public Generator() { } @Override public Object visit(final AST.FunDef funDef, final Object arg) { if (funDef.stmts.size() == 0) return null; List code = attrAST.attrCode.get(funDef); codeFunsSegment.addAll(code); funDef.pars.accept(this, arg); funDef.stmts.accept(this, arg); switch (funDef.name) { case "main" -> main = attrAST.attrFrame.get(funDef); } return null; } @Override public Object visit(final AST.VarDef varDef, final Object arg) { switch (attrAST.attrVarAccess.get(varDef)) { case Mem.AbsAccess __: { List code = attrAST.attrCode.get(varDef); codeInitSegment.addAll(code); break; } case Mem.RelAccess __: { break; } default: throw new Report.InternalError(); } return null; } } } /** * Generator seznama ukazov, ki predstavljajo podatke programa. */ public static class DataSegmentGenerator { /** * Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske * predstavitve. */ private final AttrAST attrAST; /** * Seznam ukazov, ki predstavljajo podatke programa. */ private final Vector dataSegment = new Vector(); /** * Ustvari nov generator seznama ukazov, ki predstavljajo podatke programa. * * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna * pomnilniske predstavitve. */ public DataSegmentGenerator(final AttrAST attrAST) { this.attrAST = attrAST; } /** * Izracuna seznam ukazov, ki predstavljajo podatke programa. * * @return Seznam ukazov, ki predstavljajo podatke programa. */ public List dataSegment() { attrAST.ast.accept(new Generator(), null); return Collections.unmodifiableList(dataSegment); } /** * Obiskovalec, ki izracuna seznam ukazov, ki predstavljajo podatke programa. */ private class Generator implements AST.FullVisitor { @SuppressWarnings({"doclint:missing"}) public Generator() { } @Override public Object visit(final AST.VarDef varDef, final Object arg) { List data = attrAST.attrData.get(varDef); if (data != null) dataSegment.addAll(data); varDef.inits.accept(this, arg); return null; } @Override public Object visit(final AST.AtomExpr atomExpr, final Object arg) { List data = attrAST.attrData.get(atomExpr); if (data != null) dataSegment.addAll(data); return null; } } } // --- ZAGON --- /** * Zagon izracuna pomnilniske predstavitve kot samostojnega programa. * * @param cmdLineArgs Argumenti v ukazni vrstici. */ public static void main(final String[] cmdLineArgs) { System.out.println("This is PINS'25 compiler (code generation):"); 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); // pomnilniska predstavitev: final Memory.AttrAST memoryAttrAST = Memory.organize(semanAttrAST); // generiranje kode: final AttrAST codegenAttrAST = CodeGen.generate(memoryAttrAST); (new AST.Logger(codegenAttrAST)).log(); { int addr = 0; final List codeSegment = (new CodeSegmentGenerator(codegenAttrAST)).codeSegment(); { System.out.println("\n\033[1mCODE SEGMENT:\033[0m"); for (final PDM.CodeInstr instr : codeSegment) { System.out.printf("%8d [%s] %s\n", addr, instr.size(), (instr instanceof PDM.LABEL ? "" : " ") + instr.toString()); addr += instr.size(); } } final List dataSegment = (new DataSegmentGenerator(codegenAttrAST)).dataSegment(); { System.out.println("\n\033[1mDATA SEGMENT:\033[0m"); for (final PDM.DataInstr instr : dataSegment) { System.out.printf("%8d [%s] %s\n", addr, (instr instanceof PDM.SIZE) ? " " : instr.size(), (instr instanceof PDM.LABEL ? "" : " ") + instr.toString()); addr += instr.size(); } } System.out.println(); } } // 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); } } }