initial commit, template added

This commit is contained in:
2025-10-16 18:54:20 +02:00
commit ececfae877
69 changed files with 10398 additions and 0 deletions

View File

@@ -0,0 +1,294 @@
package ssw.mj.impl;
import ssw.mj.codegen.Label;
import ssw.mj.codegen.Operand;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
public final class Code {
public enum OpCode {
load,
load_0,
load_1,
load_2,
load_3,
store,
store_0,
store_1,
store_2,
store_3,
getstatic,
putstatic,
getfield,
putfield,
const_0,
const_1,
const_2,
const_3,
const_4,
const_5,
const_m1,
const_,
add,
sub,
mul,
div,
rem,
neg,
shl,
shr,
inc,
new_,
newarray,
aload,
astore,
baload,
bastore,
arraylength,
pop,
dup,
dup2,
jmp,
jeq,
jne,
jlt,
jle,
jgt,
jge,
call,
return_,
enter,
exit,
read,
print,
bread,
bprint,
trap,
nop;
public int code() {
return ordinal() + 1;
}
public String cleanName() {
String name = name();
if (name.endsWith("_")) {
name = name.substring(0, name.length() - 1);
}
return name;
}
public static OpCode get(int code) {
if (code < 1 || code > values().length) {
return null;
}
return values()[code - 1];
}
}
public enum CompOp {
eq, ne, lt, le, gt, ge;
public static CompOp invert(CompOp op) {
if (op == null) {
throw new IllegalArgumentException("Compare operator must not be null!");
}
return switch (op) {
case eq -> ne;
case ne -> eq;
case lt -> ge;
case le -> gt;
case gt -> le;
case ge -> lt;
default ->
// Cannot happen, we covered all six compare operations as well as null parameter
// This is purely to prevent the compiler from complaining about a missing return statement
throw new IllegalArgumentException("Impossible compare operator");
};
}
public static OpCode toOpCode(CompOp op) {
return switch (op) {
case eq -> OpCode.jeq;
case ge -> OpCode.jge;
case gt -> OpCode.jgt;
case le -> OpCode.jle;
case lt -> OpCode.jlt;
case ne -> OpCode.jne;
};
}
}
/**
* Code buffer
*/
public byte[] buf;
/**
* Program counter. Indicates next free byte in code buffer.
*/
public int pc;
/**
* PC of main method (set by parser).
*/
public int mainpc;
/**
* Length of static data in words (set by parser).
*/
public int dataSize;
/**
* According parser.
*/
private final Parser parser;
// ----- initialization
public Code(Parser p) {
parser = p;
buf = new byte[100];
pc = 0;
mainpc = -1;
dataSize = 0;
}
// ----- code storage management
public void put(OpCode code) {
put(code.code());
}
public void put(int x) {
if (pc == buf.length) {
buf = Arrays.copyOf(buf, buf.length * 2);
}
buf[pc++] = (byte) x;
}
public void put2(int x) {
put(x >> 8);
put(x);
}
public void put4(int x) {
put2(x >> 16);
put2(x);
}
public void put2(int pos, int x) {
int oldpc = pc;
pc = pos;
put2(x);
pc = oldpc;
}
/**
* Write the code buffer to the output stream.
*/
public void write(OutputStream os) throws IOException {
int codeSize = pc;
ByteArrayOutputStream header = new ByteArrayOutputStream();
DataOutputStream headerWriter = new DataOutputStream(header);
headerWriter.writeByte('M');
headerWriter.writeByte('J');
headerWriter.writeInt(codeSize);
headerWriter.writeInt(dataSize);
headerWriter.writeInt(mainpc);
headerWriter.close();
os.write(header.toByteArray());
os.write(buf, 0, codeSize);
os.flush();
os.close();
}
// ======================================================
// TODO Exercise UE-P-5-6: implementation of code generation
// ======================================================
// TODO Exercise UE-P-5: Various code generation methods such as load or assign
/**
* Load the operand x onto the expression stack.
*/
public void load(Operand x) {
// TODO Exercise UE-P-5
}
/**
* Load an integer constant onto the expression stack.
*/
public void loadConst(int n) {
// TODO Exercise UE-P-5
}
/**
* Generate an assignment x = y.
*/
public void assign(Operand x, Operand y) {
// TODO Exercise UE-P-5
}
/**
* Generate an increment instruction that increments x by n.
*/
public void inc(Operand x, int n) {
// TODO Exercise UE-P-5
}
/**
* Prepares the left-hand side of a compound assignment.
*/
public void prepareLhsOfCompoundAssignment(Operand x) {
Operand.Kind kindBeforeLoad = x.kind;
// TODO Exercise UE-P-5
// TODO: Field accesses (such as x.y) or array accesses (such as arr[2]) on the left-hand side of
// an compound assignment (e.g., arr[2] += 4) need to correctly use dup or dup2 before load. Implement here.
// Do not switch kind to Stack after loading x.
// We still need its kind later on during the assign().
x.kind = kindBeforeLoad;
}
// --------------------
public void methodCall(Operand x) {
// TODO Exercise UE-P-6
}
/**
* Unconditional jump.
*/
public void jump(Label lab) {
// TODO Exercise UE-P-6
}
/**
* True Jump. Generates conditional jump instruction and links it to true
* jump chain.
*/
public void tJump(CompOp op, Label to) {
// TODO Exercise UE-P-6
}
/**
* False Jump. Generates conditional jump instruction and links it to false
* jump chain.
*/
public void fJump(CompOp op, Label to) {
// TODO Exercise UE-P-6
}
// =================================================
// =================================================
}

