This commit is contained in:
Gašper Dobrovoljc 2025-05-08 23:42:03 +02:00
parent e868f3030c
commit a0cad251cd
No known key found for this signature in database
GPG Key ID: 0E7E037018CFA5A5
6 changed files with 580 additions and 43 deletions

3
.idea/misc.xml generated
View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Black"> <component name="Black">
<option name="sdkName" value="Python 3.12" /> <option name="sdkName" value="Python 3.12" />
@ -6,7 +7,7 @@
<option name="enabled" value="true" /> <option name="enabled" value="true" />
<option name="wasEnabledAtLeastOnce" value="true" /> <option name="wasEnabledAtLeastOnce" value="true" />
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="homebrew-23" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="homebrew-23" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" /> <output url="file://$PROJECT_DIR$/out" />
</component> </component>
</project> </project>

View File

@ -1,6 +1,7 @@
JAVA = java --enable-preview JAVA = java --enable-preview
PHASE =
.PHONY : % .PHONY : %
% : %.pins25 % : %.pins25
$(JAVA) -classpath ../bin pins25.phase.SynAn $< $(JAVA) -classpath ../bin pins25.phase.$(PHASE) $<

153
src/pins25/common/Mem.java Normal file
View File

@ -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<RelAccess> debugPars;
/** Dostopi do lokalnih spremenljivk. */
public final List<RelAccess> 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<RelAccess> debugPars, final List<RelAccess> 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<Integer> inits;
/**
* Ustvari nov dostop do spremenljivke.
*
* @param size Velikost spremenljivke.
* @param inits Zacetna vrednost spremenljivke.
*/
public Access(final Integer size, final Vector<Integer> inits) {
this.size = size;
this.inits = inits == null ? null : Collections.unmodifiableList(new Vector<Integer>(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<Integer> 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<Integer> inits,
final String debugName) {
super(size, inits);
this.offset = offset;
this.depth = depth;
this.debugName = debugName;
}
}
}

View File

@ -149,46 +149,6 @@ public record Token(Report.Location location, Symbol symbol, String lexeme) impl
* Simbol {@code )}. * Simbol {@code )}.
*/ */
RPAREN; 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 @Override

View File

@ -409,8 +409,8 @@ public class LexAn implements AutoCloseable {
nextChar(); nextChar();
if (buffChar == 'n' || buffChar == '\\' || buffChar == '"') { if (buffChar == 'n' || buffChar == '\\' || buffChar == '"') {
} else if (isHex()) { } else if (isHex()) {
nextChar();
lexeme.append((char) buffChar); lexeme.append((char) buffChar);
nextChar();
if (!isHex()) { if (!isHex()) {
throw new Report.Error(currentLocation(), "Invalid ascii code '" + (char) buffChar + "'."); throw new Report.Error(currentLocation(), "Invalid ascii code '" + (char) buffChar + "'.");
} }

View File

@ -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.
* <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>
* </ol>
*/
public static class AttrAST extends SemAn.AttrAST {
/**
* Atribut: klicni zapis funkcije.
*/
public final Map<AST.FunDef, Mem.Frame> attrFrame;
/**
* Atribut: dostop do parametra.
*/
public final Map<AST.ParDef, Mem.RelAccess> attrParAccess;
/**
* Atribut: dostop do spremenljivke.
*/
public final Map<AST.VarDef, Mem.Access> 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<AST.FunDef, Mem.Frame> attrFrame,
final Map<AST.ParDef, Mem.RelAccess> attrParAccess, final Map<AST.VarDef, Mem.Access> 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<Integer> 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<AST.FunDef, Mem.Frame>(),
new HashMap<AST.ParDef, Mem.RelAccess>(), new HashMap<AST.VarDef, Mem.Access>());
(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<Mem.RelAccess> debugVars = new Vector<>();
}
/**
* Obiskovalec, ki izracuna pomnilnisko predstavitev.
*/
private class MemoryVisitor implements AST.FullVisitor<FrameBuilder, FrameBuilder> {
@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<Mem.RelAccess> debugPars = new Vector<>();
for (AST.ParDef parDef : funDef.pars) {
Vector<Integer> 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<Integer> 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<Integer> 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<Integer> decodeStrConst(final AST.AtomExpr strAtomExpr, final Report.Locatable loc) {
final Vector<Integer> value = new Vector<Integer>();
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);
}
}
}