basic generator
This commit is contained in:
		
							parent
							
								
									21dca82f4a
								
							
						
					
					
						commit
						a845321785
					
				| 
						 | 
				
			
			@ -0,0 +1,296 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.RuleContext;
 | 
			
		||||
import org.antlr.v4.runtime.tree.ParseTree;
 | 
			
		||||
import org.antlr.v4.runtime.tree.RuleNode;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.model.*;
 | 
			
		||||
import pp.s1184725.boppi.BasicParser.*;
 | 
			
		||||
 | 
			
		||||
public class BoppiBasicGenerator extends BasicBaseVisitor<Void> {
 | 
			
		||||
	public Program prog;
 | 
			
		||||
	private Annotations an;
 | 
			
		||||
	private int regNum;
 | 
			
		||||
	private final Reg zero = new Reg("zero");
 | 
			
		||||
 | 
			
		||||
	static Map<Integer, OpCode> ops = new HashMap<Integer, OpCode>() {
 | 
			
		||||
		private static final long serialVersionUID = 8979722313842633807L;
 | 
			
		||||
		{
 | 
			
		||||
			put(BasicLexer.AND, OpCode.and);
 | 
			
		||||
			put(BasicLexer.DIVIDE, OpCode.div);
 | 
			
		||||
			put(BasicLexer.EQ, OpCode.cmp_EQ);
 | 
			
		||||
			put(BasicLexer.GT, OpCode.cmp_GT);
 | 
			
		||||
			put(BasicLexer.GTE, OpCode.cmp_GE);
 | 
			
		||||
			put(BasicLexer.LEQ, OpCode.cmp_LE);
 | 
			
		||||
			put(BasicLexer.LT, OpCode.cmp_LT);
 | 
			
		||||
			put(BasicLexer.MINUS, OpCode.sub);
 | 
			
		||||
			put(BasicLexer.MULTIPLY, OpCode.mult);
 | 
			
		||||
			put(BasicLexer.NEQ, OpCode.cmp_NE);
 | 
			
		||||
			put(BasicLexer.PLUS, OpCode.add);
 | 
			
		||||
			put(BasicLexer.OR, OpCode.or);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	public BoppiBasicGenerator(Annotations annotations) {
 | 
			
		||||
		an = annotations;
 | 
			
		||||
		prog = new Program();
 | 
			
		||||
		regNum = 0;
 | 
			
		||||
 | 
			
		||||
		emit(OpCode.loadI, new Num(0), zero);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Reg getReg(ParseTree parseTree) {
 | 
			
		||||
		Reg reg = an.registers.get(parseTree);
 | 
			
		||||
		if (reg == null) {
 | 
			
		||||
			reg = new Reg("r_" + (regNum++));
 | 
			
		||||
			an.registers.put(parseTree, reg);
 | 
			
		||||
		}
 | 
			
		||||
		return reg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private Reg copyReg(ParseTree source, ParseTree destination) {
 | 
			
		||||
		Reg reg = an.registers.get(source);
 | 
			
		||||
		an.registers.put(destination, reg);
 | 
			
		||||
		return reg;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs an operation from the parameters and adds it to the program
 | 
			
		||||
	 * under construction.
 | 
			
		||||
	 */
 | 
			
		||||
	private Op emit(Label label, OpCode opCode, Operand... args) {
 | 
			
		||||
		Op result = new Op(label, opCode, args);
 | 
			
		||||
		this.prog.addInstr(result);
 | 
			
		||||
		return result;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Constructs an operation from the parameters and adds it to the program
 | 
			
		||||
	 * under construction.
 | 
			
		||||
	 */
 | 
			
		||||
	private Op emit(OpCode opCode, Operand... args) {
 | 
			
		||||
		return emit((Label) null, opCode, args);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitChildren(RuleNode tree) {
 | 
			
		||||
		super.visitChildren(tree);
 | 
			
		||||
		if (an.registers.get(tree) == null)
 | 
			
		||||
			for (int i = tree.getChildCount() - 1; i >= 0; i--)
 | 
			
		||||
				if (tree.getChild(i) instanceof RuleContext) {
 | 
			
		||||
					copyReg(tree.getChild(i), tree);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitAssign(AssignContext ctx) {
 | 
			
		||||
		visit(ctx.singleExpr());
 | 
			
		||||
		copyReg(ctx.singleExpr(), ctx);
 | 
			
		||||
		
 | 
			
		||||
		switch (an.types.get(ctx)) {
 | 
			
		||||
		case CHAR:
 | 
			
		||||
			emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(ctx.variable()).getOffset()));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			emit(OpCode.storeAI, getReg(ctx), zero, new Num(an.variables.get(ctx.variable()).getOffset()));
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitBool(BoolContext ctx) {
 | 
			
		||||
		emit(OpCode.loadI, new Num(ctx.TRUE() != null ? 1 : 0), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitNumber(NumberContext ctx) {
 | 
			
		||||
		emit(OpCode.loadI, new Num(Integer.parseInt(ctx.LITERAL10().getText())), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitInfix1(Infix1Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
		emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitInfix2(Infix2Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
		emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitInfix3(Infix3Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
		emit(ops.get(ctx.op.getType()), getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitInfix4(Infix4Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
		emit(OpCode.and, getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitInfix5(Infix5Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
		emit(OpCode.or, getReg(ctx.lhs), getReg(ctx.rhs), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitPrefix1(Prefix1Context ctx) {
 | 
			
		||||
		visitChildren(ctx);
 | 
			
		||||
 | 
			
		||||
		switch (ctx.op.getType()) {
 | 
			
		||||
		case BasicLexer.MINUS:
 | 
			
		||||
			emit(OpCode.rsubI, new Num(0), getReg(ctx.singleExpr()), getReg(ctx));
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitVar(VarContext ctx) {
 | 
			
		||||
		switch (an.types.get(ctx)) {
 | 
			
		||||
		case CHAR:
 | 
			
		||||
			emit(OpCode.cloadAI, zero, new Num(an.variables.get(ctx.variable()).getOffset()), getReg(ctx));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			emit(OpCode.loadAI, zero, new Num(an.variables.get(ctx.variable()).getOffset()), getReg(ctx));
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitChar(CharContext ctx) {
 | 
			
		||||
		emit(OpCode.loadI, new Num(ctx.CHAR().getText().codePointAt(1)), getReg(ctx));
 | 
			
		||||
		emit(OpCode.i2c, getReg(ctx), getReg(ctx));
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitRead(ReadContext ctx) {
 | 
			
		||||
		for (VariableContext expr : ctx.variable()) {
 | 
			
		||||
			visit(expr);
 | 
			
		||||
			switch (an.types.get(expr)) {
 | 
			
		||||
			case BOOL:
 | 
			
		||||
			case INT:
 | 
			
		||||
				emit(OpCode.in, new Str(""), getReg(ctx));
 | 
			
		||||
				emit(OpCode.storeAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset()));
 | 
			
		||||
				break;
 | 
			
		||||
			case CHAR:
 | 
			
		||||
				//Get input until at least 1 character
 | 
			
		||||
				Label getTarget = new Label("lcin_l"+ctx.hashCode());
 | 
			
		||||
				Label continueTarget = new Label("lcin_e"+ctx.hashCode());
 | 
			
		||||
				Reg countReg = new Reg("tempReg");
 | 
			
		||||
				emit(getTarget, OpCode.cin, new Str(""));
 | 
			
		||||
				emit(OpCode.pop, countReg);
 | 
			
		||||
				emit(OpCode.cbr, countReg, continueTarget, getTarget);
 | 
			
		||||
				
 | 
			
		||||
				//Get character
 | 
			
		||||
				emit(continueTarget, OpCode.cpop, getReg(ctx));
 | 
			
		||||
				emit(OpCode.cstoreAI, getReg(ctx), zero, new Num(an.variables.get(expr).getOffset()));
 | 
			
		||||
				
 | 
			
		||||
				//Pop all remaining characters
 | 
			
		||||
				Label loopTarget = new Label("lcpop_l"+ctx.hashCode());
 | 
			
		||||
				Label iterTarget = new Label("lcpop_c"+ctx.hashCode());
 | 
			
		||||
				Label stopTarget = new Label("lcpop_e"+ctx.hashCode());
 | 
			
		||||
				Reg tempReg2 = new Reg("tempReg2");
 | 
			
		||||
				emit(loopTarget, OpCode.subI, countReg, new Num(1), countReg);
 | 
			
		||||
				emit(OpCode.cbr, countReg, iterTarget, stopTarget);
 | 
			
		||||
				emit(iterTarget, OpCode.cpop, tempReg2);
 | 
			
		||||
				emit(OpCode.jumpI, loopTarget);
 | 
			
		||||
				emit(stopTarget, OpCode.nop);
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				emit(OpCode.out, new Str("Unknown type: "), an.registers.get(expr));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitIf(IfContext ctx) {
 | 
			
		||||
		Label toTrue = new Label("if_t"+ctx.hashCode());
 | 
			
		||||
		Label toFalse = new Label("if_f"+ctx.hashCode());
 | 
			
		||||
		Label toEnd = new Label("if_e"+ctx.hashCode());
 | 
			
		||||
		
 | 
			
		||||
		visit(ctx.cond);
 | 
			
		||||
		if (ctx.onFalse == null) {
 | 
			
		||||
			emit(OpCode.cbr, getReg(ctx.cond), toTrue, toEnd);
 | 
			
		||||
			
 | 
			
		||||
			emit(toTrue, OpCode.nop);
 | 
			
		||||
			visit(ctx.onTrue);
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			emit(OpCode.cbr, getReg(ctx.cond), toTrue, toFalse);
 | 
			
		||||
			
 | 
			
		||||
			emit(toTrue, OpCode.nop);
 | 
			
		||||
			visit(ctx.onTrue);
 | 
			
		||||
			if (an.types.get(ctx) != SimpleType.VOID)
 | 
			
		||||
				emit(OpCode.i2i, getReg(ctx.onTrue), getReg(ctx));
 | 
			
		||||
			emit(OpCode.jumpI, toEnd);
 | 
			
		||||
			
 | 
			
		||||
			emit(toFalse, OpCode.nop);
 | 
			
		||||
			visit(ctx.onFalse);
 | 
			
		||||
			if (an.types.get(ctx) != SimpleType.VOID)
 | 
			
		||||
				emit(OpCode.i2i, getReg(ctx.onFalse), getReg(ctx));
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
		emit(toEnd, OpCode.nop);
 | 
			
		||||
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitWhile(WhileContext ctx) {
 | 
			
		||||
		Label toLoop = new Label("while_t"+ctx.hashCode());
 | 
			
		||||
		Label toCond = new Label("while_f"+ctx.hashCode());
 | 
			
		||||
		Label toEnd = new Label("while_e"+ctx.hashCode());
 | 
			
		||||
		
 | 
			
		||||
		emit(OpCode.jumpI, toCond);
 | 
			
		||||
		emit(toLoop, OpCode.nop);
 | 
			
		||||
		visit(ctx.onTrue);
 | 
			
		||||
		emit(toCond, OpCode.nop);
 | 
			
		||||
		visit(ctx.cond);
 | 
			
		||||
		emit(OpCode.cbr, getReg(ctx.cond), toLoop, toEnd);
 | 
			
		||||
		emit(toEnd, OpCode.nop);
 | 
			
		||||
		
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public Void visitWrite(WriteContext ctx) {
 | 
			
		||||
		for (ExprContext expr : ctx.expr()) {
 | 
			
		||||
			visit(expr);
 | 
			
		||||
			switch (an.types.get(expr)) {
 | 
			
		||||
			case BOOL:
 | 
			
		||||
			case INT:
 | 
			
		||||
				emit(OpCode.out, new Str(""), getReg(expr));
 | 
			
		||||
				break;
 | 
			
		||||
			case CHAR:
 | 
			
		||||
				Reg tempReg = new Reg("tempReg");
 | 
			
		||||
				emit(OpCode.cpush, getReg(expr));
 | 
			
		||||
				emit(OpCode.loadI, new Num(1), tempReg);
 | 
			
		||||
				emit(OpCode.push, tempReg);
 | 
			
		||||
				emit(OpCode.cout, new Str(""));
 | 
			
		||||
				break;
 | 
			
		||||
			default:
 | 
			
		||||
				emit(OpCode.out, new Str("Unknown type: "), getReg(expr));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,8 +2,6 @@ package pp.s1184725.boppi;
 | 
			
		|||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.eval.Machine;
 | 
			
		||||
 | 
			
		||||
public class CachingSymbolTable<T> {
 | 
			
		||||
	protected Stack<Map<String,Variable<T>>> symbolMapStack;
 | 
			
		||||
	protected Stack<Integer> offsets;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,7 +54,8 @@ public class CachingSymbolTable<T> {
 | 
			
		|||
			throw new Exception(String.format("Identifier '%s' already declared in this scope.", id));
 | 
			
		||||
		
 | 
			
		||||
		Variable<T> var = new Variable<>(type, offset);
 | 
			
		||||
		offset += Machine.INT_SIZE;
 | 
			
		||||
		// TODO refactor to get type size
 | 
			
		||||
		offset += ((SimpleType)type).getSize();
 | 
			
		||||
		symbolMapStack.peek().put(id, var);
 | 
			
		||||
		symbolCache.put(id, var);
 | 
			
		||||
		return var;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,30 @@
 | 
			
		|||
package pp.s1184725.boppi;
 | 
			
		||||
import static pp.s1184725.boppi.BasicLexer.*;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.eval.Machine;
 | 
			
		||||
 | 
			
		||||
public enum SimpleType {
 | 
			
		||||
	INT, CHAR, BOOL, VOID;
 | 
			
		||||
	INT(Machine.INT_SIZE), CHAR(Machine.DEFAULT_CHAR_SIZE), BOOL(Machine.INT_SIZE), VOID(0);
 | 
			
		||||
	
 | 
			
		||||
	private final int size;
 | 
			
		||||
	
 | 
			
		||||
	public int getSize() {
 | 
			
		||||
		return size;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private SimpleType(int typeSize) {
 | 
			
		||||
		size = typeSize;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	public static SimpleType fromToken(int token) {
 | 
			
		||||
		switch (token) {
 | 
			
		||||
		case BasicLexer.INTTYPE:
 | 
			
		||||
		case INTTYPE:
 | 
			
		||||
			return SimpleType.INT;
 | 
			
		||||
 | 
			
		||||
		case BasicLexer.CHARTYPE:
 | 
			
		||||
		case CHARTYPE:
 | 
			
		||||
			return SimpleType.CHAR;
 | 
			
		||||
 | 
			
		||||
		case BasicLexer.BOOLTYPE:
 | 
			
		||||
		case BOOLTYPE:
 | 
			
		||||
			return SimpleType.BOOL;
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,51 @@
 | 
			
		|||
package pp.s1184725.boppi.test;
 | 
			
		||||
 | 
			
		||||
import java.io.ByteArrayInputStream;
 | 
			
		||||
import java.io.ByteArrayOutputStream;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.nio.charset.StandardCharsets;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.logging.LogRecord;
 | 
			
		||||
import java.util.logging.Logger;
 | 
			
		||||
 | 
			
		||||
import org.antlr.v4.runtime.ParserRuleContext;
 | 
			
		||||
import org.apache.commons.lang3.tuple.Pair;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
 | 
			
		||||
import pp.iloc.Simulator;
 | 
			
		||||
import pp.s1184725.boppi.Annotations;
 | 
			
		||||
import pp.s1184725.boppi.BasicParser;
 | 
			
		||||
import pp.s1184725.boppi.BasicParserHelper;
 | 
			
		||||
import pp.s1184725.boppi.BoppiBasicChecker;
 | 
			
		||||
import pp.s1184725.boppi.BoppiBasicGenerator;
 | 
			
		||||
 | 
			
		||||
public class GeneratorTest {
 | 
			
		||||
	static private List<LogRecord> generateAndGetLog(String code) {
 | 
			
		||||
		Logger logger = Logger.getAnonymousLogger();
 | 
			
		||||
		Pair<BasicParser, List<LogRecord>> pair = BasicParserHelper.getParserWithLog(code, logger);
 | 
			
		||||
		Annotations annotater = new Annotations();
 | 
			
		||||
		ParserRuleContext tree = pair.getLeft().program();
 | 
			
		||||
		new BoppiBasicChecker(logger, annotater).visit(tree);
 | 
			
		||||
		BoppiBasicGenerator generator = new BoppiBasicGenerator(annotater);
 | 
			
		||||
		generator.visit(tree);
 | 
			
		||||
//		System.out.println(generator.prog.prettyPrint());
 | 
			
		||||
//		System.out.println(BasicParserHelper.getAnnotatedDOT(tree, annotater));
 | 
			
		||||
		Simulator s = new Simulator(generator.prog);
 | 
			
		||||
		InputStream in = new ByteArrayInputStream("1\nq\n".getBytes(StandardCharsets.UTF_8));
 | 
			
		||||
		ByteArrayOutputStream out = new ByteArrayOutputStream();
 | 
			
		||||
		s.setIn(in);
 | 
			
		||||
		s.setOut(out);
 | 
			
		||||
		s.run();
 | 
			
		||||
		System.out.println(out.toString());
 | 
			
		||||
		return pair.getRight();
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	@Test
 | 
			
		||||
	public void basicTest() {
 | 
			
		||||
//		generateAndGetLog("print(5*3)");
 | 
			
		||||
//		generateAndGetLog("var int x; var int y; x := 3*(y := 4); print(x,y)");
 | 
			
		||||
//		generateAndGetLog("print('T', 'e', 's', 't', '!')");
 | 
			
		||||
//		generateAndGetLog("var int i; if read(i) == 1 then var char c; read(c); print(c,c); else print(i) fi");
 | 
			
		||||
		generateAndGetLog("var int i; i := 10; while i > 0 do print('A', i); i := i-1 od");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue