initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
out
|
||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# GitHub Copilot persisted chat sessions
|
||||
/copilot/chatSessions
|
||||
104
.idea/MicroJava.iml
generated
Normal file
104
.idea/MicroJava.iml
generated
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/MicroJava Compiler/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/MicroJava Tests/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/MicroJava Tests/tests" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/MicroJava VM Tests/test" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/gson-2.10.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/javassist.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/apiguardian-api-1.1.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-jupiter-5.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-jupiter-api-5.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-jupiter-engine-5.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-jupiter-params-5.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-platform-commons-1.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/junit-platform-engine-1.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="module-library" scope="TEST">
|
||||
<library>
|
||||
<CLASSES>
|
||||
<root url="jar://$MODULE_DIR$/lib/opentest4j-1.3.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
</component>
|
||||
</module>
|
||||
10
.idea/codeStyles/Project.xml
generated
Normal file
10
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<codeStyleSettings language="JAVA">
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
9
.idea/exercise02.iml
generated
Normal file
9
.idea/exercise02.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.net.http.HttpClient,newHttpClient" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
17
.idea/libraries/junit_jupiter.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="libraryTable">
|
||||
<library name="junit.jupiter" type="repository">
|
||||
<properties maven-id="org.junit.jupiter:junit-jupiter:5.12.2" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-5.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-api-5.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-platform-commons-1.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/apiguardian-api-1.1.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-params-5.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-jupiter-engine-5.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit-platform-engine-1.12.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
5
.idea/misc.xml
generated
Normal file
5
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="openjdk-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/MicroJava.iml" filepath="$PROJECT_DIR$/.idea/MicroJava.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
19
.idea/runConfigurations/UE_P_1.xml
generated
Normal file
19
.idea/runConfigurations/UE_P_1.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-1" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="ssw.mj.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="ssw.mj.test" />
|
||||
<option name="MAIN_CLASS_NAME" value="ssw.mj.test.ScannerTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
16
.idea/runConfigurations/UE_P_2.xml
generated
Normal file
16
.idea/runConfigurations/UE_P_2.xml
generated
Normal file
@@ -0,0 +1,16 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-2" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="pattern" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<patterns>
|
||||
<pattern testClass="ssw.mj.test.ScannerTest" />
|
||||
<pattern testClass="ssw.mj.test.ParserTest" />
|
||||
</patterns>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
17
.idea/runConfigurations/UE_P_3.xml
generated
Normal file
17
.idea/runConfigurations/UE_P_3.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-3" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="pattern" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<patterns>
|
||||
<pattern testClass="ssw.mj.test.ScannerTest" />
|
||||
<pattern testClass="ssw.mj.test.ParserTest" />
|
||||
<pattern testClass="ssw.mj.test.RecoverTest" />
|
||||
</patterns>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
18
.idea/runConfigurations/UE_P_4.xml
generated
Normal file
18
.idea/runConfigurations/UE_P_4.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-4" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="pattern" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<patterns>
|
||||
<pattern testClass="ssw.mj.test.ScannerTest" />
|
||||
<pattern testClass="ssw.mj.test.ParserTest" />
|
||||
<pattern testClass="ssw.mj.test.RecoverTest" />
|
||||
<pattern testClass="ssw.mj.test.SymbolTableTest" />
|
||||
</patterns>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
19
.idea/runConfigurations/UE_P_5.xml
generated
Normal file
19
.idea/runConfigurations/UE_P_5.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-5" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="pattern" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<patterns>
|
||||
<pattern testClass="ssw.mj.test.ScannerTest" />
|
||||
<pattern testClass="ssw.mj.test.ParserTest" />
|
||||
<pattern testClass="ssw.mj.test.RecoverTest" />
|
||||
<pattern testClass="ssw.mj.test.SymbolTableTest" />
|
||||
<pattern testClass="ssw.mj.test.SimpleCodeGenerationTest" />
|
||||
</patterns>
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
13
.idea/runConfigurations/UE_P_6.xml
generated
Normal file
13
.idea/runConfigurations/UE_P_6.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="UE-P-6" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="directory" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<dir value="$PROJECT_DIR$/MicroJava Tests/tests" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
13
.idea/runConfigurations/_for_lecturers__Run_verbose_tracing.xml
generated
Normal file
13
.idea/runConfigurations/_for_lecturers__Run_verbose_tracing.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="(for lecturers) Run verbose tracing" type="JUnit" factoryName="JUnit">
|
||||
<module name="MicroJava" />
|
||||
<option name="MAIN_CLASS_NAME" value="" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="directory" />
|
||||
<option name="VM_PARAMETERS" value="-ea -verbose:class -verbose:dynload -Djava.system.class.loader=ssw.mj.TracingClassLoader" />
|
||||
<dir value="$PROJECT_DIR$/MicroJava Tests/tests" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
27
MicroJava Compiler/Prims.mj
Normal file
27
MicroJava Compiler/Prims.mj
Normal file
@@ -0,0 +1,27 @@
|
||||
program Prims
|
||||
{
|
||||
|
||||
void print_prims (int n)
|
||||
int[] numbers;
|
||||
int i, j;
|
||||
{
|
||||
numbers = new int[n];
|
||||
i = 0;
|
||||
while(i < n) {
|
||||
if(1 < i && numbers[i] == 0) {
|
||||
print(i);
|
||||
print('\n');
|
||||
j = i;
|
||||
while(j < n) {
|
||||
numbers[j]++;
|
||||
j += i;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void main () {
|
||||
print_prims(1000);
|
||||
}
|
||||
}
|
||||
BIN
MicroJava Compiler/Prims.obj
Normal file
BIN
MicroJava Compiler/Prims.obj
Normal file
Binary file not shown.
87
MicroJava Compiler/StudentList.mj
Normal file
87
MicroJava Compiler/StudentList.mj
Normal file
@@ -0,0 +1,87 @@
|
||||
program StudentList
|
||||
final int MAXLEN = 20;
|
||||
|
||||
class Student {
|
||||
int matrNr;
|
||||
char[] name;
|
||||
}
|
||||
|
||||
Student[] list;
|
||||
int stCnt;
|
||||
|
||||
{
|
||||
void printString (char[] str)
|
||||
int i;
|
||||
{
|
||||
if (str != null) {
|
||||
i = 0;
|
||||
while (i < len(str)) {
|
||||
print(str[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init () {
|
||||
list = new Student[MAXLEN];
|
||||
stCnt = 0;
|
||||
}
|
||||
|
||||
void add (Student s)
|
||||
int i;
|
||||
{
|
||||
/* insert sorted by matrNr */
|
||||
i = stCnt - 1;
|
||||
while (i >= 0 && s.matrNr < list[i].matrNr) {
|
||||
list[i+1] = list[i];
|
||||
i--;
|
||||
}
|
||||
list[i+1] = s;
|
||||
stCnt++;
|
||||
}
|
||||
|
||||
int find (int matrNr)
|
||||
int l, r, x;
|
||||
|
||||
{
|
||||
/* binary search */
|
||||
l = 0; r = stCnt-1;
|
||||
while (l <= r && matrNr != list[x].matrNr) {
|
||||
x = (l+r)/2;
|
||||
if (matrNr < list[x].matrNr) r = x-1;
|
||||
else l = x+1;
|
||||
}
|
||||
if (matrNr == list[x].matrNr) return x;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void printStudent (int i) {
|
||||
print('m'); print('['); print(i); print(']'); print('=');
|
||||
print(list[i].matrNr); print(','); printString(list[i].name);
|
||||
print('\n');
|
||||
}
|
||||
|
||||
void main()
|
||||
Student s;
|
||||
{
|
||||
init();
|
||||
s = new Student; s.matrNr = 1234567; s.name = new char[3];
|
||||
s.name[0] = 'X'; s.name[1] = '\\'; s.name[2] = 'Y';
|
||||
add(s);
|
||||
s = new Student; s.matrNr = 9876543; s.name = new char[4];
|
||||
s.name[0] = 'M'; s.name[1] = 'r'; s.name[2] = '.'; s.name[3] = 'X';
|
||||
add(s);
|
||||
s = new Student; s.matrNr = 9090900; s.name = new char[2];
|
||||
s.name[0] = 'A'; s.name[1] = 'l';
|
||||
add(s);
|
||||
|
||||
printStudent(0);
|
||||
printStudent(1);
|
||||
printStudent(2);
|
||||
|
||||
print(9876543); print(' '); printString(list[find(9876543)].name); print('\n');
|
||||
print(1234567); print(' '); printString(list[find(1234567)].name); print('\n');
|
||||
print(9090900); print(' '); printString(list[find(9090900)].name); print('\n');
|
||||
}
|
||||
}
|
||||
BIN
MicroJava Compiler/StudentList.obj
Normal file
BIN
MicroJava Compiler/StudentList.obj
Normal file
Binary file not shown.
6
MicroJava Compiler/StudentListOutput.txt
Normal file
6
MicroJava Compiler/StudentListOutput.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
m[0]=1234567,X\Y
|
||||
m[1]=9090900,Al
|
||||
m[2]=9876543,Mr.X
|
||||
9876543 Mr.X
|
||||
1234567 X\Y
|
||||
9090900 Al
|
||||
75
MicroJava Compiler/TestProgram.mj
Normal file
75
MicroJava Compiler/TestProgram.mj
Normal file
@@ -0,0 +1,75 @@
|
||||
program Test
|
||||
|
||||
class Inner {
|
||||
int i;
|
||||
char[] a;
|
||||
char ch;
|
||||
}
|
||||
|
||||
int ii, jj;
|
||||
int[] arr;
|
||||
char a, b;
|
||||
char[] arr2;
|
||||
|
||||
final int const1 = 3;
|
||||
final char const2 = 'w';
|
||||
{
|
||||
int Func (int i) { return i*2; }
|
||||
|
||||
void Math ()
|
||||
int i, j, k, l, m;
|
||||
Inner in;
|
||||
Inner[] arr;
|
||||
{
|
||||
arr = new Inner[12];
|
||||
arr[7] = new Inner;
|
||||
arr[7].i = 17;
|
||||
|
||||
in = new Inner;
|
||||
in.i = 5;
|
||||
i = 2; j=3; k=4;
|
||||
l = i*j*k*4*Func(2)*Func(k);
|
||||
m = l+i-k+(const1%ord(const2))*in.i;
|
||||
i = m+arr[7].i;
|
||||
i += ii;
|
||||
if (i == 3119) print(1);
|
||||
}
|
||||
|
||||
void Structs ()
|
||||
int i;
|
||||
{
|
||||
while (i<17) {
|
||||
if (i>10) ii++;
|
||||
else if (i<5) ii--;
|
||||
else ii+=8;
|
||||
i++;
|
||||
}
|
||||
ii++;
|
||||
if (ii == 67) print(2);
|
||||
i = 3;
|
||||
while (i<15) {
|
||||
ii*=2;
|
||||
if (i == 10) break;
|
||||
i++;
|
||||
}
|
||||
if(ii == 17152) print(3);
|
||||
}
|
||||
|
||||
int Call(int val)
|
||||
int i;
|
||||
{
|
||||
if (val<10) val = Call(val+1)-1;
|
||||
else val = -2;
|
||||
return val;
|
||||
}
|
||||
|
||||
void main () {
|
||||
ii = 17;
|
||||
Math();
|
||||
Structs();
|
||||
ii = 1;
|
||||
ii = Call(ii);
|
||||
if (ii == -11) print(4);
|
||||
print('\n');
|
||||
}
|
||||
}
|
||||
BIN
MicroJava Compiler/TestProgram.obj
Normal file
BIN
MicroJava Compiler/TestProgram.obj
Normal file
Binary file not shown.
11
MicroJava Compiler/Trap.mj
Normal file
11
MicroJava Compiler/Trap.mj
Normal file
@@ -0,0 +1,11 @@
|
||||
program Test
|
||||
{
|
||||
|
||||
int Trap()
|
||||
{
|
||||
}
|
||||
|
||||
void main () {
|
||||
print(Trap());
|
||||
}
|
||||
}
|
||||
56
MicroJava Compiler/src/ssw/mj/Compiler.java
Normal file
56
MicroJava Compiler/src/ssw/mj/Compiler.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package ssw.mj;
|
||||
|
||||
import ssw.mj.impl.Parser;
|
||||
import ssw.mj.impl.Scanner;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* <code>Compiler</code> is the driver for the MicroJava-Compiler.
|
||||
* <p>
|
||||
* Execute<br>
|
||||
* <code>java ssw.mj.Compiler <<i>MJ-Source-Filename</i>></code><br>
|
||||
* to start compilation.
|
||||
*/
|
||||
public class Compiler {
|
||||
|
||||
private static String objFileName(String s) {
|
||||
int i = s.lastIndexOf('.');
|
||||
if (i < 0) {
|
||||
return s + ".obj";
|
||||
}
|
||||
return s.substring(0, i) + ".obj";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// --- get the filename
|
||||
if (args.length != 1) {
|
||||
System.out.println("usage: java Compiler filename.mj");
|
||||
return;
|
||||
}
|
||||
String inFilename = args[0];
|
||||
String outFilename = objFileName(inFilename);
|
||||
|
||||
try {
|
||||
Scanner scanner = new Scanner(new BufferedReader(new FileReader(inFilename)));
|
||||
|
||||
System.out.println("-----------------------------------");
|
||||
System.out.println("Parsing file " + inFilename);
|
||||
|
||||
Parser parser = new Parser(scanner);
|
||||
parser.parse();
|
||||
if (scanner.errors.numErrors() == 0) {
|
||||
parser.code.write(new BufferedOutputStream(new FileOutputStream(outFilename)));
|
||||
}
|
||||
|
||||
if (scanner.errors.numErrors() > 0) {
|
||||
System.out.println(scanner.errors.dump());
|
||||
System.out.println(scanner.errors.numErrors() + " errors.");
|
||||
} else {
|
||||
System.out.println("No errors.");
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
System.out.println("I/O Error: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
140
MicroJava Compiler/src/ssw/mj/Errors.java
Normal file
140
MicroJava Compiler/src/ssw/mj/Errors.java
Normal file
@@ -0,0 +1,140 @@
|
||||
package ssw.mj;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Errors {
|
||||
public static class PanicMode extends Error {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
// Nothing to implement here.
|
||||
}
|
||||
|
||||
public enum Message {
|
||||
// ----- error messages first used in ScannerTest
|
||||
EMPTY_CHARCONST("empty character constant"),
|
||||
UNDEFINED_ESCAPE("undefined escape character sequence ''\\{0}''"),
|
||||
MISSING_QUOTE("missing '' at end of character constant"),
|
||||
INVALID_CHAR("invalid character {0}"),
|
||||
BIG_NUM("{0} too big for integer constant"),
|
||||
EOF_IN_COMMENT("unexpected end of file in comment"),
|
||||
EOF_IN_CHAR("unexpected end of file in char"),
|
||||
ILLEGAL_LINE_END("illegal line end in character constant"),
|
||||
|
||||
// ----- error messages first used in ParserTest
|
||||
INVALID_ADD_OP("unexpected token. + or - expected"), // cannot occur in current grammar version, but still must be placed correctly (in default case of respective switch)
|
||||
INVALID_ASSIGN_OP("unexpected token. =, +=, -=, *=, /=, %= expected"), // cannot occur in current grammar version, but still must be placed correctly (in default case of respective switch)
|
||||
INVALID_MUL_OP("unexpected token. *, /, % expected"), // cannot occur in current grammar version, but still must be placed correctly (in default case of respective switch)
|
||||
INVALID_METHOD_DECL("invalid start of method decl: type name or void expected"), // cannot occur in current grammar version, but still must be placed correctly (in default case of respective switch)
|
||||
INVALID_STATEMENT("unexpected token. identifier, if, while, break, return, read, print, '{' or ; expected"), // cannot occur in current grammar version, but still must be placed correctly (in default case of respective switch)
|
||||
INVALID_DESIGNATOR_STATEMENT("unexpected token. assignment token (=, +=, -=, *=, /=, %=), method call (\"(\"), increment (++) or decrement (--) expected"),
|
||||
INVALID_CONST_TYPE("number or character constant expected"),
|
||||
INVALID_FACTOR("unexpected token. identifier, number, character constant, new or \"(\" expected"),
|
||||
INVALID_REL_OP("unexpected token. ==, !=, >, >=, <, <= expected"),
|
||||
TOKEN_EXPECTED("{0} expected"),
|
||||
|
||||
// ----- error messages first used in RecoverTest
|
||||
DECLARATION_RECOVERY("start or follow of declaration expected"),
|
||||
METHOD_DECL_RECOVERY("start or follow of method declaration expected"),
|
||||
STATEMENT_RECOVERY("start or follow of statement expected"),
|
||||
|
||||
// ----- error messages first used in SymbolTableTest
|
||||
INCOMPATIBLE_TYPES("incompatible types"), // mainly used in SimpleCodeGenerationTest, but also in SymbolTableTest
|
||||
DUPLICATE_NAME_IN_SCOPE("{0} already declared in current scope"),
|
||||
MAIN_WITH_PARAMS("main method must not have any parameters"),
|
||||
MAIN_NOT_VOID("main method must return void"),
|
||||
FIELD_NOT_FOUND("{0} is not a field"),
|
||||
TYPE_EXPECTED("type expected"),
|
||||
NAME_NOT_FOUND("{0} not found"),
|
||||
TOO_MANY_FIELDS("too many fields"),
|
||||
TOO_MANY_GLOBALS("too many global variables"),
|
||||
TOO_MANY_LOCALS("too many local variables"),
|
||||
CLASS_TYPE_EXPECTED("can only instantiate new object for a class"),
|
||||
|
||||
// ----- error messages first used in SimpleCodeGenerationTest
|
||||
ARRAY_INDEX_EXPECTS_INT("array index must be an integer"),
|
||||
ARRAY_SIZE_EXPECTS_INT("array size must be an integer"),
|
||||
CANNOT_STORE_TO_READONLY("cannot store to readonly operand of kind {0}"),
|
||||
VOID_CALL_IN_EXPRESSION("cannot use void method as part of expression"),
|
||||
MAIN_NOT_FOUND("mainPC is -1, main not found (did you forget to set code.mainPC? ;))"),
|
||||
INDEXED_ACCESS_TO_NON_ARRAY("indexed object is not an array"),
|
||||
FIELD_ACCESS_TO_NON_CLASS("accessed object is not of kind class"),
|
||||
ILLEGAL_OPERAND_KIND("cannot create operand symbol table object of type {0}"),
|
||||
CANNOT_LOAD_OPERAND("already loaded (stack) or loadable operand (const, local, static, field, array element) expected"),
|
||||
ILLEGAL_PRINT_ARGUMENT("can only print int or char values"),
|
||||
ILLEGAL_READ_ARGUMENT("can only read int or char values"),
|
||||
INC_DEC_EXPECTS_INT("increment and decrement only allowed for int"),
|
||||
UNARY_MINUS_EXPECTS_INT("unary minus only allowed for int"),
|
||||
|
||||
// ----- error messages first used in CodeGenerationTest
|
||||
ILLEGAL_REFERENCE_COMPARISON("only equality and unequality checks are allowed for reference types"),
|
||||
ILLEGAL_METHOD_RETURN_TYPE("methods may only return int or char"),
|
||||
WRONG_ARGUMENT_COUNT("number of arguments and formal parameters does not match"),
|
||||
BREAK_OUTSIDE_LOOP("break is not within a loop"),
|
||||
CALL_TO_NON_METHOD("called object is not a method"),
|
||||
ARGUMENT_TYPE_MISMATCH("argument type does not match formal parameter type"),
|
||||
MISSING_RETURN_VALUE("return expression required in non-void method"),
|
||||
UNEXPECTED_RETURN_VALUE("no return expression allowed in void method"),
|
||||
RETURN_TYPE_MISMATCH("return type must match method type");
|
||||
|
||||
private final String msg;
|
||||
|
||||
Message(String msg) {
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String format(Object... params) {
|
||||
int expectedParams = 0;
|
||||
while (msg.contains("{" + expectedParams + "}")) {
|
||||
expectedParams++;
|
||||
}
|
||||
if (params.length != expectedParams) {
|
||||
throw new Error("incorrect number of error message parameters. Expected %d but got %d".formatted(expectedParams, params.length));
|
||||
}
|
||||
return MessageFormat.format(msg, params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List of error messages.
|
||||
*/
|
||||
private final List<String> errors;
|
||||
|
||||
/**
|
||||
* Initialization (must be called before compilation).
|
||||
*/
|
||||
public Errors() {
|
||||
errors = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new error message to the list of errors.
|
||||
*/
|
||||
public void error(int line, int col, Message msg, Object... msgParams) {
|
||||
errors.add("-- line " + line + " col " + col + ": " + msg.format(msgParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of errors.
|
||||
*/
|
||||
public int numErrors() {
|
||||
return errors.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* String representation for JUnit test cases.
|
||||
*/
|
||||
public String dump() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String error : errors) {
|
||||
sb.append(error).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public List<String> getErrors() {
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
517
MicroJava Compiler/src/ssw/mj/Interpreter.java
Normal file
517
MicroJava Compiler/src/ssw/mj/Interpreter.java
Normal file
@@ -0,0 +1,517 @@
|
||||
// MicroJava Virtual Machine
|
||||
// -------------------------
|
||||
// Syntax: java ssw.mj.Run fileName [-debug]
|
||||
// ===========================================================================
|
||||
// by Hanspeter Moessenboeck, 2002-10-28
|
||||
// edited by Albrecht Woess, 2002-10-30
|
||||
package ssw.mj;
|
||||
|
||||
import ssw.mj.impl.Code;
|
||||
import ssw.mj.impl.Code.OpCode;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Interpreter {
|
||||
|
||||
private final boolean debug; // debug output on or off
|
||||
private final byte[] code; // code array
|
||||
private final int[] data; // global data
|
||||
private final int[] heap; // dynamic heap
|
||||
private final int[] stack; // expression stack
|
||||
private final int[] local; // method stack
|
||||
private final int startPC; // address of main() method
|
||||
private int pc; // program counter
|
||||
private int fp, sp; // frame pointer, stack pointer on method stack
|
||||
private int esp; // expression stack pointer
|
||||
private int free; // next free heap address
|
||||
private static final int heapSize = 100000, // size of the heap in words
|
||||
mStackSize = 4000, // size of the method stack in words
|
||||
eStackSize = 30; // size of the expression stack in words
|
||||
|
||||
private void write(String s, int len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
io.write(' ');
|
||||
}
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
io.write(s.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static class BufferIO implements IO {
|
||||
|
||||
private final StringBuffer output;
|
||||
private final String input;
|
||||
|
||||
private int inputPos;
|
||||
|
||||
public BufferIO(String input) {
|
||||
output = new StringBuffer();
|
||||
this.input = input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char read() {
|
||||
if (inputPos >= input.length()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return input.charAt(inputPos++);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char c) {
|
||||
output.append(c);
|
||||
}
|
||||
|
||||
public String getOutput() {
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static final IO ConsoleIO = new IO() {
|
||||
|
||||
@Override
|
||||
public char read() {
|
||||
try {
|
||||
int i = System.in.read();
|
||||
if (i == -1) {
|
||||
return 0;
|
||||
}
|
||||
return (char) i;
|
||||
} catch (IOException ex) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char c) {
|
||||
System.out.print(c);
|
||||
}
|
||||
};
|
||||
|
||||
public interface IO {
|
||||
char read();
|
||||
|
||||
void write(char c);
|
||||
}
|
||||
|
||||
private final IO io;
|
||||
|
||||
public Interpreter(byte[] code, int startPC, int dataSize, IO io, boolean debug) {
|
||||
this.code = code;
|
||||
this.startPC = startPC;
|
||||
this.io = io;
|
||||
this.debug = debug;
|
||||
heap = new int[heapSize]; // fixed sized heap
|
||||
data = new int[dataSize]; // global data as specified in
|
||||
// classfile
|
||||
stack = new int[eStackSize]; // expression stack
|
||||
local = new int[mStackSize]; // method stack
|
||||
fp = 0;
|
||||
sp = 0;
|
||||
esp = 0;
|
||||
free = 1; // no block should start at address 0
|
||||
}
|
||||
|
||||
// ----- expression stack
|
||||
private void push(int val) throws IllegalStateException {
|
||||
if (esp == eStackSize) {
|
||||
throw new IllegalStateException("expression stack overflow");
|
||||
}
|
||||
stack[esp++] = val;
|
||||
}
|
||||
|
||||
private int pop() throws IllegalStateException {
|
||||
if (esp == 0) {
|
||||
throw new IllegalStateException("expression stack underflow");
|
||||
}
|
||||
return stack[--esp];
|
||||
}
|
||||
|
||||
// ----- method stack
|
||||
private void PUSH(int val) throws IllegalStateException {
|
||||
if (sp == mStackSize) {
|
||||
throw new IllegalStateException("method stack overflow");
|
||||
}
|
||||
local[sp++] = val;
|
||||
}
|
||||
|
||||
private int POP() throws IllegalStateException {
|
||||
if (sp == 0) {
|
||||
throw new IllegalStateException("method stack underflow");
|
||||
}
|
||||
return local[--sp];
|
||||
}
|
||||
|
||||
// ----- instruction fetch
|
||||
private byte next(boolean dbgPrint) {
|
||||
byte b = code[pc++];
|
||||
if (debug && dbgPrint) {
|
||||
System.out.print(b + " ");
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
private short next2(boolean dbgPrint) {
|
||||
short s = (short) (((next(false) << 8)
|
||||
+ (next(false) & 0xff)) << 16 >> 16);
|
||||
if (debug && dbgPrint) {
|
||||
System.out.print(s + " ");
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private int next4() {
|
||||
return next4(true);
|
||||
}
|
||||
|
||||
private int next4(boolean dbgPrint) {
|
||||
int n = (next2(false) << 16) + (next2(false) & 0xffff);
|
||||
if (debug && dbgPrint) {
|
||||
System.out.print(n + " ");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate heap block of size bytes
|
||||
*/
|
||||
private int alloc(int size) throws IllegalStateException {
|
||||
int adr = free;
|
||||
free += ((size + 3) >> 2); // skip to next free adr
|
||||
// (>> 2 to convert byte to word)
|
||||
if (free > heapSize) {
|
||||
throw new IllegalStateException("heap overflow");
|
||||
}
|
||||
return adr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve byte n from val. Byte 0 is MSB
|
||||
*/
|
||||
private static byte getByte(int val, int n) {
|
||||
return (byte) (val << (8 * n) >>> 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace byte n in val by b
|
||||
*/
|
||||
private static int setByte(int val, int n, byte b) {
|
||||
int delta = (3 - n) * 8;
|
||||
int mask = ~(255 << delta); // mask all 1 except on chosen byte
|
||||
int by = (b & 255) << delta;
|
||||
return (val & mask) ^ by;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read int from standard input stream
|
||||
*/
|
||||
private int readInt() {
|
||||
int val = 0;
|
||||
int prev = ' ';
|
||||
int b = io.read();
|
||||
while (b < '0' || b > '9') {
|
||||
prev = b;
|
||||
b = io.read();
|
||||
}
|
||||
while (b >= '0' && b <= '9') {
|
||||
val = 10 * val + b - '0';
|
||||
b = io.read();
|
||||
}
|
||||
if (prev == '-') {
|
||||
val = -val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
private void printInstr() {
|
||||
int op = code[pc - 1];
|
||||
OpCode opCode = Code.OpCode.get(op);
|
||||
String instr = (opCode != null) ? opCode.cleanName() : "???";
|
||||
System.out.printf("%5d: %s ", pc - 1, instr);
|
||||
}
|
||||
|
||||
private void printStack() {
|
||||
for (int i = 0; i < esp; i++) {
|
||||
System.out.print(stack[i] + " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// ----- actual interpretation
|
||||
public void run() throws IllegalStateException {
|
||||
Code.OpCode op;
|
||||
int adr, val, val2, off, idx, len, i;
|
||||
pc = startPC;
|
||||
|
||||
if (debug) { // header for debug output
|
||||
System.out.println();
|
||||
System.out.println(" pos: instruction operands");
|
||||
System.out.println(" | expressionstack");
|
||||
System.out.println("-----------------------------");
|
||||
}
|
||||
|
||||
for (; ; ) { // terminated by return instruction
|
||||
op = Code.OpCode.get(next(false));
|
||||
if (debug) {
|
||||
printInstr();
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
// load/store local variables
|
||||
case load -> push(local[fp + next(true)]);
|
||||
case load_0, load_1, load_2, load_3 -> push(local[fp + op.code() - OpCode.load_0.code()]); // mapping
|
||||
|
||||
// on
|
||||
// range
|
||||
// 0..3
|
||||
case store -> local[fp + next(true)] = pop();
|
||||
case store_0, store_1, store_2, store_3 -> local[fp + op.code() - OpCode.store_0.code()] = pop(); // mapping
|
||||
|
||||
// on
|
||||
// range
|
||||
// 0..3
|
||||
// load/store global variables
|
||||
case getstatic -> push(data[next2(true)]);
|
||||
case putstatic -> data[next2(true)] = pop();
|
||||
|
||||
|
||||
// load/store object fields
|
||||
case getfield -> {
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
push(heap[adr + next2(true)]);
|
||||
}
|
||||
case putfield -> {
|
||||
val = pop();
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
heap[adr + next2(true)] = val;
|
||||
}
|
||||
|
||||
// load constants
|
||||
case const_0, const_1, const_2, const_3, const_4, const_5 ->
|
||||
push(op.code() - OpCode.const_0.code()); // map opcode to
|
||||
|
||||
// 0..5
|
||||
case const_m1 -> push(-1);
|
||||
case const_ -> push(next4());
|
||||
|
||||
|
||||
// arithmetic operations
|
||||
case add -> push(pop() + pop());
|
||||
case sub -> push(-pop() + pop());
|
||||
case mul -> push(pop() * pop());
|
||||
case div -> {
|
||||
val = pop();
|
||||
if (val == 0) {
|
||||
throw new IllegalStateException("division by zero");
|
||||
}
|
||||
push(pop() / val);
|
||||
}
|
||||
case rem -> {
|
||||
val = pop();
|
||||
if (val == 0) {
|
||||
throw new IllegalStateException("division by zero");
|
||||
}
|
||||
push(pop() % val);
|
||||
}
|
||||
case neg -> push(-pop());
|
||||
case shl -> {
|
||||
val = pop();
|
||||
push(pop() << val);
|
||||
}
|
||||
case shr -> {
|
||||
val = pop();
|
||||
push(pop() >> val);
|
||||
}
|
||||
case inc -> {
|
||||
off = fp + next(true);
|
||||
local[off] += next(true);
|
||||
}
|
||||
|
||||
// object creation
|
||||
case new_ -> push(alloc(next2(true) * 4));
|
||||
case newarray -> {
|
||||
val = next(true);
|
||||
len = pop();
|
||||
if (val == 0) {
|
||||
adr = alloc(len + 4);
|
||||
} else {
|
||||
adr = alloc(len * 4 + 4);
|
||||
}
|
||||
heap[adr] = len;
|
||||
push(adr + 1); // skip length field of array
|
||||
}
|
||||
|
||||
// array access
|
||||
case aload -> {
|
||||
idx = pop();
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
len = heap[adr - 1];
|
||||
if (idx < 0 || idx >= len) {
|
||||
throw new IllegalStateException("index out of bounds");
|
||||
}
|
||||
push(heap[adr + idx]);
|
||||
}
|
||||
case astore -> {
|
||||
val = pop();
|
||||
idx = pop();
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
len = heap[adr - 1];
|
||||
if (debug) {
|
||||
System.out.println("\nArraylength = " + len);
|
||||
System.out.println("Address = " + adr);
|
||||
System.out.println("Index = " + idx);
|
||||
System.out.println("Value = " + val);
|
||||
}
|
||||
if (idx < 0 || idx >= len) {
|
||||
throw new IllegalStateException("index out of bounds");
|
||||
}
|
||||
heap[adr + idx] = val;
|
||||
}
|
||||
case baload -> {
|
||||
idx = pop();
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
len = heap[adr - 1];
|
||||
if (idx < 0 || idx >= len) {
|
||||
throw new IllegalStateException("index out of bounds");
|
||||
}
|
||||
push(getByte(heap[adr + idx / 4], idx % 4));
|
||||
}
|
||||
case bastore -> {
|
||||
val = pop();
|
||||
idx = pop();
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
len = heap[adr - 1];
|
||||
if (idx < 0 || idx >= len) {
|
||||
throw new IllegalStateException("index out of bounds");
|
||||
}
|
||||
heap[adr + idx / 4] = setByte(heap[adr + idx / 4], idx % 4,
|
||||
(byte) val);
|
||||
}
|
||||
case arraylength -> {
|
||||
adr = pop();
|
||||
if (adr == 0) {
|
||||
throw new IllegalStateException("null reference used");
|
||||
}
|
||||
push(heap[adr - 1]);
|
||||
}
|
||||
|
||||
// stack manipulation
|
||||
case pop -> pop();
|
||||
case dup -> {
|
||||
val = pop();
|
||||
push(val);
|
||||
push(val);
|
||||
}
|
||||
case dup2 -> {
|
||||
val = pop();
|
||||
val2 = pop();
|
||||
push(val2);
|
||||
push(val);
|
||||
push(val2);
|
||||
push(val);
|
||||
}
|
||||
|
||||
// jumps
|
||||
case jmp -> {
|
||||
off = next2(true);
|
||||
pc += off - 3;
|
||||
}
|
||||
case jeq, jne, jlt, jle, jgt, jge -> {
|
||||
off = next2(true);
|
||||
val2 = pop();
|
||||
val = pop();
|
||||
boolean cond = false;
|
||||
switch (op) {
|
||||
case jeq -> cond = val == val2;
|
||||
case jne -> cond = val != val2;
|
||||
case jlt -> cond = val < val2;
|
||||
case jle -> cond = val <= val2;
|
||||
case jgt -> cond = val > val2;
|
||||
case jge -> cond = val >= val2;
|
||||
default -> {
|
||||
assert false;
|
||||
}
|
||||
}
|
||||
if (cond) {
|
||||
pc += off - 3;
|
||||
}
|
||||
}
|
||||
|
||||
// method calls
|
||||
case call -> {
|
||||
off = next2(true);
|
||||
PUSH(pc);
|
||||
pc += off - 3;
|
||||
}
|
||||
case return_ -> {
|
||||
if (sp == 0) {
|
||||
return;
|
||||
}
|
||||
pc = POP();
|
||||
}
|
||||
case enter -> {
|
||||
int psize = next(true);
|
||||
int lsize = next(true);
|
||||
PUSH(fp);
|
||||
fp = sp;
|
||||
for (i = 0; i < lsize; i++) {
|
||||
PUSH(0);
|
||||
}
|
||||
assert sp == (fp + lsize);
|
||||
for (i = psize - 1; i >= 0; i--) {
|
||||
local[fp + i] = pop();
|
||||
}
|
||||
}
|
||||
case exit -> {
|
||||
sp = fp;
|
||||
fp = POP();
|
||||
}
|
||||
|
||||
// I/O
|
||||
case read -> push(readInt());
|
||||
case print -> {
|
||||
len = pop();
|
||||
val = pop();
|
||||
String s = String.valueOf(val);
|
||||
len = len - s.length();
|
||||
write(s, len);
|
||||
}
|
||||
case bread -> push(io.read());
|
||||
case bprint -> {
|
||||
len = pop() - 1;
|
||||
val = pop();
|
||||
write(Character.toString((char) val), len);
|
||||
}
|
||||
case nop -> {
|
||||
}
|
||||
// nothing to do
|
||||
case trap -> throw new IllegalStateException("trap(" + next(true) + ")");
|
||||
default -> throw new IllegalStateException("wrong opcode " + op);
|
||||
}
|
||||
if (debug) {
|
||||
System.out.println();
|
||||
System.out.print(" | ");
|
||||
printStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
MicroJava Compiler/src/ssw/mj/Recorder.java
Normal file
158
MicroJava Compiler/src/ssw/mj/Recorder.java
Normal file
@@ -0,0 +1,158 @@
|
||||
package ssw.mj;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import ssw.mj.scanner.Token;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Recorder {
|
||||
/**
|
||||
* Result class for Gson serialization
|
||||
*/
|
||||
public static class RecorderTraceEntry {
|
||||
public final String in;
|
||||
public final String name;
|
||||
public final Map<String, String> params;
|
||||
public final TraceEntryType type;
|
||||
public final TraceEntryOperation operation;
|
||||
public final Token laToken;
|
||||
public final Token token;
|
||||
public final transient RecorderTraceEntry parent; // transient <=> do not serialize
|
||||
|
||||
public RecorderTraceEntry(TraceEntryType type, TraceEntryOperation operation, String in, String name, Map<String, String> params, Token token, Token laToken, RecorderTraceEntry parent) {
|
||||
this.type = type;
|
||||
this.operation = operation;
|
||||
this.in = in;
|
||||
this.name = name;
|
||||
this.params = params;
|
||||
this.token = token;
|
||||
this.laToken = laToken;
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
public static final Gson gson = new Gson();
|
||||
|
||||
// Type of operation executed
|
||||
public enum TraceEntryOperation {
|
||||
enter, exit
|
||||
}
|
||||
|
||||
// Type of trace
|
||||
public enum TraceEntryType {
|
||||
check, error, scan, custom, recover
|
||||
}
|
||||
|
||||
public Recorder() {
|
||||
this.trace = new ArrayList<>();
|
||||
}
|
||||
|
||||
private final List<RecorderTraceEntry> trace;
|
||||
private RecorderTraceEntry currentEntry = null;
|
||||
|
||||
private final Map<String, Integer> idIndex = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Record a check operation
|
||||
*
|
||||
* @param laToken current token
|
||||
* @param expected expected token kind
|
||||
*/
|
||||
public void checkEnter(Token token, Token laToken, Token.Kind expected) {
|
||||
enter(TraceEntryType.check, "check", token, laToken, Map.of("expected", expected.toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a scan operation
|
||||
*
|
||||
* @param laToken current token
|
||||
*/
|
||||
public void scanEnter(Token token, Token laToken) {
|
||||
enter(TraceEntryType.scan, "scan", token, laToken, Map.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an error operation
|
||||
*
|
||||
* @param laToken current token
|
||||
* @param error error message
|
||||
* @param msgParams parameters for error message
|
||||
*/
|
||||
public void errorEnter(Token token, Token laToken, Errors.Message error, Object[] msgParams) {
|
||||
enter(TraceEntryType.error, "error", token, laToken, Map.of("type", error.toString(), "message", msgParams.length == 0 ? error.format() : error.format(msgParams)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Record a recover operation, i.e., a method repeatedly scanning to recover from an error
|
||||
*
|
||||
* @param laToken current token
|
||||
* @param name name of function
|
||||
*/
|
||||
public void recoverEnter(Token token, Token laToken, String name) {
|
||||
enter(TraceEntryType.recover, name, token, laToken, Map.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an enter operation for a custom function, i.e., a grammar rule
|
||||
*
|
||||
* @param laToken current token
|
||||
* @param name name of function
|
||||
*/
|
||||
public void customEnter(Token token, Token laToken, String name) {
|
||||
enter(TraceEntryType.custom, name, token, laToken, Map.of());
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an enter trace for an arbitrary operation
|
||||
*
|
||||
* @param type type of operation
|
||||
* @param name name of function
|
||||
* @param laToken current token
|
||||
* @param params parameters of function
|
||||
*/
|
||||
private void enter(TraceEntryType type, String name, Token token, Token laToken, Map<String, String> params) {
|
||||
String in = currentEntry == null ? null : currentEntry.name;
|
||||
|
||||
currentEntry = new RecorderTraceEntry(type, TraceEntryOperation.enter, in, getName(name), params, token, laToken, currentEntry);
|
||||
|
||||
trace.add(currentEntry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record an exit trace for the current operation
|
||||
*
|
||||
* @param laToken current token
|
||||
*/
|
||||
public void exit(Token token, Token laToken) {
|
||||
if (currentEntry == null) {
|
||||
throw new IllegalStateException("Cannot exit trace, no trace is active");
|
||||
}
|
||||
|
||||
trace.add(new RecorderTraceEntry(currentEntry.type, TraceEntryOperation.exit, currentEntry.in, currentEntry.name, currentEntry.params, token, laToken, null));
|
||||
|
||||
currentEntry = currentEntry.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the recorder
|
||||
*/
|
||||
public void reset() {
|
||||
trace.clear();
|
||||
currentEntry = null;
|
||||
idIndex.clear();
|
||||
}
|
||||
|
||||
public List<RecorderTraceEntry> getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
private String getName(String name) {
|
||||
if (idIndex.containsKey(name)) {
|
||||
idIndex.put(name, idIndex.get(name) + 1);
|
||||
} else {
|
||||
idIndex.put(name, 1);
|
||||
}
|
||||
|
||||
return name + "$" + idIndex.get(name);
|
||||
}
|
||||
}
|
||||
84
MicroJava Compiler/src/ssw/mj/Run.java
Normal file
84
MicroJava Compiler/src/ssw/mj/Run.java
Normal file
@@ -0,0 +1,84 @@
|
||||
// MicroJava Virtual Machine
|
||||
// -------------------------
|
||||
// Syntax: java ssw.mj.Run fileName [-debug]
|
||||
// ===========================================================================
|
||||
// by Hanspeter Moessenboeck, 2002-10-28
|
||||
// edited by Albrecht Woess, 2002-10-30
|
||||
package ssw.mj;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class Run {
|
||||
|
||||
// ----- VM internals
|
||||
static Interpreter load(String name, boolean debug) throws IOException {
|
||||
int codeSize;
|
||||
byte[] sig = new byte[2];
|
||||
DataInputStream in = new DataInputStream(new FileInputStream(name));
|
||||
in.read(sig, 0, 2);
|
||||
if (sig[0] != 'M' || sig[1] != 'J') {
|
||||
in.close();
|
||||
throw new FormatException("wrong marker");
|
||||
}
|
||||
codeSize = in.readInt();
|
||||
if (codeSize <= 0) {
|
||||
in.close();
|
||||
throw new FormatException("codeSize <= 0");
|
||||
}
|
||||
int dataSize = in.readInt();
|
||||
if (dataSize < 0) {
|
||||
in.close();
|
||||
throw new FormatException("dataSize < 0");
|
||||
}
|
||||
int startPC = in.readInt();
|
||||
if (startPC < 0 || startPC >= codeSize) {
|
||||
in.close();
|
||||
throw new FormatException("startPC not in code area");
|
||||
}
|
||||
byte[] code = new byte[codeSize];
|
||||
in.read(code, 0, codeSize);
|
||||
in.close();
|
||||
|
||||
return new Interpreter(code, startPC, dataSize, Interpreter.ConsoleIO, debug);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String fileName = null;
|
||||
boolean debug = false;
|
||||
for (String arg : args) {
|
||||
if (arg.equals("-debug")) {
|
||||
debug = true;
|
||||
} else {
|
||||
fileName = arg;
|
||||
}
|
||||
}
|
||||
if (fileName == null) {
|
||||
System.out.println("Syntax: java ssw.mj.Run filename [-debug]");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Interpreter r = load(fileName, debug);
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
r.run();
|
||||
|
||||
System.out.print("\nCompletion took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
} catch (FileNotFoundException e) {
|
||||
System.out.println("-- file " + fileName + " not found");
|
||||
} catch (FormatException e) {
|
||||
System.out.println("-- corrupted object file " + fileName + ": " + e.getMessage());
|
||||
} catch (IOException e) {
|
||||
System.out.println("-- error reading file " + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FormatException extends IOException {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
FormatException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
173
MicroJava Compiler/src/ssw/mj/TracingClassLoader.java
Normal file
173
MicroJava Compiler/src/ssw/mj/TracingClassLoader.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package ssw.mj;
|
||||
|
||||
import javassist.*;
|
||||
import javassist.expr.ExprEditor;
|
||||
import javassist.expr.MethodCall;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
|
||||
public class TracingClassLoader extends ClassLoader {
|
||||
private ClassPool pool;
|
||||
|
||||
public TracingClassLoader(ClassLoader parent) {
|
||||
super(parent);
|
||||
|
||||
// The class pool is used by Javassist to hold the classes that are being manipulated
|
||||
// We create it once and it stores the CtClass objects of all loaded classes
|
||||
pool = ClassPool.getDefault();
|
||||
// We need to add the class path to the ClassPool
|
||||
// otherwise, Javassist will not be able to find classes on the class path
|
||||
addClassPathToPool();
|
||||
}
|
||||
|
||||
private void addClassPathToPool() {
|
||||
// Hopefully, this is the correct way to get the class path
|
||||
String classpath = System.getProperty("java.class.path");
|
||||
String[] classpathEntries = classpath.split(File.pathSeparator);
|
||||
for (String cp : classpathEntries) {
|
||||
try {
|
||||
pool.appendClassPath(cp);
|
||||
} catch (NotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
We do not rely on the parent class loader to load the class. Instead, we use Javassist to
|
||||
manipulate the class as needed and then define the class using the byte code generated by Javassist.
|
||||
|
||||
If we followed the default delegation model for loading classes, we would override the findClass method.
|
||||
But this method would not be called if the parent class loader can find the class. Therefore, we override
|
||||
the loadClass method instead.
|
||||
*/
|
||||
@Override
|
||||
public Class<?> loadClass(String name) throws ClassNotFoundException {
|
||||
// Check if the class has already been loaded by this class loader
|
||||
Class<?> foundClazz = findLoadedClass(name);
|
||||
if (foundClazz != null) {
|
||||
// otherwise we get java.lang.LinkageError: loader ssw.mj.TracingClassLoader @<hashcode> attempted duplicate class definition for <classname>
|
||||
return foundClazz;
|
||||
}
|
||||
/* if (name.startsWith("java.")
|
||||
|| name.startsWith("javax.")
|
||||
|| name.startsWith("jdk.internal.")
|
||||
|| name.startsWith("sun.")
|
||||
|| name.startsWith("com.sun.")
|
||||
|| name.startsWith("org.w3c.")
|
||||
|| name.startsWith("org.xml.")
|
||||
) { */
|
||||
if (!name.startsWith("ssw")) {
|
||||
// class library classes should be loaded by the bootstrap class loader
|
||||
// there are three class loaders: bootstrap, extension, and system class loader
|
||||
// - the bootstrap class loader is implemented in native code and is responsible for loading the core Java classes
|
||||
// - the extension class loader (also called platform class loader) is implemented in Java and is responsible for loading the classes in the extension directories (i.e., jre/lib/ext)
|
||||
// - the system class loader (also sometimes called the app class loader) is implemented in Java and is responsible for loading the classes in the class path
|
||||
// --- The TracingClassLoader is meant to be used as the system class loader
|
||||
return super.loadClass(name);
|
||||
}
|
||||
CtClass cc = null;
|
||||
try {
|
||||
cc = pool.get(name);
|
||||
if (!cc.isFrozen() && cc.getName().equals("ssw.mj.impl.Parser")) {
|
||||
cc.addField(CtField.make("public static ssw.mj.Recorder __recorder__ = new ssw.mj.Recorder();", cc));
|
||||
|
||||
// Difference between getMethods and getDeclaredMethods: https://stackoverflow.com/a/73069812/2938364
|
||||
CtMethod[] methods = cc.getDeclaredMethods();
|
||||
for (CtMethod method : methods) {
|
||||
// System.out.println("Instrumenting: " + method.getName());
|
||||
|
||||
// TODO: Check if EBNF contains rule for method.getName() (ignoreCase)
|
||||
// e.g., we want to instrument program or ProGram if EBNF has a production for Program = ... .
|
||||
// We could use an array/set with all production names for this.
|
||||
if (Character.isUpperCase(method.getName().charAt(0))) {
|
||||
method.insertBefore("__recorder__.customEnter(t, la, \"%s\");".formatted(method.getName()));
|
||||
method.insertAfter("__recorder__.exit(t, la);");
|
||||
} else if (method.getName().equals("parse")) {
|
||||
// We need to reset the recorder once we perform a new parse
|
||||
// since it's declared as a static field in the parser
|
||||
method.insertBefore("__recorder__.reset();");
|
||||
} else if (method.getName().equals("check")) {
|
||||
method.insertBefore("__recorder__.checkEnter(t, la, $1);");
|
||||
method.insertAfter("__recorder__.exit(t, la);");
|
||||
} else if (method.getName().equals("scan")) {
|
||||
method.insertBefore("__recorder__.scanEnter(t, la);");
|
||||
method.insertAfter("__recorder__.exit(t, la);");
|
||||
} else if (method.getName().equals("error")) {
|
||||
method.instrument(new ExprEditor() {
|
||||
@Override
|
||||
public void edit(MethodCall m) throws CannotCompileException {
|
||||
if (m.getClassName().equals("ssw.mj.Errors") && m.getMethodName().equals("error")) {
|
||||
// We record enter and exit directly one after another, but we do not need more details than that
|
||||
method.insertAt(m.getLineNumber(), "__recorder__.errorEnter(t, la, $1, $2); __recorder__.exit(t, la);");
|
||||
}
|
||||
super.edit(m);
|
||||
}
|
||||
});
|
||||
} else if (method.getName().startsWith("recover")) {
|
||||
method.insertBefore("__recorder__.recoverEnter(t, la, \"%s\");".formatted(method.getName()));
|
||||
method.insertAfter("__recorder__.exit(t, la);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cc.debugWriteFile();
|
||||
|
||||
byte[] byteCode = cc.toBytecode();
|
||||
Class<?> newClazz = defineClass(name, byteCode, 0, byteCode.length);
|
||||
return newClazz;
|
||||
} catch (NotFoundException | CannotCompileException | IOException e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Could not load " + name + "\n" + e);
|
||||
throw new ClassNotFoundException("Could not load " + name, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return super.findClass(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* See ClassLoaders.AppClassLoader
|
||||
* Called by the VM to support dynamic additions to the class path
|
||||
*
|
||||
* @see java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch
|
||||
*/
|
||||
void appendToClassPathForInstrumentation(String path) {
|
||||
try {
|
||||
System.out.println("appendToClassPathForInstrumentation:" + path);
|
||||
pool.appendClassPath(path);
|
||||
} catch (NotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name) {
|
||||
if (name.contains("JUnitStarter")) {
|
||||
System.out.println("JUnitStarter");
|
||||
}
|
||||
return super.getResource(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<URL> getResources(String name) throws IOException {
|
||||
if (name.contains("JUnitStarter")) {
|
||||
System.out.println("JUnitStarter");
|
||||
}
|
||||
return super.getResources(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
if (name.contains("JUnitStarter")) {
|
||||
System.out.println("JUnitStarter");
|
||||
}
|
||||
return super.getResourceAsStream(name);
|
||||
}
|
||||
}
|
||||
218
MicroJava Compiler/src/ssw/mj/Visualizer.java
Normal file
218
MicroJava Compiler/src/ssw/mj/Visualizer.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package ssw.mj;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import ssw.mj.scanner.Token;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class Visualizer {
|
||||
// Config Flags
|
||||
private static final boolean debug = false;
|
||||
private static final String visualizerUrl = "https://ssw.jku.at/General/Staff/Weninger/Teaching/CB/Visualization/v1_0";
|
||||
// private static final String visualizerUrl = "http://localhost:5173/General/Staff/Weninger/Teaching/CB/Visualization/v1_0";
|
||||
|
||||
public static final Gson gson = debug ? new GsonBuilder().setPrettyPrinting().create() : new Gson();
|
||||
|
||||
// Remember when the browser was last opened, to ensure that we
|
||||
// don't open a huge number of tabs
|
||||
private static LocalDateTime browserLastOpened;
|
||||
|
||||
public static class VisualizerException extends RuntimeException {
|
||||
public VisualizerException(String message) {
|
||||
super("[Recorder]: " + message);
|
||||
}
|
||||
}
|
||||
|
||||
private interface WithDescription {
|
||||
String getDescription();
|
||||
}
|
||||
|
||||
public enum VisualizationType implements WithDescription {
|
||||
SCANNER_V1 {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Scanner Visualization";
|
||||
}
|
||||
},
|
||||
PARSER_V1 {
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Parser Visualization";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record ScannerPayload(List<Token> receivedTokens, List<Token> expectedTokens) {
|
||||
public String toJson() {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
private record ParserPayload(List<Recorder.RecorderTraceEntry> traces) {
|
||||
public String toJson() {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms scanner tokens to our visualizer format and ingests them to the desired method
|
||||
*
|
||||
* @param receivedTokens The tokens received from the student's scanner
|
||||
* @param expectedTokens The tokens expected by the test case
|
||||
*/
|
||||
public static void createScannerVisualization(String sourcecode,
|
||||
List<Token> receivedTokens,
|
||||
List<Token> expectedTokens,
|
||||
boolean openInBrowserAfterwards) {
|
||||
String payload = new ScannerPayload(receivedTokens, expectedTokens).toJson();
|
||||
VisualizerIngestData visData = new VisualizerIngestData(VisualizationType.SCANNER_V1, sourcecode, payload);
|
||||
|
||||
Path path = ingestFile(visData);
|
||||
if (openInBrowserAfterwards) {
|
||||
openBrowser(path.toUri().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secret recorder from the parser and ingest the tokens to the visualizer
|
||||
*/
|
||||
public static void createParserVisualization(String sourcecode, boolean openInBrowserAfterwards) {
|
||||
Recorder recorder = getRecorderFromParserUsingReflection();
|
||||
|
||||
// If we don't have a recorder, we can't create a visualization
|
||||
if (recorder == null) return;
|
||||
|
||||
String payload = new ParserPayload(recorder.getTrace()).toJson();
|
||||
|
||||
VisualizerIngestData visData = new VisualizerIngestData(VisualizationType.PARSER_V1, sourcecode, payload);
|
||||
|
||||
Path path = ingestFile(visData);
|
||||
if (openInBrowserAfterwards) {
|
||||
openBrowser(path.toUri().toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Data to be sent to the ingest endpoint
|
||||
private record VisualizerIngestData(VisualizationType type, String sourcecode, String payload) {
|
||||
public String toJson() {
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a visualization file
|
||||
*
|
||||
* @param data The visualization data
|
||||
* @return The path at which the visualization file has been created
|
||||
*/
|
||||
private static Path ingestFile(VisualizerIngestData data) {
|
||||
String html = "<!DOCTYPE html>\n" +
|
||||
"<html lang=\"en\">\n" +
|
||||
" <head>\n" +
|
||||
" <meta charset=\"UTF-8\">\n" +
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n" +
|
||||
" <title>Compiler Visualization</title>\n" +
|
||||
" </head>\n" +
|
||||
" <body>\n" +
|
||||
" <div id=\"app\"></div>\n" +
|
||||
"\n" +
|
||||
" <script id=\"__DATA__\" type=\"application/json\">\n" +
|
||||
" " + (data.toJson()) + "\n" + // Inject the visualization data
|
||||
" </script>\n" +
|
||||
"\n" +
|
||||
" <script type=\"text/javascript\">\n" +
|
||||
" let data = JSON.parse(document.getElementById('__DATA__').textContent);\n" +
|
||||
" let app = document.getElementById('app');\n" +
|
||||
"\n" +
|
||||
" let iframe = document.createElement('iframe');\n" +
|
||||
" iframe.src = '" + visualizerUrl + "';\n" +
|
||||
" iframe.style.position = 'fixed';\n" +
|
||||
" iframe.style.top = 0;\n" +
|
||||
" iframe.style.left = 0;\n" +
|
||||
" iframe.style.right = 0;\n" +
|
||||
" iframe.style.bottom = 0;\n" +
|
||||
" iframe.style.border = 0;\n" +
|
||||
" iframe.style.width = '100%';\n" +
|
||||
" iframe.style.height = '100%';\n" +
|
||||
"\n" +
|
||||
" app.appendChild(iframe);\n" +
|
||||
" \n" +
|
||||
" iframe.addEventListener('load', () => {\n" +
|
||||
" setTimeout(() => {\n" +
|
||||
" iframe.contentWindow.postMessage({\n" +
|
||||
" type: 'boot',\n" +
|
||||
" data\n" +
|
||||
" }, '*');\n" +
|
||||
" }, 100);\n" +
|
||||
" });\n" +
|
||||
" </script>\n" +
|
||||
" </body>\n" +
|
||||
"</html>";
|
||||
|
||||
try {
|
||||
// Get a temporary file to write the visualization to
|
||||
Path path = Files.createTempFile("microjava-visualization", ".html");
|
||||
Files.writeString(path, html);
|
||||
|
||||
System.out.println(data.type.getDescription() + " created successfully. View it at: " + path.toUri().toString());
|
||||
return path;
|
||||
} catch (IOException e) {
|
||||
throw new VisualizerException("Failed to create visualization: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to open the browser if possible and not rate limited
|
||||
*
|
||||
* @param url The url to open
|
||||
*/
|
||||
private static void openBrowser(String url) {
|
||||
if (browserLastOpened != null && browserLastOpened.plusSeconds(5).isAfter(LocalDateTime.now())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(new URI(url));
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use reflection to get the secret recorder from the parser
|
||||
*
|
||||
* @return The recorder
|
||||
*/
|
||||
private static Recorder getRecorderFromParserUsingReflection() {
|
||||
try {
|
||||
// Get the parser class
|
||||
Class<?> parserClass = Class.forName("ssw.mj.impl.Parser");
|
||||
|
||||
// __recorder__ is a static field that has secretly been added in the Parser class by the TracingClassLoader
|
||||
// during a run of the application / the unit tests with the CLI setting
|
||||
// -Djava.system.class.loader=ssw.mj.TracingClassLoader
|
||||
Field recorderField = parserClass.getField("__recorder__");
|
||||
|
||||
// Get the value of the field
|
||||
Recorder recorder = (Recorder) recorderField.get(null);
|
||||
|
||||
return recorder;
|
||||
} catch (Exception e) {
|
||||
// throw new VisualizerException("Failed to get recorder from parser: " + e.getMessage());
|
||||
|
||||
// We don't want to throw an exception here, since we might not have a recorder
|
||||
// when the custom class loader is not used
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
MicroJava Compiler/src/ssw/mj/codegen/Decoder.java
Normal file
97
MicroJava Compiler/src/ssw/mj/codegen/Decoder.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package ssw.mj.codegen;
|
||||
|
||||
import ssw.mj.impl.Code;
|
||||
import ssw.mj.impl.Code.OpCode;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Decoder {
|
||||
private byte[] codeBuf; // code buffer
|
||||
private int cur; // address of next byte to decode
|
||||
private int adr; // address of currently decoded instruction
|
||||
|
||||
private int getAndMove() {
|
||||
return codeBuf[cur++];
|
||||
}
|
||||
|
||||
private int getAndMove2() {
|
||||
return (getAndMove() << 8) + (getAndMove() & 0xFF);
|
||||
}
|
||||
|
||||
private int getAndMove4() {
|
||||
return (getAndMove2() << 16) + (getAndMove2() & 0xFFFF);
|
||||
}
|
||||
|
||||
private String jumpDist() {
|
||||
int dist = getAndMove2();
|
||||
int pos = adr + dist;
|
||||
return dist + " (=" + pos + ")";
|
||||
}
|
||||
|
||||
public String decode(Code code) {
|
||||
return decode(code.buf, 0, code.pc);
|
||||
}
|
||||
|
||||
public String decode(byte[] buf, int off, int len) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
codeBuf = buf;
|
||||
cur = off;
|
||||
adr = cur;
|
||||
while (cur < len) {
|
||||
sb.append(adr);
|
||||
sb.append(": ");
|
||||
sb.append(decode(OpCode.get(getAndMove())));
|
||||
sb.append("\n");
|
||||
adr = cur;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String decode(OpCode opCode) {
|
||||
if (opCode == null) {
|
||||
return "--error, unknown opcode--";
|
||||
}
|
||||
|
||||
return switch (opCode) {
|
||||
// Operations without parameters in the code buffer
|
||||
case load_0, load_1, load_2, load_3, store_0, store_1, store_2, store_3, const_0, const_1, const_2, const_3, const_4, const_5, const_m1, add, sub, mul, div, rem, neg, shl, shr, aload, astore, baload, bastore, arraylength, pop, dup, dup2, exit, return_, read, print, bread, bprint ->
|
||||
opCode.cleanName();
|
||||
// Operations with one 1 byte parameter in the code buffer
|
||||
case load, store, newarray, trap -> opCode.cleanName() + " " + getAndMove();
|
||||
// Operations with one 2 byte parameter in the code buffer
|
||||
case getstatic, putstatic, getfield, putfield, new_ -> opCode.cleanName() + " " + getAndMove2();
|
||||
// Operations with one 4 byte parameter in the code buffer
|
||||
case const_ -> opCode.cleanName() + " " + getAndMove4();
|
||||
// Operations with two 1 byte parameters in the code buffer
|
||||
case inc, enter -> opCode.cleanName() + " " + getAndMove() + ", " + getAndMove();
|
||||
// Operations with a jump distance as a parameter in the code buffer
|
||||
case jmp, jeq, jne, jlt, jle, jgt, jge, call -> opCode.cleanName() + " " + jumpDist();
|
||||
default -> "--error--";
|
||||
};
|
||||
}
|
||||
|
||||
public void decodeFile(String filename) throws IOException {
|
||||
DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(filename)));
|
||||
byte[] sig = new byte[2];
|
||||
in.read(sig, 0, 2);
|
||||
System.out.println("" + (char) sig[0] + (char) sig[1]);
|
||||
int codeSize = in.readInt();
|
||||
System.out.println("codesize = " + codeSize);
|
||||
System.out.println("datasize = " + in.readInt());
|
||||
System.out.println("startPC = " + in.readInt());
|
||||
byte[] code = new byte[codeSize];
|
||||
in.read(code);
|
||||
System.out.println(decode(code, 0, codeSize));
|
||||
in.close();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
if (args.length > 0) {
|
||||
Decoder dec = new Decoder();
|
||||
dec.decodeFile(args[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
65
MicroJava Compiler/src/ssw/mj/codegen/Label.java
Normal file
65
MicroJava Compiler/src/ssw/mj/codegen/Label.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package ssw.mj.codegen;
|
||||
|
||||
import ssw.mj.impl.Code;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class Label {
|
||||
|
||||
/**
|
||||
* Jump destination address.
|
||||
*/
|
||||
private int adr;
|
||||
|
||||
/**
|
||||
* List of unresolved forward jumps
|
||||
*/
|
||||
private List<Integer> fixupList;
|
||||
|
||||
/**
|
||||
* The code buffer this Label belongs to.
|
||||
*/
|
||||
private final Code code;
|
||||
|
||||
public Label(Code code) {
|
||||
this.code = code;
|
||||
fixupList = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates code for a jump to this label.
|
||||
*/
|
||||
public void put() {
|
||||
if (isDefined()) {
|
||||
// jump destination already known
|
||||
code.put2(adr - (code.pc - 1));
|
||||
} else {
|
||||
// remember address to patch
|
||||
fixupList.add(code.pc);
|
||||
// insert place holder
|
||||
code.put2(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines <code>this</code> label to be at the current pc position
|
||||
*/
|
||||
public void here() {
|
||||
if (isDefined()) {
|
||||
// should never happen
|
||||
throw new IllegalStateException("label defined twice");
|
||||
}
|
||||
|
||||
for (int pos : fixupList) {
|
||||
code.put2(pos, code.pc - (pos - 1));
|
||||
}
|
||||
|
||||
fixupList = null;
|
||||
adr = code.pc;
|
||||
}
|
||||
|
||||
private boolean isDefined() {
|
||||
return fixupList == null;
|
||||
}
|
||||
}
|
||||
152
MicroJava Compiler/src/ssw/mj/codegen/Operand.java
Normal file
152
MicroJava Compiler/src/ssw/mj/codegen/Operand.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package ssw.mj.codegen;
|
||||
|
||||
import ssw.mj.impl.Code;
|
||||
import ssw.mj.impl.Code.CompOp;
|
||||
import ssw.mj.impl.Parser;
|
||||
import ssw.mj.impl.Tab;
|
||||
import ssw.mj.symtab.Obj;
|
||||
import ssw.mj.symtab.Struct;
|
||||
|
||||
import static ssw.mj.Errors.Message.ILLEGAL_OPERAND_KIND;
|
||||
|
||||
public class Operand {
|
||||
/**
|
||||
* Possible operands.
|
||||
*/
|
||||
public enum Kind {
|
||||
Con(true),
|
||||
Local(false),
|
||||
Static(false),
|
||||
Stack(true),
|
||||
Fld(false),
|
||||
Elem(false),
|
||||
Meth(true),
|
||||
Cond(true),
|
||||
None(true);
|
||||
|
||||
public final boolean isReadOnly;
|
||||
|
||||
Kind(boolean isReadOnly) {
|
||||
this.isReadOnly = isReadOnly;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind of the operand.
|
||||
*/
|
||||
public Kind kind;
|
||||
/**
|
||||
* The type of the operand (reference to symbol table).
|
||||
*/
|
||||
public Struct type;
|
||||
/**
|
||||
* Only for Con: Value of the constant.
|
||||
*/
|
||||
public int val;
|
||||
/**
|
||||
* Only for Local, Static, Fld, Meth: Offset of the element.
|
||||
*/
|
||||
public int adr;
|
||||
/**
|
||||
* Only for Cond: Relational operator.
|
||||
*/
|
||||
public CompOp op;
|
||||
/**
|
||||
* Only for Meth: Method object from the symbol table.
|
||||
*/
|
||||
public Obj obj;
|
||||
/**
|
||||
* Only for Cond: Target for true jumps.
|
||||
*/
|
||||
public Label tLabel;
|
||||
/**
|
||||
* Only for Cond: Target for false jumps.
|
||||
*/
|
||||
public Label fLabel;
|
||||
|
||||
/**
|
||||
* Constructor for named objects: constants, variables, methods
|
||||
*/
|
||||
public Operand(Obj o, Parser parser) {
|
||||
type = o.type;
|
||||
val = o.val;
|
||||
adr = o.adr;
|
||||
switch (o.kind) {
|
||||
case Con -> kind = Kind.Con;
|
||||
case Var -> {
|
||||
if (o.level == 0) {
|
||||
kind = Kind.Static;
|
||||
} else {
|
||||
kind = Kind.Local;
|
||||
}
|
||||
}
|
||||
case Meth -> {
|
||||
kind = Kind.Meth;
|
||||
obj = o;
|
||||
}
|
||||
default -> {
|
||||
kind = Kind.None;
|
||||
parser.error(ILLEGAL_OPERAND_KIND, o.kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for compare operations
|
||||
*/
|
||||
public Operand(CompOp op, Code code) {
|
||||
this(code);
|
||||
this.kind = Kind.Cond;
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
public Operand(Code code) {
|
||||
tLabel = new Label(code);
|
||||
fLabel = new Label(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for stack operands
|
||||
*/
|
||||
public Operand(Struct type) {
|
||||
this.kind = Kind.Stack;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for integer constants
|
||||
*/
|
||||
public Operand(int x) {
|
||||
kind = Kind.Con;
|
||||
type = Tab.intType;
|
||||
val = x;
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
return kind.isReadOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Op[");
|
||||
switch (kind) {
|
||||
case Con -> {
|
||||
sb.append(type).append(' ');
|
||||
sb.append(val);
|
||||
}
|
||||
case Local, Static, Fld -> {
|
||||
sb.append(kind).append(' ');
|
||||
sb.append(type).append(' ');
|
||||
sb.append(adr);
|
||||
}
|
||||
case Cond -> sb.append(op);
|
||||
case Meth -> sb.append(obj);
|
||||
case Elem, Stack -> {
|
||||
sb.append(kind).append(' ');
|
||||
sb.append(type);
|
||||
}
|
||||
}
|
||||
return sb.append(']').toString();
|
||||
}
|
||||
|
||||
}
|
||||
294
MicroJava Compiler/src/ssw/mj/impl/Code.java
Normal file
294
MicroJava Compiler/src/ssw/mj/impl/Code.java
Normal file
@@ -0,0 +1,294 @@
|
||||
package ssw.mj.impl;
|
||||
|
||||
import ssw.mj.codegen.Label;
|
||||
import ssw.mj.codegen.Operand;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class Code {
|
||||
|
||||
public enum OpCode {
|
||||
load,
|
||||
load_0,
|
||||
load_1,
|
||||
load_2,
|
||||
load_3,
|
||||
store,
|
||||
store_0,
|
||||
store_1,
|
||||
store_2,
|
||||
store_3,
|
||||
getstatic,
|
||||
putstatic,
|
||||
getfield,
|
||||
putfield,
|
||||
const_0,
|
||||
const_1,
|
||||
const_2,
|
||||
const_3,
|
||||
const_4,
|
||||
const_5,
|
||||
const_m1,
|
||||
const_,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
div,
|
||||
rem,
|
||||
neg,
|
||||
shl,
|
||||
shr,
|
||||
inc,
|
||||
new_,
|
||||
newarray,
|
||||
aload,
|
||||
astore,
|
||||
baload,
|
||||
bastore,
|
||||
arraylength,
|
||||
pop,
|
||||
dup,
|
||||
dup2,
|
||||
jmp,
|
||||
jeq,
|
||||
jne,
|
||||
jlt,
|
||||
jle,
|
||||
jgt,
|
||||
jge,
|
||||
call,
|
||||
return_,
|
||||
enter,
|
||||
exit,
|
||||
read,
|
||||
print,
|
||||
bread,
|
||||
bprint,
|
||||
trap,
|
||||
nop;
|
||||
|
||||
public int code() {
|
||||
return ordinal() + 1;
|
||||
}
|
||||
|
||||
public String cleanName() {
|
||||
String name = name();
|
||||
if (name.endsWith("_")) {
|
||||
name = name.substring(0, name.length() - 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static OpCode get(int code) {
|
||||
if (code < 1 || code > values().length) {
|
||||
return null;
|
||||
}
|
||||
return values()[code - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public enum CompOp {
|
||||
eq, ne, lt, le, gt, ge;
|
||||
|
||||
public static CompOp invert(CompOp op) {
|
||||
if (op == null) {
|
||||
throw new IllegalArgumentException("Compare operator must not be null!");
|
||||
}
|
||||
return switch (op) {
|
||||
case eq -> ne;
|
||||
case ne -> eq;
|
||||
case lt -> ge;
|
||||
case le -> gt;
|
||||
case gt -> le;
|
||||
case ge -> lt;
|
||||
default ->
|
||||
// Cannot happen, we covered all six compare operations as well as null parameter
|
||||
// This is purely to prevent the compiler from complaining about a missing return statement
|
||||
throw new IllegalArgumentException("Impossible compare operator");
|
||||
};
|
||||
}
|
||||
|
||||
public static OpCode toOpCode(CompOp op) {
|
||||
return switch (op) {
|
||||
case eq -> OpCode.jeq;
|
||||
case ge -> OpCode.jge;
|
||||
case gt -> OpCode.jgt;
|
||||
case le -> OpCode.jle;
|
||||
case lt -> OpCode.jlt;
|
||||
case ne -> OpCode.jne;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Code buffer
|
||||
*/
|
||||
public byte[] buf;
|
||||
|
||||
/**
|
||||
* Program counter. Indicates next free byte in code buffer.
|
||||
*/
|
||||
public int pc;
|
||||
|
||||
/**
|
||||
* PC of main method (set by parser).
|
||||
*/
|
||||
public int mainpc;
|
||||
|
||||
/**
|
||||
* Length of static data in words (set by parser).
|
||||
*/
|
||||
public int dataSize;
|
||||
|
||||
/**
|
||||
* According parser.
|
||||
*/
|
||||
private final Parser parser;
|
||||
|
||||
// ----- initialization
|
||||
|
||||
public Code(Parser p) {
|
||||
parser = p;
|
||||
buf = new byte[100];
|
||||
pc = 0;
|
||||
mainpc = -1;
|
||||
dataSize = 0;
|
||||
}
|
||||
|
||||
// ----- code storage management
|
||||
|
||||
public void put(OpCode code) {
|
||||
put(code.code());
|
||||
}
|
||||
|
||||
public void put(int x) {
|
||||
if (pc == buf.length) {
|
||||
buf = Arrays.copyOf(buf, buf.length * 2);
|
||||
}
|
||||
buf[pc++] = (byte) x;
|
||||
}
|
||||
|
||||
public void put2(int x) {
|
||||
put(x >> 8);
|
||||
put(x);
|
||||
}
|
||||
|
||||
public void put4(int x) {
|
||||
put2(x >> 16);
|
||||
put2(x);
|
||||
}
|
||||
|
||||
public void put2(int pos, int x) {
|
||||
int oldpc = pc;
|
||||
pc = pos;
|
||||
put2(x);
|
||||
pc = oldpc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the code buffer to the output stream.
|
||||
*/
|
||||
public void write(OutputStream os) throws IOException {
|
||||
int codeSize = pc;
|
||||
|
||||
ByteArrayOutputStream header = new ByteArrayOutputStream();
|
||||
DataOutputStream headerWriter = new DataOutputStream(header);
|
||||
headerWriter.writeByte('M');
|
||||
headerWriter.writeByte('J');
|
||||
headerWriter.writeInt(codeSize);
|
||||
headerWriter.writeInt(dataSize);
|
||||
headerWriter.writeInt(mainpc);
|
||||
headerWriter.close();
|
||||
|
||||
os.write(header.toByteArray());
|
||||
|
||||
os.write(buf, 0, codeSize);
|
||||
os.flush();
|
||||
os.close();
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// TODO Exercise UE-P-5-6: implementation of code generation
|
||||
// ======================================================
|
||||
|
||||
// TODO Exercise UE-P-5: Various code generation methods such as load or assign
|
||||
|
||||
/**
|
||||
* Load the operand x onto the expression stack.
|
||||
*/
|
||||
public void load(Operand x) {
|
||||
// TODO Exercise UE-P-5
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an integer constant onto the expression stack.
|
||||
*/
|
||||
public void loadConst(int n) {
|
||||
// TODO Exercise UE-P-5
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an assignment x = y.
|
||||
*/
|
||||
public void assign(Operand x, Operand y) {
|
||||
// TODO Exercise UE-P-5
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an increment instruction that increments x by n.
|
||||
*/
|
||||
public void inc(Operand x, int n) {
|
||||
// TODO Exercise UE-P-5
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the left-hand side of a compound assignment.
|
||||
*/
|
||||
public void prepareLhsOfCompoundAssignment(Operand x) {
|
||||
Operand.Kind kindBeforeLoad = x.kind;
|
||||
// TODO Exercise UE-P-5
|
||||
// TODO: Field accesses (such as x.y) or array accesses (such as arr[2]) on the left-hand side of
|
||||
// an compound assignment (e.g., arr[2] += 4) need to correctly use dup or dup2 before load. Implement here.
|
||||
|
||||
|
||||
// Do not switch kind to Stack after loading x.
|
||||
// We still need its kind later on during the assign().
|
||||
x.kind = kindBeforeLoad;
|
||||
}
|
||||
|
||||
// --------------------
|
||||
|
||||
public void methodCall(Operand x) {
|
||||
// TODO Exercise UE-P-6
|
||||
}
|
||||
|
||||
/**
|
||||
* Unconditional jump.
|
||||
*/
|
||||
public void jump(Label lab) {
|
||||
// TODO Exercise UE-P-6
|
||||
}
|
||||
|
||||
/**
|
||||
* True Jump. Generates conditional jump instruction and links it to true
|
||||
* jump chain.
|
||||
*/
|
||||
public void tJump(CompOp op, Label to) {
|
||||
// TODO Exercise UE-P-6
|
||||
}
|
||||
|
||||
/**
|
||||
* False Jump. Generates conditional jump instruction and links it to false
|
||||
* jump chain.
|
||||
*/
|
||||
public void fJump(CompOp op, Label to) {
|
||||
// TODO Exercise UE-P-6
|
||||
}
|
||||
|
||||
// =================================================
|
||||
// =================================================
|
||||
}
|
||||
561
MicroJava Compiler/src/ssw/mj/impl/Parser.java
Normal file
561
MicroJava Compiler/src/ssw/mj/impl/Parser.java
Normal file
@@ -0,0 +1,561 @@
|
||||
package ssw.mj.impl;
|
||||
|
||||
import javassist.bytecode.analysis.ControlFlow;
|
||||
import javassist.compiler.ast.MethodDecl;
|
||||
import ssw.mj.Errors;
|
||||
import ssw.mj.Errors.Message;
|
||||
import ssw.mj.scanner.Token;
|
||||
|
||||
import javax.xml.stream.FactoryConfigurationError;
|
||||
import java.util.EnumSet;
|
||||
|
||||
import static ssw.mj.Errors.Message.*;
|
||||
import static ssw.mj.scanner.Token.Kind.*;
|
||||
|
||||
public final class Parser {
|
||||
|
||||
/**
|
||||
* Maximum number of global variables per program
|
||||
*/
|
||||
private static final int MAX_GLOBALS = 32767;
|
||||
|
||||
/**
|
||||
* Maximum number of fields per class
|
||||
*/
|
||||
private static final int MAX_FIELDS = 32767;
|
||||
|
||||
/**
|
||||
* Maximum number of local variables per method
|
||||
*/
|
||||
private static final int MAX_LOCALS = 127;
|
||||
|
||||
/**
|
||||
* Last recognized token;
|
||||
*/
|
||||
private Token t;
|
||||
|
||||
/**
|
||||
* Lookahead token (not recognized).)
|
||||
*/
|
||||
private Token la;
|
||||
|
||||
/**
|
||||
* Shortcut to kind attribute of lookahead token (la).
|
||||
*/
|
||||
private Token.Kind sym;
|
||||
|
||||
/**
|
||||
* According scanner
|
||||
*/
|
||||
public final Scanner scanner;
|
||||
|
||||
/**
|
||||
* According code buffer
|
||||
*/
|
||||
public final Code code;
|
||||
|
||||
/**
|
||||
* According symbol table
|
||||
*/
|
||||
public final Tab tab;
|
||||
|
||||
private static int MIN_ERROR_DIST = 3;
|
||||
|
||||
private int errorDist = MIN_ERROR_DIST;
|
||||
|
||||
public Parser(Scanner scanner) {
|
||||
this.scanner = scanner;
|
||||
tab = new Tab(this);
|
||||
code = new Code(this);
|
||||
// Pseudo token to avoid crash when 1st symbol has scanner error.
|
||||
la = new Token(none, 1, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads ahead one symbol.
|
||||
*/
|
||||
private void scan() {
|
||||
t = la;
|
||||
la = scanner.next();
|
||||
sym = la.kind;
|
||||
errorDist++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies symbol and reads ahead.
|
||||
*/
|
||||
private void check(Token.Kind expected) {
|
||||
if (sym == expected) {
|
||||
scan();
|
||||
} else {
|
||||
error(TOKEN_EXPECTED, expected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds error message to the list of errors.
|
||||
*/
|
||||
public void error(Message msg, Object... msgParams) {
|
||||
// TODO Exercise UE-P-3: Replace panic mode with error recovery (i.e., keep track of error distance)
|
||||
// TODO Exercise UE-P-3: Hint: Replacing panic mode also affects scan() method
|
||||
|
||||
if (errorDist >= MIN_ERROR_DIST) {
|
||||
scanner.errors.error(la.line, la.col, msg, msgParams);
|
||||
}
|
||||
|
||||
errorDist = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the analysis.
|
||||
*/
|
||||
public void parse() {
|
||||
scan(); // scan first symbol, initializes look-ahead
|
||||
Program(); // start analysis
|
||||
check(eof);
|
||||
}
|
||||
|
||||
|
||||
// ===============================================
|
||||
// TODO Exercise UE-P-2: Implementation of parser
|
||||
// TODO Exercise UE-P-3: Error recovery methods
|
||||
// TODO Exercise UE-P-4: Symbol table handling
|
||||
// TODO Exercise UE-P-5-6: Code generation
|
||||
// ===============================================
|
||||
|
||||
// TODO Exercise UE-P-3: Error distance
|
||||
|
||||
// TODO Exercise UE-P-2 + Exercise 3: Sets to handle certain first, follow, and recover sets
|
||||
|
||||
// Initialize first and follow sets.
|
||||
static final EnumSet<Token.Kind> firstConstDecl = EnumSet.of(Token.Kind.final_);
|
||||
static final EnumSet<Token.Kind> firstVarDecl = EnumSet.of(Token.Kind.ident);
|
||||
static final EnumSet<Token.Kind> firstClassDecl = EnumSet.of(Token.Kind.class_);
|
||||
static final EnumSet<Token.Kind> breakDecl = EnumSet.of(lbrace, eof);
|
||||
static final EnumSet<Token.Kind> firstStatement = EnumSet.of(ident, if_, while_, break_, return_, read, print, lbrace, semicolon);
|
||||
static final EnumSet<Token.Kind> breakStatement = EnumSet.of(rbrace, else_, eof);
|
||||
static final EnumSet<Token.Kind> firstAsignop = EnumSet.of(assign, plusas, minusas, timesas, slashas, remas);
|
||||
static final EnumSet<Token.Kind> firstFactor = EnumSet.of(ident, number, charConst, new_, lpar);
|
||||
static final EnumSet<Token.Kind> firstMulop = EnumSet.of(times, slash, rem);
|
||||
static final EnumSet<Token.Kind> firstMethodDecl = EnumSet.of(ident, void_);
|
||||
static final EnumSet<Token.Kind> breakMethodDecl = EnumSet.of(rbrace, eof);
|
||||
|
||||
|
||||
static <T extends Enum<T>> EnumSet<T> enumUnion(EnumSet<T> first, EnumSet<T> ...sets) {
|
||||
final var copy = EnumSet.copyOf(first);
|
||||
for (final var set : sets) {
|
||||
copy.addAll(set);
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
|
||||
// TODO Exercise UE-P-2: One top-down parsing method per production
|
||||
|
||||
/**
|
||||
* Program = <br>
|
||||
* "program" ident <br>
|
||||
* { ConstDecl | VarDecl | ClassDecl } <br>
|
||||
* "{" { MethodDecl } "}" .
|
||||
*/
|
||||
private void Program() {
|
||||
// TODO Exercise UE-P-2
|
||||
|
||||
check(Token.Kind.program);
|
||||
check(Token.Kind.ident);
|
||||
|
||||
while (!breakDecl.contains(sym)) {
|
||||
if (firstVarDecl.contains(sym)) {
|
||||
VarDecl();
|
||||
} else if (firstClassDecl.contains(sym)) {
|
||||
ClassDecl();
|
||||
} else if (firstConstDecl.contains(sym)){
|
||||
ConstDecl();
|
||||
} else {
|
||||
recoverDecl();
|
||||
}
|
||||
}
|
||||
|
||||
check(lbrace);
|
||||
|
||||
while (!breakMethodDecl.contains(sym)) {
|
||||
if (firstMethodDecl.contains(sym)) {
|
||||
MethodDecl();
|
||||
} else {
|
||||
recoverMethodDecl();
|
||||
}
|
||||
}
|
||||
|
||||
check(rbrace);
|
||||
}
|
||||
|
||||
private void ConstDecl() {
|
||||
check(Token.Kind.final_);
|
||||
Type();
|
||||
check(Token.Kind.ident);
|
||||
check(Token.Kind.assign);
|
||||
|
||||
if (sym == Token.Kind.number) {
|
||||
scan();
|
||||
} else if (sym == Token.Kind.charConst) {
|
||||
scan();
|
||||
} else {
|
||||
error(TOKEN_EXPECTED, "number or character constant");
|
||||
}
|
||||
|
||||
check(Token.Kind.semicolon);
|
||||
}
|
||||
|
||||
private void VarDecl() {
|
||||
Type();
|
||||
check(ident);
|
||||
|
||||
while (sym == Token.Kind.comma) {
|
||||
scan();
|
||||
check(ident);
|
||||
}
|
||||
|
||||
check(semicolon);
|
||||
}
|
||||
|
||||
private void ClassDecl() {
|
||||
check(class_);
|
||||
check(ident);
|
||||
check(lbrace);
|
||||
|
||||
while (firstVarDecl.contains(sym)) {
|
||||
VarDecl();
|
||||
}
|
||||
|
||||
check(rbrace);
|
||||
}
|
||||
|
||||
private void Type() {
|
||||
check(ident);
|
||||
|
||||
if (sym == lbrack) {
|
||||
scan();
|
||||
check(rbrack);
|
||||
}
|
||||
}
|
||||
|
||||
private void MethodDecl() {
|
||||
if (sym == ident) {
|
||||
Type();
|
||||
} else if (sym == void_) {
|
||||
scan();
|
||||
}
|
||||
|
||||
check(ident);
|
||||
check(lpar);
|
||||
|
||||
if (sym == ident) {
|
||||
FormPars();
|
||||
}
|
||||
|
||||
check(rpar);
|
||||
|
||||
while (firstVarDecl.contains(sym)) {
|
||||
VarDecl();
|
||||
}
|
||||
|
||||
Block();
|
||||
}
|
||||
|
||||
private void FormPars() {
|
||||
Type();
|
||||
check(ident);
|
||||
|
||||
while (sym == comma) {
|
||||
scan();
|
||||
Type();
|
||||
check(ident);
|
||||
}
|
||||
}
|
||||
|
||||
private void Block() {
|
||||
check(lbrace);
|
||||
Statement();
|
||||
check(rbrace);
|
||||
}
|
||||
|
||||
private void Statement() {
|
||||
while (!breakStatement.contains(sym)) {
|
||||
switch (sym) {
|
||||
case ident -> {
|
||||
Designator();
|
||||
|
||||
if (firstAsignop.contains(sym)) {
|
||||
AssignOp();
|
||||
Expr();
|
||||
} else if (sym == lpar) {
|
||||
ActPars();
|
||||
} else if (sym == pplus) {
|
||||
scan();
|
||||
} else if (sym == mminus) {
|
||||
scan();
|
||||
} else {
|
||||
error(TOKEN_EXPECTED, "unexpected token. assignment token (=, +=, -=, *=, /=, %=), method call (\"(\"), increment (++) or decrement (--)");
|
||||
}
|
||||
|
||||
check(semicolon);
|
||||
}
|
||||
case if_ -> {
|
||||
scan();
|
||||
check(lpar);
|
||||
Condition();
|
||||
check(rpar);
|
||||
Statement();
|
||||
|
||||
if (sym == else_) {
|
||||
scan();
|
||||
Statement();
|
||||
}
|
||||
}
|
||||
case while_ -> {
|
||||
scan();
|
||||
check(lpar);
|
||||
Condition();
|
||||
check(rpar);
|
||||
Statement();
|
||||
}
|
||||
case break_-> {
|
||||
scan();
|
||||
check(semicolon);
|
||||
}
|
||||
case return_ -> {
|
||||
scan();
|
||||
|
||||
if (sym == minus || firstFactor.contains(sym)) {
|
||||
Expr();
|
||||
}
|
||||
|
||||
check(semicolon);
|
||||
}
|
||||
case read -> {
|
||||
scan();
|
||||
check(lpar);
|
||||
Designator();
|
||||
check(rpar);
|
||||
check(semicolon);
|
||||
}
|
||||
case print -> {
|
||||
scan();
|
||||
check(lpar);
|
||||
Expr();
|
||||
|
||||
if (sym == comma) {
|
||||
scan();
|
||||
check(number);
|
||||
}
|
||||
|
||||
check(rpar);
|
||||
check(semicolon);
|
||||
}
|
||||
case lbrace -> Block();
|
||||
case semicolon -> scan();
|
||||
default -> recoverStatement();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private void Designator() {
|
||||
check(ident);
|
||||
|
||||
while (sym == period || sym == lbrack) {
|
||||
if (sym == period) {
|
||||
scan();
|
||||
check(ident);
|
||||
} else {
|
||||
scan();
|
||||
|
||||
if (sym == tilde) {
|
||||
scan();
|
||||
}
|
||||
|
||||
Expr();
|
||||
|
||||
check(rbrack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void AssignOp() {
|
||||
switch (sym) {
|
||||
case assign -> scan();
|
||||
case plusas -> scan();
|
||||
case minusas -> scan();
|
||||
case timesas -> scan();
|
||||
case slashas -> scan();
|
||||
case remas -> scan();
|
||||
default -> error(TOKEN_EXPECTED, "unexpected token. assignment token (=, +=, -=, *=, /=, %=), method call (\"(\"), increment (++) or decrement (--)");
|
||||
}
|
||||
}
|
||||
|
||||
private void Expr() {
|
||||
if (sym == minus) {
|
||||
scan();
|
||||
}
|
||||
|
||||
Term();
|
||||
|
||||
while (sym == plus || sym == minus) {
|
||||
Addop();
|
||||
Term();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ActPars() {
|
||||
|
||||
check(lpar);
|
||||
|
||||
if (sym == minus || firstFactor.contains(sym)) {
|
||||
Expr();
|
||||
|
||||
while (sym == comma) {
|
||||
scan();
|
||||
Expr();
|
||||
}
|
||||
}
|
||||
|
||||
check(rpar);
|
||||
}
|
||||
|
||||
private void Term() {
|
||||
Factor();
|
||||
|
||||
while (firstMulop.contains(sym)) {
|
||||
Mulop();
|
||||
Factor();
|
||||
}
|
||||
}
|
||||
|
||||
private void Addop() {
|
||||
if (sym == plus) {
|
||||
scan();
|
||||
} else if (sym == minus) {
|
||||
scan();
|
||||
} else {
|
||||
error(TOKEN_EXPECTED, "minus or plus expected");
|
||||
}
|
||||
}
|
||||
|
||||
private void Factor() {
|
||||
switch(sym) {
|
||||
case ident -> Designator();
|
||||
case number -> scan();
|
||||
case charConst -> scan();
|
||||
case new_ -> {
|
||||
scan();
|
||||
check(ident);
|
||||
if (sym == lbrack) {
|
||||
scan();
|
||||
Expr();
|
||||
check(rbrack);
|
||||
}
|
||||
}
|
||||
case lpar -> {
|
||||
scan();
|
||||
Expr();
|
||||
check(rpar);
|
||||
}
|
||||
default -> error(TOKEN_EXPECTED, "unexpected token. identifier, number, character constant, new or \"(\"");
|
||||
}
|
||||
}
|
||||
|
||||
private void Mulop() {
|
||||
if (sym == times) {
|
||||
scan();
|
||||
} else if (sym == slash) {
|
||||
scan();
|
||||
} else if (sym == rem) {
|
||||
scan();
|
||||
} else {
|
||||
error(TOKEN_EXPECTED, "expected *, /, %");
|
||||
}
|
||||
}
|
||||
|
||||
private void Condition() {
|
||||
CondTerm();
|
||||
|
||||
while (sym == or) {
|
||||
scan();
|
||||
CondTerm();
|
||||
}
|
||||
}
|
||||
|
||||
private void CondTerm() {
|
||||
CondFact();
|
||||
|
||||
while (sym == and) {
|
||||
scan();
|
||||
CondFact();
|
||||
}
|
||||
}
|
||||
|
||||
private void CondFact() {
|
||||
Expr();
|
||||
Relop();
|
||||
Expr();
|
||||
}
|
||||
|
||||
private void Relop() {
|
||||
switch (sym) {
|
||||
case eql -> scan();
|
||||
case neq -> scan();
|
||||
case gtr -> scan();
|
||||
case lss -> scan();
|
||||
case leq -> scan();
|
||||
case geq -> scan();
|
||||
default -> error(TOKEN_EXPECTED, "unexpected token. ==, !=, >, >=, <, <=");
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
// TODO Exercise UE-P-3: Error recovery methods: recoverDecl, recoverMethodDecl and recoverStat (+ TODO Exercise UE-P-5: Check idents for Type kind)
|
||||
|
||||
private void recoverDecl() {
|
||||
final var firstDeclarationUnion = enumUnion(firstConstDecl, firstVarDecl, firstClassDecl);
|
||||
final var recoverDeclSet = enumUnion(breakDecl, firstDeclarationUnion);
|
||||
error(DECLARATION_RECOVERY);
|
||||
|
||||
do {
|
||||
scan();
|
||||
} while (!recoverDeclSet.contains(sym));
|
||||
errorDist = 0;
|
||||
}
|
||||
|
||||
private void recoverMethodDecl() {
|
||||
error(METHOD_DECL_RECOVERY);
|
||||
|
||||
final var recoveryMethoDeclSet = enumUnion(breakMethodDecl, firstMethodDecl);
|
||||
do {
|
||||
scan();
|
||||
} while (!recoveryMethoDeclSet.contains(sym));
|
||||
errorDist = 0;
|
||||
}
|
||||
|
||||
private void recoverStatement() {
|
||||
error(STATEMENT_RECOVERY);
|
||||
|
||||
var recoveryStatementSet = enumUnion(breakStatement, firstStatement);
|
||||
recoveryStatementSet.remove(ident);
|
||||
recoveryStatementSet.remove(lbrace);
|
||||
|
||||
do {
|
||||
scan();
|
||||
} while (!recoveryStatementSet.contains(sym));
|
||||
errorDist = 0;
|
||||
}
|
||||
|
||||
// ====================================
|
||||
// ====================================
|
||||
}
|
||||
434
MicroJava Compiler/src/ssw/mj/impl/Scanner.java
Normal file
434
MicroJava Compiler/src/ssw/mj/impl/Scanner.java
Normal file
@@ -0,0 +1,434 @@
|
||||
package ssw.mj.impl;
|
||||
|
||||
import ssw.mj.Errors;
|
||||
import ssw.mj.Interpreter;
|
||||
import ssw.mj.scanner.Token;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Map;
|
||||
|
||||
import static ssw.mj.scanner.Token.Kind.*;
|
||||
import static ssw.mj.scanner.Token.Kind.number;
|
||||
|
||||
public class Scanner {
|
||||
|
||||
// Scanner Skeleton - do not rename fields / methods !
|
||||
private static final char EOF = (char) -1;
|
||||
private static final char LF = '\n';
|
||||
|
||||
/**
|
||||
* Input data to read from.
|
||||
*/
|
||||
private final Reader in;
|
||||
|
||||
/**
|
||||
* Lookahead character. (= next (unhandled) character in the input stream)
|
||||
*/
|
||||
private char ch;
|
||||
|
||||
/**
|
||||
* Current line in input stream.
|
||||
*/
|
||||
private int line;
|
||||
|
||||
/**
|
||||
* Current column in input stream.
|
||||
*/
|
||||
private int col;
|
||||
|
||||
/**
|
||||
* According errors object.
|
||||
*/
|
||||
public final Errors errors;
|
||||
|
||||
public Scanner(Reader r) {
|
||||
// store reader
|
||||
in = r;
|
||||
|
||||
// initialize error handling support
|
||||
errors = new Errors();
|
||||
|
||||
line = 1;
|
||||
col = 0;
|
||||
nextCh(); // read 1st char into ch, incr col to 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds error message to the list of errors.
|
||||
*/
|
||||
public final void error(Token t, Errors.Message msg, Object... msgParams) {
|
||||
errors.error(t.line, t.col, msg, msgParams);
|
||||
|
||||
// reset token content (consistent JUnit tests)
|
||||
t.numVal = 0;
|
||||
t.val = null;
|
||||
}
|
||||
|
||||
|
||||
// ================================================
|
||||
// TODO Exercise UE-P-1: Implement Scanner (next() + private helper methods)
|
||||
// ================================================
|
||||
|
||||
// TODO Exercise UE-P-1: Keywords
|
||||
/**
|
||||
* Mapping from keyword names to appropriate token codes.
|
||||
*/
|
||||
private static final Map<String, Token.Kind> keywords;
|
||||
|
||||
static {
|
||||
keywords = Map.ofEntries(
|
||||
Map.entry("void", void_),
|
||||
Map.entry("class", class_),
|
||||
Map.entry("program", program),
|
||||
Map.entry("if", if_),
|
||||
Map.entry("else", else_),
|
||||
Map.entry("while", while_),
|
||||
Map.entry("read", read),
|
||||
Map.entry("print", print),
|
||||
Map.entry("return", return_),
|
||||
Map.entry("break", break_),
|
||||
Map.entry("final", final_),
|
||||
Map.entry("new", new_)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns next token. To be used by parser.
|
||||
*/
|
||||
public Token next() {
|
||||
// TODO Exercise UE-P-1: implementation of next method
|
||||
while (Character.isWhitespace(ch)) {
|
||||
nextCh();
|
||||
}
|
||||
|
||||
final var ogLine = line;
|
||||
final var ogCol = col;
|
||||
|
||||
if (isLetter(ch)) {
|
||||
final var name = readName();
|
||||
|
||||
return getTokenByName(name, ogLine, ogCol);
|
||||
}
|
||||
|
||||
if (isDigit(ch)) {
|
||||
final var stringNumber = readNumber();
|
||||
|
||||
try {
|
||||
final var number = Integer.parseInt(stringNumber);
|
||||
|
||||
var token = new Token(Token.Kind.number, ogLine, ogCol);
|
||||
token.numVal = number;
|
||||
token.val = stringNumber;
|
||||
|
||||
return token;
|
||||
} catch (NumberFormatException exception) {
|
||||
final var token = new Token(number, ogLine, ogCol);
|
||||
error(token, Errors.Message.BIG_NUM, stringNumber);
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
if (ch == '\'') {
|
||||
nextCh();
|
||||
|
||||
if (ch == '\r') {
|
||||
nextCh();
|
||||
}
|
||||
|
||||
var charContent = ch;
|
||||
var lfEscaped = false;
|
||||
var tickEscaped = false;
|
||||
|
||||
if (ch == '\\') {
|
||||
nextCh();
|
||||
charContent = switch (ch) {
|
||||
case 'n' -> '\n';
|
||||
case 'r' -> '\r';
|
||||
case 't' -> '\t';
|
||||
case '\\' -> '\\';
|
||||
case '\'' -> '\'';
|
||||
default -> '\0';
|
||||
};
|
||||
|
||||
if (charContent == '\n') {
|
||||
lfEscaped = true;
|
||||
}
|
||||
|
||||
if (charContent == '\'') {
|
||||
tickEscaped = true;
|
||||
}
|
||||
|
||||
if (charContent == '\0') {
|
||||
final var token = new Token(charConst, ogLine, ogCol);
|
||||
error(token, Errors.Message.UNDEFINED_ESCAPE, ch);
|
||||
}
|
||||
}
|
||||
|
||||
if (charContent == LF && !lfEscaped || charContent == EOF || charContent == '\'' && !tickEscaped) {
|
||||
final var token = new Token(charConst, ogLine, ogCol);
|
||||
final var message = switch (charContent) {
|
||||
case LF -> Errors.Message.ILLEGAL_LINE_END;
|
||||
case EOF -> Errors.Message.EOF_IN_CHAR;
|
||||
case '\'' -> Errors.Message.EMPTY_CHARCONST;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
error(token, message);
|
||||
token.val = '\0' + "";
|
||||
token.numVal = 0;
|
||||
|
||||
if (charContent == '\'') {
|
||||
nextCh();
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
nextCh();
|
||||
|
||||
if (ch != '\'') {
|
||||
final var token = new Token(charConst, ogLine, ogCol);
|
||||
error(token, Errors.Message.MISSING_QUOTE);
|
||||
token.val = '\0' + "";
|
||||
token.numVal = 0;
|
||||
return token;
|
||||
}
|
||||
|
||||
nextCh();
|
||||
|
||||
final var token = new Token(charConst, ogLine, ogCol);
|
||||
token.val = charContent + "";
|
||||
token.numVal = charContent;
|
||||
return token;
|
||||
}
|
||||
|
||||
final var ogChar = ch;
|
||||
nextCh();
|
||||
return switch(ogChar) {
|
||||
case ';' -> new Token(Token.Kind.semicolon, ogLine, ogCol);
|
||||
case ',' -> new Token(Token.Kind.comma, ogLine, ogCol);
|
||||
case '.' -> new Token(Token.Kind.period, ogLine, ogCol);
|
||||
case '{' -> new Token(Token.Kind.lbrace, ogLine, ogCol);
|
||||
case '}' -> new Token(Token.Kind.rbrace, ogLine, ogCol);
|
||||
case '[' -> new Token(Token.Kind.lbrack, ogLine, ogCol);
|
||||
case ']' -> new Token(Token.Kind.rbrack, ogLine, ogCol);
|
||||
case '(' -> new Token(Token.Kind.lpar, ogLine, ogCol);
|
||||
case ')' -> new Token(Token.Kind.rpar, ogLine, ogCol);
|
||||
case (char) -1 -> new Token(Token.Kind.eof, ogLine, ogCol);
|
||||
case '~' -> new Token(Token.Kind.tilde, ogLine, ogCol);
|
||||
case '=' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.eql, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.assign, ogLine, ogCol);
|
||||
}
|
||||
case '+' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.plusas, ogLine, ogCol);
|
||||
}
|
||||
|
||||
if (ch == '+') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.pplus, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.plus, ogLine, ogCol);
|
||||
}
|
||||
case '-' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.minusas, ogLine, ogCol);
|
||||
}
|
||||
|
||||
if (ch == '-') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.mminus, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.minus, ogLine, ogCol);
|
||||
}
|
||||
case '*' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.timesas, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.times, ogLine, ogCol);
|
||||
}
|
||||
case '/' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.slashas, ogLine, ogCol);
|
||||
}
|
||||
|
||||
if (ch == '*') {
|
||||
nextCh();
|
||||
final var success = skipComment();
|
||||
if (success) {
|
||||
yield next();
|
||||
} else {
|
||||
final var token = new Token(eof, ogLine, ogCol);
|
||||
error(token, Errors.Message.EOF_IN_COMMENT);
|
||||
yield next();
|
||||
}
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.slash, ogLine, ogCol);
|
||||
}
|
||||
case '%' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.remas, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.rem, ogLine, ogCol);
|
||||
}
|
||||
case '!' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.neq, ogLine, ogCol);
|
||||
}
|
||||
|
||||
final var token = new Token(none, ogLine, ogCol);
|
||||
error(token, Errors.Message.INVALID_CHAR, ogChar);
|
||||
yield token;
|
||||
}
|
||||
case '>' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.geq, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.gtr, ogLine, ogCol);
|
||||
}
|
||||
case '<' -> {
|
||||
if (ch == '=') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.leq, ogLine, ogCol);
|
||||
}
|
||||
|
||||
yield new Token(Token.Kind.lss, ogLine, ogCol);
|
||||
}
|
||||
case '&' -> {
|
||||
if (ch == '&') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.and, ogLine, ogCol);
|
||||
}
|
||||
|
||||
final var token = new Token(none, ogLine, ogCol);
|
||||
error(token, Errors.Message.INVALID_CHAR, ogChar);
|
||||
yield token;
|
||||
}
|
||||
case '|' -> {
|
||||
if (ch == '|') {
|
||||
nextCh();
|
||||
yield new Token(Token.Kind.or, ogLine, ogCol);
|
||||
}
|
||||
|
||||
final var token = new Token(none, ogLine, ogCol);
|
||||
error(token, Errors.Message.INVALID_CHAR, ogChar);
|
||||
yield token;
|
||||
}
|
||||
default -> {
|
||||
final var token = new Token(none, ogLine, ogCol);
|
||||
error(token, Errors.Message.INVALID_CHAR, ogChar);
|
||||
yield token;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void nextCh() {
|
||||
// TODO Exercise UE-P-1: implementation of nextCh method and other private helper methods
|
||||
try {
|
||||
final var intChar = in.read();
|
||||
final var nextChar = (char) intChar;
|
||||
|
||||
if (ch == '\n') {
|
||||
line++;
|
||||
col = 0;
|
||||
}
|
||||
|
||||
col++;
|
||||
ch = nextChar;
|
||||
} catch (IOException e) {
|
||||
System.err.println("Could not read Stream");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Exercise UE-P-1: private helper methods used by next(), as discussed in the exercise
|
||||
|
||||
// -----------------------------------------------
|
||||
|
||||
private boolean isLetter(char c) {
|
||||
return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z';
|
||||
}
|
||||
|
||||
private boolean isDigit(char c) {
|
||||
return '0' <= c && c <= '9';
|
||||
}
|
||||
|
||||
public String readName() {
|
||||
final var builder = new StringBuilder();
|
||||
|
||||
while (isLetter(ch) || ch == '_' || isDigit(ch)) {
|
||||
builder.append(ch);
|
||||
nextCh();
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public String readNumber() {
|
||||
final var builder = new StringBuilder();
|
||||
|
||||
while (isDigit(ch)) {
|
||||
builder.append(ch);
|
||||
nextCh();
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private Token getTokenByName(String name, int line, int col) {
|
||||
final var kind = keywords.getOrDefault(name, Token.Kind.ident);
|
||||
|
||||
var token = new Token(kind, line, col);
|
||||
|
||||
if (kind == Token.Kind.ident) {
|
||||
token.val = name;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private boolean skipComment() {
|
||||
var commentCount = 1;
|
||||
while (commentCount != 0) {
|
||||
final var lastCh = ch;
|
||||
nextCh();
|
||||
|
||||
if (lastCh == '/' && ch == '*') {
|
||||
commentCount++;
|
||||
nextCh();
|
||||
}
|
||||
|
||||
if (lastCh == '*' && ch == '/') {
|
||||
commentCount--;
|
||||
nextCh();
|
||||
}
|
||||
|
||||
if (ch == EOF && commentCount != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ================================================
|
||||
// ================================================
|
||||
}
|
||||
100
MicroJava Compiler/src/ssw/mj/impl/Tab.java
Normal file
100
MicroJava Compiler/src/ssw/mj/impl/Tab.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package ssw.mj.impl;
|
||||
|
||||
import ssw.mj.symtab.Obj;
|
||||
import ssw.mj.symtab.Scope;
|
||||
import ssw.mj.symtab.Struct;
|
||||
|
||||
public final class Tab {
|
||||
|
||||
// Universe
|
||||
public static final Struct noType = new Struct(Struct.Kind.None);
|
||||
public static final Struct intType = new Struct(Struct.Kind.Int);
|
||||
public static final Struct charType = new Struct(Struct.Kind.Char);
|
||||
public static final Struct nullType = new Struct(Struct.Kind.Class);
|
||||
|
||||
public final Obj noObj, chrObj;
|
||||
public Obj ordObj, lenObj;
|
||||
|
||||
/**
|
||||
* Only used for reporting errors.
|
||||
*/
|
||||
private final Parser parser;
|
||||
/**
|
||||
* The current top scope.
|
||||
*/
|
||||
public Scope curScope = null;
|
||||
// First scope opening (universe) will increase this to -1
|
||||
/**
|
||||
* Nesting level of current scope.
|
||||
*/
|
||||
private int curLevel = -2;
|
||||
|
||||
public Tab(Parser p) {
|
||||
parser = p;
|
||||
|
||||
// setting up "universe" (= predefined names)
|
||||
// opening scope (curLevel goes to -1, which is the universe level)
|
||||
openScope();
|
||||
|
||||
noObj = new Obj(Obj.Kind.Var, "noObj", noType);
|
||||
|
||||
insert(Obj.Kind.Type, "int", intType);
|
||||
insert(Obj.Kind.Type, "char", charType);
|
||||
insert(Obj.Kind.Con, "null", nullType);
|
||||
|
||||
chrObj = insert(Obj.Kind.Meth, "chr", charType);
|
||||
openScope();
|
||||
Obj iVarObj = insert(Obj.Kind.Var, "i", intType);
|
||||
iVarObj.level = 1;
|
||||
chrObj.nPars = curScope.nVars();
|
||||
chrObj.locals = curScope.locals();
|
||||
closeScope();
|
||||
|
||||
// TODO Exercise UE-P-4: build "ord" universe method and store in ordObj
|
||||
|
||||
// TODO Exercise UE-P-4: build "len" universe method and store in lenObj
|
||||
|
||||
// still on level -1
|
||||
// now that the universe is constructed, the next node that will be added is the Program itself
|
||||
// (which will open its own scope with level 0)
|
||||
}
|
||||
|
||||
// ===============================================
|
||||
// TODO Exercise UE-P-4: implementation of symbol table
|
||||
// ===============================================
|
||||
|
||||
public void openScope() {
|
||||
curScope = new Scope(curScope);
|
||||
curLevel++;
|
||||
}
|
||||
|
||||
public void closeScope() {
|
||||
curScope = curScope.outer();
|
||||
curLevel--;
|
||||
}
|
||||
|
||||
public Obj insert(Obj.Kind kind, String name, Struct type) {
|
||||
// TODO Exercise UE-P-4
|
||||
return noObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object with <code>name</code> from the innermost scope.
|
||||
*/
|
||||
public Obj find(String name) {
|
||||
// TODO Exercise UE-P-4
|
||||
return noObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the field <code>name</code> from the fields of
|
||||
* <code>type</code>.
|
||||
*/
|
||||
public Obj findField(String name, Struct type) {
|
||||
// TODO Exercise UE-P-4
|
||||
return noObj;
|
||||
}
|
||||
|
||||
// ===============================================
|
||||
// ===============================================
|
||||
}
|
||||
126
MicroJava Compiler/src/ssw/mj/scanner/Token.java
Normal file
126
MicroJava Compiler/src/ssw/mj/scanner/Token.java
Normal file
@@ -0,0 +1,126 @@
|
||||
package ssw.mj.scanner;
|
||||
|
||||
/**
|
||||
* A <code>Token</code> represents a terminal symbol. Tokens are provided by the
|
||||
* scanner for the parser. They hold additional information about the symbol.
|
||||
*/
|
||||
public class Token {
|
||||
public enum Kind {
|
||||
|
||||
// @formatter:off
|
||||
none("none"),
|
||||
ident("identifier"),
|
||||
number("number"),
|
||||
charConst("character constant"),
|
||||
plus("+"),
|
||||
minus("-"),
|
||||
times("*"),
|
||||
slash("/"),
|
||||
rem("%"),
|
||||
eql("=="),
|
||||
neq("!="),
|
||||
lss("<"),
|
||||
leq("<="),
|
||||
gtr(">"),
|
||||
geq(">="),
|
||||
and("&&"),
|
||||
or("||"),
|
||||
assign("="),
|
||||
plusas("+="),
|
||||
minusas("-="),
|
||||
timesas("*="),
|
||||
slashas("/="),
|
||||
remas("%="),
|
||||
pplus("++"),
|
||||
mminus("--"),
|
||||
semicolon(";"),
|
||||
comma(","),
|
||||
period("."),
|
||||
lpar("("),
|
||||
rpar(")"),
|
||||
lbrack("["),
|
||||
rbrack("]"),
|
||||
lbrace("{"),
|
||||
rbrace("}"),
|
||||
tilde("~"),
|
||||
break_("break"),
|
||||
class_("class"),
|
||||
else_("else"),
|
||||
final_("final"),
|
||||
if_("if"),
|
||||
new_("new"),
|
||||
print("print"),
|
||||
program("program"),
|
||||
read("read"),
|
||||
return_("return"),
|
||||
void_("void"),
|
||||
while_("while"),
|
||||
eof("end of file");
|
||||
// @formatter:on
|
||||
|
||||
private final String label;
|
||||
|
||||
Kind(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String label() {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token class (none, ident, ...).
|
||||
*/
|
||||
public Kind kind;
|
||||
|
||||
/**
|
||||
* Line number of this token.
|
||||
*/
|
||||
public final int line;
|
||||
|
||||
/**
|
||||
* Column number of this token.
|
||||
*/
|
||||
public final int col;
|
||||
|
||||
/**
|
||||
* Value of this token (for numbers or character constants).
|
||||
*/
|
||||
public int numVal;
|
||||
|
||||
/**
|
||||
* String representation of this token.
|
||||
*/
|
||||
public String val;
|
||||
|
||||
/**
|
||||
* Constructor that sets the fields required for all tokens.
|
||||
*/
|
||||
public Token(Kind kind, int line, int col) {
|
||||
this.kind = kind;
|
||||
this.val = kind.label(); // Set val to kind text by default. Will be overwritten by terminal classes (ident, number, charConst)
|
||||
this.line = line;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this Token object. This only includes
|
||||
* the relevant attributes of the token.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "line " + line + ", col " + col + ", kind " + kind;
|
||||
if (kind == Kind.ident) {
|
||||
result = result + ", val " + val;
|
||||
} else if (kind == Kind.number || kind == Kind.charConst) {
|
||||
result = result + ", val " + val + ", numVal " + numVal;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
144
MicroJava Compiler/src/ssw/mj/symtab/Obj.java
Normal file
144
MicroJava Compiler/src/ssw/mj/symtab/Obj.java
Normal file
@@ -0,0 +1,144 @@
|
||||
package ssw.mj.symtab;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* MicroJava Symbol Table Objects: Every named object in a program is stored in
|
||||
* an <code>Obj</code> node. Every scope has a list of objects declared within
|
||||
* it.
|
||||
*/
|
||||
public class Obj {
|
||||
/**
|
||||
* Possible codes for object kinds.
|
||||
*/
|
||||
public enum Kind {
|
||||
Con, Var, Type, Meth, Prog
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind of the object node.
|
||||
*/
|
||||
public final Kind kind;
|
||||
|
||||
/**
|
||||
* Name of the object node.
|
||||
*/
|
||||
public final String name;
|
||||
|
||||
/**
|
||||
* Type of the object node.
|
||||
*/
|
||||
public final Struct type;
|
||||
|
||||
/**
|
||||
* Only for Con: Value of the constant.
|
||||
*/
|
||||
public int val;
|
||||
|
||||
/**
|
||||
* Only for Var, Meth: Offset of the element.
|
||||
*/
|
||||
public int adr;
|
||||
|
||||
/**
|
||||
* Only for Var: Declaration level (0..global, 1..local)
|
||||
*/
|
||||
public int level;
|
||||
|
||||
/**
|
||||
* Only for Meth: Number of parameters.
|
||||
*/
|
||||
public int nPars;
|
||||
|
||||
// This is a Collections.emptyMap() on purpose, do not change this line
|
||||
// If you finished reading the locals of a method, use meth.locals = curScope.locals() and close the scope afterward
|
||||
/**
|
||||
* Only for Meth / Prog: List of local variables / global declarations.
|
||||
*/
|
||||
public Map<String, Obj> locals = Collections.emptyMap();
|
||||
|
||||
public Obj(Kind kind, String name, Struct type) {
|
||||
this.kind = kind;
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb;
|
||||
switch (kind) {
|
||||
case Prog -> {
|
||||
sb = new StringBuilder();
|
||||
sb.append(kind).append(' ');
|
||||
sb.append(name);
|
||||
return sb.toString();
|
||||
}
|
||||
case Con -> {
|
||||
sb = new StringBuilder();
|
||||
sb.append("const ");
|
||||
sb.append(type).append(' ');
|
||||
sb.append(name).append('=');
|
||||
sb.append(renderValue());
|
||||
return sb.toString();
|
||||
}
|
||||
case Var -> {
|
||||
sb = new StringBuilder();
|
||||
sb.append(renderVarPrefix()).append(' ');
|
||||
sb.append(type).append(' ');
|
||||
sb.append(name).append('@');
|
||||
renderAddress(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
case Meth -> {
|
||||
sb = new StringBuilder(type.toString()).append(' ');
|
||||
sb.append(name).append('(');
|
||||
boolean first = true;
|
||||
Iterator<Entry<String, Obj>> it = locals.entrySet().iterator();
|
||||
for (int i = 0; i < nPars; i++) {
|
||||
if (!first) {
|
||||
sb.append(", ");
|
||||
}
|
||||
Entry<String, Obj> e = it.next();
|
||||
sb.append(e.getValue().type).append(' ').append(e.getKey());
|
||||
first = false;
|
||||
}
|
||||
sb.append(')').append('@');
|
||||
renderAddress(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
case Type -> {
|
||||
sb = new StringBuilder();
|
||||
sb.append("Type ");
|
||||
sb.append(type).append(' ');
|
||||
sb.append(name);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Unknown Obj " + kind);
|
||||
}
|
||||
|
||||
private void renderAddress(StringBuilder sb) {
|
||||
if (kind == Kind.Meth || level == 0) {
|
||||
sb.append("0x").append(Integer.toHexString(adr));
|
||||
} else {
|
||||
sb.append(adr);
|
||||
}
|
||||
}
|
||||
|
||||
private String renderVarPrefix() {
|
||||
if (level == 0) {
|
||||
return "global";
|
||||
}
|
||||
return "local";
|
||||
}
|
||||
|
||||
private String renderValue() {
|
||||
if (type.kind == Struct.Kind.Char) {
|
||||
return Character.toString((char) val);
|
||||
}
|
||||
return Integer.toString(val);
|
||||
}
|
||||
}
|
||||
58
MicroJava Compiler/src/ssw/mj/symtab/Scope.java
Normal file
58
MicroJava Compiler/src/ssw/mj/symtab/Scope.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package ssw.mj.symtab;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* MicroJava Symbol Table Scopes
|
||||
*/
|
||||
public final class Scope {
|
||||
/**
|
||||
* Reference to enclosing scope.
|
||||
*/
|
||||
private final Scope outer;
|
||||
/**
|
||||
* Declarations of this scope.
|
||||
*/
|
||||
private final Map<String, Obj> locals = new LinkedHashMap<>();
|
||||
/**
|
||||
* Number of variables in this scope.
|
||||
*/
|
||||
private int nVars;
|
||||
|
||||
public Scope(Scope outer) {
|
||||
this.outer = outer;
|
||||
}
|
||||
|
||||
public int nVars() {
|
||||
return nVars;
|
||||
}
|
||||
|
||||
public Obj findGlobal(String name) {
|
||||
Obj res = findLocal(name);
|
||||
if (res == null && outer != null) {
|
||||
res = outer.findGlobal(name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public Obj findLocal(String name) {
|
||||
return locals.get(name);
|
||||
}
|
||||
|
||||
public void insert(Obj o) {
|
||||
locals.put(o.name, o);
|
||||
if (o.kind == Obj.Kind.Var) {
|
||||
nVars++;
|
||||
}
|
||||
}
|
||||
|
||||
public Scope outer() {
|
||||
return outer;
|
||||
}
|
||||
|
||||
public Map<String, Obj> locals() {
|
||||
return Collections.unmodifiableMap(locals);
|
||||
}
|
||||
}
|
||||
116
MicroJava Compiler/src/ssw/mj/symtab/Struct.java
Normal file
116
MicroJava Compiler/src/ssw/mj/symtab/Struct.java
Normal file
@@ -0,0 +1,116 @@
|
||||
package ssw.mj.symtab;
|
||||
|
||||
import ssw.mj.impl.Tab;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public final class Struct {
|
||||
|
||||
/**
|
||||
* Possible codes for structure kinds.
|
||||
*/
|
||||
public enum Kind {
|
||||
None, Int, Char, Arr, Class
|
||||
}
|
||||
|
||||
/**
|
||||
* Kind of the structure node.
|
||||
*/
|
||||
public final Kind kind;
|
||||
/**
|
||||
* Only for Arr: Type of the array elements.
|
||||
*/
|
||||
public final Struct elemType;
|
||||
|
||||
/**
|
||||
* Only for Class: First element of the linked list of local variables.
|
||||
* <br>
|
||||
* This is a Collections.emptyMap() (which is immutable) on purpose, do not change this line.
|
||||
* When you finished reading the fields of a class, use clazz.fields = curScope.locals() and close the scope afterward
|
||||
*/
|
||||
public Map<String, Obj> fields = Collections.emptyMap();
|
||||
|
||||
public Struct(Kind kind) {
|
||||
this.kind = kind;
|
||||
this.elemType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array structure with a specified element type.
|
||||
*/
|
||||
public Struct(Struct elemType) {
|
||||
this.kind = Kind.Arr;
|
||||
this.elemType = elemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the field <code>name</code>.
|
||||
*/
|
||||
public Obj findField(String name) {
|
||||
return fields.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for Class: Number of fields.
|
||||
*/
|
||||
public int nrFields() {
|
||||
return fields.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (this == Tab.nullType) {
|
||||
return "null";
|
||||
}
|
||||
switch (kind) {
|
||||
case Int, Char, None -> {
|
||||
return kind.toString();
|
||||
}
|
||||
case Arr -> {
|
||||
return elemType + "[]";
|
||||
}
|
||||
case Class -> {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Class{");
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, Obj> e : fields.entrySet()) {
|
||||
String fieldName = e.getKey();
|
||||
Obj field = e.getValue();
|
||||
if (!first) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(fieldName).append('=').append(field.type);
|
||||
first = false;
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Unknown Struct " + kind);
|
||||
}
|
||||
|
||||
public boolean isRefType() {
|
||||
return kind == Kind.Class || kind == Kind.Arr;
|
||||
}
|
||||
|
||||
public boolean isEqual(Struct other) {
|
||||
if (kind == Kind.Arr) {
|
||||
return other.kind == Kind.Arr && elemType.isEqual(other.elemType);
|
||||
}
|
||||
return this == other;
|
||||
}
|
||||
|
||||
public boolean compatibleWith(Struct other) {
|
||||
return this.isEqual(other)
|
||||
|| (this == Tab.nullType && other.isRefType())
|
||||
|| (other == Tab.nullType && this.isRefType());
|
||||
}
|
||||
|
||||
public boolean assignableTo(Struct dest) {
|
||||
return this.isEqual(dest) || (this == Tab.nullType && dest.isRefType())
|
||||
// this is necessary for the standard function len
|
||||
|| (this.kind == Kind.Arr && dest.kind == Kind.Arr
|
||||
&& dest.elemType == Tab.noType);
|
||||
}
|
||||
}
|
||||
54
MicroJava Tests/resources/animals.mj
Normal file
54
MicroJava Tests/resources/animals.mj
Normal file
@@ -0,0 +1,54 @@
|
||||
program Animals
|
||||
class Animal {
|
||||
char[] name;
|
||||
}
|
||||
{
|
||||
void setName(Animal a, char[] name) {
|
||||
a.name = name;
|
||||
}
|
||||
void aPrint(Animal a)
|
||||
int i, l;
|
||||
char c;
|
||||
{
|
||||
l = len(a.name);
|
||||
i = 0;
|
||||
while (i < l) {
|
||||
print(a.name[i]);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
void main()
|
||||
int a;
|
||||
Animal[] animals;
|
||||
char[] cat;
|
||||
char[] dog;
|
||||
char[] octopus;
|
||||
{
|
||||
cat = new char[3];
|
||||
cat[0] = 'c';
|
||||
cat[1] = 'a';
|
||||
cat[2] = 't';
|
||||
dog = new char[3];
|
||||
dog[0] = 'd';
|
||||
dog[1] = 'o';
|
||||
dog[2] = 'g';
|
||||
octopus = new char[7];
|
||||
octopus[0] = 'o';
|
||||
octopus[1] = 'c';
|
||||
octopus[2] = 't';
|
||||
octopus[3] = 'o';
|
||||
octopus[4] = 'p';
|
||||
octopus[5] = 'u';
|
||||
octopus[6] = 's';
|
||||
|
||||
animals = new Animal[3];
|
||||
animals[0] = new Animal;
|
||||
animals[1] = new Animal;
|
||||
animals[2] = new Animal;
|
||||
setName(animals[0],cat);
|
||||
setName(animals[1],dog);
|
||||
setName(animals[2],octopus);
|
||||
read(a);
|
||||
aPrint(animals[a]);
|
||||
}
|
||||
}
|
||||
1842
MicroJava Tests/resources/bytecodes.txt
Normal file
1842
MicroJava Tests/resources/bytecodes.txt
Normal file
File diff suppressed because it is too large
Load Diff
14
MicroJava Tests/resources/relops.mj
Normal file
14
MicroJava Tests/resources/relops.mj
Normal file
@@ -0,0 +1,14 @@
|
||||
program Test
|
||||
{
|
||||
void main()
|
||||
int a;
|
||||
{
|
||||
read(a);
|
||||
if (a == 1) { print('='); print('='); print(','); }
|
||||
if (a != 1) { print('!'); print('='); print(','); }
|
||||
if (a < 1) { print('<'); print(','); }
|
||||
if (a <= 1) { print('<'); print('='); print(','); }
|
||||
if (a > 1) { print('>'); print(','); }
|
||||
if (a >= 1) { print('>'); print('='); print(','); }
|
||||
}
|
||||
}
|
||||
880
MicroJava Tests/tests/ssw/mj/test/CodeGenerationTest.java
Normal file
880
MicroJava Tests/tests/ssw/mj/test/CodeGenerationTest.java
Normal file
@@ -0,0 +1,880 @@
|
||||
package ssw.mj.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ssw.mj.test.support.BaseCompilerTestCase;
|
||||
|
||||
import static ssw.mj.Errors.Message.*;
|
||||
|
||||
public class CodeGenerationTest extends BaseCompilerTestCase {
|
||||
|
||||
/**
|
||||
* Symbol table for most examples of this test class.
|
||||
*/
|
||||
private void expectExampleSymTab() {
|
||||
expectSymTabUniverse();
|
||||
expectSymTab("Program A:");
|
||||
expectSymTab(" Constant: int max = 12");
|
||||
expectSymTab(" Global Variable 0: char c");
|
||||
expectSymTab(" Global Variable 1: int i");
|
||||
expectSymTab(" Type B: class (2 fields)");
|
||||
expectSymTab(" Local Variable 0: int x");
|
||||
expectSymTab(" Local Variable 1: int y");
|
||||
expectSymTab(" Method: void main (3 locals, 0 parameters)");
|
||||
expectSymTab(" Local Variable 0: int[] iarr");
|
||||
expectSymTab(" Local Variable 1: class (2 fields) b");
|
||||
expectSymTab(" Local Variable 2: int n");
|
||||
}
|
||||
|
||||
private void expectSymTabWithSum() {
|
||||
expectSymTabUniverse();
|
||||
expectSymTab("Program A:");
|
||||
expectSymTab(" Constant: int max = 12");
|
||||
expectSymTab(" Global Variable 0: char c");
|
||||
expectSymTab(" Global Variable 1: int i");
|
||||
expectSymTab(" Type B: class (2 fields)");
|
||||
expectSymTab(" Local Variable 0: int x");
|
||||
expectSymTab(" Local Variable 1: int y");
|
||||
expectSymTab(" Method: void main (4 locals, 0 parameters)");
|
||||
expectSymTab(" Local Variable 0: int[] iarr");
|
||||
expectSymTab(" Local Variable 1: class (2 fields) b");
|
||||
expectSymTab(" Local Variable 2: int n");
|
||||
expectSymTab(" Local Variable 3: int sum");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp11() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(i); " + LF + //
|
||||
" if (i <= n) n = 1;" + LF + //
|
||||
" print(n); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("1", "0");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp12() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(i); " + LF + //
|
||||
" n = 1; " + LF + //
|
||||
" if (i <= n && n < 0) n = 2;" + LF + //
|
||||
" print(n); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("2", "1");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp13() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(i); " + LF + //
|
||||
" n = 1; " + LF + //
|
||||
" if (i <= n || i < 10) n = 2;" + LF + //
|
||||
" print(n); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "2");
|
||||
addExpectedRun("2", "2");
|
||||
addExpectedRun("20", "1");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp14() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(i); " + LF + //
|
||||
" n = 1; " + LF + //
|
||||
" if (i <= n || i < 10 && i > 5) n = 2;" + LF + //
|
||||
" print(n); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "2");
|
||||
addExpectedRun("2", "1");
|
||||
addExpectedRun("6", "2");
|
||||
addExpectedRun("20", "1");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp15() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(n); " + LF + //
|
||||
" while (i <= n) { i++; }" + LF + //
|
||||
" print(i); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("-1", "0");
|
||||
addExpectedRun("1", "2");
|
||||
addExpectedRun("10", "11");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp16() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(i); " + LF + //
|
||||
" if (i <= max) n = 1; else n = 2;" + LF + //
|
||||
" print(n); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectExampleSymTab();
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("13", "2");
|
||||
addExpectedRun("12", "1");
|
||||
addExpectedRun("-1", "1");
|
||||
addExpectedRun("-13", "1");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp17() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n; int sum;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(n); " + LF + //
|
||||
" sum = 0; " + LF + //
|
||||
" while (i <= n) { sum += i; i++; }" + LF + //
|
||||
" print(sum); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectSymTabWithSum();
|
||||
addExpectedRun("0", "0");
|
||||
addExpectedRun("-1", "0");
|
||||
addExpectedRun("1", "1");
|
||||
addExpectedRun("10", "55");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bsp18() {
|
||||
initCode("program A" + LF + //
|
||||
" final int max = 12;" + LF + //
|
||||
" char c; int i;" + LF + //
|
||||
" class B { int x, y; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int[] iarr; B b; int n; int sum;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(n); " + LF + //
|
||||
" sum = 0; " + LF + //
|
||||
" i = 2;" + LF + //
|
||||
" while (i <= n) { sum += i; i++; }" + LF + //
|
||||
" print(sum); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectSymTabWithSum();
|
||||
addExpectedRun("0", "0");
|
||||
addExpectedRun("-1", "0");
|
||||
addExpectedRun("1", "0");
|
||||
addExpectedRun("10", "54");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodCall() {
|
||||
initCode("program A" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void bar() {" + LF + // 3
|
||||
" print('b');" + LF + // 4
|
||||
" print('a');" + LF + // 5
|
||||
" print('r');" + LF + // 6
|
||||
" }" + LF + // 7
|
||||
" void foo() {" + LF + // 8
|
||||
" print('f');" + LF + // 9
|
||||
" print('o');" + LF + // 10
|
||||
" print('o');" + LF + // 11
|
||||
" }" + LF + // 12
|
||||
" void main () {" + LF + // 13
|
||||
" foo();" + LF + // 14
|
||||
" }" + LF + // 15
|
||||
"}"); // 16
|
||||
|
||||
addExpectedRun("", "foo");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fib() {
|
||||
initCode("program A" + LF + //
|
||||
"{" + LF + //
|
||||
" int fib(int n) {" + LF + //
|
||||
" if (n <= 1) return 1; " + LF + //
|
||||
" return fib(n-1) + fib(n-2); " + LF + //
|
||||
" }" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(n); " + LF + //
|
||||
" print(fib(n)); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("-1", "1");
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("1", "1");
|
||||
addExpectedRun("2", "2");
|
||||
addExpectedRun("3", "3");
|
||||
addExpectedRun("4", "5");
|
||||
addExpectedRun("5", "8");
|
||||
addExpectedRun("6", "13");
|
||||
addExpectedRun("7", "21");
|
||||
addExpectedRun("8", "34");
|
||||
addExpectedRun("9", "55");
|
||||
addExpectedRun("10", "89");
|
||||
addExpectedRun("11", "144");
|
||||
addExpectedRun("22", "28657");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fibDyn() {
|
||||
initCode("program A" + LF + //
|
||||
" int[] matrix; " + LF + //
|
||||
"{" + LF + //
|
||||
" int fib(int n) int r; {" + LF + //
|
||||
" if (n <= 1) return 1; " + LF + //
|
||||
" if(matrix[n] != 0) return matrix[n]; " + LF + //
|
||||
" r = fib(n-1) + fib(n-2); " + LF + //
|
||||
" matrix[n] = r; " + LF + //
|
||||
" return r; " + LF + //
|
||||
" }" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" matrix = new int[1000]; " + LF + //
|
||||
" read(n); " + LF + //
|
||||
" print(fib(n)); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("-1", "1");
|
||||
addExpectedRun("0", "1");
|
||||
addExpectedRun("1", "1");
|
||||
addExpectedRun("2", "2");
|
||||
addExpectedRun("3", "3");
|
||||
addExpectedRun("4", "5");
|
||||
addExpectedRun("5", "8");
|
||||
addExpectedRun("6", "13");
|
||||
addExpectedRun("7", "21");
|
||||
addExpectedRun("8", "34");
|
||||
addExpectedRun("9", "55");
|
||||
addExpectedRun("10", "89");
|
||||
addExpectedRun("11", "144");
|
||||
addExpectedRun("22", "28657");
|
||||
addExpectedRun("30", "1346269");
|
||||
addExpectedRun("40", "165580141");
|
||||
addExpectedRun("45", "1836311903");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testElseIf() {
|
||||
initCode("program Test {" + LF + // 1
|
||||
" void main() int i; {" + LF + // 2
|
||||
" read(i);" + LF + // 3
|
||||
" if (i == 1) print(9);" + LF + // 4
|
||||
" else if (i == 2) print(8);" + LF + // 5
|
||||
" else print(7);" + LF + // 6
|
||||
" }" + LF + // 7
|
||||
"}");
|
||||
addExpectedRun("1", "9");
|
||||
addExpectedRun("2", "8");
|
||||
addExpectedRun("3", "7");
|
||||
addExpectedRun("4", "7");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mainVar() {
|
||||
initCode("program Test" + LF + //
|
||||
" int main;" + LF + //
|
||||
"{" + LF + //
|
||||
"}");
|
||||
expectError(4, 2, MAIN_NOT_FOUND);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mainNotVoid() {
|
||||
initCode("program Test {" + LF + //
|
||||
" char main() { }" + LF + //
|
||||
"}");
|
||||
expectError(2, 15, MAIN_NOT_VOID);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLoop() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" break;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 10, BREAK_OUTSIDE_LOOP);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnVoid() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void test() {" + LF + //
|
||||
" return 5;" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(3, 12, UNEXPECTED_RETURN_VALUE);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongReturnType() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int test() {" + LF + //
|
||||
" return 'x';" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(3, 15, RETURN_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongReturnTypeNull() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int test() {" + LF + //
|
||||
" return null;" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(3, 16, RETURN_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noReturnVal() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int test() {" + LF + //
|
||||
" return;" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(3, 11, MISSING_RETURN_VALUE);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongReturnTypeArr() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int[] test() {" + LF + //
|
||||
" return new int[10];" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(2, 9, ILLEGAL_METHOD_RETURN_TYPE);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongReturnClass() {
|
||||
initCode("program Test" + LF + //
|
||||
" class C1 { }" + LF + //
|
||||
"{" + LF + //
|
||||
" C1 test() {" + LF + //
|
||||
" return new C1;" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}");
|
||||
expectError(4, 6, ILLEGAL_METHOD_RETURN_TYPE);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noMeth() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; {" + LF + //
|
||||
" i(10);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 7, CALL_TO_NON_METHOD);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramType() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int x) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method('a');" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 15, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramTypeArr() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int[] x) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(new char[10]);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 24, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramTypeClass() {
|
||||
initCode("program Test" + LF + //
|
||||
" class C1 { }" + LF + //
|
||||
" class C2 { }" + LF + //
|
||||
"{" + LF + //
|
||||
" void method(C1 c1) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(new C2);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(7, 18, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moreParams() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int x, char c) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(1, 'a', 1);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 21, WRONG_ARGUMENT_COUNT);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lessParams() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int x, char c) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(1);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 13, WRONG_ARGUMENT_COUNT);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incompTypesCond() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; { " + LF + //
|
||||
" if (i > null) { }" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 17, INCOMPATIBLE_TYPES);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incompTypesCondArr() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int[] ia; char[] ca; { " + LF + //
|
||||
" if (ia > ca) { }" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 16, INCOMPATIBLE_TYPES);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void incompTypesCondClass() {
|
||||
initCode("program Test" + LF + //
|
||||
" class C1 { }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main() C1 c1; int i; { " + LF + //
|
||||
" if (c1 > i) { };" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(5, 15, INCOMPATIBLE_TYPES);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongEqCheck() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int[] ia1, ia2; {" + LF + //
|
||||
" if (ia1 > ia2) { }" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 18, ILLEGAL_REFERENCE_COMPARISON);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleBreak() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" while(42 > 0) /* while(true) */" + LF + //
|
||||
" {" + LF + //
|
||||
" break;" + LF + //
|
||||
" }" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBreak() {
|
||||
initCode("program A" + LF + //
|
||||
" int i;" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int n;" + LF + //
|
||||
" {" + LF + //
|
||||
" read(n); " + LF + //
|
||||
" while (i <= n) { while(1 < 2) { if(1 == 1) { break; } } if(i == 5) break; i++; }"
|
||||
+ LF + //
|
||||
" print(i); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
addExpectedRun("10", "5");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedBreak() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() " + LF + //
|
||||
" int n, o;" + LF + //
|
||||
" {" + LF + //
|
||||
" o = 21;" + LF + //
|
||||
" while(83 < 84)" + LF + //
|
||||
" {" + LF + //
|
||||
" while(167 < 168)" + LF + //
|
||||
" {" + LF + //
|
||||
" break;" + LF + //
|
||||
" }" + LF + //
|
||||
" break;" + LF + //
|
||||
" }" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lenTest() {
|
||||
initCode("program A" + LF + //
|
||||
" class A { int[] x; }" + LF + //
|
||||
" class B { A a; }" + LF + //
|
||||
" class C { B b; }" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" C[] c;" + LF + //
|
||||
" {" + LF + //
|
||||
" c = new C[5];" + LF + //
|
||||
" print(len(c));" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("5");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void basicOrdChrTest() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; char c; {" + LF + //
|
||||
" i = ord('A');" + LF + //
|
||||
" print(i);" + LF + //
|
||||
" i = ord('*');" + LF + //
|
||||
" print(i);" + LF + //
|
||||
" c = chr(49);" + LF + //
|
||||
" print(c);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("65421");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trappingOrdChrTest() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int trap() {" + LF + //
|
||||
" print(7 * 7);" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() int i; char c; {" + LF + //
|
||||
" ord('!');" + LF + //
|
||||
" chr(42);" + LF + //
|
||||
" i = ord('!');" + LF + //
|
||||
" c = chr(42);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unusedReturnVal() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int getUnused() {" + LF + //
|
||||
" return 351;" + LF + //
|
||||
" }" + LF + //
|
||||
" int polluteAndGet() {" + LF + //
|
||||
" getUnused();" + LF + //
|
||||
" return 42;" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" print(932 + polluteAndGet());" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("974");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coverUniverseMethod() {
|
||||
initCode("program Test {" + LF + //
|
||||
" int cast(char c) { return ord(c); }" + LF + //
|
||||
" int ord(char c) { return cast(c) - 30; }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" print(chr(ord('A')));" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("#");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramType2() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int x, int y) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(1, 'a');" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 18, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramTypeArr2() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void method(int[] x, int y) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(new int[10], new char[10]);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(4, 37, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paramTypeClass2() {
|
||||
initCode("program Test" + LF + //
|
||||
" class C1 { }" + LF + //
|
||||
" class C2 { }" + LF + //
|
||||
"{" + LF + //
|
||||
" void method(C1 c1, C2 c2) { }" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" method(new C1, new C1);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(7, 26, ARGUMENT_TYPE_MISMATCH);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRelops() {
|
||||
initFile("relops.mj");
|
||||
addExpectedRun("0", "!=,<,<=,");
|
||||
addExpectedRun("1", "==,<=,>=,");
|
||||
addExpectedRun("2", "!=,>,>=,");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnimals() {
|
||||
initFile("animals.mj");
|
||||
addExpectedRun("0", "cat");
|
||||
addExpectedRun("1", "dog");
|
||||
addExpectedRun("2", "octopus");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareNeg() {
|
||||
initCode("program A" + LF + //
|
||||
"{" + LF + //
|
||||
" void main ()" + LF + //
|
||||
" int neg;" + LF + //
|
||||
" {" + LF + //
|
||||
" neg = -42;" + LF + //
|
||||
" if (neg == -42) print(42);" + LF + //
|
||||
" else print(neg); " + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
addExpectedRun("42");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
// index from end tests
|
||||
@Test
|
||||
public void arrayFromEndWithFunctionCall() {
|
||||
initCode("""
|
||||
program Test
|
||||
final int len = 2;
|
||||
{
|
||||
int const1() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void main() int[] a; {
|
||||
a = new int[len];
|
||||
a[0] = 13;
|
||||
a[~const1()] = 42;
|
||||
print(a[~const1()]);
|
||||
}
|
||||
}""");
|
||||
addExpectedRun("42");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPalindrom() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void toPalindrom(char[] in, char[] out) int i, l; {
|
||||
l = len(in);
|
||||
i = 0;
|
||||
while (i < l) {
|
||||
out[i] = in[i];
|
||||
out[~(i + 1)] = in[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void printText(char[] text) int i; {
|
||||
i = 0;
|
||||
while (i < len(text)) {
|
||||
print(text[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void main() char[] a, out; int i; {
|
||||
a = new char[5];
|
||||
a[0] = 'l';
|
||||
a[1] = 'a';
|
||||
a[2] = 'g';
|
||||
a[3] = 'e';
|
||||
a[4] = 'r';
|
||||
|
||||
out = new char[10];
|
||||
|
||||
toPalindrom(a, out);
|
||||
printText(out);
|
||||
|
||||
a = new char[2];
|
||||
a[0] = 'o';
|
||||
a[1] = 't';
|
||||
|
||||
out = new char[4];
|
||||
|
||||
toPalindrom(a, out);
|
||||
printText(out);
|
||||
}
|
||||
}""");
|
||||
addExpectedRun("lagerregalotto");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void iterateArrayFromEnd() {
|
||||
initCode("""
|
||||
program Test
|
||||
final int len = 3;
|
||||
{
|
||||
void main() int[] a; int i; {
|
||||
a = new int[len];
|
||||
a[0] = 1;
|
||||
a[1] = 2;
|
||||
a[2] = 3;
|
||||
i = 1;
|
||||
while (i <= len) {
|
||||
print(a[~i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}""");
|
||||
addExpectedRun("321");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
}
|
||||
406
MicroJava Tests/tests/ssw/mj/test/ParserTest.java
Normal file
406
MicroJava Tests/tests/ssw/mj/test/ParserTest.java
Normal file
@@ -0,0 +1,406 @@
|
||||
package ssw.mj.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ssw.mj.scanner.Token;
|
||||
import ssw.mj.test.support.BaseCompilerTestCase;
|
||||
|
||||
import static ssw.mj.Errors.Message.*;
|
||||
|
||||
public class ParserTest extends BaseCompilerTestCase {
|
||||
|
||||
@Test
|
||||
public void testWorkingFinalDecls() {
|
||||
initCode("program Test" + LF + // 1
|
||||
" final int i = 1;" + LF + // 2
|
||||
" final int j = 1;" + LF + // 3
|
||||
" final int k = 1;" + LF + // 4
|
||||
"{ void main() { } }"); // 5
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingDecls() {
|
||||
initCode("program Test" + LF + // 1
|
||||
" int i;" + LF + // 2
|
||||
" int j, k;" + LF + // 3
|
||||
"{ void main() { } }"); // 4
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethods() {
|
||||
initCode("program Test" + LF + // 1
|
||||
" int i;" + LF + // 2
|
||||
" int j, k;" + LF + // 3
|
||||
"{" + LF + // 4
|
||||
" void foo() { }" + LF + // 5
|
||||
" void bar() { }" + LF + // 6
|
||||
" void main() { }" + LF + // 7
|
||||
" }" + LF // 8
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodsWithParameters() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo(int i) { }" + LF + // 3
|
||||
" void bar(int i, char c) { }" + LF + // 4
|
||||
" void main() { }" + LF + // 5
|
||||
" }" + LF // 6
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodsWithLocals() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo() int i; { }" + LF + // 3
|
||||
" void bar() int i; char c; { }" + LF + // 4
|
||||
" void main() { }" + LF + // 5
|
||||
" }" + LF // 6
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodsWithParametersAndLocals() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo(char ch) int i; { }" + LF + // 3
|
||||
" void bar(int x, int y) int i; char c; { }" + LF + // 4
|
||||
" void main() { }" + LF + // 5
|
||||
" }" + LF // 6
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodCall() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo(char ch) int i; { }" + LF + // 3
|
||||
" void main() { foo('a'); }" + LF + // 4
|
||||
" }" + LF // 5
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodCallTwoParams() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo(char ch, int x) int i; { }" + LF + // 3
|
||||
" void main() { foo('a', 1); }" + LF + // 4
|
||||
" }" + LF // 5
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingMethodCallThreeParams() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo(char ch, int x, char ch2) int i; { }" + LF + // 3
|
||||
" void main() { foo('a', 1, 'b'); }" + LF + // 4
|
||||
" }" + LF // 5
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingClass() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"class X { int i; int j; }" + LF + // 2
|
||||
"{" + LF + // 3
|
||||
" void main() X x; { x = new X; }" + LF + // 4
|
||||
"}" + LF // 5
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingArray() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void main() int[] x; { x = new int[10]; }" + LF + // 3
|
||||
"}" + LF // 4
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingIncDec() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void main() int i; { i--; i++; }" + LF + // 3
|
||||
" }" + LF // 4
|
||||
);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingElseIf() {
|
||||
initCode("program Test {" + LF + // 1
|
||||
" void main() int i; {" + LF + // 2
|
||||
" if (i > 10) i++;" + LF + // 3
|
||||
" else if (i < 5) i--;" + LF + // 4
|
||||
" else i += 8;" + LF + // 5
|
||||
" }" + LF + // 6
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingLoop() {
|
||||
initCode("program Test {" + LF + // 1
|
||||
" void main () int i; {" + LF + // 2
|
||||
" i = 0;" + LF + // 3
|
||||
" while (i < 42) {" + LF + // 4
|
||||
" i++;" + LF + // 5
|
||||
" }" + LF + // 6
|
||||
" }" + LF + // 7
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mulAssign() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; {" + LF + //
|
||||
" i = 2;" + LF + //
|
||||
" i *= 2;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnExpr() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() { }" + LF + //
|
||||
" int wrong1() { " + LF + //
|
||||
" return 2 + 3;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongConstDecl() {
|
||||
initCode("program Test" + LF + //
|
||||
" final int i = a;" + LF + //
|
||||
"{ void main() { } }");
|
||||
expectError(2, 17, INVALID_CONST_TYPE);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongDesignFollow() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; {" + LF + //
|
||||
" i**;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 6, INVALID_DESIGNATOR_STATEMENT);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongFactor() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main () int i; { " + LF + //
|
||||
" i = i + if;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 13, INVALID_FACTOR);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongRelOp() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() int i; {" + LF + //
|
||||
" if (i x 5);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 11, INVALID_REL_OP);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongStart() {
|
||||
initCode("noprogram Test { }");
|
||||
expectError(1, 1, TOKEN_EXPECTED, "program");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noProgName() {
|
||||
initCode("program { }");
|
||||
expectError(1, 9, TOKEN_EXPECTED, "identifier");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongVarDecl() {
|
||||
initCode("program Test " + LF + //
|
||||
"int var1,,,,var2;" + LF + //
|
||||
"{ void main() { } }");
|
||||
expectError(2, 10, TOKEN_EXPECTED, Token.Kind.ident.label());
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eofExpected() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {}" + LF + //
|
||||
"}moretext");
|
||||
expectError(3, 2, TOKEN_EXPECTED, "end of file");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidEOF1() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {");
|
||||
expectError(2, 16, TOKEN_EXPECTED, "}");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidEOF2() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" if ()");
|
||||
expectError(3, 9, INVALID_FACTOR);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidEOF3() {
|
||||
initCode("program Test" + LF + //
|
||||
" class C {" + LF + //
|
||||
" int i");
|
||||
expectError(3, 10, TOKEN_EXPECTED, ";");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWorkingReadAndPrint() {
|
||||
initCode("program Test {" + LF + // 1
|
||||
" void main() int i; {" + LF + // 2
|
||||
" read(i);" + LF +
|
||||
" print(i);" + LF +
|
||||
" }" + LF + //3
|
||||
"}");//4
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
// index from end tests
|
||||
@Test
|
||||
public void wrongTildeInExpr() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int i; {
|
||||
i = ~1;
|
||||
}
|
||||
}""");
|
||||
expectError(3, 9, INVALID_FACTOR);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongTildeCompoundAssign() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int i; {
|
||||
i ~= 2;
|
||||
}
|
||||
}""");
|
||||
expectError(3, 7, INVALID_DESIGNATOR_STATEMENT);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongDoubleTilde() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int[] a; int i; {
|
||||
i = a[~~1];
|
||||
}
|
||||
}
|
||||
""");
|
||||
expectError(3, 12, INVALID_FACTOR);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constantArrayAccessFromEnd() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int[] a; int i; {
|
||||
a = new int[3];
|
||||
a[~1] = 3;
|
||||
a[~2] = 2;
|
||||
a[~3] = 1;
|
||||
i = a[~1];
|
||||
print(i);
|
||||
}
|
||||
}""");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void computedArrayAccessFromEnd() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int[] a; int i; {
|
||||
a = new int[3];
|
||||
a[~(2 * 6 - 11)] = 3;
|
||||
a[~(9 - 11 + 4)] = 2;
|
||||
a[~(-(-3))] = 1;
|
||||
i = a[~(a[0] + 2 * 6 - 11)];
|
||||
print(i);
|
||||
}
|
||||
}""");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicArrayAccessFromEnd() {
|
||||
initCode("""
|
||||
program Test {
|
||||
void main() int[] a; int i; {
|
||||
read(i);
|
||||
a = new int[i];
|
||||
a[~i] = 1;
|
||||
a[~(i - 1)] = 2;
|
||||
a[~(i - 2)] = 3;
|
||||
i = a[~i];
|
||||
print(i);
|
||||
}
|
||||
}""");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globalArrayAccessFromEnd() {
|
||||
initCode("""
|
||||
program Test
|
||||
final int len = 3;
|
||||
{
|
||||
void main() int[] a; int i; {
|
||||
a = new int[len];
|
||||
a[~len] = 1;
|
||||
a[~(len - 1)] = 2;
|
||||
a[~(len - 2)] = 3;
|
||||
i = a[~len];
|
||||
print(i);
|
||||
}
|
||||
}""");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
}
|
||||
177
MicroJava Tests/tests/ssw/mj/test/RecoverTest.java
Normal file
177
MicroJava Tests/tests/ssw/mj/test/RecoverTest.java
Normal file
@@ -0,0 +1,177 @@
|
||||
package ssw.mj.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ssw.mj.Errors.Message;
|
||||
import ssw.mj.test.support.BaseCompilerTestCase;
|
||||
|
||||
import static ssw.mj.Errors.Message.*;
|
||||
|
||||
public class RecoverTest extends BaseCompilerTestCase {
|
||||
@Test
|
||||
public void wrongGlobalDecl() {
|
||||
initCode("program Test" + LF + //
|
||||
" 123;" + LF + //
|
||||
"{ void main() { } }");
|
||||
expectError(2, 3, DECLARATION_RECOVERY);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongMethDecl1() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() { }" + LF + //
|
||||
" program wrong1() { " + LF + //
|
||||
" if (1>2);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 3, METHOD_DECL_RECOVERY);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongMethDecl2() {
|
||||
initCode("program Test {" + LF + //
|
||||
" program wrong1() { " + LF + //
|
||||
" if (1>2);" + LF + //
|
||||
" }" + LF + //
|
||||
" void main() { }" + LF + //
|
||||
" program wrong2() {" + LF + //
|
||||
" if (1>2);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(2, 3, METHOD_DECL_RECOVERY);
|
||||
expectError(6, 3, METHOD_DECL_RECOVERY);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongMethDecl3() {
|
||||
initCode("program Test {" + LF + //
|
||||
" program wrong1() { }" + LF + //
|
||||
" void main() { }" + LF + //
|
||||
" program wrong2() { }" + LF + //
|
||||
"}");
|
||||
expectError(2, 3, METHOD_DECL_RECOVERY);
|
||||
expectError(4, 3, METHOD_DECL_RECOVERY);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongStat() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() { " + LF + //
|
||||
" 123;" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 5, STATEMENT_RECOVERY);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleErrors() {
|
||||
initCode("program Test " + LF + //
|
||||
" int x" + LF + //
|
||||
"{" + LF + //
|
||||
" void main( {" + LF + //
|
||||
" if (1 x 2);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 1, TOKEN_EXPECTED, ";");
|
||||
expectError(4, 14, TOKEN_EXPECTED, ")");
|
||||
expectError(5, 11, INVALID_REL_OP);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
// ---- multiple errors & recovery
|
||||
@Test
|
||||
public void noRecover1() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main this method will never recover");
|
||||
|
||||
expectError(2, 13, TOKEN_EXPECTED, "(");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noRecover2() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() { " + LF + //
|
||||
" if this method will never recover");
|
||||
|
||||
expectError(3, 8, TOKEN_EXPECTED, "(");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverDecl1() {
|
||||
initCode("program Test" + LF + //
|
||||
" int i1, if" + LF + //
|
||||
" in i2;" + LF + //
|
||||
" final int i3 = 0;" + LF + //
|
||||
"{" + LF + //
|
||||
" void main() { " + LF + //
|
||||
" if (i1 < i3);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectError(2, 11, TOKEN_EXPECTED, "identifier");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverDecl2() {
|
||||
initCode("program Test" + LF + //
|
||||
" int i1, if" + LF + //
|
||||
" in i2;" + LF + //
|
||||
" int i3;" + LF + //
|
||||
"{" + LF + //
|
||||
" void main() { " + LF + //
|
||||
" if (i1 < i3);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectError(2, 11, TOKEN_EXPECTED, "identifier");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoverStat() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() { " + LF + //
|
||||
" 567 since distance stays too small no follow up errors here;" + LF + //
|
||||
" if (1 < 2);" + LF + //
|
||||
" if (1 x 2);" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
|
||||
expectError(3, 5, STATEMENT_RECOVERY);
|
||||
expectError(5, 11, INVALID_REL_OP);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetErrDist() {
|
||||
initCode("program Test {" + LF + //
|
||||
" void main() {" + LF + //
|
||||
" if () if () if();" + LF + //
|
||||
" }" + LF + //
|
||||
"}");
|
||||
expectError(3, 9, INVALID_FACTOR);
|
||||
expectError(3, 15, INVALID_FACTOR);
|
||||
expectError(3, 20, INVALID_FACTOR);
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illegalMethodStart() {
|
||||
initCode("program Test" + LF + // 1
|
||||
"{" + LF + // 2
|
||||
" void foo()" + LF + // 3
|
||||
" void foo(char x) { }" + LF + // 4
|
||||
" void main() { }" + LF + // 5
|
||||
"}" + LF // 6
|
||||
);
|
||||
expectError(4, 3, Message.TOKEN_EXPECTED, "{");
|
||||
parseVerifyVisualize();
|
||||
}
|
||||
}
|
||||
888
MicroJava Tests/tests/ssw/mj/test/ScannerTest.java
Normal file
888
MicroJava Tests/tests/ssw/mj/test/ScannerTest.java
Normal file
@@ -0,0 +1,888 @@
|
||||
package ssw.mj.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import ssw.mj.test.support.BaseCompilerTestCase;
|
||||
|
||||
import static ssw.mj.Errors.Message.*;
|
||||
import static ssw.mj.scanner.Token.Kind.*;
|
||||
|
||||
public class ScannerTest extends BaseCompilerTestCase {
|
||||
private static final char invalidChar = (char) 65533;
|
||||
|
||||
@Test
|
||||
public void oneToken() {
|
||||
initScannerCode(";");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(eof, 1, 2);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoTokens() {
|
||||
initScannerCode(";;");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(semicolon, 1, 2);
|
||||
expectToken(eof, 1, 3);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void space() {
|
||||
initScannerCode("; ;");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(semicolon, 1, 4);
|
||||
expectToken(eof, 1, 5);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tabulator() {
|
||||
initScannerCode(";\t\t;");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(semicolon, 1, 4);
|
||||
expectToken(eof, 1, 5);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noToken() {
|
||||
initScannerCode("");
|
||||
|
||||
expectToken(eof, 1, 1);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void crLfLineSeparators() {
|
||||
initScannerCode(";" + CR + LF + " ;" + CR + LF + " ; ");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(semicolon, 2, 2);
|
||||
expectToken(semicolon, 3, 3);
|
||||
expectToken(eof, 3, 5);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lFLineSeparators() {
|
||||
initScannerCode(";" + LF + " ;" + LF + " ; ");
|
||||
|
||||
expectToken(semicolon, 1, 1);
|
||||
expectToken(semicolon, 2, 2);
|
||||
expectToken(semicolon, 3, 3);
|
||||
expectToken(eof, 3, 5);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar1() {
|
||||
initScannerCode(" {" + invalidChar + "} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, invalidChar);
|
||||
expectToken(rbrace, 1, 4);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar2() {
|
||||
initScannerCode(" {\0} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, '\0');
|
||||
expectToken(rbrace, 1, 4);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar3() {
|
||||
initScannerCode(" {&} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, '&');
|
||||
expectToken(rbrace, 1, 4);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar4() {
|
||||
initScannerCode(" {|} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, '|');
|
||||
expectToken(rbrace, 1, 4);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar5() {
|
||||
initScannerCode(" {!} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, '!');
|
||||
expectToken(rbrace, 1, 4);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidChar6() {
|
||||
initScannerCode(" {ident" + invalidChar + "} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "ident");
|
||||
expectToken(none, 1, 8);
|
||||
expectError(1, 8, INVALID_CHAR, invalidChar);
|
||||
expectToken(rbrace, 1, 9);
|
||||
expectToken(eof, 1, 11);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ident() {
|
||||
initScannerCode(" {i I i1 i_ i1I_i} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "i");
|
||||
expectToken(ident, 1, 5, "I");
|
||||
expectToken(ident, 1, 7, "i1");
|
||||
expectToken(ident, 1, 10, "i_");
|
||||
expectToken(ident, 1, 13, "i1I_i");
|
||||
expectToken(rbrace, 1, 18);
|
||||
expectToken(eof, 1, 20);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void indentSepararator() {
|
||||
initScannerCode(" {i[i<i0i_i>i]i} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "i");
|
||||
expectToken(lbrack, 1, 4);
|
||||
expectToken(ident, 1, 5, "i");
|
||||
expectToken(lss, 1, 6);
|
||||
expectToken(ident, 1, 7, "i0i_i");
|
||||
expectToken(gtr, 1, 12);
|
||||
expectToken(ident, 1, 13, "i");
|
||||
expectToken(rbrack, 1, 14);
|
||||
expectToken(ident, 1, 15, "i");
|
||||
expectToken(rbrace, 1, 16);
|
||||
expectToken(eof, 1, 18);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleIdent() {
|
||||
initScannerCode("i");
|
||||
|
||||
expectToken(ident, 1, 1, "i");
|
||||
expectToken(eof, 1, 2);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void number() {
|
||||
initScannerCode(" {123 2147483647} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(number, 1, 3, 123);
|
||||
expectToken(number, 1, 7, 2147483647);
|
||||
expectToken(rbrace, 1, 17);
|
||||
expectToken(eof, 1, 19);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleNumber() {
|
||||
initScannerCode("123");
|
||||
|
||||
expectToken(number, 1, 1, 123);
|
||||
expectToken(eof, 1, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeNumber() {
|
||||
initScannerCode(" {-123} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(minus, 1, 3);
|
||||
expectToken(number, 1, 4, 123);
|
||||
expectToken(rbrace, 1, 7);
|
||||
expectToken(eof, 1, 9);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bigNumber() {
|
||||
initScannerCode(" {2147483648} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectInvalidToken(number, 1, 3);
|
||||
expectError(1, 3, BIG_NUM, "2147483648");
|
||||
expectToken(rbrace, 1, 13);
|
||||
expectToken(eof, 1, 15);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void negativeBigNumber() {
|
||||
initScannerCode(" {-2147483648} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(minus, 1, 3);
|
||||
expectInvalidToken(number, 1, 4);
|
||||
expectError(1, 4, BIG_NUM, "2147483648");
|
||||
expectToken(rbrace, 1, 14);
|
||||
expectToken(eof, 1, 16);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reallyBigNumber() {
|
||||
initScannerCode(" {1234567890123456789012345678901234567890} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectInvalidToken(number, 1, 3);
|
||||
expectError(1, 3, BIG_NUM, "1234567890123456789012345678901234567890");
|
||||
expectToken(rbrace, 1, 43);
|
||||
expectToken(eof, 1, 45);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numberIdent() {
|
||||
initScannerCode(" {123abc123 123break} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(number, 1, 3, 123);
|
||||
expectToken(ident, 1, 6, "abc123");
|
||||
expectToken(number, 1, 13, 123);
|
||||
expectToken(break_, 1, 16);
|
||||
expectToken(rbrace, 1, 21);
|
||||
expectToken(eof, 1, 23);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void numbersSeparated() {
|
||||
initScannerCode("123.456,789");
|
||||
|
||||
expectToken(number, 1, 1, 123);
|
||||
expectToken(period, 1, 4);
|
||||
expectToken(number, 1, 5, 456);
|
||||
expectToken(comma, 1, 8);
|
||||
expectToken(number, 1, 9, 789);
|
||||
expectToken(eof, 1, 12);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identsSeparated() {
|
||||
initScannerCode("abc.def,ghi\njkl");
|
||||
|
||||
expectToken(ident, 1, 1, "abc");
|
||||
expectToken(period, 1, 4);
|
||||
expectToken(ident, 1, 5, "def");
|
||||
expectToken(comma, 1, 8);
|
||||
expectToken(ident, 1, 9, "ghi");
|
||||
expectToken(ident, 2, 1, "jkl");
|
||||
expectToken(eof, 2, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newlineBetweenIdentifiersAndTokens() {
|
||||
initScannerCode("anIdentifier" + LF + "class" + LF + "anotherIdentifier");
|
||||
|
||||
expectToken(ident, 1, 1, "anIdentifier");
|
||||
expectToken(class_, 2, 1);
|
||||
expectToken(ident, 3, 1, "anotherIdentifier");
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newlineAndSpacesBetweenIdentifiersAndTokens() {
|
||||
initScannerCode(" anIdentifier" + LF + " class" + LF + " anotherIdentifier");
|
||||
|
||||
expectToken(ident, 1, 2, "anIdentifier");
|
||||
expectToken(class_, 2, 3);
|
||||
expectToken(ident, 3, 4, "anotherIdentifier");
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void charConst() {
|
||||
initScannerCode(" {' ' 'A' 'z' '0' '!' '\"' '" + invalidChar + "' '\0'} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, ' ');
|
||||
expectToken(charConst, 1, 7, 'A');
|
||||
expectToken(charConst, 1, 11, 'z');
|
||||
expectToken(charConst, 1, 15, '0');
|
||||
expectToken(charConst, 1, 19, '!');
|
||||
expectToken(charConst, 1, 23, '"');
|
||||
expectToken(charConst, 1, 27, invalidChar);
|
||||
expectToken(charConst, 1, 31, '\0');
|
||||
expectToken(rbrace, 1, 34);
|
||||
expectToken(eof, 1, 36);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleCharConst() {
|
||||
initScannerCode("'x'");
|
||||
|
||||
expectToken(charConst, 1, 1, 'x');
|
||||
expectToken(eof, 1, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void escapeCharConst() {
|
||||
initScannerCode(" {'\\n' '\\r' '\\\\' '\\''} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\n');
|
||||
expectToken(charConst, 1, 8, '\r');
|
||||
expectToken(charConst, 1, 13, '\\');
|
||||
expectToken(charConst, 1, 18, '\'');
|
||||
expectToken(rbrace, 1, 22);
|
||||
expectToken(eof, 1, 24);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleEscapeCharConst() {
|
||||
initScannerCode("'\\n'");
|
||||
|
||||
expectToken(charConst, 1, 1, '\n');
|
||||
expectToken(eof, 1, 5);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyCharConst() {
|
||||
initScannerCode(" {''} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, EMPTY_CHARCONST);
|
||||
expectToken(rbrace, 1, 5);
|
||||
expectToken(eof, 1, 7);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unclosedCharConst() {
|
||||
initScannerCode(" {'a} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, MISSING_QUOTE);
|
||||
expectToken(rbrace, 1, 5);
|
||||
expectToken(eof, 1, 7);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyAndUnclosedCharConst() {
|
||||
initScannerCode(" ''' ");
|
||||
|
||||
expectToken(charConst, 1, 2, '\0');
|
||||
expectError(1, 2, EMPTY_CHARCONST);
|
||||
expectToken(charConst, 1, 4, '\0');
|
||||
expectError(1, 4, MISSING_QUOTE);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unclosedEscapeCharConst() {
|
||||
initScannerCode(" {'\\r} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, MISSING_QUOTE);
|
||||
expectToken(rbrace, 1, 6);
|
||||
expectToken(eof, 1, 8);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unclosedBackslashCharConst() {
|
||||
initScannerCode(" {'\\'} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, MISSING_QUOTE);
|
||||
expectToken(rbrace, 1, 6);
|
||||
expectToken(eof, 1, 8);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidEscapeCharConst() {
|
||||
initScannerCode(" {'\\a'} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, UNDEFINED_ESCAPE, 'a');
|
||||
expectToken(rbrace, 1, 7);
|
||||
expectToken(eof, 1, 9);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidEscapeCharMissingQuote() {
|
||||
initScannerCode(" '\\a ");
|
||||
|
||||
expectToken(charConst, 1, 2, '\0');
|
||||
expectError(1, 2, UNDEFINED_ESCAPE, 'a');
|
||||
expectError(1, 2, MISSING_QUOTE);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fileEndCharConst() {
|
||||
initScannerCode(" {'");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, EOF_IN_CHAR);
|
||||
expectToken(eof, 1, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineEndCharConst() {
|
||||
initScannerCode(" {'" + LF + "'a'} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, ILLEGAL_LINE_END);
|
||||
expectToken(charConst, 2, 1, 'a');
|
||||
expectToken(rbrace, 2, 4);
|
||||
expectToken(eof, 2, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineEndWithCRCharConst() {
|
||||
initScannerCode(" {'" + CR + LF + "'a'} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(charConst, 1, 3, '\0');
|
||||
expectError(1, 3, ILLEGAL_LINE_END);
|
||||
expectToken(charConst, 2, 1, 'a');
|
||||
expectToken(rbrace, 2, 4);
|
||||
expectToken(eof, 2, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyword1() {
|
||||
initScannerCode(" { if } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(if_, 1, 4);
|
||||
expectToken(rbrace, 1, 7);
|
||||
expectToken(eof, 1, 9);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyword2() {
|
||||
initScannerCode(" {if} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(if_, 1, 3);
|
||||
expectToken(rbrace, 1, 5);
|
||||
expectToken(eof, 1, 7);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleKeyword() {
|
||||
initScannerCode("if");
|
||||
|
||||
expectToken(if_, 1, 1);
|
||||
expectToken(eof, 1, 3);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyword3() {
|
||||
initScannerCode(" {for_} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "for_");
|
||||
expectToken(rbrace, 1, 7);
|
||||
expectToken(eof, 1, 9);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyword4() {
|
||||
initScannerCode(" {&if} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(none, 1, 3);
|
||||
expectError(1, 3, INVALID_CHAR, '&');
|
||||
expectToken(if_, 1, 4);
|
||||
expectToken(rbrace, 1, 6);
|
||||
expectToken(eof, 1, 8);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caseSensitive1() {
|
||||
initScannerCode(" {For} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "For");
|
||||
expectToken(rbrace, 1, 6);
|
||||
expectToken(eof, 1, 8);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caseSensitive2() {
|
||||
initScannerCode(" {FOR} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "FOR");
|
||||
expectToken(rbrace, 1, 6);
|
||||
expectToken(eof, 1, 8);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleSingleLineComment() {
|
||||
initScannerCode(" {/* Simple / single * line comment. */} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 1, 40);
|
||||
expectToken(eof, 1, 42);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleMultiLineComment() {
|
||||
initScannerCode(" {" + LF + " /* Simple " + LF + " / multi * line " + LF //
|
||||
+ " comment. */ " + LF + " } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 5, 2);
|
||||
expectToken(eof, 5, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedSingleLineComment2() {
|
||||
initScannerCode(" {/*//*///****/**/*/} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 1, 21);
|
||||
expectToken(eof, 1, 23);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedSingleLineComment() {
|
||||
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 1, 62);
|
||||
expectToken(eof, 1, 64);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedMultiLineComment() {
|
||||
initScannerCode(" {" + LF + " /* This / is * a " + LF + " /* nested " + LF //
|
||||
+ " /* multi line */" + LF + " comment. " + LF + " */" + LF //
|
||||
+ " */ " + LF + " } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 8, 2);
|
||||
expectToken(eof, 8, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedMultiLineComment2() {
|
||||
initScannerCode(" {" + LF + " /* This / is * a " + LF + " /* nested " + LF //
|
||||
+ " /* multi /*/* double nestet */*/ line */" + LF + " comment. " + LF + " */" + LF //
|
||||
+ " */ " + LF + " } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 8, 2);
|
||||
expectToken(eof, 8, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commentAtEnd1() {
|
||||
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/ ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(eof, 1, 63);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commentAtEnd2() {
|
||||
initScannerCode(" {/* This / is * a /* nested /* single line */ comment. */*/");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(eof, 1, 62);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unclosedComment() {
|
||||
initScannerCode(" {/* This / is * a nested unclosed comment. } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectError(1, 3, EOF_IN_COMMENT);
|
||||
expectToken(eof, 1, 47);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unclosedComment2() {
|
||||
initScannerCode(" {/*/");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectError(1, 3, EOF_IN_COMMENT);
|
||||
expectToken(eof, 1, 6);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedUnclosedComment() {
|
||||
initScannerCode(" {/* This / is * a /* nested /* unclosed comment. */} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectError(1, 3, EOF_IN_COMMENT);
|
||||
expectToken(eof, 1, 55);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedUnclosedComment2() {
|
||||
initScannerCode(" {/* This / is * a nested unclosed /* comment. } */");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectError(1, 3, EOF_IN_COMMENT);
|
||||
expectToken(eof, 1, 52);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noLineComment() {
|
||||
initScannerCode(" {This is // no comment} ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(ident, 1, 3, "This");
|
||||
expectToken(ident, 1, 8, "is");
|
||||
expectToken(slash, 1, 11);
|
||||
expectToken(slash, 1, 12);
|
||||
expectToken(ident, 1, 14, "no");
|
||||
expectToken(ident, 1, 17, "comment");
|
||||
expectToken(rbrace, 1, 24);
|
||||
expectToken(eof, 1, 26);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleComments() {
|
||||
initScannerCode(" {/*a*/ /*b*/ /*c*/ } ");
|
||||
|
||||
expectToken(lbrace, 1, 2);
|
||||
expectToken(rbrace, 1, 23);
|
||||
expectToken(eof, 1, 25);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
// index from end tests
|
||||
|
||||
@Test
|
||||
public void negativeIndexFromEnd() {
|
||||
initScannerCode("~-7");
|
||||
|
||||
expectToken(tilde, 1, 1);
|
||||
expectToken(minus, 1, 2);
|
||||
expectToken(number, 1, 3, 7);
|
||||
expectToken(eof, 1, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void singleTildeChar() {
|
||||
initScannerCode("'~'");
|
||||
|
||||
expectToken(charConst, 1, 1, '~');
|
||||
expectToken(eof, 1, 4);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commentInIndexFromEnd() {
|
||||
initScannerCode("~/*comment*/7");
|
||||
|
||||
expectToken(tilde, 1, 1);
|
||||
expectToken(number, 1, 13, 7);
|
||||
expectToken(eof, 1,14);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doubleTilde() {
|
||||
initScannerCode("~~");
|
||||
|
||||
expectToken(tilde, 1, 1);
|
||||
expectToken(tilde, 1, 2);
|
||||
expectToken(eof, 1,3);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allTokens() {
|
||||
initScannerCode("anIdentifier 123 'c'" + LF //
|
||||
+ "+ - * / % == != < <= > >= && || = += -= *= /= %= ++ -- ; , . ( ) [ ] { }" + LF //
|
||||
+ "break class else final if new print program read return void while ~" + LF);
|
||||
|
||||
expectToken(ident, 1, 1, "anIdentifier");
|
||||
expectToken(number, 1, 14, 123);
|
||||
expectToken(charConst, 1, 18, 'c');
|
||||
expectToken(plus, 2, 1);
|
||||
expectToken(minus, 2, 3);
|
||||
expectToken(times, 2, 5);
|
||||
expectToken(slash, 2, 7);
|
||||
expectToken(rem, 2, 9);
|
||||
expectToken(eql, 2, 11);
|
||||
expectToken(neq, 2, 14);
|
||||
expectToken(lss, 2, 17);
|
||||
expectToken(leq, 2, 19);
|
||||
expectToken(gtr, 2, 22);
|
||||
expectToken(geq, 2, 24);
|
||||
expectToken(and, 2, 27);
|
||||
expectToken(or, 2, 30);
|
||||
expectToken(assign, 2, 33);
|
||||
expectToken(plusas, 2, 35);
|
||||
expectToken(minusas, 2, 38);
|
||||
expectToken(timesas, 2, 41);
|
||||
expectToken(slashas, 2, 44);
|
||||
expectToken(remas, 2, 47);
|
||||
expectToken(pplus, 2, 50);
|
||||
expectToken(mminus, 2, 53);
|
||||
expectToken(semicolon, 2, 56);
|
||||
expectToken(comma, 2, 58);
|
||||
expectToken(period, 2, 60);
|
||||
expectToken(lpar, 2, 62);
|
||||
expectToken(rpar, 2, 64);
|
||||
expectToken(lbrack, 2, 66);
|
||||
expectToken(rbrack, 2, 68);
|
||||
expectToken(lbrace, 2, 70);
|
||||
expectToken(rbrace, 2, 72);
|
||||
expectToken(break_, 3, 1);
|
||||
expectToken(class_, 3, 7);
|
||||
expectToken(else_, 3, 13);
|
||||
expectToken(final_, 3, 18);
|
||||
expectToken(if_, 3, 24);
|
||||
expectToken(new_, 3, 27);
|
||||
expectToken(print, 3, 31);
|
||||
expectToken(program, 3, 37);
|
||||
expectToken(read, 3, 45);
|
||||
expectToken(return_, 3, 50);
|
||||
expectToken(void_, 3, 57);
|
||||
expectToken(while_, 3, 62);
|
||||
expectToken(tilde, 3, 68);
|
||||
expectToken(eof, 4, 1);
|
||||
|
||||
scanVerifyVisualize();
|
||||
}
|
||||
}
|
||||
1076
MicroJava Tests/tests/ssw/mj/test/SimpleCodeGenerationTest.java
Normal file
1076
MicroJava Tests/tests/ssw/mj/test/SimpleCodeGenerationTest.java
Normal file
File diff suppressed because it is too large
Load Diff
1102
MicroJava Tests/tests/ssw/mj/test/SymbolTableTest.java
Normal file
1102
MicroJava Tests/tests/ssw/mj/test/SymbolTableTest.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,401 @@
|
||||
package ssw.mj.test.support;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
import ssw.mj.Errors;
|
||||
import ssw.mj.Interpreter;
|
||||
import ssw.mj.Visualizer;
|
||||
import ssw.mj.codegen.Decoder;
|
||||
import ssw.mj.impl.Parser;
|
||||
import ssw.mj.impl.Scanner;
|
||||
import ssw.mj.scanner.Token;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.StringReader;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Base class for test cases with utility methods used by all tests.
|
||||
*/
|
||||
|
||||
@Timeout(value = Configuration.TIMEOUT, threadMode = Timeout.ThreadMode.SEPARATE_THREAD)
|
||||
public abstract class BaseCompilerTestCase {
|
||||
|
||||
public static final String CR = "\r";
|
||||
public static final String LF = "\n";
|
||||
private List<String> expectedErrors;
|
||||
private List<String> expectedTokens;
|
||||
private List<Token> expectedTokensFull;
|
||||
private List<String> expectedSymTab;
|
||||
private List<String> expectedRuntimeErrors;
|
||||
private String source;
|
||||
private Scanner scanner;
|
||||
protected Parser parser;
|
||||
private String callingClassAndMethod;
|
||||
private final List<String> runInputs = new ArrayList<>();
|
||||
private final List<String> expectedOutputs = new ArrayList<>();
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
// initialize expected compiler output
|
||||
expectedErrors = new ArrayList<>();
|
||||
expectedTokens = new ArrayList<>();
|
||||
expectedTokensFull = new ArrayList<>();
|
||||
expectedSymTab = new ArrayList<>();
|
||||
expectedRuntimeErrors = new ArrayList<>();
|
||||
|
||||
if (Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES) {
|
||||
// print header for console output
|
||||
System.out.println("--------------------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
protected void initCode(String code) {
|
||||
initScannerCode(code);
|
||||
parser = new Parser(scanner);
|
||||
}
|
||||
|
||||
protected void initFile(String filename) {
|
||||
initScannerFile(filename);
|
||||
parser = new Parser(scanner);
|
||||
}
|
||||
|
||||
protected void initScannerCode(String code) {
|
||||
source = code;
|
||||
scanner = new Scanner(new StringReader(code));
|
||||
}
|
||||
|
||||
protected void initScannerFile(String filename) {
|
||||
try {
|
||||
ClassLoader classLoader = getClass().getClassLoader();
|
||||
URL resource = classLoader.getResource(filename);
|
||||
if (resource == null) {
|
||||
throw new RuntimeException("resource %s not found".formatted(filename));
|
||||
}
|
||||
String urlAsStr = resource.getFile();
|
||||
// replaces %20 Urlencoding with " " (blank space), as e.g. Linux cannot handle url paths
|
||||
String path = URLDecoder.decode(urlAsStr, StandardCharsets.UTF_8);
|
||||
File file = new File(path);
|
||||
scanner = new Scanner(new FileReader(file));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> splitString(String s) {
|
||||
StringTokenizer st = new StringTokenizer(s, "\n");
|
||||
List<String> result = new ArrayList<>();
|
||||
while (st.hasMoreTokens()) {
|
||||
result.add(st.nextToken());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void print(String title, List<String> expected, List<String> actual) {
|
||||
if (expected.isEmpty() && actual.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
System.out.format("%s - %s\n", callingClassAndMethod, title);
|
||||
if (Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES || !expected.equals(actual)) {
|
||||
System.out.format(" %-60s %s\n", "expected", "actual");
|
||||
int lines = Math.max(expected.size(), actual.size());
|
||||
for (int i = 0; i < lines; i++) {
|
||||
String expectedLine = (i < expected.size() ? expected.get(i) : "");
|
||||
String actualLine = (i < actual.size() ? actual.get(i) : "");
|
||||
System.out.format("%s %-60s %s\n", (expectedLine.equals(actualLine) ? " " : "x"), expectedLine,
|
||||
actualLine);
|
||||
}
|
||||
} else {
|
||||
if (expected.equals(actual)) {
|
||||
System.out.println(" correct (exact comparison hidden, enable via Configuration.ALSO_PRINT_SUCCESSFUL_TESTCASES)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addRun(String input, String output, String error) {
|
||||
runInputs.add(input);
|
||||
expectedOutputs.add(output);
|
||||
expectedRuntimeErrors.add(error);
|
||||
}
|
||||
|
||||
protected void addExpectedRun(String output) {
|
||||
addExpectedRun("", output);
|
||||
}
|
||||
|
||||
protected void addExpectedRun(String input, String output) {
|
||||
addRun(input, output, "");
|
||||
}
|
||||
|
||||
protected void addFailingRun(String error) {
|
||||
addFailingRun("", error);
|
||||
}
|
||||
|
||||
protected void addFailingRun(String input, String error) {
|
||||
addRun(input, "", error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scans the given code and checks the scanned tokens against the expected ones.
|
||||
* Also checks that expected errors occur.
|
||||
* Finally, the method creates a visualization of the scanned tokens if the test was run
|
||||
* with @link ssw.mj.TracingClassLoader as system classloader.
|
||||
*/
|
||||
protected void scanVerifyVisualize() {
|
||||
callingClassAndMethod = getCallingClassAndMethod(1);
|
||||
|
||||
List<Token> actualTokens = new ArrayList<>();
|
||||
|
||||
// scan only the expected number of tokens to prevent endless loops
|
||||
for (int i = 0; i < getExpectedTokens().size(); i++) {
|
||||
actualTokens.add(scanner.next());
|
||||
}
|
||||
|
||||
List<String> actualTokenStrings = actualTokens.stream().map(Token::toString).toList();
|
||||
|
||||
Visualizer.createScannerVisualization(source, actualTokens, getExpectedTokensFull(), false);
|
||||
|
||||
printErrors();
|
||||
printTokens(actualTokenStrings);
|
||||
|
||||
verifyErrors();
|
||||
verifyTokens(actualTokenStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given code and checks it for expected errors, matching sym tab and matching byte code.
|
||||
* Then it executed the interpreter for all given inputs.
|
||||
* Finally, the method creates a visualization of the parse tree if the test was run
|
||||
* with @link ssw.mj.TracingClassLoader as system classloader.
|
||||
*/
|
||||
protected void parseVerifyVisualize() {
|
||||
callingClassAndMethod = getCallingClassAndMethod(1);
|
||||
|
||||
try {
|
||||
parser.parse();
|
||||
assertEquals(Token.Kind.eof, scanner.next().kind, "Complete input should be scanned");
|
||||
} catch (Errors.PanicMode error) {
|
||||
// Ignore, nothing to do
|
||||
}
|
||||
|
||||
printErrors();
|
||||
printSymTab();
|
||||
|
||||
verifyErrors();
|
||||
verifySymTab();
|
||||
|
||||
if (ByteCodeTestSupport.GENERATE_REFERENCE_BYTE_CODE && expectedErrors.isEmpty()) {
|
||||
ByteCodeTestSupport.generateReferenceByteCode(callingClassAndMethod, parser);
|
||||
} else {
|
||||
printAndVerifyByteCode(callingClassAndMethod);
|
||||
}
|
||||
|
||||
for (int i = 0; i < runInputs.size(); i++) {
|
||||
run(i);
|
||||
}
|
||||
|
||||
Visualizer.createParserVisualization(source, false);
|
||||
}
|
||||
|
||||
private static String getCallingClassAndMethod(int up) {
|
||||
// [0] getStackTrace -> [1] getCallingMethodName -> [2] caller of getCallingMethodName -> [3] ...
|
||||
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
|
||||
StackTraceElement e = stacktrace[2 + up];
|
||||
String fullyQualifiedClassName = e.getClassName();
|
||||
String className = fullyQualifiedClassName.substring(Math.max(fullyQualifiedClassName.lastIndexOf(".") + 1, 0));
|
||||
return className + "." + e.getMethodName() + "()";
|
||||
}
|
||||
|
||||
private void run(int i) {
|
||||
Interpreter.BufferIO io = new Interpreter.BufferIO(runInputs.get(i));
|
||||
Interpreter interpreter = new Interpreter(
|
||||
parser.code.buf,
|
||||
parser.code.mainpc,
|
||||
parser.code.dataSize,
|
||||
io,
|
||||
Configuration.PRINT_INTERPRETER_DEBUG_OUTPUT);
|
||||
try {
|
||||
interpreter.run();
|
||||
} catch (IllegalStateException e) {
|
||||
verifyRuntimeError(i, e);
|
||||
}
|
||||
String output = io.getOutput();
|
||||
verifyOutput(i, output);
|
||||
}
|
||||
|
||||
|
||||
private void printErrors() {
|
||||
print("Errors", expectedErrors, getActualErrors());
|
||||
}
|
||||
|
||||
private void printTokens(List<String> actualTokens) {
|
||||
print("Tokens", getExpectedTokens(), actualTokens);
|
||||
}
|
||||
|
||||
private void printSymTab() {
|
||||
if (!expectedSymTab.isEmpty()) {
|
||||
print("Symbol Table", getExpectedSymTab(), getActualSymTab());
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyErrors() {
|
||||
assertEquals(expectedErrors, getActualErrors(), "Errors");
|
||||
}
|
||||
|
||||
private void verifyTokens(List<String> actualTokens) {
|
||||
assertEquals(getExpectedTokens(), actualTokens, "Tokens");
|
||||
assertTrue(scanner.next().toString().contains("end of file"), "Complete Input Scanned");
|
||||
}
|
||||
|
||||
private void verifySymTab() {
|
||||
if (!expectedSymTab.isEmpty()) {
|
||||
assertEquals(getExpectedSymTab(), getActualSymTab(), "Symbol Table");
|
||||
}
|
||||
}
|
||||
|
||||
private void printAndVerifyByteCode(String callingClassAndMethod) {
|
||||
if (ByteCodeTestSupport.BYTE_CODES.containsKey(callingClassAndMethod)) {
|
||||
List<String> possibleByteCodes = ByteCodeTestSupport.BYTE_CODES.get(callingClassAndMethod);
|
||||
if (possibleByteCodes.size() == 1) {
|
||||
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(0));
|
||||
print("Bytecode", expected, getActualByteCodeLines());
|
||||
// Verify that the bytecode is correct
|
||||
assertEquals(expected, getActualByteCodeLines(), "Byte Code");
|
||||
} else {
|
||||
int matchIdx = -1;
|
||||
for (int i = 0; i < possibleByteCodes.size(); i++) {
|
||||
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(i));
|
||||
if (expected.equals(getActualByteCodeLines())) {
|
||||
matchIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matchIdx < 0) {
|
||||
// No bytecode matched
|
||||
// print all
|
||||
for (int i = 0; i < possibleByteCodes.size(); i++) {
|
||||
List<String> expected = getExpectedByteCodeLines(possibleByteCodes.get(i));
|
||||
print("Possible Bytecode %d".formatted(i + 1), expected, getActualByteCodeLines());
|
||||
}
|
||||
// fail assert on first
|
||||
assertEquals(getExpectedByteCodeLines(possibleByteCodes.get(0)), getActualByteCodeLines(), "Byte Code");
|
||||
} else {
|
||||
// bytecode at idx matchIdx correctly generated
|
||||
// print working bytecode
|
||||
print("Bytecode", getExpectedByteCodeLines(possibleByteCodes.get(matchIdx)), getActualByteCodeLines());
|
||||
// assert not really necessary since we already know we matched successfully
|
||||
assertEquals(getExpectedByteCodeLines(possibleByteCodes.get(matchIdx)), getActualByteCodeLines(), "Byte Code");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyOutput(int runIdx, String actualOutput) {
|
||||
assertEquals(expectedOutputs.get(runIdx), actualOutput, "Unexpected result when input is \"" + runInputs.get(runIdx) + "\": ");
|
||||
}
|
||||
|
||||
private void verifyRuntimeError(int runIdx, IllegalStateException e) {
|
||||
assertEquals(expectedRuntimeErrors.get(runIdx), e.getMessage(), "Unexpected runtime error message when input is \"" + runInputs.get(runIdx) + "\": ");
|
||||
}
|
||||
|
||||
private List<String> getExpectedByteCodeLines(String bytecode) {
|
||||
return Arrays.stream(bytecode.split("\n")).toList();
|
||||
}
|
||||
|
||||
private List<String> getActualByteCodeLines() {
|
||||
return Arrays.stream(new Decoder().decode(parser.code).split("\n")).toList();
|
||||
}
|
||||
|
||||
private List<String> getActualErrors() {
|
||||
return splitString(scanner.errors.dump());
|
||||
}
|
||||
|
||||
private List<String> getExpectedTokens() {
|
||||
return expectedTokens;
|
||||
}
|
||||
|
||||
private List<Token> getExpectedTokensFull() {
|
||||
return expectedTokensFull;
|
||||
}
|
||||
|
||||
private List<String> getExpectedSymTab() {
|
||||
return expectedSymTab;
|
||||
}
|
||||
|
||||
private List<String> getActualSymTab() {
|
||||
return splitString(SymTabDumper.dump(parser.tab));
|
||||
}
|
||||
|
||||
protected void expectError(int line, int col, Errors.Message msg, Object... msgParams) {
|
||||
expectedErrors.add("-- line " + line + " col " + col + ": " + msg.format(msgParams));
|
||||
}
|
||||
|
||||
protected void expectToken(Token.Kind kind, int line, int col) {
|
||||
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind);
|
||||
expectedTokensFull.add(new Token(kind, line, col));
|
||||
}
|
||||
|
||||
protected void expectToken(Token.Kind kind, int line, int col, String val) {
|
||||
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + val);
|
||||
|
||||
Token token = new Token(kind, line, col);
|
||||
token.val = val;
|
||||
expectedTokensFull.add(token);
|
||||
}
|
||||
|
||||
protected void expectToken(Token.Kind kind, int line, int col, int val) {
|
||||
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + val + ", numVal " + val);
|
||||
|
||||
Token token = new Token(kind, line, col);
|
||||
token.val = String.valueOf(val);
|
||||
token.numVal = val;
|
||||
expectedTokensFull.add(token);
|
||||
}
|
||||
|
||||
protected void expectToken(Token.Kind kind, int line, int col, char ch) {
|
||||
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val " + ch + ", numVal " + (int) ch);
|
||||
|
||||
Token token = new Token(kind, line, col);
|
||||
token.val = String.valueOf(ch);
|
||||
token.numVal = ch;
|
||||
expectedTokensFull.add(token);
|
||||
}
|
||||
|
||||
protected void expectInvalidToken(Token.Kind kind, int line, int col) {
|
||||
expectedTokens.add("line " + line + ", col " + col + ", kind " + kind + ", val null, numVal 0");
|
||||
|
||||
Token token = new Token(kind, line, col);
|
||||
token.val = null;
|
||||
token.numVal = 0;
|
||||
expectedTokensFull.add(token);
|
||||
}
|
||||
|
||||
protected void expectSymTab(String line) {
|
||||
expectedSymTab.add(line);
|
||||
}
|
||||
|
||||
protected void expectSymTabUniverse() {
|
||||
// first part of the symbol table (universe) that is equal for all
|
||||
// programs
|
||||
expectSymTab("-- begin scope (0 variables) --");
|
||||
expectSymTab("Type int: int");
|
||||
expectSymTab("Type char: char");
|
||||
expectSymTab("Constant: class (0 fields) null = 0");
|
||||
expectSymTab("Method: char chr (1 locals, 1 parameters)");
|
||||
expectSymTab(" Local Variable 0: int i");
|
||||
expectSymTab("Method: int ord (1 locals, 1 parameters)");
|
||||
expectSymTab(" Local Variable 0: char ch");
|
||||
expectSymTab("Method: int len (1 locals, 1 parameters)");
|
||||
expectSymTab(" Local Variable 0: void[] arr");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package ssw.mj.test.support;
|
||||
|
||||
import ssw.mj.codegen.Decoder;
|
||||
import ssw.mj.impl.Parser;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ByteCodeTestSupport {
|
||||
/**
|
||||
* This flag is used by the lecturers to generate the reference solutions for
|
||||
* the bytecodes generated during code generation.
|
||||
* Students should not change this flag, it should stay false for the whole course.
|
||||
*/
|
||||
public static final boolean GENERATE_REFERENCE_BYTE_CODE = false;
|
||||
|
||||
// For each test, 0 to n correct byte codes can be added to bytecodes.txt
|
||||
// If one of these codes is generated by the Parser, the test does not fail.
|
||||
// This way, we can provide multiple correct solutions for the same test case.
|
||||
//
|
||||
// The keys of this map are in the format "TestClass.TestMethodName()"
|
||||
public static final HashMap<String, List<String>> BYTE_CODES = new HashMap<>();
|
||||
|
||||
static {
|
||||
File bytecodesFile = getBytecodesFile();
|
||||
if (bytecodesFile.exists()) {
|
||||
String[] lineArr;
|
||||
try (Stream<String> lines = Files.lines(bytecodesFile.toPath())) {
|
||||
lineArr = lines.toArray(String[]::new);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
String currentlyReadClassAndMethod = null;
|
||||
StringBuilder currentReadBytecode = null;
|
||||
for (String line : lineArr) {
|
||||
if (line.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
if (line.startsWith("#")) {
|
||||
if (currentlyReadClassAndMethod != null) {
|
||||
if (!BYTE_CODES.containsKey(currentlyReadClassAndMethod)) {
|
||||
BYTE_CODES.put(currentlyReadClassAndMethod, new ArrayList<>());
|
||||
}
|
||||
BYTE_CODES.get(currentlyReadClassAndMethod).add(currentReadBytecode.toString());
|
||||
}
|
||||
currentlyReadClassAndMethod = line.substring(1);
|
||||
currentReadBytecode = new StringBuilder();
|
||||
} else {
|
||||
currentReadBytecode.append(line).append("\n");
|
||||
}
|
||||
}
|
||||
if (currentlyReadClassAndMethod != null) {
|
||||
if (!BYTE_CODES.containsKey(currentlyReadClassAndMethod)) {
|
||||
BYTE_CODES.put(currentlyReadClassAndMethod, new ArrayList<>());
|
||||
}
|
||||
BYTE_CODES.get(currentlyReadClassAndMethod).add(currentReadBytecode.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static File getBytecodesFile() {
|
||||
String filename = "bytecodes.txt";
|
||||
ClassLoader classLoader = BaseCompilerTestCase.class.getClassLoader();
|
||||
URL resource = classLoader.getResource(filename);
|
||||
if (resource == null) {
|
||||
throw new RuntimeException("resource %s not found".formatted(filename));
|
||||
}
|
||||
String urlAsStr = resource.getFile();
|
||||
// replaces %20 Urlencoding with " " (blank space), as e.g. Linux cannot handle url paths
|
||||
String path = URLDecoder.decode(urlAsStr, StandardCharsets.UTF_8);
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
|
||||
public static void generateReferenceByteCode(String classAndMethod, Parser parser) {
|
||||
// Generate and store bytecode for correct test programs
|
||||
|
||||
// Output is in the form:
|
||||
// #TestClass.TestMethodName()
|
||||
// ... output from Decoder.decode() ...
|
||||
File bytecodesFile = ByteCodeTestSupport.getBytecodesFile();
|
||||
try (BufferedWriter bw = Files.newBufferedWriter(bytecodesFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
|
||||
String bytecode = new Decoder().decode(parser.code);
|
||||
bw.write("#");
|
||||
bw.write(classAndMethod);
|
||||
bw.write("\n");
|
||||
bw.write(bytecode);
|
||||
bw.flush();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
MicroJava Tests/tests/ssw/mj/test/support/Configuration.java
Normal file
27
MicroJava Tests/tests/ssw/mj/test/support/Configuration.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package ssw.mj.test.support;
|
||||
|
||||
public class Configuration {
|
||||
/**
|
||||
* set to true to print expected and actual values of all testcases,
|
||||
* not only failing ones (prints expected errors, tokens, symbol table, code)
|
||||
*/
|
||||
public static final boolean ALSO_PRINT_SUCCESSFUL_TESTCASES = Boolean.getBoolean("microjava.testcaseOutput");
|
||||
|
||||
/**
|
||||
* Set to true to print debug information of the interpreter. Equal to
|
||||
* "-debug" on the command line. <br>
|
||||
* Remark:<br>
|
||||
* This is a lot of output, some test cases might time out, e.g.
|
||||
* CodeGenerationTest.fib
|
||||
*/
|
||||
public static final boolean PRINT_INTERPRETER_DEBUG_OUTPUT = Boolean.getBoolean("microjava.interpreterOutput");
|
||||
|
||||
/**
|
||||
* Determines the timeout after which a test case should fail automatically.
|
||||
* Default: 10 seconds. The default should work for all test cases
|
||||
* on most machines.<br>
|
||||
* <em>Attention</em>: For most computers it is likely that there is an
|
||||
* endless loop in the MicroJava compiler if a test fails for a timeout.
|
||||
*/
|
||||
public static final long TIMEOUT = 10;
|
||||
}
|
||||
119
MicroJava Tests/tests/ssw/mj/test/support/SymTabDumper.java
Normal file
119
MicroJava Tests/tests/ssw/mj/test/support/SymTabDumper.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package ssw.mj.test.support;
|
||||
|
||||
import ssw.mj.impl.Tab;
|
||||
import ssw.mj.symtab.Obj;
|
||||
import ssw.mj.symtab.Scope;
|
||||
import ssw.mj.symtab.Struct;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class SymTabDumper {
|
||||
public static String dump(Tab tab) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (tab.curScope != null) {
|
||||
dump(tab.curScope, sb);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void dump(Scope scope, StringBuilder sb) {
|
||||
sb.append("-- begin scope (").append(scope.nVars()).append(" variables) --\n");
|
||||
if (!scope.locals().isEmpty()) {
|
||||
dump(scope.locals().values(), sb, "");
|
||||
}
|
||||
if (scope.outer() != null) {
|
||||
sb.append("\n");
|
||||
dump(scope.outer(), sb);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dump(Collection<Obj> objects, StringBuilder sb, String indent) {
|
||||
for (Obj obj : objects) {
|
||||
dump(obj, sb, indent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dump(Obj obj, StringBuilder sb, String indent) {
|
||||
sb.append(indent);
|
||||
|
||||
switch (obj.kind) {
|
||||
case Con -> dumpCon(obj, sb, indent);
|
||||
case Var -> dumpVar(obj, sb, indent);
|
||||
case Type -> dumpType(obj, sb, indent);
|
||||
case Meth -> dumpMethod(obj, sb, indent);
|
||||
case Prog -> dumpProgram(obj, sb);
|
||||
}
|
||||
|
||||
if (obj.locals != null) {
|
||||
sb.append("\n");
|
||||
dump(obj.locals.values(), sb, indent + " ");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
|
||||
private static void dumpCon(Obj obj, StringBuilder sb, String indent) {
|
||||
sb.append("Constant: ");
|
||||
if (obj.type != null) {
|
||||
dump(obj.type, sb, indent, false);
|
||||
}
|
||||
sb.append(" ").append(obj.name).append(" = ");
|
||||
if (obj.type == Tab.charType) {
|
||||
sb.append("'").append((char) obj.val).append("'");
|
||||
} else {
|
||||
sb.append(obj.val);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpVar(Obj obj, StringBuilder sb, String indent) {
|
||||
if (obj.level == 0) {
|
||||
sb.append("Global Variable ");
|
||||
} else {
|
||||
sb.append("Local Variable ");
|
||||
}
|
||||
sb.append(obj.adr).append(": ");
|
||||
if (obj.type != null) {
|
||||
dump(obj.type, sb, indent, false);
|
||||
}
|
||||
sb.append(" ").append(obj.name);
|
||||
}
|
||||
|
||||
private static void dumpType(Obj type, StringBuilder sb, String indent) {
|
||||
sb.append("Type ").append(type.name).append(": ");
|
||||
if (type.type != null) {
|
||||
dump(type.type, sb, indent + " ", true);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dumpMethod(Obj meth, StringBuilder sb, String indent) {
|
||||
sb.append("Method: ");
|
||||
if (meth.type != null) {
|
||||
dump(meth.type, sb, indent, false);
|
||||
}
|
||||
sb.append(" ").append(meth.name).append(" (").append(meth.locals.size()).append(" locals, ").append(meth.nPars).append(" parameters").append(")");
|
||||
}
|
||||
|
||||
private static void dumpProgram(Obj obj, StringBuilder sb) {
|
||||
sb.append("Program ").append(obj.name).append(":");
|
||||
}
|
||||
|
||||
private static void dump(Struct struct, StringBuilder sb, String indent, boolean dumpFields) {
|
||||
switch (struct.kind) {
|
||||
case None -> sb.append("void");
|
||||
case Int -> sb.append("int");
|
||||
case Char -> sb.append("char");
|
||||
case Arr -> {
|
||||
if (struct.elemType != null) {
|
||||
dump(struct.elemType, sb, indent, dumpFields);
|
||||
}
|
||||
sb.append("[]");
|
||||
}
|
||||
case Class -> {
|
||||
sb.append("class (").append(struct.nrFields()).append(" fields)");
|
||||
if (dumpFields && struct.fields != null) {
|
||||
sb.append("\n");
|
||||
dump(struct.fields.values(), sb, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1760524057,
|
||||
"narHash": "sha256-EVAqOteLBFmd7pKkb0+FIUyzTF61VKi7YmvP1tw4nEw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "544961dfcce86422ba200ed9a0b00dd4b1486ec5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
28
flake.nix
Normal file
28
flake.nix
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
config = {
|
||||
allowUnfree = true;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells."${system}".default = pkgs.mkShell {
|
||||
packages = [
|
||||
pkgs.jetbrains.idea-ultimate
|
||||
pkgs.jdk21
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
BIN
lib/apiguardian-api-1.1.2.jar
Normal file
BIN
lib/apiguardian-api-1.1.2.jar
Normal file
Binary file not shown.
BIN
lib/gson-2.10.1.jar
Normal file
BIN
lib/gson-2.10.1.jar
Normal file
Binary file not shown.
BIN
lib/javassist.jar
Normal file
BIN
lib/javassist.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-5.12.2.jar
Normal file
BIN
lib/junit-jupiter-5.12.2.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-api-5.12.2.jar
Normal file
BIN
lib/junit-jupiter-api-5.12.2.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-engine-5.12.2.jar
Normal file
BIN
lib/junit-jupiter-engine-5.12.2.jar
Normal file
Binary file not shown.
BIN
lib/junit-jupiter-params-5.12.2.jar
Normal file
BIN
lib/junit-jupiter-params-5.12.2.jar
Normal file
Binary file not shown.
BIN
lib/junit-platform-commons-1.12.2.jar
Normal file
BIN
lib/junit-platform-commons-1.12.2.jar
Normal file
Binary file not shown.
BIN
lib/junit-platform-engine-1.12.2.jar
Normal file
BIN
lib/junit-platform-engine-1.12.2.jar
Normal file
Binary file not shown.
BIN
lib/opentest4j-1.3.0.jar
Normal file
BIN
lib/opentest4j-1.3.0.jar
Normal file
Binary file not shown.
BIN
submission.zip
Normal file
BIN
submission.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user