diff --git a/.idea/misc.xml b/.idea/misc.xml index 56c0bcd..495fdee 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + - + \ No newline at end of file diff --git a/prg/Makefile b/prg/Makefile index 0ac3283..733e3a0 100644 --- a/prg/Makefile +++ b/prg/Makefile @@ -1,6 +1,7 @@ JAVA = java --enable-preview +PHASE = .PHONY : % % : %.pins25 - $(JAVA) -classpath ../bin pins25.phase.SynAn $< + $(JAVA) -classpath ../bin pins25.phase.$(PHASE) $< diff --git a/src/pins25/common/Mem.java b/src/pins25/common/Mem.java new file mode 100644 index 0000000..50ced73 --- /dev/null +++ b/src/pins25/common/Mem.java @@ -0,0 +1,153 @@ +package pins25.common; + +import java.util.*; + +/** + * Klicni zapisi in dostopi do spremenljivk. + * + * 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 abstraktnega skladovne + * stroja. + */ +public class Mem { + + @SuppressWarnings({ "doclint:missing" }) + private Mem() { + throw new Report.InternalError(); + } + + // --- KLICNI ZAPISI --- + + /** + * Klicni zapis. + */ + public static class Frame { + + /** Ime oznake, torej polno ime funkcije. */ + public final String name; + + /** Staticna globina funkcije. */ + public final Integer depth; + + /** Skupna velikost parametrov (skupaj s staticno povezavo). */ + public final Integer parsSize; + + /** + * Skupna velikost lokalnih spremenljivk (skupaj s shranjenim klicnim kazalcem + * in povratnim naslovom. + */ + public final Integer varsSize; + + /** Dostopi do parametrov. */ + public final List debugPars; + + /** Dostopi do lokalnih spremenljivk. */ + public final List debugVars; + + /** + * Ustvari nov klicni zapis. + * + * @param name Ime oznake, torej polno ime funkcije. + * @param depth Staticna globina funkcije. + * @param parsSize Skupna velikost parametrov (skupaj s staticno povezavo). + * @param varsSize Skupna velikost lokalnih spremenljivk (skupaj s shranjenim + * klicnim kazalcem in povratnim naslovom. + * @param debugPars Dostopi do parametrov. + * @param debugVars Dostopi do lokalnih spremenljivk. + */ + public Frame(final String name, final Integer depth, final Integer parsSize, final Integer varsSize, + List debugPars, final List debugVars) { + this.name = name; + this.depth = depth; + this.parsSize = parsSize; + this.varsSize = varsSize; + this.debugPars = Collections.unmodifiableList(debugPars); + this.debugVars = Collections.unmodifiableList(debugVars); + } + + } + + // --- DOSTOPI DO SPREMENLJIVK --- + + /** + * Dostop do spremenljivke. + */ + public static abstract class Access { + + /** Velikost spremenljivke. */ + public final Integer size; + + /** Zacetna vrednost spremenljivke. */ + public final List inits; + + /** + * Ustvari nov dostop do spremenljivke. + * + * @param size Velikost spremenljivke. + * @param inits Zacetna vrednost spremenljivke. + */ + public Access(final Integer size, final Vector inits) { + this.size = size; + this.inits = inits == null ? null : Collections.unmodifiableList(new Vector(inits)); + } + + } + + /** + * Absolutni dostop do spremenljivke (na staticen naslov). + */ + public static class AbsAccess extends Access { + + /** Ime oznake (ime spremenljivke). */ + public final String name; + + /** + * Ustvari nov absolutni dostop do spremenljivke. + * + * @param name Ime oznake (ime spremenljivke). + * @param size Velikost spremenljivke. + * @param inits Zacetna vrednost spremenljivke. + */ + public AbsAccess(final String name, final Integer size, final Vector inits) { + super(size, inits); + this.name = name; + } + + } + + /** + * Relativni dostop do spremenljivke (na skladu). + */ + public static class RelAccess extends Access { + + /** Odmik od vrha klicnega zapisa, torej od vrednosti klicnega kazalca. */ + public final Integer offset; + + /** Staticna globina spremenljivke. */ + public final Integer depth; + + /** Ime spremenljivke. */ + public final String debugName; + + /** + * Ustvari nov relativni dostop do spremenljivke. + * + * @param offset Odmik od vrha klicnega zapisa, torej od vrednosti klicnega + * kazalca. + * @param depth Staticna globina spremenljivke. + * @param size Velikost spremenljivke. + * @param inits Zacetna vrednost spremenljivke. + * @param debugName Ime spremenljivke. + */ + public RelAccess(final Integer offset, final Integer depth, Integer size, final Vector inits, + final String debugName) { + super(size, inits); + this.offset = offset; + this.depth = depth; + this.debugName = debugName; + } + + } + +} \ No newline at end of file diff --git a/src/pins25/common/Token.java b/src/pins25/common/Token.java index 86d06cb..2685303 100644 --- a/src/pins25/common/Token.java +++ b/src/pins25/common/Token.java @@ -149,46 +149,6 @@ public record Token(Report.Location location, Symbol symbol, String lexeme) impl * Simbol {@code )}. */ RPAREN; - - @Override - public String toString() { - return switch (this) { - case INTCONST -> "integer constant"; - case CHARCONST -> "character constant"; - case STRINGCONST -> "string constant"; - case IDENTIFIER -> "identifier"; - case FUN -> "'fun'"; - case VAR -> "'var'"; - case IF -> "'if'"; - case THEN -> "'then'"; - case ELSE -> "'else'"; - case WHILE -> "'while'"; - case DO -> "'do'"; - case LET -> "'let'"; - case IN -> "'in'"; - case END -> "'end'"; - case ASSIGN -> "'='"; - case COMMA -> "','"; - case AND -> "'&&'"; - case OR -> "'||'"; - case NOT -> "'!'"; - case EQU -> "'=='"; - case NEQ -> "'!='"; - case GTH -> "'>'"; - case LTH -> "'<'"; - case GEQ -> "'>='"; - case LEQ -> "'<='"; - case ADD -> "'+'"; - case SUB -> "'-'"; - case MUL -> "'*'"; - case DIV -> "'/'"; - case MOD -> "'%'"; - case PTR -> "'^'"; - case LPAREN -> "'('"; - case RPAREN -> "')'"; - default -> this.toString(); - }; - } } @Override diff --git a/src/pins25/phase/LexAn.java b/src/pins25/phase/LexAn.java index adcc575..888f692 100644 --- a/src/pins25/phase/LexAn.java +++ b/src/pins25/phase/LexAn.java @@ -409,8 +409,8 @@ public class LexAn implements AutoCloseable { nextChar(); if (buffChar == 'n' || buffChar == '\\' || buffChar == '"') { } else if (isHex()) { - nextChar(); lexeme.append((char) buffChar); + nextChar(); if (!isHex()) { throw new Report.Error(currentLocation(), "Invalid ascii code '" + (char) buffChar + "'."); } diff --git a/src/pins25/phase/Memory.java b/src/pins25/phase/Memory.java new file mode 100644 index 0000000..a2d4dc3 --- /dev/null +++ b/src/pins25/phase/Memory.java @@ -0,0 +1,422 @@ +package pins25.phase; + +import java.util.*; + +import pins25.common.*; + +/** + * Izracun pomnilniske predstavitve. + */ +public class Memory { + + @SuppressWarnings({"doclint:missing"}) + public Memory() { + 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. + *
+ */ + public static class AttrAST extends SemAn.AttrAST { + + /** + * Atribut: klicni zapis funkcije. + */ + public final Map attrFrame; + + /** + * Atribut: dostop do parametra. + */ + public final Map attrParAccess; + + /** + * Atribut: dostop do spremenljivke. + */ + public final Map attrVarAccess; + + /** + * Ustvari novo abstraktno sintaksno drevo z dodanimi atributi izracuna + * pomnilniske predstavitve. + * + * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi + * semanticne analize. + * @param attrFrame Attribut: klicni zapis funkcije. + * @param attrParAccess Attribut: dostop do parametra. + * @param attrVarAccess Attribut: dostop do spremenljivke. + */ + public AttrAST(final SemAn.AttrAST attrAST, final Map attrFrame, + final Map attrParAccess, final Map attrVarAccess) { + super(attrAST); + this.attrFrame = attrFrame; + this.attrParAccess = attrParAccess; + this.attrVarAccess = attrVarAccess; + } + + /** + * Ustvari novo abstraktno sintaksno drevo z dodanimi atributi izracuna + * pomnilniske predstavitve. + * + * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna + * pomnilniske predstavitve. + */ + public AttrAST(final AttrAST attrAST) { + super(attrAST); + this.attrFrame = attrAST.attrFrame; + this.attrParAccess = attrAST.attrParAccess; + this.attrVarAccess = attrAST.attrVarAccess; + } + + @Override + public String head(final AST.Node node, final boolean highlighted) { + final StringBuffer head = new StringBuffer(); + head.append(super.head(node, false)); + head.append(highlighted ? "\033[31m" : ""); + switch (node) { + case final AST.FunDef funDef: + Mem.Frame frame = attrFrame.get(funDef); + head.append(" depth=" + frame.depth); + head.append(" parsSize=" + frame.parsSize); + head.append(" varsSize=" + frame.varsSize); + break; + case final AST.ParDef parDef: { + Mem.RelAccess relAccess = attrParAccess.get(parDef); + head.append(" offset=" + relAccess.offset); + head.append(" size=" + relAccess.size); + head.append(" depth=" + relAccess.depth); + if (relAccess.inits != null) + initsToString(relAccess.inits, head); + break; + } + case final AST.VarDef varDef: { + Mem.Access access = attrVarAccess.get(varDef); + if (access != null) + switch (access) { + case final Mem.AbsAccess absAccess: + head.append(" size=" + absAccess.size); + if (absAccess.inits != null) + initsToString(absAccess.inits, head); + break; + case final Mem.RelAccess relAccess: + head.append(" offset=" + relAccess.offset); + head.append(" size=" + relAccess.size); + head.append(" depth=" + relAccess.depth); + if (relAccess.inits != null) + initsToString(relAccess.inits, head); + break; + default: + throw new Report.InternalError(); + } + break; + } + default: + break; + } + head.append(highlighted ? "\033[30m" : ""); + return head.toString(); + } + + /** + * Pripravi znakovno predstavitev zacetne vrednosti spremenmljivke. + * + * @param inits Zacetna vrednost spremenljivke. + * @param head Znakovno predstavitev zacetne vrednosti spremenmljivke. + */ + private void initsToString(final List inits, final StringBuffer head) { + head.append(" inits="); + int numPrintedVals = 0; + int valPtr = 1; + for (int init = 0; init < inits.get(0); init++) { + final int num = inits.get(valPtr++); + final int len = inits.get(valPtr++); + int oldp = valPtr; + for (int n = 0; n < num; n++) { + valPtr = oldp; + for (int l = 0; l < len; l++) { + if (numPrintedVals == 10) { + head.append("..."); + return; + } + head.append((numPrintedVals > 0 ? "," : "") + inits.get(valPtr++)); + numPrintedVals++; + } + } + } + } + + } + + /** + * Opravi izracun pomnilniske predstavitve. + * + * @param semanAttrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna + * pomnilniske predstavitve. + * @return Abstraktno sintaksno drevo z atributi po fazi pomnilniske + * predstavitve. + */ + public static AttrAST organize(SemAn.AttrAST semanAttrAST) { + AttrAST attrAST = new AttrAST(semanAttrAST, new HashMap(), + new HashMap(), new HashMap()); + (new MemoryOrganizer(attrAST)).organize(); + return attrAST; + } + + /** + * Organizator pomnilniske predstavitve. + */ + private static class MemoryOrganizer { + + /** + * Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske + * predstavitve. + */ + private final AttrAST attrAST; + + /** + * Ustvari nov organizator pomnilniske predstavitve. + * + * @param attrAST Abstraktno sintaksno drevo z dodanimi atributi izracuna + * pomnilniske predstavitve. + */ + public MemoryOrganizer(final AttrAST attrAST) { + this.attrAST = attrAST; + } + + /** + * Sprozi nov izracun pomnilniske predstavitve. + * + * @return Abstraktno sintaksno drevo z dodanimi atributi izracuna pomnilniske + * predstavitve. + */ + public AttrAST organize() { + attrAST.ast.accept(new MemoryVisitor(), null); + return new AttrAST(attrAST, Collections.unmodifiableMap(attrAST.attrFrame), + Collections.unmodifiableMap(attrAST.attrParAccess), + Collections.unmodifiableMap(attrAST.attrVarAccess)); + } + + static private class FrameBuilder { + int depth = 0; + int varOffset = 8; // FP + RA + Vector debugVars = new Vector<>(); + } + + /** + * Obiskovalec, ki izracuna pomnilnisko predstavitev. + */ + private class MemoryVisitor implements AST.FullVisitor { + + @SuppressWarnings({"doclint:missing"}) + public MemoryVisitor() { + } + + @Override + public FrameBuilder visit(AST.FunDef funDef, FrameBuilder upperFB) { + FrameBuilder frameBuilder = new FrameBuilder(); + if (upperFB != null) { + frameBuilder.depth = upperFB.depth + 1; + } + + int parOffset = 4; // +SL + Vector debugPars = new Vector<>(); + for (AST.ParDef parDef : funDef.pars) { + Vector inits = new Vector<>(); + inits.add(1); + inits.add(1); + inits.add(1); + inits.add(0); + Mem.RelAccess access = new Mem.RelAccess(parOffset, frameBuilder.depth, 4, inits, parDef.name); + parOffset += access.size; + debugPars.add(access); + attrAST.attrParAccess.put(parDef, access); + } + + AST.FullVisitor.super.visit(funDef, frameBuilder); + + Mem.Frame frame = new Mem.Frame( + funDef.name, + frameBuilder.depth, + parOffset, + frameBuilder.varOffset, + debugPars, + frameBuilder.debugVars + ); + attrAST.attrFrame.put(funDef, frame); + + return upperFB; + } + + @Override + public FrameBuilder visit(AST.VarDef varDef, FrameBuilder frameBuilder) { + Vector inits = new Vector<>(); + inits.add(varDef.inits.size()); + for (AST.Init init : varDef.inits) { + Report.Locatable valLoc = attrAST.attrLoc.get(init.value); + Report.Locatable numLoc = attrAST.attrLoc.get(init.num); + + inits.add(decodeIntConst(init.num, numLoc)); + + switch (init.value.type) { + case INTCONST: + inits.add(1); + inits.add(decodeIntConst(init.value, valLoc)); + break; + case CHRCONST: + inits.add(1); + inits.add(decodeChrConst(init.value, valLoc)); + break; + case STRCONST: + Vector str = decodeStrConst(init.value, valLoc); + inits.add(str.size()); + inits.addAll(str); + break; + } + } + + if (frameBuilder == null) { + Mem.AbsAccess access = new Mem.AbsAccess(varDef.name, inits.size(), inits); + attrAST.attrVarAccess.put(varDef, access); + } else { + Mem.RelAccess access = new Mem.RelAccess(frameBuilder.varOffset, frameBuilder.depth, inits.size(), inits, varDef.name); + frameBuilder.varOffset += inits.size(); + frameBuilder.debugVars.add(access); + attrAST.attrVarAccess.put(varDef, access); + } + + return frameBuilder; + } + } + + } + + /** + * Izracuna vrednost celostevilske konstante. + * + * @param intAtomExpr Celostevilska konstanta. + * @param loc Lokacija celostevilske konstante. + * @return Vrednost celostevilske konstante. + */ + public static Integer decodeIntConst(final AST.AtomExpr intAtomExpr, final Report.Locatable loc) { + try { + return Integer.decode(intAtomExpr.value); + } catch (NumberFormatException __) { + throw new Report.Error(loc, "Illegal integer value."); + } + } + + /** + * Izracuna vrednost znakovna konstante. + * + * @param chrAtomExpr Znakovna konstanta. + * @param loc Lokacija znakovne konstante. + * @return Vrednost znakovne konstante. + */ + public static Integer decodeChrConst(final AST.AtomExpr chrAtomExpr, final Report.Locatable loc) { + switch (chrAtomExpr.value.charAt(1)) { + case '\\': + switch (chrAtomExpr.value.charAt(2)) { + case 'n': + return 10; + case '\'': + return ((int) '\''); + case '\\': + return ((int) '\\'); + default: + return 16 * (((int) chrAtomExpr.value.charAt(2)) - ((int) '0')) + + (((int) chrAtomExpr.value.charAt(3)) - ((int) '0')); + } + default: + return ((int) chrAtomExpr.value.charAt(1)); + } + } + + /** + * Izracuna vrednost konstantnega niza. + * + * @param strAtomExpr Konstantni niz. + * @param loc Lokacija konstantnega niza. + * @return Vrendnost konstantega niza. + */ + public static Vector decodeStrConst(final AST.AtomExpr strAtomExpr, final Report.Locatable loc) { + final Vector value = new Vector(); + for (int c = 1; c < strAtomExpr.value.length() - 1; c++) { + switch (strAtomExpr.value.charAt(c)) { + case '\\': + switch (strAtomExpr.value.charAt(c + 1)) { + case 'n': + value.addLast(10); + c += 1; + break; + case '\"': + value.addLast((int) '\"'); + c += 1; + break; + case '\\': + value.addLast((int) '\\'); + c += 1; + break; + default: + value.addLast(16 * (((int) strAtomExpr.value.charAt(c + 1)) - ((int) '0')) + + (((int) strAtomExpr.value.charAt(c + 2)) - ((int) '0'))); + c += 2; + break; + } + break; + default: + value.addLast((int) strAtomExpr.value.charAt(c)); + break; + } + } + return value; + } + + // --- 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 (memory):"); + + 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 AttrAST memoryAttrAST = Memory.organize(semanAttrAST); + + (new AST.Logger(memoryAttrAST)).log(); + } + + // Upajmo, da kdaj pridemo to te tocke. + // A zavedajmo se sledecega: + // 1. Prevod je zaradi napak v programu lahko napacen :-o + // 2. Izvorni program se zdalec ni tisto, kar je programer hotel, da bi bil ;-) + Report.info("Done."); + } catch (Report.Error error) { + // Izpis opisa napake. + System.err.println(error.getMessage()); + System.exit(1); + } + } + +}