From 7fd97ed0b393c46e504ba1925ba08a844e6c6335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Dobrovoljc?= Date: Mon, 19 May 2025 14:48:38 +0200 Subject: [PATCH] WIP Code Gen --- src/pins25/common/PDM.java | 494 +++++++++++++++++++++++++++ src/pins25/phase/CodeGen.java | 566 ++++++++++++++++++++++++++++++ src/pins25/phase/Machine.java | 624 ++++++++++++++++++++++++++++++++++ 3 files changed, 1684 insertions(+) create mode 100644 src/pins25/common/PDM.java create mode 100644 src/pins25/phase/CodeGen.java create mode 100644 src/pins25/phase/Machine.java diff --git a/src/pins25/common/PDM.java b/src/pins25/common/PDM.java new file mode 100644 index 0000000..a1f856b --- /dev/null +++ b/src/pins25/common/PDM.java @@ -0,0 +1,494 @@ +package pins25.common; + +/** + * Ukazi skladovnega stroja. + * + * Vse spremenljivke (in parametri, ki so samo posebna vrsta spremenljivke), + * katerih ime se zacne z {@code debug}, so lahko nastavljene na {@code null}. + * Uporabljajo se samo za izpis sledenja delovanja skladovnega stroja. + */ +public class PDM { + + @SuppressWarnings({ "doclint:missing" }) + public PDM() { + throw new Report.InternalError(); + } + + /** + * Vrsta ukazov skladovnega stroja. + */ + public static interface Instruction { + + /** + * Vrne dolzino ukaza. + * + * @return Dolzina ukaza. + */ + Integer size(); + + } + + /** Ukazi skladovnega stroja za opis podatkov. */ + public static interface DataInstr extends Instruction { + } + + /** Ukazi skladovnega stroja za opis kode programa. */ + public static interface CodeInstr extends Instruction { + } + + /** + * Ukaz skladovnega stroja. + */ + public static abstract class INSTR implements Instruction { + + /** Lokacija dela izvorne kode, ki se prevede v ta ukaz. */ + public final Report.Location debugLocation; + + /** + * Ustvari nov ukaz skladovnega stroja. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public INSTR(final Report.Locatable debugLocation) { + this.debugLocation = debugLocation == null ? null : debugLocation.location(); + } + + @Override + public Integer size() { + return 1; + } + + } + + /** + * Oznaka. + */ + public static class LABEL extends INSTR implements DataInstr, CodeInstr { + + /** Ime oznake. */ + public final String name; + + /** + * Ustvari nov ukaz {@link LABEL}. + * + * @param name Ime oznake. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public LABEL(final String name, final Report.Locatable debugLocation) { + super(debugLocation); + this.name = name; + } + + @Override + public Integer size() { + return 0; + } + + @Override + public String toString() { + return "LABEL " + name; + } + + } + + /** + * Dodeljevanje prostora v staticnem pomnilniku. + */ + public static class SIZE extends INSTR implements DataInstr { + + /** Velikost prostora v pomnilniku. */ + public final Integer size; + + /** + * Ustvari nov ukaz {@link SIZE}. + * + * @param size Velikost prostora v pomnilniku. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public SIZE(final Integer size, final Report.Locatable debugLocation) { + super(debugLocation); + this.size = size; + } + + @Override + public Integer size() { + return size; + } + + @Override + public String toString() { + return "SIZE " + size; + } + + } + + /** + * Konstanta v v staticnem pomnilniku. + */ + public static class DATA extends INSTR implements DataInstr { + + /** + * Vrednost konstante. + */ + public final Integer intc; + + /** + * Ustvari nov ukaz {@link SIZE}. + * + * @param intc Vrednost konstante. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public DATA(final Integer intc, final Report.Locatable debugLocation) { + super(debugLocation); + this.intc = intc; + } + + @Override + public Integer size() { + return 4; + } + + @Override + public String toString() { + return "DATA " + intc; + } + + } + + /** + * Inicializacija spremenljivke. + */ + public static class INIT extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link INIT}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public INIT(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "INIT"; + } + + } + + /** + * Prenos vrednosti iz pomnilnika na sklad. + */ + public static class LOAD extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link LOAD}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public LOAD(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "LOAD"; + } + + } + + /** + * Prenos vrednosti s sklada v pomnilnik. + */ + public static class SAVE extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link SAVE}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public SAVE(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "SAVE"; + } + + } + + /** + * Spreminjanje lokacija vrha sklada. + */ + public static class POPN extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link POPN}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public POPN(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "POPN"; + } + + } + + /** + * Prenos konstante na sklad. + */ + public static class PUSH extends INSTR implements CodeInstr { + + /** Konstanta. */ + public final Integer intc; + + /** + * Ustvari nok ukaz {@link PUSH}. + * + * @param intc Konstanta. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public PUSH(final Integer intc, final Report.Locatable debugLocation) { + super(debugLocation); + this.intc = intc; + } + + @Override + public Integer size() { + return super.size() + 4; + } + + @Override + public String toString() { + return "PUSH " + intc; + } + + } + + /** + * Prenos imena oznake na sklad. + */ + public static class NAME extends INSTR implements CodeInstr { + + /** Ime oznake. */ + public final String name; + + /** + * Ustvari nok ukaz {@link NAME}. + * + * @param name Ime oznake. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public NAME(final String name, final Report.Locatable debugLocation) { + super(debugLocation); + this.name = name; + } + + @Override + public Integer size() { + return super.size() + 4; + } + + @Override + public String toString() { + return "NAME " + name; + } + + } + + /** + * Prenos vrednosti registra na sklad. + */ + public static class REGN extends INSTR implements CodeInstr { + + /** Imena registrov. */ + public enum Reg { + /** Programski stevec. */ + PC, + /** Klicni kazalec. */ + FP, + /** Skladovni zapis. */ + SP, + } + + /** Ime registra. */ + public final Reg regn; + + /** + * Ustvari nok ukaz {@link REGN}. + * + * @param regn Ime registra. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public REGN(Reg regn, final Report.Locatable debugLocation) { + super(debugLocation); + this.regn = regn; + } + + @Override + public String toString() { + return "REGN." + regn; + } + + } + + /** + * Izvedba racunske operacije. + */ + public static class OPER extends INSTR implements CodeInstr { + + /** Vrste racunskih operacij. */ + public enum Oper { + /** Negacija. */ + NOT, + /** Sprememba predznaka. */ + NEG, + /** Disjunkcija. */ + OR, + /** Konjunkcija. */ + AND, + /** Enakonst. */ + EQU, + /** Neenakost. */ + NEQ, + /** Vecji kot. */ + GTH, + /** Manjsi kot. */ + LTH, + /** Vecji ali enak. */ + GEQ, + /** Manjsi ali enak. */ + LEQ, + /** Sestevanje. */ + ADD, + /** Odstevanje. */ + SUB, + /** Mnozenje. */ + MUL, + /** Deljenje. */ + DIV, + /** Modulo. */ + MOD, + } + + /** Racunska operacija. */ + public final Oper oper; + + /** + * Ustvari nok ukaz {@link OPER}. + * + * @param oper Racunska operacija. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public OPER(final Oper oper, final Report.Locatable debugLocation) { + super(debugLocation); + this.oper = oper; + } + + @Override + public String toString() { + return "OPER." + oper; + } + + } + + /** + * Brezpogojni skok. + */ + public static class UJMP extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link UJMP}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public UJMP(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "UJMP"; + } + + } + + /** + * Pogojni skok. + */ + public static class CJMP extends INSTR implements CodeInstr { + + /** + * Ustvari nok ukaz {@link CJMP}. + * + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public CJMP(final Report.Locatable debugLocation) { + super(debugLocation); + } + + @Override + public String toString() { + return "CJMP"; + } + + } + + /** + * Klic podprograma. + */ + public static class CALL extends INSTR implements CodeInstr { + + /** Klicni zapis klicanega podprograma. */ + public final Mem.Frame debugFrame; + + /** + * Ustvari nok ukaz {@link CALL}. + * + * @param debugFrame Klicni zapis klicanega podprograma. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public CALL(final Mem.Frame debugFrame, final Report.Locatable debugLocation) { + super(debugLocation); + this.debugFrame = debugFrame; + } + + @Override + public String toString() { + return "CALL"; + } + + } + + /** + * Vrnitev iz podprograma. + */ + public static class RETN extends INSTR implements CodeInstr { + + /** Klicni zapis vracajocega se podprograma. */ + public final Mem.Frame debugFrame; + + /** + * Ustvari nok ukaz {@link RETN}. + * + * @param debugFrame Klicni zapis vracajocega se programa. + * @param debugLocation Lokacija dela izvorne kode, ki se prevede v ta ukaz. + */ + public RETN(final Mem.Frame debugFrame, final Report.Locatable debugLocation) { + super(debugLocation); + this.debugFrame = debugFrame; + } + + @Override + public String toString() { + return "RETN"; + } + + } + +} \ No newline at end of file diff --git a/src/pins25/phase/CodeGen.java b/src/pins25/phase/CodeGen.java new file mode 100644 index 0000000..9e61b0f --- /dev/null +++ b/src/pins25/phase/CodeGen.java @@ -0,0 +1,566 @@ +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() { + } + + @Override + public List visit(AST.FunDef funDef, Mem.Frame frame) { + Report.Locatable loc = attrAST.attrLoc.get(funDef); + List code = new ArrayList<>(); + + code.add(new PDM.LABEL(funDef.name, 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 code; + } + + @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 = String.valueOf(labelCounter++); + 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)); + code.add(new PDM.LOAD(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<>(); + + // TODO: invert + 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); + } + + @Override + public List visit(AST.VarExpr varExpr, Mem.Frame frame) { + Report.Locatable loc = attrAST.attrLoc.get(varExpr); + List code = new ArrayList<>(); + + AST.VarDef def = (AST.VarDef) attrAST.attrDef.get(varExpr); + Mem.Access access = attrAST.attrVarAccess.get(def); + + switch (access) { + case Mem.AbsAccess absAccess: + code.add(new PDM.NAME(absAccess.name, loc)); + code.add(new PDM.LOAD(loc)); + break; + case Mem.RelAccess relAccess: + code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc)); + code.add(new PDM.PUSH(relAccess.offset, loc)); + code.add(new PDM.OPER(PDM.OPER.Oper.ADD, loc)); + code.add(new PDM.LOAD(loc)); + break; + default: + } + + 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<>(unExpr.expr.accept(this, frame)); + + code.add(switch (unExpr.oper) { + case NOT -> new PDM.OPER(PDM.OPER.Oper.NOT, loc); + case ADD -> new PDM.OPER(PDM.OPER.Oper.ADD, loc); // TODO + case SUB -> new PDM.OPER(PDM.OPER.Oper.NEG, loc); + case MEMADDR -> new PDM.REGN(PDM.REGN.Reg.SP, loc); + case VALUEAT -> new PDM.LOAD(loc); + }); + + 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<>(); + + for (int i = 0; i < callExpr.args.size(); i++) { + AST.Expr expr = (AST.Expr) callExpr.args.get(i); + code.addAll(expr.accept(this, frame)); + code.add(new PDM.REGN(PDM.REGN.Reg.SP, loc)); + + code.add(new PDM.PUSH(0, loc)); + code.add(new PDM.OPER(PDM.OPER.Oper.ADD, loc)); + } + + code.add(new PDM.REGN(PDM.REGN.Reg.FP, loc)); // SL + + code.add(new PDM.NAME(callExpr.name, loc)); + code.add(new PDM.LOAD(loc)); + code.add(new PDM.CALL(frame, loc)); + + attrAST.attrCode.put(callExpr, 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); + } + } + +} \ No newline at end of file diff --git a/src/pins25/phase/Machine.java b/src/pins25/phase/Machine.java new file mode 100644 index 0000000..2f43723 --- /dev/null +++ b/src/pins25/phase/Machine.java @@ -0,0 +1,624 @@ +package pins25.phase; + +import java.util.*; + +import pins25.common.*; + +/** + * Skladovni stroj. + * + * Naslovi 'sistemskih' funkcij: + *
    + *
  1. {@code -1}: {@code fun exit(exitcode)}
  2. + *
  3. {@code -2}: {@code fun getint()}
  4. + *
  5. {@code -3}: {@code fun putint(intvalue)}
  6. + *
  7. {@code -4}: {@code fun getstr(straddr)}
  8. + *
  9. {@code -5}: {@code fun putstr(straddr)}
  10. + *
  11. {@code -6}: {@code fun new(size)}
  12. + *
  13. {@code -7}: {@code fun del(addr)}
  14. + *
+ */ +public class Machine { + + @SuppressWarnings({ "doclint:missing" }) + public Machine() { + throw new Report.InternalError(); + } + + /** Ali se opravi testni izpis ukazov. */ + public static boolean debugInstrsList = false; + + /** Ali se opravi testni izpis vrednost oznak. */ + public static boolean debugLabelsList = false; + + /** Ali se opravi testni izpis dogajanja na skladu. */ + public static boolean debugStack = false; + + /** + * Izvajanje skladovnega stroja. + */ + public static class Executor { + + /** Seznam ukazov kode programa. */ + private final HashMap program = new HashMap(); + + /** Pomnilnik (brez predstavitve ukazov. */ + private final HashMap memory = new HashMap(); + + /** Preslikava imen oznak v fizicne naslove. */ + private final HashMap labelToAddr = new HashMap(); + + /** Preslikava fizicnih naslovov v imena oznak. */ + private final HashMap addrToLabel = new HashMap(); + + /** Velikost segmenta z ukazi kode programa. */ + private final int codeSegmentSize; + + /** Velikost segmenta s staticnimi spremenljivkami. */ + private final int dataSegmentSize; + + /** Preslikava naslova v lokacijo kode, ki je izvor vrednosti na naslovu. */ + final HashMap debugLocs = new HashMap(); + + /** Preslikava naslova v pomen podatka, ki je shranjen na naslovu. */ + final HashMap debugDscs = new HashMap(); + + { + labelToAddr.put("exit", -1); + addrToLabel.put(-1, "exit"); + labelToAddr.put("getint", -2); + addrToLabel.put(-2, "getint"); + labelToAddr.put("putint", -3); + addrToLabel.put(-3, "putint"); + labelToAddr.put("getstr", -4); + addrToLabel.put(-4, "getstr"); + labelToAddr.put("putstr", -5); + addrToLabel.put(-5, "putstr"); + labelToAddr.put("new", -6); + addrToLabel.put(-6, "new"); + labelToAddr.put("del", -7); + addrToLabel.put(-7, "del"); + } + + /** Programski stevec. */ + private int PC; + + /** Klicni kazalec. */ + private int FP; + + /** Skladovni kazalec. */ + private int SP; + + /** Kazalec na prvi prosti naslov na kopici. */ + private int HP; + + /** + * Shrani vrednost v pomnilnik. + * + * @param addr Pomnilniski naslov. + * @param value Vrednost. + * @param debugInstr Lokacija dela izvorne kode, ki zahteva shranjevanje. + */ + private void memSAVE(int addr, int value, final PDM.INSTR debugInstr) { + if (addr < codeSegmentSize) + throw new Report.InternalError(); + if (debugStack && (debugInstr != null) && (debugInstr.debugLocation != null)) + debugLocs.put(addr, debugInstr.debugLocation.toString()); + for (int b = 0; b < 4; b++) { + int val = ((value >> (b * 8)) & 0xFF); + memory.put(addr, (byte) val); + addr += 1; + } + } + + /** + * Prebere vrednost iz pomnilnika. + * + * @param addr Pomnilniski naslov. + * @return Vrednost. + */ + private int memLOAD(int addr) { + if (addr < codeSegmentSize) + throw new Report.InternalError(); + int value = 0; + for (int b = 0; b < 4; b++) { + Byte val = memory.get(addr); + addr += 1; + if (val == null) + val = 0; + value = value | (((val < 0) ? ((int) val) + 256 : ((int) val)) << (b * 8)); + } + return value; + } + + /** + * Prenos nove vrednosti na sklad. + * + * @param value Vrednost. + * @param debugInstr Lokacija dela izvorne kode, ki prenos nove vrednosti na + * sklad. + */ + private void push(final int value, final PDM.INSTR debugInstr) { + SP -= 4; + memSAVE(SP, value, debugInstr); + } + + /** + * Prevzem vrednost z vrha sklada. + * + * @return Vrednost. + */ + private int pop() { + if (debugStack) + debugLocs.put(SP, null); + final int value = memLOAD(SP); + SP += 4; + return value; + } + + /** + * Ustvari nov skladovni stroj za podan program in ta program izvede. + * + * @param codeSegment Seznam ukazov, ki predstavljajo kodo programa. + * @param dataSegment Seznam ukazov, ki predstavljajo podatke programa. + */ + public Executor(final List codeSegment, final List dataSegment) { + + Scanner scanner = new Scanner(System.in); + + int memPtr = 0; + + if (debugLabelsList) + System.out.println("\n\033[1mCODE LABELS:\033[0m"); + for (final PDM.CodeInstr instr : codeSegment) { + switch (instr) { + case PDM.LABEL i -> { + labelToAddr.put(i.name, memPtr); + addrToLabel.put(memPtr, i.name); + if (debugLabelsList) + System.out.printf("LABEL %s = %d\n", i.name, memPtr); + memPtr -= 1; + } + case PDM.INIT i -> program.put(memPtr, i); + case PDM.LOAD i -> program.put(memPtr, i); + case PDM.SAVE i -> program.put(memPtr, i); + case PDM.POPN i -> program.put(memPtr, i); + case PDM.PUSH i -> { + program.put(memPtr, i); + memPtr += 4; + } + case PDM.NAME i -> { + program.put(memPtr, i); + memPtr += 4; + } + case PDM.REGN i -> program.put(memPtr, i); + case PDM.OPER i -> program.put(memPtr, i); + case PDM.UJMP i -> program.put(memPtr, i); + case PDM.CJMP i -> program.put(memPtr, i); + case PDM.CALL i -> program.put(memPtr, i); + case PDM.RETN i -> program.put(memPtr, i); + default -> throw new Report.InternalError(); + } + memPtr += 1; + } + codeSegmentSize = memPtr; + + if (debugLabelsList) + System.out.println("\n\033[1mDATA LABELS:\033[0m"); + for (final PDM.DataInstr instr : dataSegment) { + switch (instr) { + case PDM.LABEL i -> { + labelToAddr.put(i.name, memPtr); + addrToLabel.put(memPtr, i.name); + if (debugLabelsList) + System.out.printf("LABEL %s = %d\n", i.name, memPtr); + } + case PDM.SIZE i -> { + memPtr += i.size; + } + case PDM.DATA i -> { + memSAVE(memPtr, i.intc, i); + memPtr += 4; + } + default -> throw new Report.InternalError(); + } + } + dataSegmentSize = memPtr - codeSegmentSize; + + PC = 0; + FP = 0x10000; + SP = 0x10000; + HP = codeSegmentSize + dataSegmentSize; + + push(-1, null); + FP = SP + 0; + push(-1, null); + SP = SP + 0; + + System.out.printf("\n"); + loop: while (true) { + + if (debugStack) { + for (int stackAddr = 0x10000 - 4; stackAddr >= SP; stackAddr -= 4) { + final String debugLoc = debugLocs.get(stackAddr); + System.out.printf("%15s ", debugLoc == null ? "" : debugLoc); + if (stackAddr == FP) + System.out.printf("FP => "); + else if (stackAddr == SP) + System.out.printf("SP => "); + else + System.out.printf(" "); + System.out.printf("%6d: %12d", stackAddr, memLOAD(stackAddr)); + final String debugDsc = debugDscs.get(stackAddr); + System.out.printf(" %s", debugDsc == null ? "" : debugDsc); + System.out.printf("\n"); + } + System.out.printf("\n"); + } + + final PDM.CodeInstr instr = program.get(PC); + if (debugStack) { + System.out.printf("\033[1m%15s %5d: %s\033[0m\n\n", + ((PDM.INSTR) instr).debugLocation == null ? "" : ((PDM.INSTR) instr).debugLocation, PC, + instr.toString()); + } + + switch (instr) { + case PDM.INIT i: { + int initAddr = pop(); + int dstAddr = pop(); + final int numInits = memLOAD(initAddr); + initAddr += 4; + for (int nInit = 0; nInit < numInits; nInit++) { + int num = memLOAD(initAddr); + initAddr += 4; + int len = memLOAD(initAddr); + initAddr += 4; + for (int n = 0; n < num; n++) { + for (int l = 0; l < len; l++) { + memSAVE(dstAddr, memLOAD(initAddr + 4 * l), i); + dstAddr += 4; + } + } + initAddr += 4 * len; + } + PC += i.size(); + break; + } + case PDM.LOAD i: { + int addr = pop(); + int value = memLOAD(addr); + push(value, i); + PC += i.size(); + break; + } + case PDM.SAVE i: { + final int addr = pop(); + final int value = pop(); + memSAVE(addr, value, i); + PC += i.size(); + break; + } + case PDM.POPN i: { + int n = pop(); + if (n < 0) { + while (n < 0) { + push(0, i); + n += 4; + } + } else { + while (n > 0) { + pop(); + n -= 4; + } + } + PC += i.size(); + break; + } + case PDM.PUSH i: { + push(i.intc, i); + PC += i.size(); + break; + } + case PDM.NAME i: { + push(labelToAddr.get(i.name), i); + PC += i.size(); + break; + } + case PDM.REGN i: { + final int value = switch (i.regn) { + case PC -> PC; + case FP -> FP; + case SP -> SP; + default -> throw new Report.InternalError(); + }; + push(value, i); + PC += i.size(); + break; + } + case PDM.OPER i: { + switch (i.oper) { + case NOT: + case NEG: { + final int expr = pop(); + final int result = switch (i.oper) { + case NOT -> (expr == 0) ? 1 : 0; + case NEG -> -expr; + default -> throw new Report.InternalError(); + }; + push(result, i); + break; + } + case OR: + case AND: + case EQU: + case NEQ: + case GTH: + case LTH: + case GEQ: + case LEQ: + case ADD: + case SUB: + case MUL: + case DIV: + case MOD: { + final int snd = pop(); + final int fst = pop(); + int result = switch (i.oper) { + case OR -> (fst != 0) || (snd != 0) ? 1 : 0; + case AND -> (fst != 0) && (snd != 0) ? 1 : 0; + case EQU -> fst == snd ? 1 : 0; + case NEQ -> fst != snd ? 1 : 0; + case GTH -> fst > snd ? 1 : 0; + case LTH -> fst < snd ? 1 : 0; + case GEQ -> fst >= snd ? 1 : 0; + case LEQ -> fst <= snd ? 1 : 0; + case ADD -> fst + snd; + case SUB -> fst - snd; + case MUL -> fst * snd; + case DIV -> fst / snd; + case MOD -> fst % snd; + default -> throw new Report.InternalError(); + }; + push(result, i); + break; + } + default: + throw new Report.InternalError(); + } + PC += i.size(); + break; + } + case PDM.UJMP i: { + PC = pop(); + break; + } + case PDM.CJMP i: { + final int elsePC = pop(); + final int thenPC = pop(); + final int cond = pop(); + PC = (cond != 0) ? thenPC : elsePC; + break; + } + case PDM.CALL i: { + final int newPC = pop(); + if (newPC < 0) { + switch (newPC) { + case -1: { // exit(exitcode) + pop(); // SL + final int exitCode = pop(); + pop(); + pop(); + System.out.printf("EXIT CODE (SP=%d): %d\n", SP, exitCode); + break loop; + } + case -2: { // getint() + pop(); // SL + final int intValue = scanner.nextInt(); + push(intValue, null); // result + PC += i.size(); + break; + } + case -3: { // putint(intvalue) + pop(); // SL + final int intValue = pop(); + System.out.printf("%d", intValue); + push(1, null); // result + PC += i.size(); + break; + } + case -4: { // getstr(straddr) + pop(); // SL + int strAddr = pop(); + final String strValue = scanner.nextLine(); + for (int c = 0; c < strValue.length(); c++) { + memSAVE(strAddr, strValue.charAt(c), null); + strAddr += 4; + } + memSAVE(strAddr, 0, null); + push(1, null); // result + PC += i.size(); + break; + } + case -5: { // putstr(straddr) + pop(); // SL + int strAddr = pop(); + while (true) { + int c = memLOAD(strAddr); + if (c == 0) + break; + System.out.printf("%c", c); + strAddr += 4; + } + push(1, null); // result + PC += i.size(); + break; + } + case -6: { // new(size) + pop(); // SL + final int size = pop(); + final int addr = HP; + for (int a = addr; a < addr + size; a++) + memory.put(a, (byte) 0); + HP += size; + push(addr, null); // result + PC += i.size(); + break; + } + case -7: { // del(addr) + pop(); // SL + pop(); // addr + push(1, null); // result + PC += i.size(); + break; + } + default: + throw new Report.InternalError(); + } + } else { + if (debugStack) { + debugDscs.put(SP, "... SL"); + debugDscs.put(SP - 4, + "... FP *** " + (i.debugFrame == null ? "" : i.debugFrame.name) + " ***"); + debugDscs.put(SP - 8, "... RA "); + if (i.debugFrame != null) { + if (i.debugFrame.debugPars != null) + for (final Mem.RelAccess relAccess : i.debugFrame.debugPars) + if (relAccess.debugName != null) + debugDscs.put(SP + relAccess.offset, "... par: " + relAccess.debugName); + if (i.debugFrame.debugVars != null) + for (final Mem.RelAccess relAccess : i.debugFrame.debugVars) + if (relAccess.debugName != null) { + if (relAccess.size == 4) + debugDscs.put(SP + relAccess.offset, "... var: " + relAccess.debugName); + else { + for (int s = 0; s < relAccess.size; s += 4) + debugDscs.put(SP + relAccess.offset + s, + "... var: " + relAccess.debugName + "[" + (s / 4) + "]"); + } + } + } + } + push(FP, i); + push(PC + i.size(), i); + FP = SP + 8; + PC = newPC; + } + break; + } + case PDM.RETN i: { + if (debugStack) { + debugDscs.put(FP, null); + debugDscs.put(FP - 4, null); + debugDscs.put(FP - 8, null); + if (i.debugFrame != null) { + if (i.debugFrame.debugPars != null) + for (final Mem.RelAccess relAccess : i.debugFrame.debugPars) + if (relAccess.debugName != null) + debugDscs.put(FP + relAccess.offset, null); + if (i.debugFrame.debugVars != null) + for (final Mem.RelAccess relAccess : i.debugFrame.debugVars) + if (relAccess.debugName != null) { + if (relAccess.size == 4) + debugDscs.put(FP + relAccess.offset, null); + else { + for (int s = 0; s < relAccess.size; s += 4) + debugDscs.put(FP + relAccess.offset + s, null); + } + } + } + } + int parsSize = pop(); + final int result = pop(); + PC = memLOAD(FP - 8); + while (SP != FP) { + pop(); + } + // SP = FP; + FP = memLOAD(FP - 4); + parsSize += 4; + while (parsSize > 0) { + pop(); + parsSize -= 4; + } + push(result, i); + break; + } + default: + throw new Report.InternalError(); + } + } + + scanner.close(); + } + + } + + // --- 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 (pushdown machine):"); + + 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 CodeGen.AttrAST codegenAttrAST = CodeGen.generate(memoryAttrAST); + + final List codeSegment = (new CodeGen.CodeSegmentGenerator(codegenAttrAST)) + .codeSegment(); + final List dataSegment = (new CodeGen.DataSegmentGenerator(codegenAttrAST)) + .dataSegment(); + + if (debugInstrsList) { + int addr = 0; + { + 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(); + } + } + { + 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(); + } + } + } + + // ustvari nov stroj in izvede program: + new Executor(codeSegment, dataSegment); + } + + // 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); + } + } + +} \ No newline at end of file