diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..71f5fef
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..def6926
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.settings
+.vscode
+target/
\ No newline at end of file
diff --git a/.project b/.project
new file mode 100644
index 0000000..40cd45a
--- /dev/null
+++ b/.project
@@ -0,0 +1,23 @@
+
+
+ universe
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/README.MD b/README.MD
new file mode 100644
index 0000000..cc16c7f
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,13 @@
+# Project Info
+A program that compiles (into MIPS Assembly) or interprets code written in a generic programming language.
+
+Written using Maven. A tester file can be located at `src/main/java/com/pilers/tester/Tester.java`.
+
+Steps:
+1. The preprocessor handles macros (i.e. #include) and pipes the input program as a stream of bytes to the scanner.
+2. Lexical analysis: the scanner scans the stream of bytes into a stream of tokens - entities that store their own type (Integer, String, or Boolean), value as a string, and line number. It transfers this stream of tokens to the parser.
+3. Syntax analysis: the parser parses the stream of tokens into an abstract syntax tree (AST). It passes this tree to an environment, which acts as a manager for variable and function values. The object nature of Environments allows the creation of sub-Environments, which allows the usage of local variables inside a function that don't affect variables of the same name outside of the function. Note that the parser returns a Program object which acts as the root node ofthe AST.
+4. If the program is set to interpret, see step 5. If the program is set to compile, see steps 6 and 7.
+5. The program is executed using an interpreter environment, which essentially acts as a map of variable values.
+6. The program is analyzed using a semantic analysis environment, which performs actions such as type checking, seeing if a variable exists when it is referenced, etc.
+7. The program is compiled into MIPS Assembly code using an Emitter in a specified filepath.
\ No newline at end of file
diff --git a/all.asm b/all.asm
new file mode 100644
index 0000000..6d35e2d
Binary files /dev/null and b/all.asm differ
diff --git a/demo.asm b/demo.asm
new file mode 100644
index 0000000..b3ec6a1
Binary files /dev/null and b/demo.asm differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cb7658c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+ com.pilers.app
+ universe
+ 1.0-SNAPSHOT
+
+ universe
+
+ http://www.example.com
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
+
+ junit
+ junit
+ 4.11
+ test
+
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+
+
+
diff --git a/printSquares.asm b/printSquares.asm
new file mode 100644
index 0000000..d744e92
Binary files /dev/null and b/printSquares.asm differ
diff --git a/public/printHello.txt b/public/printHello.txt
new file mode 100644
index 0000000..b929ec7
--- /dev/null
+++ b/public/printHello.txt
@@ -0,0 +1 @@
+writeln("Hello from the internet!");
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Assignment.java b/src/main/java/com/pilers/ast/Assignment.java
new file mode 100644
index 0000000..6372cb9
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Assignment.java
@@ -0,0 +1,88 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST Assignment class
+ * Represents the assignment of a value to a variable
+ *
+ * @author Gloria Zhu
+ */
+public class Assignment extends Statement
+{
+ private String var;
+ private Expression exp;
+
+ /**
+ * Constructs an Assignment object
+ *
+ * @param var the name of the variable
+ * @param exp an expression for the value
+ * @throws TypeErrorException
+ */
+ public Assignment(String var, Expression exp)
+ {
+ this.var = var;
+ this.exp = exp;
+ }
+
+ /**
+ * Executes the assignment
+ *
+ * @param env the execution environment (InterpreterEnvironment)
+ * @throws BreakException if a break statement is executed
+ * @throws ContinueException if a continue statement is executed
+ * @throws InterpretException if there is a runtime error in assignment
+ * Happens if the varaible does not exist,
+ * or there is a type discrepancy between the
+ * variable and value
+ */
+ public void exec(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ env.setVariable(var, exp.eval(env));
+ }
+
+ /**
+ * Semantic analysis for assignment
+ * Checks if the variable exists and the types are compatible
+ *
+ * @throws CompileException if the conditions above are broken
+ * @param env the current environment
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ exp.analyze(env);
+
+ String expectedType = env.getVariableType(var);
+
+ if (expectedType == null) throw new CompileException(ErrorString.unknownIdentifier(var));
+
+ if (!expectedType.equals(exp.type))
+ throw new CompileException(ErrorString.typeAssignment(expectedType, exp.type));
+ }
+
+ /**
+ * Compiles the assignment
+ * The specific depends on the type of the varaible
+ *
+ * @param e the emitter
+ */
+ public void compile(Emitter e)
+ {
+ exp.compile(e); // most recent value is in v0
+
+ if (e.isGlobalVariable(var)) e.emit("sw $v0 "+"_data_"+var);
+ else
+ {
+ int index = e.getLocalVarIndex(var);
+ e.emit("sw $v0 " + index * 4 + "($sp)");
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/BinOp.java b/src/main/java/com/pilers/ast/BinOp.java
new file mode 100644
index 0000000..940d2b6
--- /dev/null
+++ b/src/main/java/com/pilers/ast/BinOp.java
@@ -0,0 +1,487 @@
+package com.pilers.ast;
+
+import com.pilers.environment.*;
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+import com.pilers.emitter.*;
+
+/**
+ * AST BinOp class
+ * Represents a binary operation between two values
+ *
+ * BinOp sets type during semantic analysis
+ *
+ * @author Gloria Zhu
+ */
+public class BinOp extends Expression
+{
+ private String op;
+ private Expression exp1;
+ private Expression exp2;
+
+ /**
+ * Constructs a BinOp object
+ *
+ * Deduces the resulting type of the BinOp from the two types
+ * of the expressions as well as the operand. If the combination
+ * of types/operand is invalid, ignores it for now - it will either
+ * be caught in semantic analysis (in the case of compiling) or
+ * it will throw a runtime exception (in the case of interpreting).
+ * (Just set the type to an empty string)
+ *
+ * @param op operator in string form
+ * @param exp1 left expression
+ * @param exp2 right expression
+ */
+ public BinOp(String op, Expression exp1, Expression exp2)
+ {
+ this.op = op;
+ this.exp1 = exp1;
+ this.exp2 = exp2;
+ }
+
+ /**
+ * Evaluates the binary operation according to the types of its values and what
+ * its operation is. Not all type combinations are valid, and not all operations
+ * are defined for all type combinations.
+ *
+ * For example, Integer << Integer is a valid binary operation, but String <<
+ * Integer is not.
+ *
+ * If a set of values and an operation doesn't satisfy any of the valid
+ * permutations, an error should be thrown (it currently isn't)
+ *
+ * @param env the execution environment
+ * @return the Value of the binary operation
+ * @throws BreakException if a break statement is executed
+ * @throws ContinueException if a continue statement is executed
+ * @throws InterpretException if there is a runtime exception in execution
+ */
+ public Value eval(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ Value value1 = exp1.eval(env); // evaluate the two values
+ Value value2 = exp2.eval(env);
+
+ String type1 = value1.type;
+ String type2 = value2.type;
+
+ switch(type1+" "+type2) // the type of output depends on the type of input
+ {
+
+ /**
+ * Standard operations:
+ * mathematical (Integer result)
+ * relational (Boolean result)
+ * bitwise (Integer result)
+ */
+ case("Integer Integer"):
+ int case1Val1 = Integer.parseInt(value1.getValue());
+ int case1Val2 = Integer.parseInt(value2.getValue());
+
+ switch(op)
+ {
+ // arithmetic operators
+ case ("+"):
+ type = "Integer";
+ return new Value(case1Val1 + case1Val2);
+ case ("-"):
+ type = "Integer";
+ return new Value(case1Val1 - case1Val2);
+ case ("*"):
+ type = "Integer";
+ return new Value(case1Val1 * case1Val2);
+ case ("/"):
+ type = "Integer";
+ return new Value(case1Val1 / case1Val2);
+ case ("%"):
+ type = "Integer";
+ return new Value(case1Val1 % case1Val2);
+
+ // relational operators
+ case("<="):
+ type = "Boolean";
+ return new Value(case1Val1 <= case1Val2);
+ case(">="):
+ type = "Boolean";
+ return new Value(case1Val1 >= case1Val2);
+ case("<"):
+ type = "Boolean";
+ return new Value(case1Val1 < case1Val2);
+ case(">"): return new Value(case1Val1 > case1Val2);
+ case("!="):
+ type = "Boolean";
+ return new Value(case1Val1 != case1Val2);
+ case("=="):
+ type = "Boolean";
+ return new Value(case1Val1 == case1Val2);
+
+ // bitwise operators
+ case("&"):
+ type = "Integer";
+ return new Value(case1Val1 & case1Val2);
+ case("|"):
+ type = "Integer";
+ return new Value(case1Val1 | case1Val2);
+ case("<<"):
+ type = "Integer";
+ return new Value(case1Val1 << case1Val2);
+ case(">>"):
+ type = "Integer";
+ return new Value(case1Val1 >> case1Val2);
+ case("^"):
+ type = "Integer";
+ return new Value(case1Val1 ^ case1Val2);
+ }
+
+ /**
+ * String + Integer concatenates the integer and returns a string
+ * String * Integer returns the String repeated Integer amount of times
+ */
+ case("String Integer"):
+ String case2Val1 = value1.getValue();
+ int case2Val2 = Integer.parseInt(value2.getValue());
+ type = "String";
+ switch(op)
+ {
+ case("+"): return new Value(case2Val1 + case2Val2, "String");
+ case("*"):
+ String ret = "";
+ for(int i=0;i>"):
+ case ("^"):
+ type = "Integer";
+ break;
+
+ case ("<="):
+ case (">="):
+ case ("<"):
+ case (">"):
+ case ("!="):
+ case ("=="):
+ type = "Boolean";
+ break;
+
+ default:
+ throw new CompileException(
+ ErrorString.invalidBinOperation(exp1.type, exp2.type, op));
+ }
+ break;
+
+ case ("Integer String"):
+ case ("String Integer"):
+ switch (op)
+ {
+ case ("+"):
+ case ("*"):
+ type = "String";
+ break;
+ default:
+ throw new CompileException(
+ ErrorString.invalidBinOperation(exp1.type, exp2.type, op));
+ }
+ break;
+
+ case ("String String"):
+ switch (op)
+ {
+ case ("+"):
+ type = "String";
+ break;
+ case ("=="):
+ type = "Boolean";
+ break;
+
+ default:
+ throw new CompileException(
+ ErrorString.invalidBinOperation(exp1.type, exp2.type, op));
+ }
+ break;
+
+ case ("Boolean Boolean"):
+ switch (op)
+ {
+ case ("&&"):
+ case ("||"):
+ case ("^"):
+ type = "Boolean";
+ break;
+
+ default:
+ throw new CompileException(
+ ErrorString.invalidBinOperation(exp1.type, exp2.type, op));
+ }
+ break;
+
+ default:
+ throw new CompileException(
+ ErrorString.invalidBinOperation(exp1.type, exp2.type, op));
+ }
+
+ }
+
+ /**
+ * Compiles the BinOp
+ *
+ * NOTE: This assumes no overflow for integer multiplication, and
+ * it will also floor integer divide results
+ *
+ * @param e the given emitter
+ */
+ public void compile(Emitter e)
+ {
+ e.emit("# Compiling BinOp components");
+ exp1.compile(e);
+
+ e.emitPush("$v0");
+
+ e.startCompilingBinOp();
+
+ exp2.compile(e);
+
+ e.emitPop("$t0");
+
+ e.stopCompilingBinOp();
+
+ // first argument in t0, second argument in v0
+
+ String type1 = exp1.type;
+ String type2 = exp2.type;
+
+ e.emit("# Beginning BinOp compilation");
+
+ switch(type1+" "+type2) // the type of output depends on the type of input
+ {
+
+ /**
+ * Standard operations:
+ * mathematical (Integer result)
+ * relational (Boolean result)
+ * bitwise (Integer result)
+ */
+ case("Integer Integer"):
+ switch(op)
+ {
+ // arithmetic operators
+ case ("+"): e.emit("addu $v0 $t0 $v0"); break;
+ case ("-"): e.emit("subu $v0 $t0 $v0"); break;
+ case ("*"):
+ e.emit("multu $v0 $t0");
+ e.emit("mflo $v0");
+ break;
+ case ("/"):
+ e.emit("divu $v0 $t0");
+ e.emit("mflo $v0");
+ break;
+ case ("%"):
+ e.emit("divu $v0 $t0");
+ e.emit("mfhi $v0");
+ break;
+
+ // relational operators - TODO
+ case("<="): e.emit("jal _LEQ"); break;
+ case(">="): e.emit("jal _GEQ"); break;
+ case("<"): e.emit("jal _LT"); break;
+ case(">"): e.emit("jal _GT"); break;
+ case("!="): e.emit("jal _NE"); break;
+ case("=="): e.emit("jal _E"); break;
+
+ // bitwise operators
+ case("&"): e.emit("and $v0 $t0 $v0"); break;
+ case("|"): e.emit("or $v0 $t0 $v0"); break;
+ case("<<"): e.emit("sllv $v0 $t0 $v0"); break;
+ case(">>"): e.emit("srlv $v0 $t0 $v0"); break;
+ case("^"): e.emit("xor $v0 $t0 $v0"); break;
+ }
+ break;
+
+
+ /**
+ * Same as below (just swap places before fall-through)
+ */
+ case("Integer String"):
+ // todo
+ // e.emit("move $t1 $v0");
+ // e.emit("move $v0 $t0");
+ // e.emit("move $t0 $t1");
+ break;
+
+ /**
+ * String + Integer concatenates the integer and returns a string
+ * String * Integer returns the String repeated Integer amount of times
+ */
+ case("String Integer"):
+ // todo
+ // switch(op)
+ // {
+ // case("+"): // todo
+ // // find the number of digits in the integer
+
+ // e.emit("li $t1 0"); // t1 is the counter
+
+ // // basically a while loop
+ // String whileStart = e.nextLabel();
+ // String whileStop = e.nextLabel();
+ // e.emit(whileStart+": ");
+ // //e.emit("")
+ // break;
+
+ // case("*"): // todo
+ // e.emit("li $t1 ($v0)"); // length of old string in t1
+ // e.emit("multu $t0 $t1");
+ // e.emit("mflo $t2"); // length of new string in t2
+ // e.emit("li $t3 1"); // t3 = 1 (used for decrementing)
+ // e.emit("move $t5 $v0"); // old string's address in t5
+ // // string address in v0
+ // // use t0 as the counter (for # of times to "add" the string)
+ // // use t4 as the internal counter (to iterate over each character)
+
+ // // allocate space for string
+ // e.emit("li $v0 9");
+ // // allocate enough space for strlength + str + null terminator
+ // e.emit("addiu $a0 $t2 5");
+ // e.emit("syscall"); // memory address of newly allocated space is in v0
+
+ // String keepGoingLabel = e.nextLabel();
+ // String stopLabel = e.nextLabel();
+
+ // String keepGoingLabelNested = e.nextLabel();
+ // String stopLabelNested = e.nextLabel();
+
+
+ // e.emit(keepGoingLabel + ": ");
+ // e.emit("beq $t0 0 " + stopLabel); // stop when t0 hits 0
+
+ // // concat the string
+ // e.emit("li $t4 0"); // t4 is the counter
+ // e.emit(keepGoingLabelNested + ": ");
+ // e.emit("beq $t4 $t1 " + stopLabelNested); // stop when t4==t1
+ // e.emit("multu $t0 $t1");
+ // e.emit("mflo $t6");
+ // e.emit("addu $t6 $t6 4"); // need to add 4 because of offset
+ // e.emit("sb $t6($t5) $t6($v0)");
+ // e.emit("addiu $t4 1");
+ // // concat the current character and decrement t4
+ // e.emit("j " + keepGoingLabelNested);
+ // e.emit(stopLabelNested+":");
+
+
+ // e.emit("subu $t0 $t0 $t3"); // decrement t0
+ // e.emit("j " + keepGoingLabel);
+ // e.emit(stopLabel + ":");
+ // }
+ break;
+
+
+
+ /**
+ * String + String returns the String concatenation
+ * String = String returns a Boolean if they are equal
+ */
+ case ("String String"):
+ // todo
+ break;
+
+ /**
+ * Standard logical operators
+ */
+ case ("Boolean Boolean"):
+ switch(op)
+ {
+ case ("&&"): e.emit("and $v0 $t0 $v0"); break;
+ case ("||"): e.emit("or $v0 $t0 $v0"); break;
+ case ("^"): e.emit("xor $v0 $t0 $v0"); break;
+ }
+ break;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Block.java b/src/main/java/com/pilers/ast/Block.java
new file mode 100644
index 0000000..bf37414
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Block.java
@@ -0,0 +1,73 @@
+package com.pilers.ast;
+
+import java.util.List;
+import com.pilers.environment.*;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+import com.pilers.emitter.*;
+
+/**
+ * AST Block class
+ * This represents a chunk of statements
+ *
+ * @author Gloria Zhu
+ */
+public class Block extends Statement
+{
+ private List stmts;
+
+ /**
+ * Constructs a Block object
+ *
+ * @param stmts a list of statements
+ */
+ public Block(List stmts)
+ {
+ this.stmts = stmts;
+ }
+
+ /**
+ * Execute the statements in the block
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is encountered, let it keep
+ * bubbling
+ * @throws ContinueException if a continue statement is encountered, stop the
+ * rest of execution of the block
+ * @throws InterpretException if there is as run time error during execution
+ */
+ public void exec(InterpreterEnvironment env) throws
+ BreakException, ContinueException, InterpretException
+ {
+ try
+ {
+ for (Statement s : stmts) s.exec(env);
+ }
+ catch (ContinueException e)
+ {
+ // do nothing
+ }
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Analyzes each statement 1 by 1
+ *
+ * @throws CompileException if any statements throw an error
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ for (Statement s : stmts) s.analyze(env);
+ }
+
+ /**
+ * Compiles the Block
+ * @param e the given emitter
+ */
+ public void compile(Emitter e)
+ {
+ for (Statement s : stmts) s.compile(e);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Break.java b/src/main/java/com/pilers/ast/Break.java
new file mode 100644
index 0000000..cc7f00e
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Break.java
@@ -0,0 +1,35 @@
+package com.pilers.ast;
+
+import com.pilers.environment.*;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST Break Class
+ *
+ * @author Gloria Zhu
+ */
+public class Break extends Statement
+{
+ /**
+ * A Break statement breaks out of the for and while control blocks
+ *
+ * @param env the execution environment
+ * @throws BreakException when encountered
+ */
+ public void exec(InterpreterEnvironment env) throws BreakException
+ {
+ throw new BreakException();
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * (todo) Checks that the break statement is inside a looping construct
+ *
+ * @throws CompileException if the above condition is not met
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ // TODO check that the break is inside a looping construct
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/BreakException.java b/src/main/java/com/pilers/ast/BreakException.java
new file mode 100644
index 0000000..c4066b1
--- /dev/null
+++ b/src/main/java/com/pilers/ast/BreakException.java
@@ -0,0 +1,11 @@
+package com.pilers.ast;
+
+/**
+ * Thrown when a break statement is encountered
+ *
+ * @author Gloria Zhu
+ */
+public class BreakException extends Exception
+{
+ private static final long serialVersionUID = 1L; // to satisfy superclass
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Continue.java b/src/main/java/com/pilers/ast/Continue.java
new file mode 100644
index 0000000..5d95716
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Continue.java
@@ -0,0 +1,36 @@
+package com.pilers.ast;
+
+import com.pilers.environment.*;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST Break Class
+ *
+ * @author Gloria Zhu
+ */
+public class Continue extends Statement
+{
+ /**
+ * Executes the continue statement
+ * A continue statement breaks out of a block of statements
+ *
+ * @param env the execution environment
+ * @throws ContinueException when encountered
+ */
+ public void exec(InterpreterEnvironment env) throws ContinueException
+ {
+ throw new ContinueException();
+ }
+
+ /**
+ * Performs semantic analysis on this (todo)
+ * Checks that the continue statement is inside a looping construct
+ *
+ * @throws CompileException if the above condition is not met
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ // TODO check that the continue is inside a looping construct
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/ContinueException.java b/src/main/java/com/pilers/ast/ContinueException.java
new file mode 100644
index 0000000..3e484ed
--- /dev/null
+++ b/src/main/java/com/pilers/ast/ContinueException.java
@@ -0,0 +1,11 @@
+package com.pilers.ast;
+
+/**
+ * Thrown when a continue statement is executed
+ *
+ * @author Gloria Zhu
+ */
+public class ContinueException extends Exception
+{
+ private static final long serialVersionUID = 1L; // to satisfy superclass
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Declaration.java b/src/main/java/com/pilers/ast/Declaration.java
new file mode 100644
index 0000000..bbb6897
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Declaration.java
@@ -0,0 +1,80 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+* AST Declaration class
+ *
+ * @author Gloria Zhu
+ */
+public class Declaration extends Statement
+{
+ private String type;
+ private String var;
+
+ /**
+ * Constructs an Assignment object
+ *
+ * @param type the type of the variable
+ * @param var a string
+ * @throws TypeErrorException
+ */
+ public Declaration(String type, String var)
+ {
+ this.type = type;
+ this.var = var;
+ }
+
+ /**
+ * Executes the declaration
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is executed
+ * @throws ContinueException if a continue statement is executed
+ * @throws InterpretException if there is a run time error (the variable already exists)
+ */
+ public void exec(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ env.declareVariable(type, var);
+ }
+
+ /**
+ * Performs semantic analysis on this (todo)
+ * Checks that the variable does not already exist
+ *
+ * @throws CompileException if the above condition is not met
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ String vartype = env.getVariableType(var);
+ if (vartype != null) throw new CompileException(ErrorString.duplicateIdentifier(var));
+
+ env.declareVariable(type, var);
+ }
+
+ /**
+ * Compiles the declaration
+ *
+ * @param e given emitter
+ */
+ public void compile(Emitter e)
+ {
+ // if e is not compiling a procedure, then this is a global variable and
+ // will be declared in .data
+
+ if (e.isCompilingProcedure())
+ {
+ e.addLocalVariable(var);
+ e.emit("li $v0 0 # Declaring local variable "+var);
+ // default value 0 for all variables
+ e.emitPush("$v0");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/DeclareAssignment.java b/src/main/java/com/pilers/ast/DeclareAssignment.java
new file mode 100644
index 0000000..09a9eda
--- /dev/null
+++ b/src/main/java/com/pilers/ast/DeclareAssignment.java
@@ -0,0 +1,72 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST DeclareAssignment class
+ * Represents a statement of the from 'TYPE IDENTIFER = VALUE'
+ * Basically a 2-in-1 package of a declaration and an assignment
+ *
+ * @author Gloria Zhu
+ */
+public class DeclareAssignment extends Statement
+{
+ private Declaration declaration;
+ private Assignment assignment;
+
+ /**
+ * Constructs a DeclareAssignment object
+ *
+ * @param type the type of the variable
+ * @param var a string
+ * @param exp an expression
+ * @throws TypeErrorException
+ */
+ public DeclareAssignment(String type, String var, Expression exp)
+ {
+ declaration = new Declaration(type, var);
+ assignment = new Assignment(var, exp);
+ }
+
+ /**
+ * Executes the assignment
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is executed
+ * @throws ContinueException if a continue statement is executed
+ * @throws InterpretException if there is an error during execution
+ */
+ public void exec(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ declaration.exec(env);
+ assignment.exec(env);
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Delegates the tasks to the declaration and assignment objs respectively
+ *
+ * @throws CompileException if either throws an error
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ declaration.analyze(env);
+ assignment.analyze(env);
+ }
+
+ /**
+ * Compiles the declaration/assignment
+ * @param e given emitter
+ */
+ public void compile(Emitter e)
+ {
+ declaration.compile(e);
+ assignment.compile(e);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/Expression.java b/src/main/java/com/pilers/ast/Expression.java
new file mode 100644
index 0000000..23d03df
--- /dev/null
+++ b/src/main/java/com/pilers/ast/Expression.java
@@ -0,0 +1,52 @@
+package com.pilers.ast;
+
+import com.pilers.environment.*;
+import com.pilers.emitter.*;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST Expression class
+ * @author Gloria Zhu
+ */
+public abstract class Expression
+{
+ /**
+ * The type of the expression
+ * To be used primarily during semantic analysis
+ */
+ protected String type;
+
+ /**
+ * Evaluates the expression
+ * To be implemented by subclasses
+ *
+ * @param env evaluation environment
+ * @return the value of the evaluated expression
+ * @throws BreakException if a break statement is executed
+ * @throws ContinueException if a continue statement is executed
+ * @throws InterpretException if there is a run time error during execution
+ */
+ public abstract Value eval(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException;
+
+ /**
+ * Performs semantic analysis (checks things like type, scope)
+ * To be implemented by subclasses
+ * analyze() should be used during the semantic analysis stage
+ *
+ * @throws CompileException if any errors are found during analysis
+ * @param env analysis environment (SemanticAnalysisEnvironment)
+ */
+ public abstract void analyze(SemanticAnalysisEnvironment env) throws CompileException;
+
+ /**
+ * Compiles the statement with the given emitter
+ *
+ * @param e the given emitter
+ */
+ public void compile(Emitter e)
+ {
+ throw new Error("Implement me!!!!!");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/For.java b/src/main/java/com/pilers/ast/For.java
new file mode 100644
index 0000000..6e4235b
--- /dev/null
+++ b/src/main/java/com/pilers/ast/For.java
@@ -0,0 +1,130 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * A For block uses the following syntax:
+ *
+ * FOR(ASSIGNMENT STATEMENT;BOOLEAN-VALUED EXPRESSION;ANY STATEMENT;) STATEMENT;
+ *
+ * Note the 3 semicolons instead of 2 inside the parentheses
+ *
+ * Example:
+ *
+ * FOR( i:=0 ; i<3 ; i = i + 1) WRITELN(i);
+ *
+ * @author Gloria Zhu
+ */
+public class For extends Statement
+{
+ private Statement declaration;
+ private Expression condition;
+ private Statement incrementation;
+ private Statement stmt;
+
+ /**
+ * Constructs a For object Note: this part doesn't check if the first statement
+ * is an assignment and that the expression evaluates to a boolean, that's
+ * checked when the for statement is run
+ *
+ * @param decl the initial declaration (assignment) statement
+ * @param cond some expression that evaluates to a boolean Value
+ * @param incr some statement (could be anything) but should probably be
+ * incrementation
+ * @param stmt the body statement to be executed
+ */
+ public For(Statement decl, Expression cond, Statement incr, Statement stmt)
+ {
+ declaration = decl;
+ condition = cond;
+ incrementation = incr;
+ this.stmt = stmt;
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Checks that the expression is of a boolean type
+ *
+ * @throws CompileException if the above condition is broken
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ declaration.analyze(env);
+ condition.analyze(env);
+ incrementation.analyze(env);
+ stmt.analyze(env);
+
+ if (!condition.type.equals("Boolean"))
+ throw new CompileException(ErrorString.forLoopConditionInvalid());
+ }
+
+ /**
+ * Executes the for statement Double checks that the first statement is an
+ * assignment and that the expression evaluates to a boolean; if not, it should
+ * eventually throw an error (but it currently does not)
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is encountered, terminate
+ * execution of the rest of the for loop
+ * @throws ContinueException if a continue statement is encountered, let it
+ * bubble
+ * @throws InterpretException if there is a run time exception when running
+ */
+ public void exec(InterpreterEnvironment env) throws ContinueException, InterpretException
+ {
+ try
+ {
+ declaration.exec(env);
+ Value condVal = condition.eval(env);
+
+ if (!condVal.getType().equals("Boolean"))
+ {
+ // todo throw an error eventually
+ }
+ else
+ {
+ while(condVal.getValue().equals("TRUE"))
+ {
+ stmt.exec(env); // execute statement
+ incrementation.exec(env); // execute incrementation
+ condVal = condition.eval(env); // evaluate condition again
+
+ }
+ }
+ }
+ catch (BreakException e)
+ {
+ // stop execution
+ }
+ }
+
+ /**
+ * Compiles the for statement
+ *
+ * @param e the emitter
+ */
+ public void compile(Emitter e)
+ {
+ declaration.compile(e);
+ condition.compile(e);
+
+ String keepGoingLabel = e.nextLabel();
+ String stopLabel = e.nextLabel();
+
+ e.emit(keepGoingLabel + ":");
+ e.emit("beq $v0 0 " + stopLabel); // if false, stop
+
+ stmt.compile(e);
+ incrementation.compile(e);
+
+ condition.compile(e); // recompile the condition
+ e.emit("j " + keepGoingLabel);
+
+ e.emit(stopLabel + ":");
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/If.java b/src/main/java/com/pilers/ast/If.java
new file mode 100644
index 0000000..7b955ac
--- /dev/null
+++ b/src/main/java/com/pilers/ast/If.java
@@ -0,0 +1,88 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST If class
+ *
+ * @author Gloria Zhu
+ */
+public class If extends Statement
+{
+ private Expression condition;
+ private Statement stmt;
+
+ /**
+ * Constructs an If object (does not check that the expression evaluates to a
+ * boolean, that's the job of the execution)
+ *
+ * @param condition the condition
+ * @param stmt the body statement
+ */
+ public If(Expression condition, Statement stmt)
+ {
+ this.condition = condition;
+ this.stmt = stmt;
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Checks that the expression is of a boolean type
+ *
+ * @throws CompileException if the above condition is broken
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ condition.analyze(env);
+ stmt.analyze(env);
+
+ if (!condition.type.equals("Boolean"))
+ throw new CompileException(ErrorString.ifLoopConditionInvalid());
+
+ // todo give a warning if the if block is empty?
+ }
+
+ /**
+ * Evaluates the expression, if it is type boolean and evaluates to true,
+ * executes the body statement
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is executed, let it bubble
+ * @throws ContinueException if a continue statement is executed, let it bubble
+ * @throws InterpretException if an error is thrown during execution
+ */
+ public void exec(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ Value val = condition.eval(env);
+ if (!(val.getType().equals("Boolean")))
+ {
+ // todo throw an error
+ }
+ else if (val.getValue().equals("TRUE")) stmt.exec(env);
+ }
+
+ /**
+ * Compiles the if statement
+ *
+ * @param e the emitter
+ */
+ public void compile(Emitter e)
+ {
+ condition.compile(e);
+
+ String label = e.nextLabel();
+
+ e.emit("beq $v0 0 "+label); // if false, go to label to skip the if statement
+
+ stmt.compile(e);
+
+ e.emit(label+":");
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/ProcedureCallExpression.java b/src/main/java/com/pilers/ast/ProcedureCallExpression.java
new file mode 100644
index 0000000..1eeab3e
--- /dev/null
+++ b/src/main/java/com/pilers/ast/ProcedureCallExpression.java
@@ -0,0 +1,94 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+import com.pilers.errors.ErrorString;
+
+/**
+ * AST ProcedureCallExpression class
+ * A procedure call used as an expression
+ *
+ * Assigns type during semantic analysis
+ *
+ * @author Gloria Zhu
+ */
+public class ProcedureCallExpression extends Expression
+{
+ private String name;
+ private Expression[] parameters;
+
+ /**
+ * Constructs a ProcedureCallExpression objec
+ *
+ * @param name the name of the procedure to call
+ * @param parameters the passed in parameters (as expressions)
+ */
+ public ProcedureCallExpression(String name, Expression[] parameters)
+ {
+ this.name = name;
+ this.parameters = parameters;
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Uses the helper in the procedure declaration class
+ *
+ * @throws CompileException if a CompileException is thrown
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ for(Expression e : parameters) e.analyze(env);
+
+ ProcedureDeclaration proc = env.getProcedure(name);
+
+ if (proc==null) throw new CompileException(ErrorString.unknownProcedure(name));
+
+ proc.analyzeProcedureCall(env, parameters);
+
+ this.type = proc.getReturnType();
+ }
+
+ /**
+ * Evaluates a procedure
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is executed, let it bubble
+ * @throws ContinueException if a continue statement is executed, let it bubble
+ * @return the returned value of the procedure, if any
+ * @throws InterpretException if an error is thrown
+ */
+ public Value eval(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ ProcedureDeclaration proc = env.getProcedure(name);
+
+ return proc.callProcedure(env, parameters);
+ }
+
+ /**
+ * Compiles the procedure call as if it were used as an expression
+ *
+ * @param e the emitter
+ */
+ public void compile(Emitter e)
+ {
+
+ for (int i = 0; i < parameters.length; i++)
+ {
+ parameters[i].compile(e);
+ e.emitPush("$v0");
+ e.addPushedParam();
+ }
+
+ e.resetPushedParams();
+
+ e.emit("jal " + name);
+
+ // return value is pushed inside of the procedure for clarity
+ // now: return value is in v0
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/ast/ProcedureCallStatement.java b/src/main/java/com/pilers/ast/ProcedureCallStatement.java
new file mode 100644
index 0000000..2918354
--- /dev/null
+++ b/src/main/java/com/pilers/ast/ProcedureCallStatement.java
@@ -0,0 +1,84 @@
+package com.pilers.ast;
+
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.*;
+import com.pilers.errors.InterpretException;
+import com.pilers.errors.CompileException;
+
+/**
+ * AST ProcedureCallStatement class
+ * A procedure call used as a statement
+ *
+ * @author Gloria Zhu
+ */
+public class ProcedureCallStatement extends Statement
+{
+ private String name;
+ private Expression[] parameters;
+
+ /**
+ * Constructs a ProcedureCallStatement objec
+ *
+ * @param name the name of the procedure to call
+ * @param parameters the passed in parameters (as expressions)
+ */
+ public ProcedureCallStatement(String name, Expression[] parameters)
+ {
+ this.name = name;
+ this.parameters = parameters;
+ }
+
+ /**
+ * Performs semantic analysis on this
+ * Uses the helper in the procedure declaration class
+ *
+ * @throws CompileException if a CompileException is thrown
+ * @param env the current env
+ */
+ public void analyze(SemanticAnalysisEnvironment env) throws CompileException
+ {
+ for (Expression e : parameters) e.analyze(env);
+
+ ProcedureDeclaration proc = env.getProcedure(name);
+ proc.analyzeProcedureCall(env, parameters);
+ }
+
+ /**
+ * Evaluates a procedure
+ *
+ * @param env the execution environment
+ * @throws BreakException if a break statement is executed, let it bubble
+ * @throws ContinueException if a continue statement is executed, let it bubble
+ * @throws InterpretException if there is an error thrown durign execution
+ */
+ public void exec(InterpreterEnvironment env)
+ throws BreakException, ContinueException, InterpretException
+ {
+ ProcedureDeclaration proc = env.getProcedure(name);
+ proc.callProcedure(env, parameters);
+ }
+
+ /**
+ * Compiles the procedure as if it were used as a statement
+ *
+ * @param e the emitter
+ */
+ public void compile(Emitter e)
+ {
+
+ for(int i=0;i hasOccurred = new HashMap();
+ for(String p : paramNames)
+ {
+ if (hasOccurred.get(p) != null) throw new CompileException(
+ ErrorString.duplicateParamNames(name, p));
+ if (p.equals(name)) throw new CompileException(
+ ErrorString.paramNameEqualsProcedureName(name));
+ }
+
+ env.setProcedure(name, this);
+
+ SemanticAnalysisEnvironment childEnv = new SemanticAnalysisEnvironment(env);
+
+ // setting the parameter values in the child environment
+ for (int i = 0; i < paramNames.length; i++)
+ {
+ childEnv.declareVariable(paramTypes[i], paramNames[i]);
+ }
+
+ // setting the return value variable (same name as the procedure)
+ childEnv.declareVariable(returnType, name);
+
+ stmt.analyze(childEnv);
+ }
+
+ /**
+ * Performs semantic analysis for a given set of parameters
+ *
+ * This is in the declaration class instead of the procedure call classes
+ * because it's essentially the same for both, so this avoids repetition.
+ *
+ * Checks:
+ * parameter list length is the same
+ * parameter types match those of the declaration
+ *
+ * @param env the current env
+ * @param parameters the given parameters
+ * @throws CompileException if one of the above conditions is broken
+ */
+ public void analyzeProcedureCall(
+ SemanticAnalysisEnvironment env, Expression[] parameters) throws CompileException
+ {
+ for(int i=0;i globalVars; // global variables
+
+ private boolean isCompilingProcedure;
+
+ private List procedureParams;
+ private String procedureName;
+
+ private List procedureVars;
+
+ private boolean isCompilingBinOp;
+
+ // used for calling nested procedures (need to offset stack because of pushed parameters)
+ // see parsertest13 for an example
+ private int pushedParams;
+
+ /**
+ * Creates an emitter for writing to a new file with given name
+ *
+ * @param outputFileName the output file name
+ * @param globalVars hashmap of data (name --> type)
+ */
+ public Emitter(String outputFileName, Map globalVars)
+ {
+ this.globalVars = globalVars;
+ currLabel = 0;
+ pushedParams = 0;
+ this.procedureVars = new ArrayList();
+ try
+ {
+ out = new PrintWriter(new FileWriter(outputFileName), true);
+ }
+ catch (IOException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the next valid label to use.
+ * Labels are numbers prefixed with _ so that they
+ * don't collide with subroutines. They start from 0.
+ * @return a string of the next valid label
+ */
+ public String nextLabel()
+ {
+ currLabel++;
+ return "_"+(currLabel-1);
+ }
+
+ /**
+ * Tells the emitter that it is currently compiling a procedure declaration.
+ * This is needed so that the emitter knows, when compiling variable references,
+ * whether to load/store words from the stack, or if they are in global scope
+ * and should load/store from .data Passes in the names of the parameters as
+ * well as the name of the procedure name (to be used as the name of the return
+ * value)
+ *
+ * @param params parameter names of the currently-being-compiled procedure
+ * declaration
+ * @param procName name of the procedure currently being compiled
+ */
+ public void startCompilingProcedureDeclaration(String[] params, String procName)
+ {
+ this.isCompilingProcedure = true;
+ this.procedureParams = Arrays.asList(params);
+ this.procedureName = procName;
+ }
+
+ /**
+ * Adds a "pushed param" (for compiling nested procedure calls)
+ */
+ public void addPushedParam()
+ {
+ pushedParams++;
+ }
+
+ /**
+ * Resets the "pushed param" count
+ */
+ public void resetPushedParams()
+ {
+ pushedParams = 0;
+ }
+
+ /**
+ * Lets the emitter know it's in the middle of compiling a binop
+ * This is needed because the binop pushes the first value to
+ * the stack, so all references to variables on the stack
+ * need to be offset by those 4 bytes
+ */
+ public void startCompilingBinOp()
+ {
+ this.isCompilingBinOp = true;
+ }
+
+ /**
+ * Stops compiling the bin op
+ */
+ public void stopCompilingBinOp()
+ {
+ this.isCompilingBinOp = false;
+ }
+
+ /**
+ * Tells the emitter that it has finished compiling a procedure declaration.
+ * Resets the relevant instance variables.
+ */
+ public void stopCompilingProcedureDeclaration()
+ {
+ this.isCompilingProcedure = false;
+ this.procedureParams = null;
+ this.procedureName = "";
+ this.procedureVars.clear();
+ }
+
+ /**
+ * Adds a local variable to the procedureVars list
+ * @param name name of the variable
+ */
+ public void addLocalVariable(String name)
+ {
+ procedureVars.add(name);
+ }
+
+ /**
+ * Returns if the variable with the current name is a global variable or not.
+ * First sees if the current statements being compiled are in global scope (if so,
+ * it must be a global variable). Then, if it is in limited scope (inside a
+ * procedure declaration compilation), tests if any of the parameters or if
+ * the return value have the same name, and if not, returns true as well.
+ *
+ * @param name name of the variable being tested
+ * @return if the variable is a global reference or not
+ */
+ public boolean isGlobalVariable(String name)
+ {
+ return !isCompilingProcedure ||
+ !procedureParams.contains(name) &&
+ !procedureName.equals(name) &&
+ !procedureVars.contains(name);
+ }
+
+ /**
+ * @return if the program is currently in the midst of compiling a procedure declaration
+ */
+ public boolean isCompilingProcedure()
+ {
+ return isCompilingProcedure;
+ }
+
+ /**
+ * Returns the index of the variable as a parameter inside the procedure. Allows
+ * for retrieval of values from the stack when calling the procedure.
+ * Parameters are pushed like this:
+ * left to right
+ * push a byte for the return value
+ * push the return address
+ *
+ * then, any local variable declarations are pushed whenever they come up
+ *
+ * Therefore the index of the return value is 1 + numLocalVars, and the index of the
+ * parameters goes backwards from there.
+ *
+ * @precondition the variable with this name is a local reference, not a global one
+ * @param name name of the variable in question
+ * @return the index of the variable in the parameter list
+ */
+ public int getLocalVarIndex(String name)
+ {
+ int offset = isCompilingBinOp ? 1 : 0;
+ offset += pushedParams;
+
+ int numLocalVars = procedureVars.size();
+
+ // if it's the return parameter
+ if (name.equals(procedureName)) return 1 + numLocalVars + offset;
+
+ // if it's one of the parameters
+ if (procedureParams.contains(name))
+ return procedureParams.size() - procedureParams.indexOf(name) +
+ 1 + numLocalVars + offset;
+
+ // if it's a locally declared variable
+ return procedureVars.size() - procedureVars.indexOf(name) - 1 + offset;
+ }
+
+ /**
+ * Loads a given string into v0. Allows for less repitition between
+ * places with string loading.
+ *
+ * Space is allocated for strings as such:
+ * 4 bytes that store the length of the string
+ * this will (hopefully) allow us to concatenate strings/get str lengths quickly
+ * (in the future, not implemented yet)
+ * n bytes that store the actual string
+ * 1 byte for a null terminator
+ *
+ * @param value the string in question
+ */
+ public void loadString(String value)
+ {
+ int len = value.length();
+
+ emit("li $v0 9"); // instruction to allocate space
+ // allocate enough space for strlength + str + null terminator
+ emit("li $a0 " + (len + 5));
+ emit("syscall"); // memory address of allocated space is in v0
+
+ // read in each character of the string byte by byte
+ char c;
+ char[] cArray = value.toCharArray();
+
+ // load string length into first 4 bytes
+ emit("li $t0 " + len);
+ emit("sw $t0 ($v0)");
+
+ // load in actual string
+ int i = 4;
+ for (; i < value.length() + 4; i++)
+ {
+ c = cArray[i - 4];
+ if (c!='\'') emit(String.format("li $t0 '%c'", c));
+ else emit("li $t0 '\\''");
+ emit(String.format("sb $t0 %d($v0)", i));
+ }
+
+ // load in null term
+ emit(String.format("li $t0 '%c'", '\0'));
+ emit(String.format("sb $t0 %d($v0)", i));
+ }
+
+ /**
+ * Helper method to push the return address to the stack
+ */
+ public void pushRA()
+ {
+ emit("subu $sp $sp 4 # Pushing return address");
+ emit("sw $ra ($sp)");
+ }
+
+ /**
+ * Helper method to pop the return address from the stack
+ */
+ public void popRA()
+ {
+ int numLocalVars = procedureVars.size();
+ emit("lw $ra "+numLocalVars*4+"($sp)");
+ }
+
+ /**
+ * Helper method to pop the return value into v0
+ *
+ * This doesn't need to consider BinOp offset - return
+ * value is only popped at the very end of a procedure,
+ * never inside of any other expression (I hope)
+ */
+ public void popReturnValue()
+ {
+
+ int index = getLocalVarIndex(procedureName);
+ emit("lw $v0 "+(index)*4 +"($sp)");
+ }
+
+ /**
+ * Clears all procedure variables out to save stack space
+ * (Just loads everything into a0)
+ */
+ public void clearProcedureVarsFromStack()
+ {
+ // Number of local variables + number of parameters + return value + return address
+ int thingsToClear = procedureVars.size() + procedureParams.size() + 2;
+ for(;thingsToClear>0;thingsToClear--)
+ emitPop("$a0");
+ }
+
+ /**
+ * Prints one line of code to file (with non-labels indented)
+ * Appends a newline
+ * @param code the line of code
+ */
+ public void emit(String code)
+ {
+ if (!code.endsWith(":"))
+ code = "\t" + code;
+ out.println(code);
+ }
+
+ /**
+ * Prints an array of lines of code
+ * Calls emit for a singular string
+ * @param codes the lines of code
+ */
+ public void emit(String[] codes)
+ {
+ for(String s : codes) emit(s);
+ }
+
+ /**
+ * Emits push commands
+ * @param reg the register to push from
+ */
+ public void emitPush(String reg)
+ {
+ emit("subu $sp $sp 4 # Pushing "+reg); // move stack up 4 bytes
+ emit("sw "+reg+" ($sp)"); // push value onto stack
+ }
+
+ /**
+ * Emits pop commands
+ * @param reg the register to pop to
+ */
+ public void emitPop(String reg)
+ {
+ emit("lw "+reg+" ($sp) # Popping "+reg); // pop $v0
+ emit("addu $sp $sp 4"); // move stack down 4 bytes
+ }
+
+ // These are used for compiling relational BinOps
+ // For example: 3<4, 5<6, etc.
+ // They're kind of like mini subroutines without parameters
+
+ // I did this so there wouldn't be a bunch of repetition when
+ // compiling these binops, since the same series of like 5 lines
+ // would be repeated many times
+
+ // The first argument is in t0, and the second argument is in v0 always
+
+ /**
+ * less than or equal to
+ */
+ public void emitLEQ()
+ {
+ emit("_LEQ:");
+ emit("ble $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * less than
+ */
+ public void emitLT()
+ {
+ emit("_LT:");
+ emit("blt $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * greater than or equal to
+ */
+ public void emitGEQ()
+ {
+ emit("_GEQ:");
+ emit("bge $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * greater than
+ */
+ public void emitGT()
+ {
+ emit("_GT:");
+ emit("bgt $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * equal to
+ */
+ public void emitE()
+ {
+ emit("_E:");
+ emit("beq $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * not equal to
+ */
+ public void emitNE()
+ {
+ emit("_NE:");
+ emit("bne $t0 $v0 _TRUE");
+ emit("j _FALSE");
+ }
+
+ /**
+ * Puts true boolean value into v0,
+ * then jumps to return address
+ */
+ public void emitTrue()
+ {
+ emit("_TRUE:");
+ emit("li $v0 1");
+ emit("jr $ra");
+ }
+
+ /**
+ * Puts false boolean value into v0,
+ * then jumps to return address
+ */
+ public void emitFalse()
+ {
+ emit("_FALSE:");
+ emit("li $v0 0");
+ emit("jr $ra");
+ }
+
+ /**
+ * TODO print "TRUE" or "FALSE" for booleans and not 1 and 0
+ */
+ public void emitPrintTRUE()
+ {
+ emit("TRUE:");
+ emitPush("$a0");
+ emitPush("$v0");
+ emit("li $a0 1");
+ emit("li $v0 1");
+ emit("syscall");
+ emitPop("$v0");
+ emitPop("$a0");
+ emit("jr $ra");
+ }
+
+ /**
+ * Private helper method for getting the MIPS representation of types
+ * @param in our type representation
+ * @return the MIPS representation
+ */
+ private String formatType(String in)
+ {
+ return "word"; // for now
+ }
+
+ /**
+ * @param type type of the variable
+ * @return the default value of the given variable type
+ */
+ private String getDefaultValue(String type)
+ {
+ return "0"; // for now
+ }
+
+ /**
+ * Emits all (pre-allocated) program data
+ * (for the .data section)
+ * Appends "_data_" to the beginning of their names
+ */
+ public void emitProgramData()
+ {
+ for (Map.Entry entry : globalVars.entrySet())
+ {
+ String varname = entry.getKey();
+ String vartype = entry.getValue();
+
+ String defaultVal = getDefaultValue(vartype);
+ vartype = formatType(vartype);
+
+ emit(String.format("%s: .%s %s", "_data_"+varname, vartype, defaultVal));
+ }
+ }
+
+ /**
+ * Closes the file, should be called after all calls to emit
+ */
+ public void close()
+ {
+ out.close();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/environment/Environment.java b/src/main/java/com/pilers/environment/Environment.java
new file mode 100644
index 0000000..c2e0f01
--- /dev/null
+++ b/src/main/java/com/pilers/environment/Environment.java
@@ -0,0 +1,125 @@
+package com.pilers.environment;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.pilers.ast.*;
+
+/**
+ * This is the parent class of both environment classes; it has been abstracted
+ * because there is a lot of shared functionality between SemanticAnalysisEnv
+ * and InterpreterEnv.
+ *
+ * It keeps track of variable names, types, and values; procedure names
+ * and values; and its parent environment.
+ *
+ * Note: the SemanticAnalysisEnvironment does not ever use the value map (varmapVal),
+ * only the InterpreterEnvironment does.
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public abstract class Environment
+{
+
+ /**
+ * This maps variable names to their types (represented as strings)
+ */
+ protected Map varmapType;
+ /**
+ * This maps variable names to their values
+ */
+ protected Map varmapVal;
+
+ /**
+ * This maps procedure names to their declarations
+ */
+ protected Map procmap;
+
+ /**
+ * This is the parent environment of this current env.
+ * Is null if this is the global env.
+ */
+ protected Environment parentEnv;
+
+ /**
+ * Creates a new environment with a parent env
+ *
+ * @param parentEnv the parent env
+ */
+ public Environment(Environment parentEnv)
+ {
+ this.parentEnv = parentEnv;
+ varmapType = new HashMap();
+ varmapVal = new HashMap();
+ procmap = new HashMap();
+ }
+
+ /**
+ * Sets a procedure declaration
+ *
+ * @param name name of the procedure
+ * @param procedure value of the procedure
+ */
+ public void setProcedure(String name, ProcedureDeclaration procedure)
+ {
+ procmap.put(name, procedure);
+ }
+
+ /**
+ * @return if this environment is the global env or not
+ */
+ public boolean isGlobal()
+ {
+ return this.parentEnv == null;
+ }
+
+ /**
+ * Gets a procedure's declaration value Searches up through the environments
+ * until it either finds the procedure or reaches the "null" environment (goes
+ * past the global environment)
+ *
+ * @param name name of the procedure
+ * @return the value of the procedure, or null if not found
+ */
+ public ProcedureDeclaration getProcedure(String name)
+ {
+ Environment current = this;
+ ProcedureDeclaration proc = null;
+ while (current != null && proc == null)
+ {
+ proc = current.procmap.get(name);
+ current = current.parentEnv;
+ }
+ return proc;
+ }
+
+ /**
+ * Declares a variable with a given type and name; if the isInterpreter flag
+ * is set to true, also gives the variable a default value in the value map.
+ * @param type the type of the variable, represented as a string
+ * @param name the name of the variable
+ * @param isInterpreter if the environment invoking this is an interpreter,
+ * give the variable a default value in the value map
+ * @return if the variable was successfully declared or not (if the variable
+ * already existed, this would fail)
+ */
+ public boolean declareVariable(String type, String name, boolean isInterpreter)
+ {
+ if (this.varmapType.get(name)!=null) return false;
+
+ varmapType.put(name, type);
+ if (isInterpreter)
+ {
+ if (type.equals("Integer"))
+ varmapVal.put(name, new Value(0));
+ else if (type.equals("String"))
+ varmapVal.put(name, new Value(""));
+ else if (type.equals("Boolean"))
+ varmapVal.put(name, new Value(false));
+ }
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/environment/InterpreterEnvironment.java b/src/main/java/com/pilers/environment/InterpreterEnvironment.java
new file mode 100644
index 0000000..0c51a8d
--- /dev/null
+++ b/src/main/java/com/pilers/environment/InterpreterEnvironment.java
@@ -0,0 +1,100 @@
+package com.pilers.environment;
+
+import java.util.HashMap;
+
+import com.pilers.ast.*;
+import com.pilers.errors.*;
+
+/**
+ * The InterpreterEnvironment serves as a runtime environment,
+ * to be used when interpreting a program. It is passed into
+ * every execution and evaluation, and essentially serves
+ * as a variable table.
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public class InterpreterEnvironment extends Environment
+{
+
+ /**
+ * Creates a new environment with a parent env
+ * @param parentEnv the parent env
+ */
+ public InterpreterEnvironment(InterpreterEnvironment parentEnv)
+ {
+ super(parentEnv);
+ varmapVal = new HashMap();
+ }
+
+ /**
+ * Factory method to return a new global environment (parent is null)
+ * @return the new env
+ */
+ public static InterpreterEnvironment newGlobalEnv()
+ {
+ return new InterpreterEnvironment(null);
+ }
+
+ /**
+ * Declares a variable in the variable map Calls the helper method and passes
+ * in true; the true flag tells the environemnt to give this variable a default
+ * value in the value hashmap.
+ *
+ * @param type the type of the variable
+ * @param name the name of the variable
+ * @throws InterpretException if the variable has already been declared
+ */
+ public void declareVariable(String type, String name) throws InterpretException
+ {
+ if (!declareVariable(type, name, true))
+ throw new InterpretException(ErrorString.duplicateIdentifier(name));
+ }
+
+ /**
+ * Sets a variable in the variable maps
+ *
+ * @param name the name of the variable
+ * @param value the value of the variable
+ * @throws CompileException if the variable has not been declared yet
+ * @throws CompileException if the assigned value does not have the correct type
+ */
+ public void setVariable(String name, Value value) throws InterpretException
+ {
+ Environment current = this;
+ String var = current.varmapType.get(name);
+
+ while (current.parentEnv != null && var == null)
+ {
+ current = current.parentEnv;
+ var = current.varmapType.get(name);
+ }
+
+ if (var == null) throw new InterpretException(ErrorString.unknownIdentifier(name));
+ else if (!var.equals(value.getType()))
+ throw new InterpretException(ErrorString.typeAssignment(var, value.getType()));
+
+ current.varmapVal.put(name, value);
+ }
+
+ /**
+ * Gets a variable's value
+ * Searches up through the environments until it either
+ * finds the variable's value or reaches the "null"
+ * environment (goes past the global environment)
+ *
+ * @param name the name of the variable
+ * @return the value of the variable, or null if not found
+ */
+ public Value getVariable(String name)
+ {
+ Environment current = this;
+ Value varValue = null;
+ while(current != null && varValue == null)
+ {
+ varValue = current.varmapVal.get(name);
+ current = current.parentEnv;
+ }
+ return varValue;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/environment/SemanticAnalysisEnvironment.java b/src/main/java/com/pilers/environment/SemanticAnalysisEnvironment.java
new file mode 100644
index 0000000..3e8908b
--- /dev/null
+++ b/src/main/java/com/pilers/environment/SemanticAnalysisEnvironment.java
@@ -0,0 +1,91 @@
+package com.pilers.environment;
+
+import java.util.Map;
+
+import com.pilers.errors.*;
+
+/**
+ * The SemanticAnalysisEnvironment is to be used in semantic
+ * analysis of the program. This includes things like type checking,
+ * seeing if a variable exists, etc.
+ *
+ * Potential future additions (to act as a linter):
+ * Checking for empty control statements and sending warnings
+ * Checking for never-reached code and sending warnings
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public class SemanticAnalysisEnvironment extends Environment
+{
+ /**
+ * Creates a new environment with a parent env
+ *
+ * @param parentEnv the parent env
+ */
+ public SemanticAnalysisEnvironment(SemanticAnalysisEnvironment parentEnv)
+ {
+ super(parentEnv);
+ }
+
+ /**
+ * Factory method to return a new global environment (parent is null)
+ *
+ * @return the new env
+ */
+ public static SemanticAnalysisEnvironment newGlobalEnv()
+ {
+ return new SemanticAnalysisEnvironment(null);
+ }
+
+ /**
+ * Declares a space by allocating space for it in the variable map
+ *
+ * @param type the type of the variable
+ * @param name the name of the variable
+ * @throws CompileException if the variable is already defined
+ */
+ public void declareVariable(String type, String name) throws CompileException
+ {
+ if (!declareVariable(type, name, false)) throw new CompileException("TODO: WRITE ME");
+ }
+
+ /**
+ * @param varname the name of the variable
+ * @return if the variable with the given name exists at the moment
+ */
+ public boolean variableExists(String varname)
+ {
+ return getVariableType(varname) != null;
+ }
+
+ /**
+ * Gets the type of a variable as a string
+ *
+ * @param name the name of the variable
+ * @return null if the variable does not exist
+ */
+ public String getVariableType(String name)
+ {
+ // Environment current = this;
+ // String var = current.varmapType.get(name);
+
+ // while (current.parentEnv != null && var == null)
+ // {
+ // current = current.parentEnv;
+ // var = current.varmapType.get(name);
+ // }
+
+ // return var;
+
+ return this.varmapType.get(name);
+ }
+
+ /**
+ * @return the map of variable names to types
+ */
+ public Map getVariableInfo()
+ {
+ return varmapType;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/errors/CompileException.java b/src/main/java/com/pilers/errors/CompileException.java
new file mode 100644
index 0000000..5ae910a
--- /dev/null
+++ b/src/main/java/com/pilers/errors/CompileException.java
@@ -0,0 +1,30 @@
+package com.pilers.errors;
+
+/**
+ * A CompileException is thrown during the semantic analysis of compilation
+ * It can occur for a variety of reasons, including type and scope errors
+ *
+ * When throwing a CompileException error, a string generated by the ErrorString
+ * class should be passed in.
+ *
+ * @author Gloria Zhu
+ */
+public class CompileException extends Exception
+{
+ // satisfy inheritance
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor for CompileException that includes a reason for the error
+ *
+ * This should always be called using one of the static reason generators in the
+ * ErrorString class
+ *
+ * @param reason the reason for the CompileException
+ */
+ public CompileException(String reason)
+ {
+ super(reason);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/errors/ErrorString.java b/src/main/java/com/pilers/errors/ErrorString.java
new file mode 100644
index 0000000..a992fa5
--- /dev/null
+++ b/src/main/java/com/pilers/errors/ErrorString.java
@@ -0,0 +1,182 @@
+package com.pilers.errors;
+
+/**
+ * The purpose of this class is to format error strings for the
+ * CompileException and InterpretException classes. It has been separated
+ * into its own class in order to standardize error formatting.
+ *
+ * @author Gloria Zhu
+ */
+public abstract class ErrorString
+{
+ /**
+ * @param varname identifier
+ * @return error message for an unknown identifier
+ */
+ public static String unknownIdentifier(String varname)
+ {
+ return "Unknown identifier " + varname;
+ }
+
+ /**
+ * @param procname procedure name
+ * @return error message for an unknown procedure
+ */
+ public static String unknownProcedure(String procname)
+ {
+ return "Unknown procedure " + procname;
+ }
+
+ /**
+ * @param directiveName directive name
+ * @return error message for an unknown directive (i.e. #hello)
+ */
+ public static String unknownDirective(String directiveName)
+ {
+ return "Unknown directive " + directiveName;
+ }
+
+ /**
+ * @param structname struct name
+ * @return error message for a struct that isn't declared globally
+ */
+ public static String structsMustBeGlobal(String structname)
+ {
+ return "struct " + structname + " must be declared globally";
+ }
+
+ /**
+ * @param structname struct name
+ * @return error message for a duplicate struct declaration
+ */
+ public static String duplicateStructName(String structname)
+ {
+ return "Duplicate struct found for name " + structname;
+ }
+
+ /**
+ * @param definitionVar variable being defined
+ * @return error message for a duplicate defind directive
+ */
+ public static String duplicateDefine(String definitionVar)
+ {
+ return "Duplicate define directive for " + definitionVar;
+ }
+
+ /**
+ * @param expectedType the expected type
+ * @param actualType the actual type
+ * @return error message for an invalid type assignemnt
+ */
+ public static String typeAssignment(String expectedType, String actualType)
+ {
+ return String.format("Type error: expected %s, got %s", expectedType, actualType);
+ }
+
+ /**
+ * @param type1 the first type
+ * @param type2 the second type
+ * @param operand operand represented as a string
+ * @return error message for an invalid binary operation (i.e. BOOLEAN *
+ * INTEGER)
+ */
+ public static String invalidBinOperation(String type1, String type2, String operand)
+ {
+ return String.format("Cannot perform %s operation between %s and %s",
+ operand, type1, type2);
+ }
+
+ /**
+ * @param type the type of the operand
+ * @param operand operand represented as a string
+ * @return error message for an invalid unary operation (i.e. BOOLEAN++)
+ */
+ public static String invalidUnaryOperation(String type, String operand)
+ {
+ return String.format("Cannot perform %s operation on %s", operand, type);
+ }
+
+ /**
+ * @param varname the name of the identifier
+ * @return error message for a duplicate identifier (i.e. if a variable has
+ * already been declared)
+ */
+ public static String duplicateIdentifier(String varname)
+ {
+ return "Duplicate identifier found for name " + varname;
+ }
+
+ /**
+ * @return error message for if a FOR loop's condition is not a boolean
+ */
+ public static String forLoopConditionInvalid()
+ {
+ return "Error: FOR loop condition must evaluate to a boolean";
+ }
+
+ /**
+ * @return error message for if a WHILE loop's condition is not a boolean
+ */
+ public static String whileLoopConditionInvalid()
+ {
+ return "Error: WHILE loop condition must evaluate to a boolean";
+ }
+
+ /**
+ * @return error message for if a IF loop's condition is not a boolean
+ */
+ public static String ifLoopConditionInvalid()
+ {
+ return "Error: IF loop condition must evaluate to a boolean";
+ }
+
+ /**
+ * @param procedureName name of the procedure
+ * @param expectedNumber the expected number of parameters
+ * @param actualNumber the actual number of parameters given
+ * @return error message for having the wrong number of paramters in a
+ * procedure call
+ */
+ public static String wrongNumberOfParameters(
+ String procedureName, int expectedNumber, int actualNumber)
+ {
+ return String.format("Wrong number of parameters for procedure %s() call: "+
+ "expected %d, got %d", procedureName, expectedNumber, actualNumber);
+ }
+
+ /**
+ * @param procedureName name of the procedure
+ * @param expectedType expected type of the procedure
+ * @param actualType the actual type given
+ * @param index the index of the parameter (if it's the 1st, 2nd one etc.)
+ * @return error message for passing in the wrong type of parameter during
+ * a procedure call
+ */
+ public static String invalidParameterType(
+ String procedureName, String expectedType, String actualType, int index)
+ {
+ return String.format("Invalid type for parameter %d for procedure %s() call: "+
+ "expected %s, got %s", index, procedureName, expectedType, actualType);
+ }
+
+ /**
+ * @param procedure the procedure name
+ * @param param the param name
+ * @return error message for having duplicate parameter names in a procedure declaration
+ */
+ public static String duplicateParamNames(String procedure, String param)
+ {
+ return String.format("Duplicate parameter name '%s' in procedure declaration %s()",
+ param, procedure);
+ }
+
+ /**
+ * @param name the procedure/parameter name
+ * @return error message for having a parameter with the same name as the procedure
+ */
+ public static String paramNameEqualsProcedureName(String name)
+ {
+ return String.format("Parameter name cannot equal name of procedure: "+
+ "error %s() declaration", name);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/errors/InterpretException.java b/src/main/java/com/pilers/errors/InterpretException.java
new file mode 100644
index 0000000..1d5e973
--- /dev/null
+++ b/src/main/java/com/pilers/errors/InterpretException.java
@@ -0,0 +1,30 @@
+package com.pilers.errors;
+
+/**
+ * An InterpretException is thrown during the runtime of interpretation It can
+ * occur for a variety of reasons, including type and scope errors
+ *
+ * When throwing an InterpretException error, a string generated by the
+ * ErrorString class should be passed in.
+ *
+ * @author Gloria Zhu
+ */
+public class InterpretException extends Exception
+{
+
+ // satisfy inheritance
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor for InterpretException that includes a reason for the error
+ *
+ * This should always be called using one of the static reason generators in the
+ * ErrorString class
+ *
+ * @param reason the reason for the InterpretException
+ */
+ public InterpretException(String reason)
+ {
+ super(reason);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/errors/PreprocessorException.java b/src/main/java/com/pilers/errors/PreprocessorException.java
new file mode 100644
index 0000000..9304d32
--- /dev/null
+++ b/src/main/java/com/pilers/errors/PreprocessorException.java
@@ -0,0 +1,31 @@
+package com.pilers.errors;
+
+/**
+ * A PreprocessorException is thrown during the preprocessing phase of
+ * the compiler
+ * It can be thrown for a variety of reasons, such as unknown directives
+ *
+ * When throwing a PreprocessorException error, a string generated by the
+ * ErrorString class should be passed in.
+ *
+ * @author Gloria Zhu
+ */
+public class PreprocessorException extends Exception
+{
+ // satisfy inheritance
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Constructor for PreprocessorException that includes a reason for the error
+ *
+ * This should always be called using one of the static reason generators in the
+ * ErrorString class
+ *
+ * @param reason the reason for the PreprocessorException
+ */
+ public PreprocessorException(String reason)
+ {
+ super(reason);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/errors/ScanErrorException.java b/src/main/java/com/pilers/errors/ScanErrorException.java
new file mode 100644
index 0000000..704d0fb
--- /dev/null
+++ b/src/main/java/com/pilers/errors/ScanErrorException.java
@@ -0,0 +1,77 @@
+package com.pilers.errors;
+
+/**
+ * ScanErrorException is a sub class of Exception and is thrown during the
+ * scanning phase in order to indicate a scan error.
+ *
+ * Usually, the scanning error is the result of an illegal character in the
+ * input stream. The error is also thrown when the expected value of the
+ * character stream does not match the actual value, or if a
+ * block comment is never closed in the code.
+ *
+ * @author Mr. Page
+ * @author Gloria Zhu
+ */
+public class ScanErrorException extends Exception
+{
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Default constructor for ScanErrorObjects, without a reason
+ */
+ public ScanErrorException()
+ {
+ super();
+ }
+
+ /**
+ * Constructor for ScanErrorObjects that includes a reason for the error
+ *
+ * @param reason the reason for the ScanErrorException
+ */
+ public ScanErrorException(String reason)
+ {
+ super(reason);
+ }
+
+ // Static factory methods for some common errors
+
+ /**
+ * Returns an illegalCharacter ScanErrorException
+
+ * @param expected the expected character
+ * @param actual the actual character
+ * @return the ScanErrorException
+ */
+ public static ScanErrorException illegalCharacter(char expected, char actual)
+ {
+ return new ScanErrorException("Illegal character: expected " + expected +
+ ", got " + actual + " instead.");
+ }
+
+ /**
+ * Returns an illegalLexeme ScanErrorException
+ *
+ * @param expectedType the type that was expected
+ * @param illegalLexeme the illegal lexeme that did not match the expected type
+ * @return the ScanErrorException
+ */
+ public static ScanErrorException illegalLexeme(String expectedType, String illegalLexeme)
+ {
+ return new ScanErrorException("Illegal lexeme: expected lexeme of type " +
+ expectedType + ", got " + illegalLexeme + " instead.");
+ }
+
+ /**
+ * Returns a blockCommentNotClosed ScanErrorException (thrown when
+ * a block comment is not closed before the EOF)
+ *
+ * @return the ScanErrorException
+ */
+ public static ScanErrorException blockCommentNotClosed()
+ {
+ return new ScanErrorException("Block comment not closed");
+ }
+
+}
diff --git a/src/main/java/com/pilers/parser/Parser.java b/src/main/java/com/pilers/parser/Parser.java
new file mode 100644
index 0000000..909ca6b
--- /dev/null
+++ b/src/main/java/com/pilers/parser/Parser.java
@@ -0,0 +1,487 @@
+package com.pilers.parser;
+
+import com.pilers.scanner.*;
+import com.pilers.ast.*;
+import com.pilers.ast.Value;
+import com.pilers.errors.*;
+
+import java.io.*;
+import java.util.ArrayList;
+
+/**
+ * A Parser is responsible for reading the token stream from the scanner, one
+ * token at a time, and processing it into a parse tree according to a grammar.
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public class Parser
+{
+ private Scanner sc;
+ private Token currTok;
+
+ /**
+ * Default constructor for a parser. Reads the first token in from the stream
+ * and stores it in currTok.
+ *
+ * @param scan the input scanner
+ */
+ public Parser(Scanner scan)
+ {
+ sc = scan;
+
+ try
+ {
+ currTok = sc.nextToken();
+ }
+ catch (IOException e)
+ { // if IOException, print the error and terminate
+ System.out.println(e);
+ System.exit(-1);
+ }
+ catch (ScanErrorException e)
+ { // if ScanErrorException, print the error but don't terminate
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * @return true if the Parser still has more tokens to parse
+ */
+ public boolean hasNext()
+ {
+ return currTok.getType() != Token.END;
+ }
+
+ /**
+ * Same as eat, just allows a Token parameter instead of string
+ *
+ * @param expected the expected string (wrapped in a token's value)
+ */
+ private void eat(Token expected)
+ {
+ this.eat(expected.getValue());
+ }
+
+ /**
+ * Same as eat, just allows a character parameter as well
+ *
+ * @param expected the expected string (in char form)
+ */
+ private void eat(char expected)
+ {
+ eat("" + expected);
+ }
+
+ /**
+ * If the current token matches the passed in expected string, eats it and moves
+ * to the next token.
+ *
+ * @param expected the expected string
+ * @throws IllegalArgumentException if the string & current token don't match
+ */
+ private void eat(String expected)
+ {
+ if (currTok.getValue().equals(expected))
+ { // got the expected value, read the next token
+
+ try
+ {
+ currTok = sc.nextToken();
+ }
+ catch (IOException e) // print the error and terminate
+ {
+ System.out.println(e);
+ System.exit(-1);
+ }
+ catch (ScanErrorException e) // print the error but don't terminate
+ {
+ System.out.println(e);
+ }
+
+ }
+ else // did not get the expected value, throw an error
+ {
+ throw new IllegalArgumentException(
+ String.format("Expected %s, but got %s %s",
+ expected, currTok.getTypeAsString(), currTok.getValue()));
+ }
+ }
+
+ /**
+ * Parses a value
+ *
+ * @precondition current token is a number, string, or boolean
+ * @postcondition Value token has been eaten
+ * @return a Value object
+ */
+ private Value parseValue()
+ {
+ String val = currTok.getValue();
+ String type = currTok.getTypeAsNiceString();
+ eat(currTok);
+ return new Value(val, type);
+
+ }
+
+ /**
+ * Parses an expression. An expression consists of a series of factors connected
+ * by operands.
+ *
+ * In order to account for operator precedence, the operators are stored in a 2D
+ * array (in the Token class), with each subsequent array having higher
+ * precedence in the previous one.
+ *
+ * Then, the parseExpression method takes in a parameter, which is the level of
+ * precedence. If parseExpression is called from an external method, it should
+ * always be given 0 as the starting level of precedence.
+ *
+ * It searches the operators at the given level of precedence for matches and
+ * recursively calls itself with levelOfPrecedence+1. The base case is when the
+ * level of precedence is equal to the length of the 2D array of operators, at
+ * which point it just returns parseFactor().
+ *
+ * @precondition current token is the beginning of a factor
+ * @postcondition expression has been parsed and eaten
+ * @param levelOfPrecedence the level of operator precedence to parse at
+ * @return an Expression object
+ */
+ private Expression parseExpression(int levelOfPrecedence)
+ {
+ // base case
+ if (levelOfPrecedence == Token.OPERATORS_ORDERED_BY_PRECEDENCE.length)
+ return parseFactor();
+
+ Expression val = parseExpression(levelOfPrecedence + 1);
+
+ String oper = currTok.getValue();
+
+ String[] validOpers = Token.OPERATORS_ORDERED_BY_PRECEDENCE[levelOfPrecedence];
+ while (strArrayContains(validOpers, oper))
+ {
+ eat(oper);
+ val = new BinOp(oper, val, parseExpression(levelOfPrecedence + 1));
+ oper = currTok.getValue();
+ }
+ return val;
+ }
+
+ /**
+ * Helper method for testing if a string array contains a string
+ *
+ * @param arr string array
+ * @param str string
+ * @return if the string array contains the string
+ */
+ private boolean strArrayContains(String[] arr, String str)
+ {
+ for (int i = 0; i < arr.length; i++)
+ if (arr[i].equals(str))
+ return true;
+ return false;
+ }
+
+ /**
+ * Parses a factor.
+ *
+ * A factor consists of either: another factor and a unary operator a variable
+ * name an expression in parentheses a raw Value (integer, string, boolean)
+ *
+ * @precondition current token is a unary operator, identifier, open
+ * parentheses, or beginning of a Value
+ * @postcondition factor has been parsed and eaten
+ * @return an Expression object
+ */
+ private Expression parseFactor()
+ {
+ if (strArrayContains(Token.UNARY_OPERATORS, currTok.getValue()))
+ {
+ String op = currTok.getValue();
+ eat(op);
+ return new UnaryOp(op, parseFactor());
+ }
+ else if (currTok.getType() == Token.OPEN_PAREN)
+ {
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression val = parseExpression(0);
+ eat(Token.CLOSE_PAREN_CHAR);
+ return val;
+ }
+ else if (currTok.getType() == Token.IDENTIFIER)
+ {
+ String id = currTok.getValue();
+ eat(id);
+
+ if (currTok.equals(Token.OPEN_PAREN_CHAR))
+ {
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression[] params = parseParamsList();
+ eat(Token.CLOSE_PAREN_CHAR);
+ return new ProcedureCallExpression(id, params);
+ }
+
+ return new VariableReference(id);
+ }
+ return parseValue();
+ }
+
+ /**
+ * Parses a list of parameter values separated by commas
+ *
+ * @return the array of parameters (Expressions)
+ */
+ private Expression[] parseParamsList()
+ {
+ ArrayList params = new ArrayList();
+ while (!(currTok.equals(Token.CLOSE_PAREN_CHAR)))
+ {
+ params.add(parseExpression(0));
+ if (currTok.equals(Token.COMMA_CHAR))
+ eat(Token.COMMA_CHAR);
+
+ }
+ Expression[] temp = new Expression[0];
+
+ return params.toArray(temp); // cast from Object[] --> Expression[]
+ }
+
+ /**
+ * Parses a list of parameter types and names, separated by commas
+ *
+ * @return a 2D array that contains 2 String arrays, types and names
+ */
+ private String[][] parseParamsDeclarationList()
+ {
+ ArrayList paramTypes = new ArrayList();
+ ArrayList paramNames = new ArrayList();
+ while (!(currTok.equals(Token.CLOSE_PAREN_CHAR)))
+ {
+ paramTypes.add(currTok.getValue());
+ eat(currTok);
+ paramNames.add(currTok.getValue());
+ eat(currTok);
+
+ if (currTok.equals(Token.COMMA_CHAR))
+ eat(Token.COMMA_CHAR);
+
+ }
+ String[] temp1 = new String[0];
+ String[] temp2 = new String[0];
+
+ return new String[][] { paramTypes.toArray(temp1), paramNames.toArray(temp2) };
+ }
+
+ /**
+ * Parses a program
+ * A program consists of 0 or more statements Order of
+ * procedure declarations / executed statements is arbitrary - this part is
+ * different from the documentation.
+ *
+ * @param args array of command line arguments
+ * @return the parsed program
+ */
+ public Program parseProgram(String[] args)
+ {
+ ArrayList stmts = new ArrayList();
+ while (hasNext())
+ stmts.add(parseStatement());
+
+ Statement[] temp = new Statement[0];
+
+ return new Program(stmts.toArray(temp), args); // cast from Object[] --> Statement[]
+ }
+
+ /**
+ * Parses a statement, printing and reading i/o as needed.
+ *
+ * @precondition current token is one of the following: "WRITELN" "READLN"
+ * "BEGIN" of type identifier
+ * @postcondition statement has been parsed and eaten, including the line
+ * terminator
+ *
+ * @return parsed statement
+ * @throws CompileException
+ */
+ public Statement parseStatement()
+ {
+ switch (currTok.getType())
+ {
+ case (Token.KEYWORD):
+
+ switch (currTok.getValue())
+ {
+ case "writeln": // write line
+
+ eat("writeln");
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression exp = parseExpression(0);
+ eat(Token.CLOSE_PAREN_CHAR);
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new Writeln(exp);
+
+ case "readInteger":
+ // read integer from user input
+
+ eat("readInteger");
+ eat(Token.OPEN_PAREN_CHAR);
+ String userId = currTok.getValue();
+ eat(userId);
+ eat(Token.CLOSE_PAREN_CHAR);
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new ReadInteger(userId);
+
+ case "if": // begin an if block
+ eat("if");
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression ifExpr = parseExpression(0);
+ eat(Token.CLOSE_PAREN_CHAR);
+ Statement ifStmt = parseStatement();
+
+ return new If(ifExpr, ifStmt);
+
+ case "while": // begin a while block
+ eat("while");
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression whileExpr = parseExpression(0);
+ eat(Token.CLOSE_PAREN_CHAR);
+ Statement whileStmt = parseStatement();
+
+ return new While(whileExpr, whileStmt);
+
+ case "for": // begin a for block
+ eat("for");
+ eat(Token.OPEN_PAREN_CHAR);
+ Statement forDecl = parseStatement(); // should be an assignment
+ Expression forCond = parseExpression(0); // should be a boolean
+ eat(";");
+ Statement forIncr = parseStatement();
+ eat(Token.CLOSE_PAREN_CHAR);
+
+ Statement forStmt = parseStatement();
+
+ return new For(forDecl, forCond, forIncr, forStmt);
+
+ case "break": // break statement
+ eat("break");
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new Break();
+
+ case "continue": // break statement
+ eat("continue");
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new Continue();
+
+ }
+ break;
+
+ case(Token.OPERATOR):
+ if (currTok.getValue().equals("{"))
+ {
+ eat("{");
+ ArrayList stmts = new ArrayList();
+ while (!currTok.equals("}"))
+ {
+ stmts.add(parseStatement());
+ }
+ eat("}");
+
+ return new Block(stmts);
+ }
+ break;
+ // todo: throw an error
+
+ default: // not of keyword type
+
+ // if the token does not match any keywords,
+ // check if it is an identifier
+ // (this indicates a procedure call or variable assignment)
+ if (currTok.getType() == Token.IDENTIFIER)
+ {
+ String id = currTok.getValue();
+ eat(id);
+
+ switch(currTok.getValue())
+ {
+ // variable assignment (no declaration)
+ case(""+Token.ASSIGNMENT_OPERATOR):
+ eat(Token.ASSIGNMENT_OPERATOR);
+ Expression value = parseExpression(0);
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new Assignment(id, value);
+
+ // procedure call
+ case(""+Token.OPEN_PAREN_CHAR):
+
+ // for later
+ eat(Token.OPEN_PAREN_CHAR);
+ Expression[] params = parseParamsList();
+ eat(Token.CLOSE_PAREN_CHAR);
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new ProcedureCallStatement(id, params);
+ default:
+ // todo: throw an error
+ }
+
+ }
+ // either a variable declaration, variable declaration+assignment,
+ // or procedure declaration
+ else if (currTok.getType() == Token.TYPE)
+ {
+ String type = currTok.getValue();
+ eat(type);
+
+ String id = currTok.getValue();
+ eat(id);
+
+ switch(currTok.getValue())
+ {
+ // has assignment
+ case(Token.ASSIGNMENT_OPERATOR):
+ eat(Token.ASSIGNMENT_OPERATOR);
+
+ Expression expr = parseExpression(0);
+
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new DeclareAssignment(type, id, expr);
+
+ // no assignment, just a declaration
+ case(""+Token.LINE_TERMINATOR_CHAR):
+ eat(Token.LINE_TERMINATOR_CHAR);
+
+ return new Declaration(type, id);
+
+ // procedure declaration
+ case(""+Token.OPEN_PAREN_CHAR):
+ eat(Token.OPEN_PAREN_CHAR);
+
+ String[][] params = parseParamsDeclarationList();
+ String[] paramTypes = params[0];
+ String[] paramNames = params[1];
+
+ eat(Token.CLOSE_PAREN_CHAR);
+
+ Statement stmt = parseStatement();
+
+ return new ProcedureDeclaration(id, stmt,
+ type, paramTypes, paramNames);
+
+ default:
+ // todo: throw an error
+
+ }
+
+ }
+ break;
+ }
+
+ return null;
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/ParserTester.java b/src/main/java/com/pilers/parser/ParserTester.java
new file mode 100644
index 0000000..956ede9
--- /dev/null
+++ b/src/main/java/com/pilers/parser/ParserTester.java
@@ -0,0 +1,103 @@
+package com.pilers.parser;
+
+import com.pilers.scanner.*;
+import com.pilers.ast.Program;
+import com.pilers.environment.*;
+import com.pilers.emitter.Emitter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * A class for testing Parser
+ *
+ * @author Gloria Zhu
+ */
+public class ParserTester
+{
+
+ /**
+ * Main tester method
+ * @param args arguments from the command line
+ */
+ public static void main(String[] args)
+ {
+
+ String[] testFiles = new String[] {
+ // "tests/parserTest10.txt",
+ // "tests/parserTest11.txt",
+ // "tests/parserTest12.txt",
+ // "tests/parserTest13.txt"
+ "tests/staticVariables.txt"
+ };
+
+ String[] compileDests = new String[] {
+ // "parserTest10.asm",
+ // "parserTest11.asm",
+ // "parserTest12.asm",
+ // "parserTest13.asm"
+ "printSquares.asm"
+ };
+
+ // controls
+ boolean onMyComputer = true;
+
+ boolean interpretCode = true;
+ boolean compileCode = false;
+
+ try
+ {
+ for (int i = 0; i < testFiles.length; i++)
+ {
+ System.out.println("\nNow testing " + testFiles[i]);
+
+ Scanner scanner;
+ if (onMyComputer)
+ scanner = new Scanner(Parser.class.getResourceAsStream(testFiles[i]));
+ else
+ {
+ Path currentDir = Paths.get(testFiles[i]);
+ scanner = new Scanner(new FileInputStream(new File(
+ currentDir.toAbsolutePath().toString())));
+ }
+
+ Parser pa = new Parser(scanner);
+
+ try
+ {
+ Program prog = pa.parseProgram(args);
+
+ if (interpretCode)
+ {
+ InterpreterEnvironment env = InterpreterEnvironment.newGlobalEnv();
+ prog.exec(env);
+ }
+
+ if (compileCode)
+ {
+ SemanticAnalysisEnvironment env =
+ SemanticAnalysisEnvironment.newGlobalEnv();
+ prog.analyze(env);
+
+ Emitter e = new Emitter(compileDests[i], env.getVariableInfo());
+ prog.compile(e);
+ }
+
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+}
diff --git a/src/main/java/com/pilers/parser/tests/official/parserTest10.txt b/src/main/java/com/pilers/parser/tests/official/parserTest10.txt
new file mode 100644
index 0000000..bbf2486
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/official/parserTest10.txt
@@ -0,0 +1,12 @@
+Integer f = 2;
+
+Integer bar(Integer f) {
+ writeln(f);
+}
+Integer foo(Integer d) {
+ Integer ignore = bar(d + f);
+}
+
+Integer ignore = foo(3);
+writeln(f);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/official/parserTest11.txt b/src/main/java/com/pilers/parser/tests/official/parserTest11.txt
new file mode 100644
index 0000000..f728782
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/official/parserTest11.txt
@@ -0,0 +1,8 @@
+Integer print(Integer n) {
+ writeln(n);
+}
+
+Integer n = 3;
+Integer ignore = print(5);
+writeln(n);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/official/parserTest12.txt b/src/main/java/com/pilers/parser/tests/official/parserTest12.txt
new file mode 100644
index 0000000..1a93fb2
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/official/parserTest12.txt
@@ -0,0 +1,7 @@
+Integer printSquare(Integer n) {
+ writeln(n * n);
+}
+
+Integer x = 1;
+Integer ignore = printSquare(x + 2);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/official/parserTest13.txt b/src/main/java/com/pilers/parser/tests/official/parserTest13.txt
new file mode 100644
index 0000000..28c2f67
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/official/parserTest13.txt
@@ -0,0 +1,9 @@
+Integer countUp(Integer count, Integer max) {
+ if (count<=max) {
+ writeln(count);
+ countUp(count + 1, max);
+ }
+}
+
+Integer x = countUp(2,4);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/official/printSquares.txt b/src/main/java/com/pilers/parser/tests/official/printSquares.txt
new file mode 100644
index 0000000..8b53d89
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/official/printSquares.txt
@@ -0,0 +1,19 @@
+Integer count;
+Integer ignore;
+Integer times;
+Integer printSquares(Integer low, Integer high) {
+ Integer square;
+ Integer count = low;
+ Integer times = 0;
+ while(count<=high) {
+ square = count * count;
+ writeln(square);
+ count = count + 1;
+ times = times + 1;
+ }
+}
+count = 196;
+times = 0;
+ignore = printSquares(10, 13);
+writeln(count);
+writeln(times);
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/Combination.txt b/src/main/java/com/pilers/parser/tests/old_tests/Combination.txt
new file mode 100644
index 0000000..59742ef
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/Combination.txt
@@ -0,0 +1,22 @@
+BEGIN
+ WRITELN('Combination Calculator!');
+ WRITELN('Calculate A choose B');
+
+ WRITELN('Enter A');
+ READLN(A);
+ WRITELN('Enter B');
+ READLN(B);
+ WRITELN('Calculating ' + A + ' choose ' + B);
+
+ aFactorial := 1;
+ FOR( i:= 2; i <= A; i := i+1;) aFactorial := aFactorial * i;
+
+ aMinusBFactorial := 1;
+ FOR( j:= 2; j <= A-B; j := j+1;) aMinusBFactorial := aMinusBFactorial * j;
+
+ bFactorial := 1;
+ FOR( k:=2; k <= B; k := k+1;) bFactorial := bFactorial * k;
+
+ WRITELN('Result: ' + aFactorial / aMinusBFactorial / bFactorial);
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/MathProgram.txt b/src/main/java/com/pilers/parser/tests/old_tests/MathProgram.txt
new file mode 100644
index 0000000..bc97157
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/MathProgram.txt
@@ -0,0 +1,27 @@
+// Calculates -(a * b mod c)
+
+WRITELN('');
+WRITELN('Math program');
+WRITELN('Enter a');
+READLN(a);
+WRITELN('Enter b');
+READLN(b);
+WRITELN('Enter c');
+READLN(c);
+WRITELN(-(a * b mod c));
+WRITELN('');
+
+(*
+Test cases:
+
+a = 3
+b = 3
+c = 2
+yields -1
+
+a = 4
+b = 23
+c = 3
+yields -2
+
+*)
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/anotherTest.txt b/src/main/java/com/pilers/parser/tests/old_tests/anotherTest.txt
new file mode 100644
index 0000000..d54abc1
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/anotherTest.txt
@@ -0,0 +1,7 @@
+x := 2;
+y := 'Hello';
+WRITELN(x * y);
+WRITELN(x << 1);
+WRITELN(x+1=5);
+WRITELN(!(x!=3) && x*3<5 || y='Hello');
+WRITELN('HELLO' = 'HELLO' && -3 * 2 + 5 = 17 || 3 = 2);
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/breakTest.txt b/src/main/java/com/pilers/parser/tests/old_tests/breakTest.txt
new file mode 100644
index 0000000..c0dd935
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/breakTest.txt
@@ -0,0 +1,12 @@
+i := 0;
+
+WHILE 1 + 1 = 2 DO
+BEGIN
+i := i + 1;
+IF i = 3 THEN BREAK;
+
+WRITELN(i);
+IF i = 2 THEN CONTINUE;
+WRITELN(i);
+
+END;
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/codegenTest1.txt b/src/main/java/com/pilers/parser/tests/old_tests/codegenTest1.txt
new file mode 100644
index 0000000..0198d94
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/codegenTest1.txt
@@ -0,0 +1,10 @@
+Integer count;
+BEGIN
+count := 1;
+WHILE count <= 15 DO
+BEGIN
+WRITELN(count);
+count := count + 1;
+END;
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/max.txt b/src/main/java/com/pilers/parser/tests/old_tests/max.txt
new file mode 100644
index 0000000..60373ce
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/max.txt
@@ -0,0 +1,8 @@
+PROCEDURE max(x, y);
+BEGIN
+max := x;
+IF y > x THEN max := y;
+END;
+WRITELN(max(3, 5));
+WRITELN(max(6,4));
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest0.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest0.txt
new file mode 100644
index 0000000..e4e3f4c
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest0.txt
@@ -0,0 +1,2 @@
+WRITELN(3);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest1.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest1.txt
new file mode 100644
index 0000000..9cd3661
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest1.txt
@@ -0,0 +1,4 @@
+WRITELN(6 * 2 / 3);
+WRITELN(6 / 2 * 3);
+WRITELN(6 / (2 * 3));
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest2.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest2.txt
new file mode 100644
index 0000000..9e60286
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest2.txt
@@ -0,0 +1,4 @@
+WRITELN(2 + 3 * 4);
+WRITELN(2 * 3 + 4);
+WRITELN((2 + 3) * 4);
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest3.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest3.txt
new file mode 100644
index 0000000..c0f2d90
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest3.txt
@@ -0,0 +1,8 @@
+BEGIN
+WRITELN(1);
+WRITELN(2);
+BEGIN
+WRITELN(3);
+END;
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest4.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest4.txt
new file mode 100644
index 0000000..3c726be
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest4.txt
@@ -0,0 +1,7 @@
+BEGIN
+x := 2;
+y := x + 1;
+x := x + y;
+WRITELN(x * y);
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest6.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest6.txt
new file mode 100644
index 0000000..fc3be3b
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest6.txt
@@ -0,0 +1,18 @@
+BEGIN
+x := 2;
+y := x + 1;
+x := x + y;
+WRITELN(x * y);
+IF x > y THEN
+BEGIN
+WRITELN(x);
+WRITELN(y);
+END;
+x := 0;
+WHILE x < 10 DO
+BEGIN
+WRITELN(x);
+x := x + 1;
+END;
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest7.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest7.txt
new file mode 100644
index 0000000..cee7441
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest7.txt
@@ -0,0 +1,28 @@
+PROCEDURE Add();
+BEGIN
+WRITELN(x);
+x := x + 2;
+END;
+BEGIN
+x := 2;
+y := x + 1;
+x := x + y;
+WRITELN(x * y);
+IF x > y THEN
+BEGIN
+WRITELN(x);
+WRITELN(y);
+END;
+x := 0;
+WHILE x < 10 DO
+BEGIN
+WRITELN(x);
+x := x + 1;
+END;
+ignore := Add();
+WRITELN(x);
+END;
+.
+.
+.
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/parserTest8.txt b/src/main/java/com/pilers/parser/tests/old_tests/parserTest8.txt
new file mode 100644
index 0000000..8741dc3
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/parserTest8.txt
@@ -0,0 +1,31 @@
+PROCEDURE Add(y,w,z);
+BEGIN
+WRITELN(y);
+WRITELN(w);
+WRITELN(z);
+x := x + y;
+END;
+BEGIN
+x := 2;
+y := x + 1;
+x := x + y;
+WRITELN(x * y);
+IF x > y THEN
+BEGIN
+WRITELN(x);
+WRITELN(y);
+END;
+x := 0;
+WHILE x < 10 DO
+BEGIN
+WRITELN(x);
+x := x + 1;
+END;
+WRITELN(y);
+ignore := Add(4,x,y);
+WRITELN(x);
+END;
+.
+.
+.
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/parser/tests/old_tests/typeTest.txt b/src/main/java/com/pilers/parser/tests/old_tests/typeTest.txt
new file mode 100644
index 0000000..677a1a4
--- /dev/null
+++ b/src/main/java/com/pilers/parser/tests/old_tests/typeTest.txt
@@ -0,0 +1,6 @@
+Integer x;
+
+
+x := 3;
+y := 'Hello'; // throws a type assignment error
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/preprocessor/Preprocessor.java b/src/main/java/com/pilers/preprocessor/Preprocessor.java
new file mode 100644
index 0000000..7aa9b23
--- /dev/null
+++ b/src/main/java/com/pilers/preprocessor/Preprocessor.java
@@ -0,0 +1,257 @@
+package com.pilers.preprocessor;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import com.pilers.errors.ErrorString;
+import com.pilers.errors.PreprocessorException;
+
+/**
+ * Preprocessor class
+ *
+ * Takes in a "base" file and pipes all output to a stream of bytes
+ * Currently supports:
+ * #include - include other files (inline replacement)
+ * #define - define macros
+ * #ifdef - if a macro is defined, include the following code up until endif or eof
+ * #endif - ends an ifdef
+ * #undef - undefines a macro
+ *
+ * #requestFile - makes a GET request to a specified URL
+ * allows including online files in the project
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public class Preprocessor
+{
+
+ private String filepath;
+
+ private BufferedReader in;
+
+ private HashMap definitions;
+
+ ArrayList includedFiles;
+
+ ByteArrayOutputStream out;
+
+ private boolean onMyComputer;
+
+ /**
+ * Preprocessor constructor for constructing a "child" preprocessor
+ *
+ * @param filepath file path (a URL if it's online)
+ * @param onMyComputer (different file structures on mine vs instructor computer)
+ * @param isOnline if the requested file is online not local
+ * @param out the existing output stream
+ * @param includedFiles files that have already been piped to the stream
+ * @param definitions variables
+ *
+ * @throws IOException if there is an issue with file I/O
+ */
+ public Preprocessor(
+ String filepath, boolean onMyComputer,
+ boolean isOnline,
+ ByteArrayOutputStream out,
+ ArrayList includedFiles,
+ HashMap definitions) throws IOException
+ {
+ if (isOnline)
+ {
+ try
+ {
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request =
+ HttpRequest.newBuilder().uri(
+ URI.create(filepath)).build();
+
+ HttpResponse response =
+ client.send(request, HttpResponse.BodyHandlers.ofString());
+
+ in = new BufferedReader(new StringReader(response.body()));
+ }
+ catch(Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ if (onMyComputer)
+ in = new BufferedReader(
+ new InputStreamReader(Preprocessor.class.getResourceAsStream(filepath)));
+ else
+ {
+ Path currentDir = Paths.get(filepath);
+ in = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(new File(currentDir.toAbsolutePath().toString()))));
+ }
+ }
+
+ this.filepath = filepath;
+ this.includedFiles = includedFiles;
+ this.out = out;
+ this.onMyComputer = onMyComputer;
+ this.definitions = definitions;
+ }
+
+ /**
+ * Preprocessor constructor for constructing the "base" preprocessor
+ *
+ * @param filepath file name
+ * @param onMyComputer (different file structures on mine vs instructor computer)
+ * @param isOnline if the requested file is online not local
+ * @throws IOException if there is a problem with file I/O
+ */
+ public Preprocessor(String filepath, boolean onMyComputer, boolean isOnline) throws IOException
+ {
+
+ if (isOnline)
+ {
+ try
+ {
+ HttpClient client = HttpClient.newHttpClient();
+ HttpRequest request = HttpRequest.newBuilder().uri(URI.create(filepath)).build();
+
+ HttpResponse response = client.send(
+ request, HttpResponse.BodyHandlers.ofString());
+
+ in = new BufferedReader(new StringReader(response.body()));
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ else
+ {
+ if (onMyComputer)
+ in = new BufferedReader(
+ new InputStreamReader(
+ Preprocessor.class.getResourceAsStream(filepath)));
+ else
+ {
+ Path currentDir = Paths.get(filepath);
+ in = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(
+ new File(currentDir.toAbsolutePath().toString()))));
+ }
+ }
+
+ this.filepath = filepath;
+ out = new ByteArrayOutputStream();
+ includedFiles = new ArrayList();
+ this.onMyComputer = onMyComputer;
+ definitions = new HashMap();
+ }
+
+ /**
+ * Processes the file and pipes output to a stream of bytes
+ *
+ * @throws IOException if there is an issue with File I/O
+ * @throws PreprocessorException if there is an unknown directive
+ */
+ public void process() throws IOException, PreprocessorException
+ {
+ if (includedFiles.contains(filepath)) return;
+
+ includedFiles.add(filepath);
+ String line = in.readLine();
+
+ // used for false #endif directives
+ boolean isWriting = true;
+
+ while (line != null)
+ {
+ if (line.length() > 0 && line.charAt(0) == '#')
+ {
+ String[] args = line.split(" ");
+ String directive = args[0];
+
+ switch (directive)
+ {
+ case ("#include"):
+ new Preprocessor(
+ args[1], onMyComputer,
+ false, out, includedFiles, definitions)
+ .process();
+ break;
+ case ("#define"):
+ String defVar = args[1];
+ String defVal = args[2];
+ if(definitions.get(defVar) != null) throw new
+ PreprocessorException(ErrorString.duplicateDefine(defVar));
+
+ definitions.put(defVar, defVal);
+ break;
+
+ case("#ifdef"):
+ String defVarCond = args[1];
+ isWriting = definitions.get(defVarCond) != null;
+
+ break;
+
+ case("#endif"):
+ isWriting = true;
+ break;
+
+ case("#undef"):
+ definitions.put(args[1], null);
+ break;
+
+ case("#requestFile"):
+ new Preprocessor(args[1], onMyComputer,
+ true, out, includedFiles, definitions).process();
+ break;
+
+ default:
+ throw new PreprocessorException(ErrorString.unknownDirective(args[1]));
+ }
+ }
+ else if (isWriting)
+ {
+ String[] split = line.split(" ");
+ for(int i=0;ix) max = y;
+
+ x = -1;
+ y = -1; // will not affect global variables
+}
+
+writeln("Now testing 'getSign' subroutine");
+writeln(getSign(-1)); // should print "Negative"
+writeln(getSign(0)); // should print "Zero"
+writeln(getSign(100)); // should print "Positive"
+
+writeln("Now testing 'max' subroutine");
+Integer x = 3;
+Integer y = 4;
+writeln(max(x, y)); // should print 4
+writeln(x); // should print 3 not -1
+
+writeln("Now testing for/while loops and user input");
+Integer input;
+
+writeln("Enter a number for the for loop: ");
+readInteger(input);
+for(Integer i = 0;i='0' && ch<='9';
+ }
+
+ /**
+ * @param ch input char
+ * @return if the character is a letter
+ */
+ public static boolean isLetter(char ch)
+ {
+ return ch>='A' && ch<='z';
+ }
+ /**
+ * @param ch input char
+ * @return if the character is whitespace
+ */
+ public static boolean isWhitespace(char ch)
+ {
+ for(int i=0;i=", "!=", "&&", "||", ">>", "<<", "!=", "++", "--", "==" };
+ /**
+ * One-character operators
+ */
+ public static final String[] ONECHAR_OPERATORS = new String[]
+ { "+", "-", "*", "/", "%", ">", "<", "!", "^", "&", "|", "~", "{", "}"};
+
+ /**
+ * ALL operators ordered by precedence in a 2D array
+ * Arrays are ordered in increasing order
+ *
+ * Note: this 2D array does not include the assignment operator, which should
+ * never be referenced in the same context as it. If it is, something
+ * is seriously wrong.
+ *
+ * Precedence example:
+ * TRUE | 3 + 4 >= 4
+ *
+ * Order of evaluation: +, >=, |
+ *
+ * Overall order of precedence:
+ * Logical Operators
+ * Relational operators
+ * Mathematical operators
+ */
+ public static final String[][] OPERATORS_ORDERED_BY_PRECEDENCE = new String[][]
+ {
+ new String[]{"||"}, // lowest precedence
+ new String[]{"&&"},
+ new String[]{"^"},
+ new String[]{"<=",">=","==", "!=", ">", "<"},
+ new String[]{"+", "-"},
+ new String[]{"*", "/", "%", ">>", "<<", "&", "|"}
+ };
+
+ /**
+ * All unary operators
+ */
+ public static final String[] UNARY_OPERATORS = new String[]
+ {
+ "-", "!", "~", "++", "--"
+ };
+
+ /**
+ * Whitespace characters
+ */
+ public static final char[] WHITESPACE_CHARS = new char[] { ' ', '\n', '\t', '\r' };
+ /**
+ * Line terminator char
+ */
+ public static final char LINE_TERMINATOR_CHAR = ';';
+ /**
+ * Open paren character
+ */
+ public static final char OPEN_PAREN_CHAR = '(';
+ /**
+ * Close paren character
+ */
+ public static final char CLOSE_PAREN_CHAR = ')';
+ /**
+ * Quote character
+ */
+ public static final char QUOTE_CHAR = '"';
+ /**
+ * Comma character
+ */
+ public static final char COMMA_CHAR = ',';
+
+ /**
+ * The character that ends everything
+ */
+ public static final char END_CHARACTER = '?';
+
+
+ /**
+ * All the types, in string form
+ */
+ public static final String[] TYPE_STRINGS = new String[]
+ {"END", "IDENTIFIER", "KEYWORD", "NUMBER", "OPERATOR",
+ "LINE_TERMINATOR", "OPEN_PAREN", "CLOSE_PAREN", "STRING", "BOOLEAN", "COMMA", "TYPE" };
+
+ /**
+ * Type names, but nicer; to be used in semantic analysis for type checking
+ *
+ * IMPORTANT: the only types in this list that should ever be used are:
+ * "Integer" "String" and "Boolean". The other ones have just been omitted -
+ * if they are used, something is SERIOUSLY WRONG
+ */
+ public static final String[] NICE_TYPE_STRINGS = new String[]
+ { "", "", "", "Integer", "", "", "", "", "String", "Boolean", "" };
+
+ /**
+ * the END token "." signifies the end of the input stream
+ */
+ public static final int END = 0;
+
+ /**
+ * the IDENTIFIER token is a string
+ */
+ public static final int IDENTIFIER = 1;
+ /**
+ * the KEYWORD token is a string that is apart of
+ * a set of keywords predefined in the Scanner.
+ */
+ public static final int KEYWORD = 2;
+ /**
+ * the NUMBER token is a string that represents a
+ * number. todo: support decimal numbers
+ */
+ public static final int NUMBER = 3;
+
+ /**
+ * the OPERATOR token is a string that represents
+ * an operator. it can have one or two characters.
+ */
+ public static final int OPERATOR = 4;
+
+ /**
+ * the LINE_TERMINATOR token is a string that represents
+ * the end of the line. in this case, it is a semicolon (;).
+ */
+ public static final int LINE_TERMINATOR = 5;
+ /**
+ * the OPEN_PAREN token is a string that represents an open paren.
+ */
+ public static final int OPEN_PAREN = 6;
+ /**
+ * the CLOSE_PAREN token is a string that represents a close paren
+ */
+ public static final int CLOSE_PAREN = 7;
+ /**
+ * A STRING token represents an actual string
+ * (not an identifier or boolean)
+ */
+ public static final int STRING = 8;
+ /**
+ * A BOOLEAN token represents a boolean string
+ * There is only TRUE_BOOLEAN and FALSE_BOOLEAN (for now)
+ */
+ public static final int BOOLEAN = 9;
+ /**
+ * A COMMA token is just a comma :P
+ */
+ public static final int COMMA = 10;
+ /**
+ * TYPE token represents a type (see list of types at top of file)
+ */
+ public static final int TYPE = 11;
+
+ /**
+ * the DUMMY token is used by comment scanning to pass strings
+ * if comment scanning was unsuccessful.
+ */
+ public static final int DUMMY = -1;
+
+ /**
+ * Number class
+ * for checking variable types in the parser
+ */
+ public static final String NUMBER_CLASS = "java.lang.Integer";
+ /**
+ * String class
+ * for checking variable types in the parser
+ */
+ public static final String STRING_CLASS = "java.lang.String";
+ /**
+ * Boolean class
+ * for checking variable types in the parser
+ */
+ public static final String BOOLEAN_CLASS = "java.lang.Boolean";
+
+ private String value;
+ private int type;
+ private int line;
+
+ /**
+ * Constructor for a Token object with a value and a type
+ *
+ * @param value the value (as a String)
+ * @param type the type (as an integer - use the constants in the class)
+ * @param line the line of the token
+ */
+ public Token(String value, int type, int line)
+ {
+ this.value = value;
+ this.type = type;
+ this.line = line;
+ }
+
+ /**
+ * Same constructor as above but takes a character instead Calls other
+ * constructor (casts char to string)
+ *
+ * @param value the value (as a char)
+ * @param type the type (as an integer - use the constants in the class)
+ * @param line the line of the token
+ */
+ public Token(char value, int type, int line)
+ {
+ this(""+value, type, line);
+ }
+
+ /**
+ * Sets the type of the token
+ * @param newType the new type
+ */
+ public void setType(int newType)
+ {
+ type = newType;
+ }
+
+ /**
+ * Sets the value of the token
+ * @param newValue the new value
+ */
+ public void setValue(String newValue)
+ {
+ value = newValue;
+ }
+
+ /**
+ * Sets the line of the token
+ * @param newLine the new line
+ */
+ public void setLine(int newLine)
+ {
+ line = newLine;
+ }
+
+ /**
+ * @return the type of the token (as an integer)
+ */
+ public int getType()
+ {
+ return type;
+ }
+
+ /**
+ * @return the type of the token (as a string)
+ */
+ public String getTypeAsString()
+ {
+ return TYPE_STRINGS[type];
+ }
+
+ /**
+ * This should only reference "Integer" "Boolean" and "String"
+ * If it returns something other than that, something is terribly wrong.
+ * @return the type of the token VALUE (as a string)
+ */
+ public String getTypeAsNiceString()
+ {
+ return NICE_TYPE_STRINGS[type];
+ }
+
+ /**
+ * @return the value of the token (in String form)
+ */
+ public String getValue()
+ {
+ return value;
+ }
+
+ /**
+ * @return the line of the token
+ */
+ public int getLine()
+ {
+ return line;
+ }
+
+ // Static factory methods to make token creation less messy in the Scanner.
+
+ /**
+ * @param line the line of the token
+ * @return an end token
+ */
+ public static Token endToken(int line)
+ {
+ return new Token(Token.END_CHARACTER, Token.END, line);
+ }
+
+ /**
+ * @param line the line of the token
+ * @return a line terminator token
+ */
+ public static Token lineTerminatorToken(int line)
+ {
+ return new Token(LINE_TERMINATOR_CHAR, Token.LINE_TERMINATOR, line);
+ }
+
+ /**
+ * @param line the line of the token
+ * @return an open parentheses token
+ */
+ public static Token openParenToken(int line)
+ {
+ return new Token(OPEN_PAREN_CHAR, Token.OPEN_PAREN, line);
+ }
+
+ /**
+ * @param line the line of the token
+ * @return a close parentheses token
+ */
+ public static Token closeParenToken(int line)
+ {
+ return new Token(CLOSE_PAREN_CHAR, Token.CLOSE_PAREN, line);
+ }
+
+ /**
+ * @param line the line of the token
+ * @return a comma token
+ */
+ public static Token commaToken(int line)
+ {
+ return new Token(COMMA_CHAR, Token.COMMA, line);
+ }
+
+ /**
+ * @return a dummy token
+ * @param value the value to be put into the dummy token
+ * @param line the line of the token
+ */
+ public static Token dummyToken(String value, int line)
+ {
+ return new Token(value, Token.DUMMY, line);
+ }
+
+ /**
+ * Overriden equals method
+ * @param other the other Object
+ */
+ @Override
+ public boolean equals(Object other)
+ {
+
+ if (other instanceof String) return value.equals((String) other);
+ else if (other instanceof Character) return value.equals(""+other);
+ else if (other instanceof Token)
+ {
+ Token casted = (Token)other;
+ return line == casted.line && type == casted.type && value == casted.value;
+ }
+
+ return false;
+ }
+
+ /**
+ * Overriden toString method
+ */
+ @Override
+ public String toString()
+ {
+ return String.format("%-2s %-20s%s\n", line, this.getTypeAsString(), value);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/pilers/scanner/tests/ScannerTest.txt b/src/main/java/com/pilers/scanner/tests/ScannerTest.txt
new file mode 100644
index 0000000..a5b0514
--- /dev/null
+++ b/src/main/java/com/pilers/scanner/tests/ScannerTest.txt
@@ -0,0 +1,61 @@
+(*
+Tests the full capabilities (hopefully) of the compiler
+*)
+
+// Declaring subroutines
+String getSign(Integer x) {
+ getSign = "Positive";
+ if (x==0) getSign = "Zero";
+ if (x<0) getSign = "Negative";
+}
+Integer max(Integer x, Integer y) {
+ max = x;
+ if (y>x) max = y;
+
+ x = -1;
+ y = -1; // will not affect global variables
+}
+
+writeln("Now testing 'getSign' subroutine");
+writeln(getSign(-1)); // should print "Negative"
+writeln(getSign(0)); // should print "Zero"
+writeln(getSign(100)); // should print "Positive"
+
+writeln("Now testing 'max' subroutine");
+Integer x = 3;
+Integer y = 4;
+writeln(max(x, y)); // should print 4
+writeln(x); // should print 3 not -1
+
+
+writeln("Now testing for/while loops and user input");
+Integer input;
+
+writeln("Enter a number for the for loop: ");
+readInteger(input);
+for(Integer i = 0;i= 10 DO */
+BEGIN
+WRITELN(square(x)); */
+x := x + 1.0;
+END;
+END;
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/scanner/tests/scannerTestParser.txt b/src/main/java/com/pilers/scanner/tests/scannerTestParser.txt
new file mode 100644
index 0000000..19f4d5a
--- /dev/null
+++ b/src/main/java/com/pilers/scanner/tests/scannerTestParser.txt
@@ -0,0 +1,30 @@
+// WRITELN('HELLO' == 'HELLO' && -3 * 2 + 5 == 17 || 3 == 2);
+
+PROCEDURE Add();
+BEGIN
+WRITELN(x);
+x := x + 2;
+END;
+BEGIN
+x := 2;
+y := x + 1;
+x := x + y;
+WRITELN(x * y);
+IF x > y THEN
+BEGIN
+WRITELN(x);
+WRITELN(y);
+END;
+x := 0;
+WHILE x < 10 DO
+BEGIN
+WRITELN(x);
+x := x + 1;
+END;
+ignore := Add();
+WRITELN(x);
+END;
+.
+.
+.
+.
\ No newline at end of file
diff --git a/src/main/java/com/pilers/tester/Tester.java b/src/main/java/com/pilers/tester/Tester.java
new file mode 100644
index 0000000..7b1dee6
--- /dev/null
+++ b/src/main/java/com/pilers/tester/Tester.java
@@ -0,0 +1,84 @@
+package com.pilers.tester;
+
+import com.pilers.ast.Program;
+import com.pilers.emitter.Emitter;
+import com.pilers.environment.InterpreterEnvironment;
+import com.pilers.environment.SemanticAnalysisEnvironment;
+import com.pilers.parser.Parser;
+import com.pilers.preprocessor.Preprocessor;
+import com.pilers.scanner.Scanner;
+
+/**
+ * Overarching Tester class for the entire compiler/interpreter :)
+ *
+ * @author Gloria Zhu
+ * @version 8/19/22
+ */
+public class Tester
+{
+
+ /**
+ * Main method that tests the compiler/interpreter
+ *
+ * @param args arguments from the cmd line
+ */
+ public static void main(String[] args)
+ {
+ // controls
+ boolean onMyComputer = true;
+ boolean interpretCode = false;
+ boolean compileCode = true;
+
+ String[] testFiles = new String[] {
+ "demo.txt",
+ "all.txt" };
+
+ String[] compileDestinations = new String[] {
+ "demo.asm",
+ "all.asm" };
+
+ for(int i=0;i