Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,45 @@

This project is part of the https://compilerprogramming.github.io/ project.

The EZ (pronounced EeeZee) programming language is designed to allow us to learn various compiler techniques.
The EZ (pronounced EeZee) programming language is designed to allow us to learn various compiler techniques.

## The EZ Language

The EZ programming language is a tiny statically typed language with syntax inspired by Swift.
The language has the following features:

* Integer, Struct and 1-Dimensional Array types
* If and While statements
* Functions

The language syntax is described [ANTLR Grammar](antlr-parser/src/main/antlr4/com/compilerprogramming/ezlang/antlr/EZLanguage.g4).
The language is intentionally very simple and is meant to have just enough functionality to experiment with compiler implementation techniques.

## Modules

The project is under development and subject to change. At this point in time, we have following initial implementations:

* lexer - a simple tokenizer
* parser - a recursive descent parser and AST
* types - the type definitions
* semantic - semantic analyzer
* stackvm - a bytecode compiler that generates stack IR (bytecode interpreter not yet available)
* registervm - a bytecode compiler that generates a linear register IR and a bytecode interpreter that can execute the IR

## How can you contribute?

Obviously firstly any contributes that improve and fix bugs are welcome. I am not keen on language extensions at this stage, but eventually
we will be extending the language to explore more advanced features.

I am also interested in creating implementations of this project in C++, Go, Rust, swift, D, C, etc. If you are interested in working on such a
port please contact me via [Discussions](https://github.com/orgs/CompilerProgramming/discussions).

## Community Discussions

There is a [community discussion forum](https://github.com/orgs/CompilerProgramming/discussions).

## What's next

The project has only just got started, there is lots to do!. See the plan in the [website](https://compilerprogramming.github.io/).
More documentation to follow, but for now please refer to the source code and the site above.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.compilerprogramming.ezlang.exceptions;

public class InterpreterException extends RuntimeException {
public InterpreterException(String message) {super(message);}
public InterpreterException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.compilerprogramming.ezlang.bytecode;

import com.compilerprogramming.ezlang.types.Symbol;
import com.compilerprogramming.ezlang.types.Type;
import com.compilerprogramming.ezlang.types.TypeDictionary;

public class RegisterVMCompiler {
public class BytecodeCompiler {

public void compile(TypeDictionary typeDictionary) {
for (Symbol symbol: typeDictionary.getLocalSymbols()) {
if (symbol instanceof Symbol.FunctionTypeSymbol functionSymbol) {
functionSymbol.code = new FunctionBuilder(functionSymbol);
Type.TypeFunction functionType = (Type.TypeFunction) functionSymbol.type;
functionType.code = new BytecodeFunction(functionSymbol);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import java.util.ArrayList;
import java.util.List;

public class FunctionBuilder {
public class BytecodeFunction {

public BasicBlock entry;
public BasicBlock exit;
public int maxLocalReg;
public int maxStackSize;
private int bid = 0;
private BasicBlock currentBlock;
private BasicBlock currentBreakTarget;
Expand All @@ -28,7 +30,7 @@ public class FunctionBuilder {
*/
private List<Operand> virtualStack = new ArrayList<>();

public FunctionBuilder(Symbol.FunctionTypeSymbol functionSymbol) {
public BytecodeFunction(Symbol.FunctionTypeSymbol functionSymbol) {
AST.FuncDecl funcDecl = (AST.FuncDecl) functionSymbol.functionDecl;
setVirtualRegisters(funcDecl.scope);
this.bid = 0;
Expand All @@ -40,6 +42,10 @@ public FunctionBuilder(Symbol.FunctionTypeSymbol functionSymbol) {
exitBlockIfNeeded();
}

public int frameSize() {
return maxLocalReg+maxStackSize;
}

private void exitBlockIfNeeded() {
if (currentBlock != null &&
currentBlock != exit) {
Expand All @@ -57,6 +63,8 @@ private void setVirtualRegisters(Scope scope) {
}
}
scope.maxReg = reg;
if (maxLocalReg < scope.maxReg)
maxLocalReg = scope.maxReg;
for (Scope childScope: scope.children) {
setVirtualRegisters(childScope);
}
Expand All @@ -78,9 +86,11 @@ private void compileBlock(AST.BlockStmt block) {

private void compileReturn(AST.ReturnStmt returnStmt) {
if (returnStmt.expr != null) {
compileExpr(returnStmt.expr);
boolean isIndexed = compileExpr(returnStmt.expr);
if (isIndexed)
codeIndexedLoad();
if (virtualStack.size() == 1)
code(new Instruction.Move(pop(), new Operand.ReturnRegisterOperand()));
code(new Instruction.Return(pop()));
else if (virtualStack.size() > 1)
throw new CompilerException("Virtual stack has more than one item at return");
}
Expand Down Expand Up @@ -269,32 +279,35 @@ private boolean compileExpr(AST.Expr expr) {

private boolean compileCallExpr(AST.CallExpr callExpr) {
compileExpr(callExpr.callee);
var callee = top();
if (!(callee instanceof Operand.TempRegisterOperand) ) {
var origCallee = pop();
callee = createTemp();
code(new Instruction.Move(origCallee, callee));
}
List<Operand> args = new ArrayList<>();
var callee = pop();
Type.TypeFunction calleeType = null;
if (callee instanceof Operand.LocalFunctionOperand functionOperand)
calleeType = functionOperand.functionType;
else throw new CompilerException("Cannot call a non function type");
var returnStackPos = virtualStack.size();
List<Operand.RegisterOperand> args = new ArrayList<>();
for (AST.Expr expr: callExpr.args) {
boolean indexed = compileExpr(expr);
if (indexed)
codeIndexedLoad();
var arg = top();
if (!(arg instanceof Operand.TempRegisterOperand) ) {
var origArg = pop();
arg = createTemp();
arg = createTemp(origArg.type);
code(new Instruction.Move(origArg, arg));
}
args.add(arg);
args.add((Operand.RegisterOperand) arg);
}
code(new Instruction.Call(callee, args.toArray(new Operand[args.size()])));
// Similute the actions on the stack
for (int i = 0; i < args.size()+1; i++)
// Simulate the actions on the stack
for (int i = 0; i < args.size(); i++)
pop();
Operand.TempRegisterOperand ret = null;
if (callExpr.callee.type instanceof Type.TypeFunction tf &&
tf.returnType != null)
createTemp();
!(tf.returnType instanceof Type.TypeVoid)) {
ret = createTemp(tf.returnType);
assert ret.regnum-maxLocalReg == returnStackPos;
}
code(new Instruction.Call(returnStackPos, ret, calleeType, args.toArray(new Operand.RegisterOperand[args.size()])));
return false;
}

Expand Down Expand Up @@ -347,13 +360,20 @@ private boolean compileSetFieldExpr(AST.SetFieldExpr setFieldExpr) {
}

private void codeNew(Type type) {
var temp = createTemp();
code(new Instruction.Move(new Operand.NewTypeOperand(type), temp));
var temp = createTemp(type);
if (type instanceof Type.TypeArray typeArray) {
code(new Instruction.NewArray(typeArray, temp));
}
else if (type instanceof Type.TypeStruct typeStruct) {
code(new Instruction.NewStruct(typeStruct, temp));
}
else
throw new CompilerException("Unexpected type: " + type);
}

private void codeStoreAppend() {
var operand = pop();
code(new Instruction.AStoreAppend(top(), operand));
code(new Instruction.AStoreAppend((Operand.RegisterOperand) top(), operand));
}

private boolean compileNewExpr(AST.NewExpr newExpr) {
Expand Down Expand Up @@ -399,7 +419,7 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
Operand right = pop();
Operand left = pop();
if (left instanceof Operand.ConstantOperand leftconstant &&
right instanceof Operand.ConstantOperand rightconstant) {
right instanceof Operand.ConstantOperand rightconstant) {
long value = 0;
switch (opCode) {
case "+": value = leftconstant.value + rightconstant.value; break;
Expand All @@ -415,11 +435,11 @@ private boolean compileBinaryExpr(AST.BinaryExpr binaryExpr) {
case ">=": value = leftconstant.value <= rightconstant.value ? 1 : 0; break;
default: throw new CompilerException("Invalid binary op");
}
pushConstant(value);
pushConstant(value, leftconstant.type);
}
else {
var temp = createTemp();
code(new Instruction.BinaryInstruction(opCode, temp, left, right));
var temp = createTemp(binaryExpr.type);
code(new Instruction.Binary(opCode, temp, left, right));
}
return false;
}
Expand All @@ -433,35 +453,38 @@ private boolean compileUnaryExpr(AST.UnaryExpr unaryExpr) {
Operand top = pop();
if (top instanceof Operand.ConstantOperand constant) {
switch (opCode) {
case "-": pushConstant(-constant.value); break;
case "!": pushConstant(constant.value == 0?1:0); break;
case "-": pushConstant(-constant.value, constant.type); break;
// Maybe below we should explicitly set Int
case "!": pushConstant(constant.value == 0?1:0, constant.type); break;
default: throw new CompilerException("Invalid unary op");
}
}
else {
var temp = createTemp();
code(new Instruction.UnaryInstruction(opCode, temp, top));
var temp = createTemp(unaryExpr.type);
code(new Instruction.Unary(opCode, temp, top));
}
return false;
}

private boolean compileConstantExpr(AST.LiteralExpr constantExpr) {
pushConstant(constantExpr.value.num.intValue());
pushConstant(constantExpr.value.num.intValue(), constantExpr.type);
return false;
}

private void pushConstant(long value) {
virtualStack.add(new Operand.ConstantOperand(value));
private void pushConstant(long value, Type type) {
pushOperand(new Operand.ConstantOperand(value, type));
}

private Operand.TempRegisterOperand createTemp() {
var tempRegister = new Operand.TempRegisterOperand(virtualStack.size());
virtualStack.add(tempRegister);
private Operand.TempRegisterOperand createTemp(Type type) {
var tempRegister = new Operand.TempRegisterOperand(virtualStack.size()+maxLocalReg, type);
pushOperand(tempRegister);
if (maxStackSize < virtualStack.size())
maxStackSize = virtualStack.size();
return tempRegister;
}

private void pushLocal(int regnum, String varName) {
virtualStack.add(new Operand.LocalRegisterOperand(regnum, varName));
pushOperand(new Operand.LocalRegisterOperand(regnum, varName));
}

private void pushOperand(Operand operand) {
Expand All @@ -478,14 +501,28 @@ private Operand top() {

private void codeIndexedLoad() {
Operand indexed = pop();
var temp = createTemp();
code(new Instruction.Move(indexed, temp));
var temp = createTemp(indexed.type);
if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
code(new Instruction.ArrayLoad(loadIndexedOperand, temp));
}
else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
code(new Instruction.GetField(loadFieldOperand, temp));
}
else
code(new Instruction.Move(indexed, temp));
}

private void codeIndexedStore() {
Operand value = pop();
Operand indexed = pop();
code(new Instruction.Move(value, indexed));
if (indexed instanceof Operand.LoadIndexedOperand loadIndexedOperand) {
code(new Instruction.ArrayStore(value, loadIndexedOperand));
}
else if (indexed instanceof Operand.LoadFieldOperand loadFieldOperand) {
code(new Instruction.SetField(value, loadFieldOperand));
}
else
code(new Instruction.Move(value, indexed));
}

private boolean vstackEmpty() {
Expand Down
Loading
Loading