View File

@@ -0,0 +1,144 @@
package ssw.mj.impl;
import ssw.mj.Errors;
import ssw.mj.Errors.Message;
import ssw.mj.scanner.Token;
import static ssw.mj.Errors.Message.TOKEN_EXPECTED;
import static ssw.mj.scanner.Token.Kind.eof;
import static ssw.mj.scanner.Token.Kind.none;
public final class Parser {
/**
* Maximum number of global variables per program
*/
private static final int MAX_GLOBALS = 32767;
/**
* Maximum number of fields per class
*/
private static final int MAX_FIELDS = 32767;
/**
* Maximum number of local variables per method
*/
private static final int MAX_LOCALS = 127;
/**
* Last recognized token;
*/
private Token t;
/**
* Lookahead token (not recognized).)
*/
private Token la;
/**
* Shortcut to kind attribute of lookahead token (la).
*/
private Token.Kind sym;
/**
* According scanner
*/
public final Scanner scanner;
/**
* According code buffer
*/
public final Code code;
/**
* According symbol table
*/
public final Tab tab;
public Parser(Scanner scanner) {
this.scanner = scanner;
tab = new Tab(this);
code = new Code(this);
// Pseudo token to avoid crash when 1st symbol has scanner error.
la = new Token(none, 1, 1);
}
/**
* Reads ahead one symbol.
*/
private void scan() {
t = la;
la = scanner.next();
sym = la.kind;
}
/**
* Verifies symbol and reads ahead.
*/
private void check(Token.Kind expected) {
if (sym == expected) {
scan();
} else {
error(TOKEN_EXPECTED, expected);
}
}
/**
* Adds error message to the list of errors.
*/
public void error(Message msg, Object... msgParams) {
// TODO Exercise UE-P-3: Replace panic mode with error recovery (i.e., keep track of error distance)
// TODO Exercise UE-P-3: Hint: Replacing panic mode also affects scan() method
scanner.errors.error(la.line, la.col, msg, msgParams);
throw new Errors.PanicMode();
}
/**
* Starts the analysis.
*/
public void parse() {
scan(); // scan first symbol, initializes look-ahead
Program(); // start analysis
check(eof);
}
// ===============================================
// TODO Exercise UE-P-2: Implementation of parser
// TODO Exercise UE-P-3: Error recovery methods
// TODO Exercise UE-P-4: Symbol table handling
// TODO Exercise UE-P-5-6: Code generation
// ===============================================
// TODO Exercise UE-P-3: Error distance
// TODO Exercise UE-P-2 + Exercise 3: Sets to handle certain first, follow, and recover sets
static {
// Initialize first and follow sets.
}
// ---------------------------------
// TODO Exercise UE-P-2: One top-down parsing method per production
/**
* Program = <br>
* "program" ident <br>
* { ConstDecl | VarDecl | ClassDecl } <br>
* "{" { MethodDecl } "}" .
*/
private void Program() {
// TODO Exercise UE-P-2
}
// ...
// ------------------------------------
// TODO Exercise UE-P-3: Error recovery methods: recoverDecl, recoverMethodDecl and recoverStat (+ TODO Exercise UE-P-5: Check idents for Type kind)
// ====================================
// ====================================
}

View File

@@ -0,0 +1,108 @@
package ssw.mj.impl;
import ssw.mj.Errors;
import ssw.mj.scanner.Token;
import java.io.Reader;
import java.util.HashMap;
import java.util.Map;
import static ssw.mj.scanner.Token.Kind.none;
public class Scanner {
// Scanner Skeleton - do not rename fields / methods !
private static final char EOF = (char) -1;
private static final char LF = '\n';
/**
* Input data to read from.
*/
private final Reader in;
/**
* Lookahead character. (= next (unhandled) character in the input stream)
*/
private char ch;
/**
* Current line in input stream.
*/
private int line;
/**
* Current column in input stream.
*/
private int col;
/**
* According errors object.
*/
public final Errors errors;
public Scanner(Reader r) {
// store reader
in = r;
// initialize error handling support
errors = new Errors();
line = 1;
col = 0;
nextCh(); // read 1st char into ch, incr col to 1
}
/**
* Adds error message to the list of errors.
*/
public final void error(Token t, Errors.Message msg, Object... msgParams) {
errors.error(t.line, t.col, msg, msgParams);
// reset token content (consistent JUnit tests)
t.numVal = 0;
t.val = null;
}
// ================================================
// TODO Exercise UE-P-1: Implement Scanner (next() + private helper methods)
// ================================================
// TODO Exercise UE-P-1: Keywords
/**
* Mapping from keyword names to appropriate token codes.
*/
private static final Map<String, Token.Kind> keywords;
static {
keywords = new HashMap<>();
}
/**
* Returns next token. To be used by parser.
*/
public Token next() {
// TODO Exercise UE-P-1: implementation of next method
Token t = new Token(none, 1, 1);
return t;
}
private void nextCh() {
// TODO Exercise UE-P-1: implementation of nextCh method and other private helper methods
}
// TODO Exercise UE-P-1: private helper methods used by next(), as discussed in the exercise
// -----------------------------------------------
private boolean isLetter(char c) {
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
}
private boolean isDigit(char c) {
return '0' <= c && c <= '9';
}
// ================================================
// ================================================
}

View File

@@ -0,0 +1,100 @@
package ssw.mj.impl;
import ssw.mj.symtab.Obj;
import ssw.mj.symtab.Scope;
import ssw.mj.symtab.Struct;
public final class Tab {
// Universe
public static final Struct noType = new Struct(Struct.Kind.None);
public static final Struct intType = new Struct(Struct.Kind.Int);
public static final Struct charType = new Struct(Struct.Kind.Char);
public static final Struct nullType = new Struct(Struct.Kind.Class);
public final Obj noObj, chrObj;
public Obj ordObj, lenObj;
/**
* Only used for reporting errors.
*/
private final Parser parser;
/**
* The current top scope.
*/
public Scope curScope = null;
// First scope opening (universe) will increase this to -1
/**
* Nesting level of current scope.
*/
private int curLevel = -2;
public Tab(Parser p) {
parser = p;
// setting up "universe" (= predefined names)
// opening scope (curLevel goes to -1, which is the universe level)
openScope();
noObj = new Obj(Obj.Kind.Var, "noObj", noType);
insert(Obj.Kind.Type, "int", intType);
insert(Obj.Kind.Type, "char", charType);
insert(Obj.Kind.Con, "null", nullType);
chrObj = insert(Obj.Kind.Meth, "chr", charType);
openScope();
Obj iVarObj = insert(Obj.Kind.Var, "i", intType);
iVarObj.level = 1;
chrObj.nPars = curScope.nVars();
chrObj.locals = curScope.locals();
closeScope();
// TODO Exercise UE-P-4: build "ord" universe method and store in ordObj
// TODO Exercise UE-P-4: build "len" universe method and store in lenObj
// still on level -1
// now that the universe is constructed, the next node that will be added is the Program itself
// (which will open its own scope with level 0)
}
// ===============================================
// TODO Exercise UE-P-4: implementation of symbol table
// ===============================================
public void openScope() {
curScope = new Scope(curScope);
curLevel++;
}
public void closeScope() {
curScope = curScope.outer();
curLevel--;
}
public Obj insert(Obj.Kind kind, String name, Struct type) {
// TODO Exercise UE-P-4
return noObj;
}
/**
* Retrieves the object with <code>name</code> from the innermost scope.
*/
public Obj find(String name) {
// TODO Exercise UE-P-4
return noObj;
}
/**
* Retrieves the field <code>name</code> from the fields of
* <code>type</code>.
*/
public Obj findField(String name, Struct type) {
// TODO Exercise UE-P-4
return noObj;
}
// ===============================================
// ===============================================
}