23 Commits
Author SHA1 Message Date
simonkellet 3f58bdfad9 added bash file to compile and run files quicker 2022-09-20 12:16:52 +01:00
simonkellet 6377dd8250 added ignore to .tam files 2022-09-20 12:06:27 +01:00
simonkellet 955517af29 added build dir 2022-09-20 11:59:31 +01:00
Sandy Brownlee 3dade998ba possible fix for windows 2022-09-15 17:42:16 +01:00
Sandy Brownlee 048cbac0b4 Added bardemo 2022-09-15 15:54:24 +01:00
Sandy Brownlee 77846e5800 Merge branch 'java11' of github.com:sandybrownlee/Triangle-Tools into java11 2022-09-15 11:27:06 +01:00
Sandy Brownlee f4e1a08f61 minor fix to tests 2022-09-09 17:08:30 +01:00
Sandy Brownlee c3c2076d9e repeat until 2022-09-09 16:33:13 +01:00
Sandy Brownlee 64c25f1486 Added pounddemo 2022-09-09 15:46:22 +01:00
Sandy Brownlee c469297712 Amended comment 2 type 2022-09-09 15:26:16 +01:00
Sandy Brownlee a2cac87f81 Changed master project to Java 11 2022-09-09 14:39:53 +01:00
Sandy Brownlee b3375e2050 Added single jar build to root project 2022-09-09 12:20:49 +01:00
Sandy Brownlee 11be265081 Added object filename option to Compiler 2022-09-09 12:02:55 +01:00
Sandy Brownlee 6820b55cf1 Streamlined unit tests 2022-09-08 19:47:22 +01:00
Sandy Brownlee f709234c4e Added unit tests for bad syntax in hi-newcomment.tri 2022-09-08 19:38:02 +01:00
Sandy Brownlee 07b0d85bdf added test 2022-09-08 17:57:48 +01:00
Sandy Brownlee e8780a5b11 Merge branch 'java11' of github.com:sandybrownlee/Triangle-Tools into java11 2022-09-08 09:23:21 +01:00
Sandy Brownlee 90977fb64f added comment tests 2022-09-08 09:23:04 +01:00
Sandy BrownleeandGitHub 3150ae8fa9 Merge branch 'deryckb:main' into java11 2022-09-07 19:56:44 +01:00
Sandy Brownlee a026ec3840 add while example 2022-09-05 17:15:08 +01:00
Sandy Brownlee 3de9d5e0cc Merge branch 'main' into java11 2022-09-01 16:48:54 +01:00
Sandy Brownlee ee57c9f711 added assignments example 2022-08-19 15:00:32 +01:00
Sandy Brownlee 01f468f07d Java 11 compatibility 2022-08-18 13:22:06 +01:00
22 changed files with 310 additions and 49 deletions
+3
View File
@@ -60,3 +60,6 @@ local.properties
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project
/.gradle/
# tam files
*.tam
@@ -1,7 +1,7 @@
apply plugin: 'java'
apply plugin: 'application'
sourceCompatibility = 17
sourceCompatibility = 11
dependencies {
implementation project(':Triangle.AbstractMachine')
@@ -1,7 +1,7 @@
apply plugin: 'java'
apply plugin: 'application'
sourceCompatibility = 17
sourceCompatibility = 11
dependencies {
implementation project(':Triangle.AbstractMachine')
+1 -1
View File
@@ -1,4 +1,4 @@
apply plugin: 'java-library'
apply plugin: 'eclipse'
sourceCompatibility = 17
sourceCompatibility = 11
+10 -1
View File
@@ -1,12 +1,21 @@
apply plugin: 'java'
apply plugin: 'application'
sourceCompatibility = 17
sourceCompatibility = 11
repositories {
mavenCentral()
}
dependencies {
implementation project(':Triangle.AbstractMachine')
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
}
application {
mainClass = 'Triangle.Compiler'
}
// allow access to programs for unit tests
sourceSets.test.resources.srcDir file("$rootDir/programs")
@@ -34,6 +34,8 @@ public class Compiler {
/** The filename for the object program, normally obj.tam. */
static String objectName = "obj.tam";
static boolean showTree = false;
private static Scanner scanner;
private static Parser parser;
private static Checker checker;
@@ -71,7 +73,7 @@ public class Compiler {
}
scanner = new Scanner(source);
reporter = new ErrorReporter();
reporter = new ErrorReporter(false);
parser = new Parser(scanner, reporter);
checker = new Checker(reporter);
emitter = new Emitter(reporter);
@@ -80,7 +82,7 @@ public class Compiler {
// scanner.enableDebugging();
theAST = parser.parseProgram(); // 1st pass
if (reporter.numErrors == 0) {
if (reporter.getNumErrors() == 0) {
// if (showingAST) {
// drawer.draw(theAST);
// }
@@ -89,13 +91,13 @@ public class Compiler {
if (showingAST) {
drawer.draw(theAST);
}
if (reporter.numErrors == 0) {
if (reporter.getNumErrors() == 0) {
System.out.println("Code Generation ...");
encoder.encodeRun(theAST, showingTable); // 3rd pass
}
}
boolean successful = (reporter.numErrors == 0);
boolean successful = (reporter.getNumErrors() == 0);
if (successful) {
emitter.saveObjectProgram(objectName);
System.out.println("Compilation was successful.");
@@ -114,16 +116,29 @@ public class Compiler {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("Usage: tc filename [tree]");
System.out.println("Usage: tc filename [-o=outputfilename] [tree]");
System.exit(1);
}
String sourceName = args[0];
boolean tree = (args.length > 1 && args[1].equalsIgnoreCase("tree"));
var compiledOK = compileProgram(sourceName, objectName, tree, false);
parseArgs(args);
if (!tree) {
String sourceName = args[0];
var compiledOK = compileProgram(sourceName, objectName, showTree, false);
if (!showTree) {
System.exit(compiledOK ? 0 : 1);
}
}
private static void parseArgs(String[] args) {
for (String s : args) {
var sl = s.toLowerCase();
if (sl.equals("tree")) {
showTree = true;
} else if (sl.startsWith("-o=")) {
objectName = s.substring(3);
}
}
}
}
@@ -18,25 +18,44 @@ import triangle.syntacticAnalyzer.SourcePosition;
public class ErrorReporter {
int numErrors;
private int numErrors;
ErrorReporter() {
private boolean throwExceptions;
/**
* @param throwExceptions if true, throw exceptions (good for unit tests) otherwise write to stdout
*/
public ErrorReporter(boolean throwExceptions) {
numErrors = 0;
this.throwExceptions = throwExceptions;
}
public void reportError(String message, String tokenName, SourcePosition pos) {
System.out.print("ERROR: ");
numErrors++;
String s = ("ERROR: ");
for (int p = 0; p < message.length(); p++)
if (message.charAt(p) == '%')
System.out.print(tokenName);
s += tokenName;
else
System.out.print(message.charAt(p));
System.out.println(" " + pos.start + ".." + pos.finish);
numErrors++;
s += message.charAt(p);
s += (" " + pos.start + ".." + pos.finish);
if (throwExceptions) {
throw new RuntimeException(s);
} else {
System.out.println(s);
}
}
public void reportRestriction(String message) {
System.out.println("RESTRICTION: " + message);
}
public int getNumErrors() {
return numErrors;
}
}
@@ -39,8 +39,8 @@ public class ConstFormalParameter extends FormalParameter implements ConstantDec
@Override
public boolean equals(Object fpAST) {
if (fpAST instanceof ConstFormalParameter cfpAST) {
return T.equals(cfpAST.T);
if (fpAST instanceof ConstFormalParameter) {
return T.equals(((ConstFormalParameter)fpAST).T);
} else {
return false;
}
@@ -39,8 +39,8 @@ public class VarFormalParameter extends FormalParameter implements VariableDecla
@Override
public boolean equals(Object fpAST) {
if (fpAST instanceof VarFormalParameter vfpAST) {
return T.equals(vfpAST.T);
if (fpAST instanceof VarFormalParameter) {
return T.equals(((VarFormalParameter)fpAST).T);
} else {
return false;
}
@@ -132,7 +132,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
public Void visitCallCommand(CallCommand ast, Void arg) {
var binding = ast.I.visit(this);
if (binding instanceof ProcedureDeclaration procedure) {
if (binding instanceof ProcedureDeclaration) {
ProcedureDeclaration procedure = (ProcedureDeclaration)binding;
ast.APS.visit(this, procedure.getFormals());
} else {
reportUndeclaredOrError(binding, ast.I, "\"%\" is not a procedure identifier");
@@ -203,7 +204,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
var e2Type = ast.E2.visit(this);
var binding = ast.O.visit(this);
if (binding instanceof BinaryOperatorDeclaration bbinding) {
if (binding instanceof BinaryOperatorDeclaration) {
BinaryOperatorDeclaration bbinding = (BinaryOperatorDeclaration)binding;
if (bbinding.ARG1 == StdEnvironment.anyType) {
// this operator must be "=" or "\="
checkAndReportError(e1Type.equals(e2Type), "incompatible argument types for \"%\"", ast.O, ast);
@@ -222,7 +224,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
public TypeDenoter visitCallExpression(CallExpression ast, Void arg) {
var binding = ast.I.visit(this);
if (binding instanceof FunctionDeclaration function) {
if (binding instanceof FunctionDeclaration) {
FunctionDeclaration function = (FunctionDeclaration)binding;
ast.APS.visit(this, function.getFormals());
return ast.type = function.getType();
}
@@ -277,7 +280,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
var eType = ast.E.visit(this);
var binding = ast.O.visit(this);
if (binding instanceof UnaryOperatorDeclaration ubinding) {
if (binding instanceof UnaryOperatorDeclaration) {
UnaryOperatorDeclaration ubinding = (UnaryOperatorDeclaration)binding;
checkAndReportError(eType.equals(ubinding.ARG), "wrong argument type for \"%\"", ast.O);
return ast.type = ubinding.RES;
}
@@ -472,8 +476,9 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public Void visitConstActualParameter(ConstActualParameter ast, FormalParameter arg) {
var eType = ast.E.visit(this);
if (arg instanceof ConstFormalParameter param) {
checkAndReportError(eType.equals(param.T), "wrong type for const actual parameter", ast.E);
if (arg instanceof ConstFormalParameter) {
ConstFormalParameter param = (ConstFormalParameter)arg;
checkAndReportError(eType.equals(((ConstFormalParameter)arg).T), "wrong type for const actual parameter", ast.E);
} else {
reportError("const actual parameter not expected here", ast);
}
@@ -483,10 +488,12 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public Void visitFuncActualParameter(FuncActualParameter ast, FormalParameter arg) {
var binding = ast.I.visit(this);
if (binding instanceof FunctionDeclaration function) {
if (binding instanceof FunctionDeclaration) {
FunctionDeclaration function = (FunctionDeclaration)binding;
var formals = function.getFormals();
var functionType = function.getType();
if (arg instanceof FuncFormalParameter param) {
if (arg instanceof FuncFormalParameter) {
FuncFormalParameter param = (FuncFormalParameter)arg;
if (!formals.equals(param.getFormals())) {
reportError("wrong signature for function \"%\"", ast.I);
} else if (!functionType.equals(param.T)) {
@@ -504,9 +511,11 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public Void visitProcActualParameter(ProcActualParameter ast, FormalParameter arg) {
var binding = ast.I.visit(this);
if (binding instanceof ProcedureDeclaration procedure) {
if (binding instanceof ProcedureDeclaration) {
ProcedureDeclaration procedure = (ProcedureDeclaration)binding;
var formals = procedure.getFormals();
if (arg instanceof ProcFormalParameter param) {
if (arg instanceof ProcFormalParameter) {
ProcFormalParameter param = (ProcFormalParameter)arg;
checkAndReportError(formals.equals(param.getFormals()), "wrong signature for procedure \"%\"", ast.I);
} else {
reportError("proc actual parameter not expected here", ast);
@@ -522,7 +531,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
var vType = ast.V.visit(this);
if (!ast.V.variable) {
reportError("actual parameter is not a variable", ast.V);
} else if (arg instanceof VarFormalParameter parameter) {
} else if (arg instanceof VarFormalParameter) {
VarFormalParameter parameter = (VarFormalParameter)arg;
checkAndReportError(vType.equals(parameter.T), "wrong type for var actual parameter", ast.V);
} else {
reportError("var actual parameter not expected here", ast.V);
@@ -538,7 +548,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public Void visitMultipleActualParameterSequence(MultipleActualParameterSequence ast, FormalParameterSequence arg) {
if (arg instanceof MultipleFormalParameterSequence formals) {
if (arg instanceof MultipleFormalParameterSequence) {
MultipleFormalParameterSequence formals = (MultipleFormalParameterSequence)arg;
ast.AP.visit(this, formals.FP);
ast.APS.visit(this, formals.FPS);
} else {
@@ -549,7 +560,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public Void visitSingleActualParameterSequence(SingleActualParameterSequence ast, FormalParameterSequence arg) {
if (arg instanceof SingleFormalParameterSequence formal) {
if (arg instanceof SingleFormalParameterSequence) {
SingleFormalParameterSequence formal = (SingleFormalParameterSequence)arg;
ast.AP.visit(this, formal.FP);
} else {
reportError("incorrect number of actual parameters", ast);
@@ -592,7 +604,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
@Override
public TypeDenoter visitSimpleTypeDenoter(SimpleTypeDenoter ast, Void arg) {
var binding = ast.I.visit(this);
if (binding instanceof TypeDeclaration decl) {
if (binding instanceof TypeDeclaration) {
TypeDeclaration decl = (TypeDeclaration)binding;
return decl.T;
}
@@ -679,7 +692,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
ast.type = null;
var vType = ast.V.visit(this);
ast.variable = ast.V.variable;
if (vType instanceof RecordTypeDenoter record) {
if (vType instanceof RecordTypeDenoter) {
RecordTypeDenoter record = (RecordTypeDenoter)vType;
ast.type = checkFieldIdentifier(record.FT, ast.I);
checkAndReportError(ast.type != StdEnvironment.errorType, "no field \"%\" in this record type",
ast.I);
@@ -695,10 +709,12 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
ast.type = StdEnvironment.errorType;
var binding = ast.I.visit(this);
if (binding instanceof ConstantDeclaration constant) {
if (binding instanceof ConstantDeclaration) {
ConstantDeclaration constant = (ConstantDeclaration)binding;
ast.variable = false;
return ast.type = constant.getType();
} else if (binding instanceof VariableDeclaration variable) {
} else if (binding instanceof VariableDeclaration) {
VariableDeclaration variable = (VariableDeclaration)binding;
ast.variable = true;
return ast.type = variable.getType();
}
@@ -714,7 +730,8 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
var eType = ast.E.visit(this);
if (vType != StdEnvironment.errorType) {
if (vType instanceof ArrayTypeDenoter arrayType) {
if (vType instanceof ArrayTypeDenoter) {
ArrayTypeDenoter arrayType = (ArrayTypeDenoter)vType;
checkAndReportError(eType.equals(StdEnvironment.integerType), "Integer expression expected here",
ast.E);
ast.type = arrayType.T;
@@ -799,14 +816,16 @@ public final class Checker implements ActualParameterVisitor<FormalParameter, Vo
}
private static TypeDenoter checkFieldIdentifier(FieldTypeDenoter ast, Identifier I) {
if (ast instanceof MultipleFieldTypeDenoter ft) {
if (ast instanceof MultipleFieldTypeDenoter) {
MultipleFieldTypeDenoter ft = (MultipleFieldTypeDenoter)ast;
if (ft.I.spelling.compareTo(I.spelling) == 0) {
I.decl = ast;
return ft.T;
} else {
return checkFieldIdentifier(ft.FT, I);
}
} else if (ast instanceof SingleFieldTypeDenoter ft) {
} else if (ast instanceof SingleFieldTypeDenoter) {
SingleFieldTypeDenoter ft = (SingleFieldTypeDenoter)ast;
if (ft.I.spelling.compareTo(I.spelling) == 0) {
I.decl = ast;
return ft.T;
@@ -14,26 +14,36 @@
package triangle.syntacticAnalyzer;
import java.net.URL;
public class SourceFile {
public static final char EOL = '\n';
public static final char EOT = '\u0000';
java.io.File sourceFile;
java.io.FileInputStream source;
java.io.InputStream source;
int currentLine;
public static SourceFile ofPath(String pathname) {
try {
return new SourceFile(pathname);
SourceFile sf = new SourceFile();
sf.sourceFile = new java.io.File(pathname);
sf.source = new java.io.FileInputStream(sf.sourceFile);
return sf;
} catch (java.io.IOException s) {
return null;
}
}
private SourceFile(String pathname) throws java.io.FileNotFoundException {
sourceFile = new java.io.File(pathname);
source = new java.io.FileInputStream(sourceFile);
public static SourceFile fromResource(String handle) {
SourceFile sf = new SourceFile();
//sf.sourceFile = new java.io.File(pathname);
sf.source = sf.getClass().getResourceAsStream(handle);
return sf;
}
private SourceFile() {
currentLine = 1;
}
@@ -42,6 +42,13 @@ public class Drawer {
FontMetrics fontMetrics = frame.getFontMetrics(font);
// another class of visitor is used for drawing the tree: LayoutVisitor
// LayoutVisitor is passed to the AST which, in turn, calls visitProgram
// and then each AST node is visited. This ultimately constructs a
// DrawingTree, which is structurally the same as the AST but is decorated
// with coordinates (and has only DrawingTree objects as nodes)
// Each DrawingTree object knows how to paint itself, so it's passed to a
// DrawerPanel and DrawerFrame for display
LayoutVisitor layout = new LayoutVisitor(fontMetrics);
theDrawing = (DrawingTree) theAST.visit(layout, null);
theDrawing.position(new Point(2048, 10));
@@ -14,6 +14,9 @@
package triangle.treeDrawer;
/**
* used to keep track of the position for components in the tree to be drawn
*/
class Polyline {
int dx, dy;
Polyline link;
@@ -0,0 +1,84 @@
package triangle.syntacticAnalyser;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
import triangle.ErrorReporter;
import triangle.syntacticAnalyzer.Parser;
import triangle.syntacticAnalyzer.Scanner;
import triangle.syntacticAnalyzer.SourceFile;
public class TestScanner {
@Test
public void testHi() {
compileExpectSuccess("/hi.tri");
}
@Test
public void testHiNewComment() {
compileExpectFailure("/hi-newcomment.tri");
}
@Test
public void testHiNewComment2() {
compileExpectFailure("/hi-newcomment2.tri");
}
@Test
public void testBarDemo() {
compileExpectFailure("/bardemo.tri");
}
@Test
public void testRepeatUntil() {
compileExpectFailure("/repeatuntil.tri");
}
private void compileExpectSuccess(String filename) {
// build.gradle has a line sourceSets.test.resources.srcDir file("$rootDir/programs")
// which adds the programs directory to the list of places Java can easily find files
// getResource() below searches for a file, which is in /programs
//SourceFile source = SourceFile.ofPath(this.getClass().getResource(filename).getFile().toString());
SourceFile source = SourceFile.fromResource(filename);
Scanner scanner = new Scanner(source);
ErrorReporter reporter = new ErrorReporter(true);
Parser parser = new Parser(scanner, reporter);
parser.parseProgram();
// we should get to here with no exceptions
assertEquals("Problem compiling " + filename, 0, reporter.getNumErrors());
}
private void compileExpectFailure(String filename) {
//SourceFile source = SourceFile.ofPath(this.getClass().getResource(filename).getFile().toString());
SourceFile source = SourceFile.fromResource(filename);
Scanner scanner = new Scanner(source);
ErrorReporter reporter = new ErrorReporter(true);
Parser parser = new Parser(scanner, reporter);
// we expect an exception here as the program has invalid syntax
assertThrows(RuntimeException.class, new ThrowingRunnable() {
public void run(){
parser.parseProgram();
}
});
// currently this program will fail
assertNotEquals("Problem compiling " + filename, 0, reporter.getNumErrors());
}
}
+11
View File
@@ -0,0 +1,11 @@
apply plugin: 'java'
sourceCompatibility = 11
subprojects.each { subproject ->
evaluationDependsOn(subproject.path)
}
jar {
from subprojects.sourceSets.main.output
}
Binary file not shown.
+15
View File
@@ -0,0 +1,15 @@
! this won't work until after some work in the practicals
let
var a : Integer;
var b : Integer
in
begin
a := 1;
b := 2;
putint(a);
putint(b);
putint(|a);
putint(|b)
end
+7
View File
@@ -0,0 +1,7 @@
! this won't work until after some work in the practicals
# new comment
begin
put('H'); put('i'); put('!')
end
+11
View File
@@ -0,0 +1,11 @@
! this won't work until after some work in the practicals
# new comment
$
another new comment
$
begin
put('H'); put('i'); put('!')
end
+14
View File
@@ -0,0 +1,14 @@
! this won't work until after some lab work
let
var a : Integer
in
begin
a := 0;
repeat
begin
put('a');
a := a + 1;
end
until a < 5
end
+11
View File
@@ -0,0 +1,11 @@
let
var a : Integer
in
begin
a := 0;
while a < 5 do
begin
put('a');
a := a + 1;
end
end
Executable
+23
View File
@@ -0,0 +1,23 @@
#!/bin/bash
RED='\033[0;31m' # Red
GRN='\033[0;32m' # Green
NC='\033[0m' # No Color
file=""
if [ -z "$1" ]
then
printf "${RED}Usage: $0 file${NC}"
exit 1 # Exit with code 1, failure
else
file=$1
fi
printf "${GRN}Compiling file: $1.tri to $1.tam ...${NC}\n"
# Compile to tam
java -cp build/libs/Triangle-Tools.jar triangle.Compiler programs/$1.tri -o=$1.tam &> /dev/null
printf "${GRN}Running file: $1.tam ...${NC}\n"
# Run the Program
java -cp build/libs/Triangle-Tools.jar triangle.abstractMachine.Interpreter $1.tam