WIP Code Gen

This commit is contained in:
Gašper Dobrovoljc 2025-05-19 14:48:38 +02:00
parent b50325d3a5
commit 7fd97ed0b3
No known key found for this signature in database
GPG Key ID: 0E7E037018CFA5A5
3 changed files with 1684 additions and 0 deletions

494
src/pins25/common/PDM.java Normal file
View File

@ -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";
}
}
}

View File

@ -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.
* <p>
* 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>
* <li>({@link Memory}) klicni zapis funkcije;</li>
* <li>({@link Memory}) dostop do parametra;</li>
* <li>({@link Memory}) dostop do spremenljivke;</li>
* <li>({@link CodeGen}) seznam ukazov, ki predstavljajo kodo programa;</li>
* <li>({@link CodeGen}) seznam ukazov, ki predstavljajo podatke programa.</li>
* </ol>
*/
public static class AttrAST extends Memory.AttrAST {
/**
* Atribut: seznam ukazov, ki predstavljajo kodo programa.
*/
public final Map<AST.Node, List<PDM.CodeInstr>> attrCode;
/**
* Atribut: seznam ukazov, ki predstavljajo podatke programa.
*/
public final Map<AST.Node, List<PDM.DataInstr>> 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<AST.Node, List<PDM.CodeInstr>> attrCode,
final Map<AST.Node, List<PDM.DataInstr>> 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<PDM.CodeInstr> 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<PDM.DataInstr> 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<AST.Node, List<PDM.CodeInstr>>(),
new HashMap<AST.Node, List<PDM.DataInstr>>());
(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<List<PDM.CodeInstr>, Mem.Frame> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public List<PDM.CodeInstr> visit(AST.FunDef funDef, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(funDef);
List<PDM.CodeInstr> 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<PDM.CodeInstr> visit(AST.AtomExpr atomExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(atomExpr);
List<PDM.CodeInstr> 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<PDM.DataInstr> 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<PDM.CodeInstr> visit(AST.BinExpr binExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(binExpr);
List<PDM.CodeInstr> 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<PDM.CodeInstr> visit(AST.Nodes<? extends AST.Node> nodes, Mem.Frame frame) {
List<PDM.CodeInstr> code = new ArrayList<>();
for (AST.Node node : nodes) {
code.addAll(node.accept(this, frame));
}
attrAST.attrCode.put(nodes, code);
return code;
}
@Override
public List<PDM.CodeInstr> visit(AST.ExprStmt exprStmt, Mem.Frame frame) {
return exprStmt.expr.accept(this, frame);
}
@Override
public List<PDM.CodeInstr> visit(AST.VarExpr varExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(varExpr);
List<PDM.CodeInstr> 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<PDM.CodeInstr> visit(AST.UnExpr unExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(unExpr);
List<PDM.CodeInstr> 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<PDM.CodeInstr> visit(AST.CallExpr callExpr, Mem.Frame frame) {
Report.Locatable loc = attrAST.attrLoc.get(callExpr);
List<PDM.CodeInstr> 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<PDM.CodeInstr> codeInitSegment = new Vector<PDM.CodeInstr>();
/**
* Seznam ukazov funkcij.
*/
private final Vector<PDM.CodeInstr> codeFunsSegment = new Vector<PDM.CodeInstr>();
/**
* 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<PDM.CodeInstr> 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<PDM.CodeInstr> codeSegment = new Vector<PDM.CodeInstr>();
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<Object, Object> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public Object visit(final AST.FunDef funDef, final Object arg) {
if (funDef.stmts.size() == 0)
return null;
List<PDM.CodeInstr> 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<PDM.CodeInstr> 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<PDM.DataInstr> dataSegment = new Vector<PDM.DataInstr>();
/**
* 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<PDM.DataInstr> 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<Object, Object> {
@SuppressWarnings({"doclint:missing"})
public Generator() {
}
@Override
public Object visit(final AST.VarDef varDef, final Object arg) {
List<PDM.DataInstr> 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<PDM.DataInstr> 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<PDM.CodeInstr> 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<PDM.DataInstr> 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);
}
}
}

View File

@ -0,0 +1,624 @@
package pins25.phase;
import java.util.*;
import pins25.common.*;
/**
* Skladovni stroj.
*
* Naslovi 'sistemskih' funkcij:
* <ol>
* <li>{@code -1}: {@code fun exit(exitcode)}</li>
* <li>{@code -2}: {@code fun getint()}</li>
* <li>{@code -3}: {@code fun putint(intvalue)}</li>
* <li>{@code -4}: {@code fun getstr(straddr)}</li>
* <li>{@code -5}: {@code fun putstr(straddr)}</li>
* <li>{@code -6}: {@code fun new(size)}</li>
* <li>{@code -7}: {@code fun del(addr)}</li>
* </ol>
*/
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<Integer, PDM.CodeInstr> program = new HashMap<Integer, PDM.CodeInstr>();
/** Pomnilnik (brez predstavitve ukazov. */
private final HashMap<Integer, Byte> memory = new HashMap<Integer, Byte>();
/** Preslikava imen oznak v fizicne naslove. */
private final HashMap<String, Integer> labelToAddr = new HashMap<String, Integer>();
/** Preslikava fizicnih naslovov v imena oznak. */
private final HashMap<Integer, String> addrToLabel = new HashMap<Integer, String>();
/** 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<Integer, String> debugLocs = new HashMap<Integer, String>();
/** Preslikava naslova v pomen podatka, ki je shranjen na naslovu. */
final HashMap<Integer, String> debugDscs = new HashMap<Integer, String>();
{
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<PDM.CodeInstr> codeSegment, final List<PDM.DataInstr> 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<PDM.CodeInstr> codeSegment = (new CodeGen.CodeSegmentGenerator(codegenAttrAST))
.codeSegment();
final List<PDM.DataInstr> 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);
}
}
}