Homework 1
This commit is contained in:
parent
49bca7f856
commit
12f678a924
43 changed files with 3703 additions and 0 deletions
11
.classpath
Normal file
11
.classpath
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="test"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||||
|
<classpathentry kind="lib" path="lib/antlr-4.4-complete.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/junit-4.12.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
|
||||||
|
<classpathentry kind="lib" path="lib/javaliParserObf.jar"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
245
.gitignore
vendored
Normal file
245
.gitignore
vendored
Normal file
|
@ -0,0 +1,245 @@
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/java,linux,macos,windows,intellij,intellij+all
|
||||||
|
# Edit at https://www.gitignore.io/?templates=java,linux,macos,windows,intellij,intellij+all
|
||||||
|
|
||||||
|
### Intellij ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### Intellij Patch ###
|
||||||
|
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
|
||||||
|
|
||||||
|
# *.iml
|
||||||
|
# modules.xml
|
||||||
|
# .idea/misc.xml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# Sonarlint plugin
|
||||||
|
.idea/**/sonarlint/
|
||||||
|
|
||||||
|
# SonarQube Plugin
|
||||||
|
.idea/**/sonarIssues.xml
|
||||||
|
|
||||||
|
# Markdown Navigator plugin
|
||||||
|
.idea/**/markdown-navigator.xml
|
||||||
|
.idea/**/markdown-navigator/
|
||||||
|
|
||||||
|
### Intellij+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
|
||||||
|
### Intellij+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
#
|
||||||
|
#.idea/
|
||||||
|
#
|
||||||
|
## Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
#
|
||||||
|
#*.iml
|
||||||
|
#modules.xml
|
||||||
|
#.idea/misc.xml
|
||||||
|
#*.ipr
|
||||||
|
#
|
||||||
|
## Sonarlint plugin
|
||||||
|
#.idea/sonarlint
|
||||||
|
#
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
### Linux ###
|
||||||
|
*~
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/java,linux,macos,windows,intellij,intellij+all
|
23
.project
Normal file
23
.project
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>Javali-HW1</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.xtext.ui.shared.xtextBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.xtext.ui.shared.xtextNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding/<project>=UTF-8
|
12
.settings/org.eclipse.jdt.core.prefs
Normal file
12
.settings/org.eclipse.jdt.core.prefs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
eclipse.preferences.version=1
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||||
|
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||||
|
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||||
|
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||||
|
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||||
|
org.eclipse.jdt.core.compiler.source=1.8
|
5
Grade.txt
Normal file
5
Grade.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Grade: 24/25
|
||||||
|
|
||||||
|
31/31 tests passed [20/20]
|
||||||
|
|
||||||
|
Submitted test cases [4/5]
|
87
build.xml
Normal file
87
build.xml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<project name="compiler-design" default="compile" basedir="." xmlns:jacoco="antlib:org.jacoco.ant">
|
||||||
|
|
||||||
|
<!-- A very simple ant file that primarily exists to run cup and lex;
|
||||||
|
it also includes targets to build the compiler and run tests for those
|
||||||
|
who would rather not use Eclipse -->
|
||||||
|
|
||||||
|
<!-- Set project properties. -->
|
||||||
|
<property name="src.dir" value="${basedir}/src"/>
|
||||||
|
<property name="test.dir" value="${basedir}/test"/>
|
||||||
|
<property name="javali_tests.dir" value="${basedir}/javali_tests"/>
|
||||||
|
<property name="parser.dir" value="${src.dir}/cd/frontend/parser"/>
|
||||||
|
<property name="parser.jar" value="${basedir}/lib/javaliParserObf.jar"/>
|
||||||
|
<property name="build.dir" value="${basedir}/bin"/>
|
||||||
|
<property name="junit.jar" value="${basedir}/lib/junit-4.12.jar"/>
|
||||||
|
<property name="hamcrest.jar" value="${basedir}/lib/hamcrest-core-1.3.jar"/>
|
||||||
|
<property name="antlr.jar" value="${basedir}/lib/antlr-4.4-complete.jar"/>
|
||||||
|
<property name="antlr.profile" value="false"/>
|
||||||
|
<property name="antlr.report" value="false"/>
|
||||||
|
<property name="jacocoant.jar" value="${basedir}/lib/jacocoant.jar"/>
|
||||||
|
<property name="coverage.file" location="${build.dir}/jacoco.exec"/>
|
||||||
|
<property name="min.coverage" value="0.5"/>
|
||||||
|
<property name="coverage.check" value="cd.frontend.*:cd.backend.*"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Cleans generated code, but NOT the parser source! -->
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="${build.dir}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Builds the compiler framework for HW1. -->
|
||||||
|
<target name="compile">
|
||||||
|
<mkdir dir="${build.dir}"/>
|
||||||
|
|
||||||
|
<javac debug="true" destdir="${build.dir}" includeantruntime="false">
|
||||||
|
<src path="${src.dir}"/>
|
||||||
|
<src path="${test.dir}"/>
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="${antlr.jar}"/>
|
||||||
|
<pathelement location="${junit.jar}"/>
|
||||||
|
<pathelement location="${hamcrest.jar}"/>
|
||||||
|
<pathelement location="${parser.jar}"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- Deletes all byproducts of running the tests -->
|
||||||
|
<target name="clean-test">
|
||||||
|
<delete>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.err"/>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.s"/>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.bin"/>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.dot"/>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.exe"/>
|
||||||
|
<fileset dir="${javali_tests.dir}" includes="**/*.ref"/>
|
||||||
|
</delete>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
|
||||||
|
<classpath path="${jacocoant.jar}"/>
|
||||||
|
</taskdef>
|
||||||
|
|
||||||
|
<!-- Runs the tests. Use the compile target first! -->
|
||||||
|
<target name="test" depends="compile">
|
||||||
|
<jacoco:coverage destfile="${coverage.file}">
|
||||||
|
<junit fork="true" forkmode="once" failureproperty="tests-failed" outputtoformatters="false">
|
||||||
|
<formatter type="brief" usefile="false"/>
|
||||||
|
<batchtest skipNonTests="true">
|
||||||
|
<fileset dir="bin" includes="**/*.class" />
|
||||||
|
</batchtest>
|
||||||
|
<assertions enablesystemassertions="true" />
|
||||||
|
<sysproperty key="cd.meta_hidden.Version" value="REFERENCE" />
|
||||||
|
<classpath>
|
||||||
|
<pathelement location="${build.dir}"/>
|
||||||
|
<pathelement location="${junit.jar}"/>
|
||||||
|
<pathelement location="${hamcrest.jar}"/>
|
||||||
|
<pathelement location="${antlr.jar}"/>
|
||||||
|
<pathelement location="${parser.jar}"/>
|
||||||
|
</classpath>
|
||||||
|
</junit>
|
||||||
|
</jacoco:coverage>
|
||||||
|
<fail if="tests-failed" />
|
||||||
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
24
javali_tests/HW1/EightVariables.javali
Normal file
24
javali_tests/HW1/EightVariables.javali
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
/* Test expression evaluation with 8 variables */
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int r1, r2, r3;
|
||||||
|
int i0, i1, i2, i3, i4, i5, i6, i7;
|
||||||
|
|
||||||
|
i0 = 0;
|
||||||
|
i1 = 1;
|
||||||
|
i2 = 2;
|
||||||
|
i3 = 3;
|
||||||
|
i4 = 4;
|
||||||
|
i5 = 5;
|
||||||
|
i6 = 6;
|
||||||
|
i7 = 7;
|
||||||
|
|
||||||
|
r1 = i0 + (i1 + ( i2 + ( i3 + ( i4 + (i5 + (i6 + i7))))));
|
||||||
|
r2 = ((((((i0 + i1) + i2) + i3) + i4) + i5) + i6) + i7;
|
||||||
|
r3 = ((i0 + i1) + (i2 + i3)) + ((i4 + i5) + (i6 + i7));
|
||||||
|
|
||||||
|
write(r1); writeln();
|
||||||
|
write(r2); writeln();
|
||||||
|
write(r3); writeln();
|
||||||
|
}
|
||||||
|
}
|
7
javali_tests/HW1/HelloWorld.javali
Normal file
7
javali_tests/HW1/HelloWorld.javali
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/* Kind of like hello world */
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
write(53110);
|
||||||
|
writeln();
|
||||||
|
}
|
||||||
|
}
|
19
javali_tests/HW1/ImmediateOperands.javali
Normal file
19
javali_tests/HW1/ImmediateOperands.javali
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
/* Check if code selection for immediate operands works */
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int i0;
|
||||||
|
i0 = 0;
|
||||||
|
|
||||||
|
i0 = 5 + i0;
|
||||||
|
write(i0);
|
||||||
|
writeln();
|
||||||
|
|
||||||
|
i0 = i0 + 5;
|
||||||
|
write(i0);
|
||||||
|
writeln();
|
||||||
|
|
||||||
|
i0 = i0 + 5 + 3;
|
||||||
|
write(i0);
|
||||||
|
writeln();
|
||||||
|
}
|
||||||
|
}
|
18
javali_tests/HW1/Negation.javali
Normal file
18
javali_tests/HW1/Negation.javali
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int A,B,a,b,c,d;
|
||||||
|
|
||||||
|
A = 1;
|
||||||
|
B = 1;
|
||||||
|
|
||||||
|
a = A * (-B);
|
||||||
|
b = -A * B;
|
||||||
|
c = -(A + B);
|
||||||
|
d = -(A * B);
|
||||||
|
|
||||||
|
write(a); writeln();
|
||||||
|
write(b); writeln();
|
||||||
|
write(c); writeln();
|
||||||
|
write(d); writeln();
|
||||||
|
}
|
||||||
|
}
|
21
javali_tests/HW1_nop90/Division.javali
Executable file
21
javali_tests/HW1_nop90/Division.javali
Executable file
|
@ -0,0 +1,21 @@
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int a, b, c, d, e;
|
||||||
|
|
||||||
|
a = 10;
|
||||||
|
b = -1;
|
||||||
|
c = 0;
|
||||||
|
d = 100;
|
||||||
|
e = 2;
|
||||||
|
|
||||||
|
write(a / b); writeln();
|
||||||
|
write(d / e); writeln();
|
||||||
|
write(c / d); writeln();
|
||||||
|
write(b / a + c); writeln();
|
||||||
|
write(d / e * a / b * c); writeln();
|
||||||
|
write(d / e * a / b); writeln();
|
||||||
|
write(d / e + a / b); writeln();
|
||||||
|
write(d / e * a * b * c); writeln();
|
||||||
|
write(d / e * a - b + c); writeln();
|
||||||
|
}
|
||||||
|
}
|
20
javali_tests/HW1_nop90/EightVariablesWrite.javali
Normal file
20
javali_tests/HW1_nop90/EightVariablesWrite.javali
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/* Test expression evaluation with 8 variables */
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int r1, r2, r3;
|
||||||
|
int i0, i1, i2, i3, i4, i5, i6, i7;
|
||||||
|
|
||||||
|
i0 = 0;
|
||||||
|
i1 = 1;
|
||||||
|
i2 = 2;
|
||||||
|
i3 = 3;
|
||||||
|
i4 = 4;
|
||||||
|
i5 = 5;
|
||||||
|
i6 = 6;
|
||||||
|
i7 = 7;
|
||||||
|
|
||||||
|
write(i0 + (i1 + ( i2 + ( i3 + ( i4 + (i5 + (i6 + i7))))))); writeln();
|
||||||
|
write(((((((i0 + i1) + i2) + i3) + i4) + i5) + i6) + i7); writeln();
|
||||||
|
write(((i0 + i1) + (i2 + i3)) + ((i4 + i5) + (i6 + i7))); writeln();
|
||||||
|
}
|
||||||
|
}
|
31
javali_tests/HW1_nop90/Multiplication.javali
Executable file
31
javali_tests/HW1_nop90/Multiplication.javali
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int r1;
|
||||||
|
int i0, i1;
|
||||||
|
int x,y,z;
|
||||||
|
|
||||||
|
i0 = 5;
|
||||||
|
i1 = 2;
|
||||||
|
|
||||||
|
r1 = i1 * 3;
|
||||||
|
write(r1); writeln();
|
||||||
|
|
||||||
|
r1 = i0 * i1;
|
||||||
|
write(r1); writeln();
|
||||||
|
|
||||||
|
r1 = r1 * i0 * i1 * 3;
|
||||||
|
write(r1); writeln();
|
||||||
|
|
||||||
|
y = 5;
|
||||||
|
z = 10;
|
||||||
|
x = (-y * z);
|
||||||
|
write(x); writeln();
|
||||||
|
|
||||||
|
y = 0;
|
||||||
|
z = - 10;
|
||||||
|
x = (y * z);
|
||||||
|
write(x); writeln();
|
||||||
|
write(y * z); writeln();
|
||||||
|
write(0 * -10); writeln();
|
||||||
|
}
|
||||||
|
}
|
25
javali_tests/HW1_nop90/Overflow.javali
Executable file
25
javali_tests/HW1_nop90/Overflow.javali
Executable file
|
@ -0,0 +1,25 @@
|
||||||
|
/* Test what happens for Integers that are to big for 32bits
|
||||||
|
2147483647 (=0x7FFFFFFF) is the biggest integer in 32bits (IntMAX)
|
||||||
|
*/
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int x,y,z;
|
||||||
|
x = 2147483647;
|
||||||
|
write( x ); writeln();
|
||||||
|
/* add 1 to IntMax and output it */
|
||||||
|
write( x + 1); writeln();
|
||||||
|
|
||||||
|
/* read an int bigger than IntMAX */
|
||||||
|
x = read();
|
||||||
|
write(x); writeln();
|
||||||
|
|
||||||
|
/* performe some operation that should generate an int overflow */
|
||||||
|
x = 2147483640;
|
||||||
|
y = 60000;
|
||||||
|
write( x + y); writeln();
|
||||||
|
z = 20000000;
|
||||||
|
write( y * z); writeln();
|
||||||
|
write( (y * z) ); writeln();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
1
javali_tests/HW1_nop90/Overflow.javali.in
Executable file
1
javali_tests/HW1_nop90/Overflow.javali.in
Executable file
|
@ -0,0 +1 @@
|
||||||
|
2147483647
|
26
javali_tests/HW1_nop90/ReadWrite.javali
Executable file
26
javali_tests/HW1_nop90/ReadWrite.javali
Executable file
|
@ -0,0 +1,26 @@
|
||||||
|
/* Test read/write native functions */
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int r1, r2, readvar, a1;
|
||||||
|
|
||||||
|
r1 = 6;
|
||||||
|
/* r2 = 22 */
|
||||||
|
r2 = read();
|
||||||
|
|
||||||
|
write(r1); writeln(); // 6
|
||||||
|
|
||||||
|
/* test expressions inside write() */
|
||||||
|
write(r1 - 3); writeln(); // 3
|
||||||
|
write(r1 - 6); writeln(); // 0
|
||||||
|
write(r1 - 7); writeln(); // -1
|
||||||
|
/* should output 111 */
|
||||||
|
readvar = read(); // 1
|
||||||
|
write( (r1 + (r2 * 5)) + readvar); // 117
|
||||||
|
write(- r1); // -6
|
||||||
|
writeln();
|
||||||
|
/* should output 15 */
|
||||||
|
a1 = read(); // -15
|
||||||
|
write(- a1); // 15
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
6
javali_tests/HW1_nop90/ReadWrite.javali.in
Executable file
6
javali_tests/HW1_nop90/ReadWrite.javali.in
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
22
|
||||||
|
|
||||||
|
|
||||||
|
1
|
||||||
|
|
||||||
|
-15
|
15
javali_tests/HW1_nop90/UnaryOperators.javali
Executable file
15
javali_tests/HW1_nop90/UnaryOperators.javali
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int a, b;
|
||||||
|
|
||||||
|
a = 1;
|
||||||
|
b = 2;
|
||||||
|
|
||||||
|
write(+a); writeln();
|
||||||
|
write(-a); writeln();
|
||||||
|
write(+a --b); writeln();
|
||||||
|
write(-a--b); writeln();
|
||||||
|
write(-----a-----5--b); writeln();
|
||||||
|
write(-----a*---------b);writeln();
|
||||||
|
}
|
||||||
|
}
|
24
javali_tests/HW1_nop90/noParentheses.javali
Executable file
24
javali_tests/HW1_nop90/noParentheses.javali
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
/* test what happens if there are no parentheses */
|
||||||
|
|
||||||
|
class Main {
|
||||||
|
void main() {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
int z;
|
||||||
|
|
||||||
|
x = 5;
|
||||||
|
y = 10;
|
||||||
|
z = 100;
|
||||||
|
|
||||||
|
write( x + y + z); writeln();
|
||||||
|
/* */
|
||||||
|
write( - x + y - z); writeln();
|
||||||
|
|
||||||
|
/* should output 205 */
|
||||||
|
write( x + 2 * z); writeln();
|
||||||
|
|
||||||
|
write( x + 2 * z / x + 1); writeln();
|
||||||
|
write(+x); writeln();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
148
src/cd/Config.java
Normal file
148
src/cd/Config.java
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package cd;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
public static enum SystemKind {
|
||||||
|
LINUX,
|
||||||
|
WINDOWS,
|
||||||
|
MACOSX
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What kind of system we are on
|
||||||
|
*/
|
||||||
|
public static final SystemKind systemKind;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the extension used for assembler files on this platform.
|
||||||
|
* Currently always {@code .s}.
|
||||||
|
*/
|
||||||
|
public static final String ASMEXT = ".s";
|
||||||
|
|
||||||
|
/** Defines the extension used for binary files on this platform. */
|
||||||
|
public static final String BINARYEXT;
|
||||||
|
|
||||||
|
/** Defines the name of the main function to be used in .s file */
|
||||||
|
public static final String MAIN;
|
||||||
|
|
||||||
|
/** Defines the name of the printf function to be used in .s file */
|
||||||
|
public static final String PRINTF;
|
||||||
|
|
||||||
|
/** Defines the name of the scanf function to be used in .s file */
|
||||||
|
public static final String SCANF;
|
||||||
|
|
||||||
|
/** Defines the name of the calloc function to be used in .s file */
|
||||||
|
public static final String CALLOC;
|
||||||
|
|
||||||
|
/** Defines the name of the exit function to be used in .s file */
|
||||||
|
public static final String EXIT;
|
||||||
|
|
||||||
|
/** The assembler directive used to define a constant string */
|
||||||
|
public static final String DOT_STRING;
|
||||||
|
|
||||||
|
/** The assembler directive used to define a constant int */
|
||||||
|
public static final String DOT_INT;
|
||||||
|
|
||||||
|
/** The assembler directive used to start the text section */
|
||||||
|
public static final String TEXT_SECTION;
|
||||||
|
|
||||||
|
/** The assembler directive used to start the section for integer data */
|
||||||
|
public static final String DATA_INT_SECTION;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The assembler directive used to start the section for string data (if
|
||||||
|
* different from {@link #DATA_INT_SECTION}
|
||||||
|
*/
|
||||||
|
public static final String DATA_STR_SECTION;
|
||||||
|
|
||||||
|
/** Comment separator used in assembly files */
|
||||||
|
public static final String COMMENT_SEP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the assembler command to use. Should be a string array where each
|
||||||
|
* entry is one argument. Use the special string "$0" to refer to the output
|
||||||
|
* file, and $1 to refer to the ".s" file.
|
||||||
|
*/
|
||||||
|
public static final String[] ASM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory from which to run the assembler. In a CYGWIN installation,
|
||||||
|
* this can make a big difference!
|
||||||
|
*/
|
||||||
|
public static final File ASM_DIR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sizeof a pointer in bytes in the target platform.
|
||||||
|
*/
|
||||||
|
public static final int SIZEOF_PTR = 4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of java executable in JRE path
|
||||||
|
*/
|
||||||
|
public static final String JAVA_EXE;
|
||||||
|
|
||||||
|
static {
|
||||||
|
|
||||||
|
final String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
|
||||||
|
if(os.contains("windows") || os.contains("nt")) {
|
||||||
|
systemKind = SystemKind.WINDOWS;
|
||||||
|
BINARYEXT = ".exe";
|
||||||
|
MAIN = "_main";
|
||||||
|
PRINTF = "_printf";
|
||||||
|
SCANF = "_scanf";
|
||||||
|
CALLOC = "_calloc";
|
||||||
|
EXIT = "_exit";
|
||||||
|
// These are set up for a Cygwin installation on C:,
|
||||||
|
// you can change as registersNeeded.
|
||||||
|
ASM = new String[]{"gcc", "-o", "$0", "$1"};
|
||||||
|
ASM_DIR = new File("C:\\CYGWIN\\BIN");
|
||||||
|
JAVA_EXE = "javaw.exe";
|
||||||
|
DOT_STRING = ".string";
|
||||||
|
DOT_INT = ".int";
|
||||||
|
TEXT_SECTION = ".section .text";
|
||||||
|
DATA_INT_SECTION = ".section .data";
|
||||||
|
DATA_STR_SECTION = ".section .data";
|
||||||
|
COMMENT_SEP = "#";
|
||||||
|
}
|
||||||
|
else if(os.contains("mac os x") || os.contains("darwin")) {
|
||||||
|
systemKind = SystemKind.MACOSX;
|
||||||
|
BINARYEXT = ".bin";
|
||||||
|
MAIN = "_main";
|
||||||
|
PRINTF = "_printf";
|
||||||
|
SCANF = "_scanf";
|
||||||
|
CALLOC = "_calloc";
|
||||||
|
EXIT = "_exit";
|
||||||
|
ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"};
|
||||||
|
ASM_DIR = new File(".");
|
||||||
|
JAVA_EXE = "java";
|
||||||
|
DOT_STRING = ".asciz";
|
||||||
|
DOT_INT = ".long";
|
||||||
|
TEXT_SECTION = ".text";
|
||||||
|
DATA_INT_SECTION = ".data";
|
||||||
|
DATA_STR_SECTION = ".cstring";
|
||||||
|
COMMENT_SEP = "#";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
systemKind = SystemKind.LINUX;
|
||||||
|
BINARYEXT = ".bin";
|
||||||
|
MAIN = "main";
|
||||||
|
PRINTF = "printf";
|
||||||
|
SCANF = "scanf";
|
||||||
|
CALLOC = "calloc";
|
||||||
|
EXIT = "exit";
|
||||||
|
ASM = new String[]{"gcc", "-m32", "-o", "$0", "$1"};
|
||||||
|
ASM_DIR = new File(".");
|
||||||
|
JAVA_EXE = "java";
|
||||||
|
DOT_STRING = ".string";
|
||||||
|
DOT_INT = ".int";
|
||||||
|
TEXT_SECTION = ".section .text";
|
||||||
|
DATA_INT_SECTION = ".section .data";
|
||||||
|
DATA_STR_SECTION = ".section .data";
|
||||||
|
COMMENT_SEP = "#";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
126
src/cd/Main.java
Normal file
126
src/cd/Main.java
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
package cd;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||||
|
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
import org.antlr.v4.runtime.misc.ParseCancellationException;
|
||||||
|
|
||||||
|
import cd.backend.codegen.AstCodeGenerator;
|
||||||
|
import cd.frontend.parser.JavaliAstVisitor;
|
||||||
|
import cd.frontend.parser.JavaliLexer;
|
||||||
|
import cd.frontend.parser.JavaliParser;
|
||||||
|
import cd.frontend.parser.JavaliParser.UnitContext;
|
||||||
|
import cd.frontend.parser.ParseFailure;
|
||||||
|
import cd.ir.Ast.ClassDecl;
|
||||||
|
import cd.util.debug.AstDump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entrypoint for the compiler. Consists of a series
|
||||||
|
* of routines which must be invoked in order. The main()
|
||||||
|
* routine here invokes these routines, as does the unit testing
|
||||||
|
* code. This is not the <b>best</b> programming practice, as the
|
||||||
|
* series of calls to be invoked is duplicated in two places in the
|
||||||
|
* code, but it will do for now. */
|
||||||
|
public class Main {
|
||||||
|
|
||||||
|
// Set to non-null to write debug info out
|
||||||
|
public Writer debug = null;
|
||||||
|
|
||||||
|
// Set to non-null to write dump of control flow graph (Advanced Compiler Design)
|
||||||
|
public File cfgdumpbase;
|
||||||
|
|
||||||
|
public void debug(String format, Object... args) {
|
||||||
|
if (debug != null) {
|
||||||
|
String result = String.format(format, args);
|
||||||
|
try {
|
||||||
|
debug.write(result);
|
||||||
|
debug.write('\n');
|
||||||
|
debug.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parse command line, invoke compile() routine */
|
||||||
|
public static void main(String args[]) throws IOException {
|
||||||
|
Main m = new Main();
|
||||||
|
|
||||||
|
for (String arg : args) {
|
||||||
|
if (arg.equals("-d"))
|
||||||
|
m.debug = new OutputStreamWriter(System.err);
|
||||||
|
else {
|
||||||
|
FileReader fin = new FileReader(arg);
|
||||||
|
|
||||||
|
// Parse:
|
||||||
|
List<ClassDecl> astRoots = m.parse(fin);
|
||||||
|
|
||||||
|
// Run the semantic check:
|
||||||
|
m.semanticCheck(astRoots);
|
||||||
|
|
||||||
|
// Generate code:
|
||||||
|
String sFile = arg + Config.ASMEXT;
|
||||||
|
try (FileWriter fout = new FileWriter(sFile)) {
|
||||||
|
m.generateCode(astRoots, fout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Parses an input stream into an AST
|
||||||
|
* @throws IOException */
|
||||||
|
public List<ClassDecl> parse(Reader reader) throws IOException {
|
||||||
|
List<ClassDecl> result = new ArrayList<ClassDecl>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JavaliLexer lexer = new JavaliLexer(new ANTLRInputStream(reader));
|
||||||
|
JavaliParser parser = new JavaliParser(new CommonTokenStream(lexer));
|
||||||
|
parser.setErrorHandler(new BailErrorStrategy());
|
||||||
|
UnitContext unit = parser.unit();
|
||||||
|
|
||||||
|
JavaliAstVisitor visitor = new JavaliAstVisitor();
|
||||||
|
visitor.visit(unit);
|
||||||
|
result = visitor.classDecls;
|
||||||
|
} catch (ParseCancellationException e) {
|
||||||
|
ParseFailure pf = new ParseFailure(0, "?");
|
||||||
|
pf.initCause(e);
|
||||||
|
throw pf;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug("AST Resulting From Parsing Stage:");
|
||||||
|
dumpAst(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void semanticCheck(List<ClassDecl> astRoots) {
|
||||||
|
{
|
||||||
|
// Not registersNeeded until later. Ignore.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generateCode(List<ClassDecl> astRoots, Writer out) {
|
||||||
|
{
|
||||||
|
AstCodeGenerator cg = AstCodeGenerator.createCodeGenerator(this, out);
|
||||||
|
cg.go(astRoots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Dumps the AST to the debug stream */
|
||||||
|
private void dumpAst(List<ClassDecl> astRoots) throws IOException {
|
||||||
|
if (this.debug == null) return;
|
||||||
|
this.debug.write(AstDump.toString(astRoots));
|
||||||
|
}
|
||||||
|
}
|
14
src/cd/ToDoException.java
Normal file
14
src/cd/ToDoException.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package cd;
|
||||||
|
|
||||||
|
/** TAs insert this to mark code that students need to write */
|
||||||
|
public class ToDoException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = 4054810321239901944L;
|
||||||
|
|
||||||
|
public ToDoException() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ToDoException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
164
src/cd/backend/codegen/AssemblyEmitter.java
Normal file
164
src/cd/backend/codegen/AssemblyEmitter.java
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import cd.Config;
|
||||||
|
import cd.backend.codegen.RegisterManager.Register;
|
||||||
|
|
||||||
|
public class AssemblyEmitter {
|
||||||
|
public Writer out;
|
||||||
|
public StringBuilder indent = new StringBuilder();
|
||||||
|
public int counter = 0;
|
||||||
|
|
||||||
|
public AssemblyEmitter(Writer out) {
|
||||||
|
this.out = out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an constant operand. */
|
||||||
|
static String constant(int i) {
|
||||||
|
return "$" + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an constant operand with the address of a label. */
|
||||||
|
static String labelAddress(String lbl) {
|
||||||
|
return "$" + lbl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an operand relative to another operand. */
|
||||||
|
static String registerOffset(int offset, Register reg) {
|
||||||
|
return String.format("%d(%s)", offset, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates an operand addressing an item in an array */
|
||||||
|
static String arrayAddress(Register arrReg, Register idxReg) {
|
||||||
|
final int offset = Config.SIZEOF_PTR * 2; // one word each in front for
|
||||||
|
// vptr and length
|
||||||
|
final int mul = Config.SIZEOF_PTR; // assume all arrays of 4-byte elem
|
||||||
|
return String.format("%d(%s,%s,%d)", offset, arrReg, idxReg, mul);
|
||||||
|
}
|
||||||
|
|
||||||
|
void increaseIndent(String comment) {
|
||||||
|
indent.append(" ");
|
||||||
|
if (comment != null)
|
||||||
|
emitComment(comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void decreaseIndent() {
|
||||||
|
indent.setLength(indent.length() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitCommentSection(String name) {
|
||||||
|
int indentLen = indent.length();
|
||||||
|
int breakLen = 68 - indentLen - name.length();
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append(Config.COMMENT_SEP).append(" ");
|
||||||
|
for (int i = 0; i < indentLen; i++)
|
||||||
|
sb.append("_");
|
||||||
|
sb.append(name);
|
||||||
|
for (int i = 0; i < breakLen; i++)
|
||||||
|
sb.append("_");
|
||||||
|
|
||||||
|
try {
|
||||||
|
out.write(sb.toString());
|
||||||
|
out.write("\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitComment(String comment) {
|
||||||
|
emitRaw(Config.COMMENT_SEP + " " + comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, Register src, String dest) {
|
||||||
|
emit(op, src.repr, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, String src, Register dest) {
|
||||||
|
emit(op, src, dest.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, Register src, Register dest) {
|
||||||
|
emit(op, src.repr, dest.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, String src, String dest) {
|
||||||
|
emitRaw(String.format("%s %s, %s", op, src, dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, int src, Register dest) {
|
||||||
|
emit(op, constant(src), dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, String dest) {
|
||||||
|
emitRaw(op + " " + dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op) {
|
||||||
|
emitRaw(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, Register reg) {
|
||||||
|
emit(op, reg.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emit(String op, int dest) {
|
||||||
|
emit(op, constant(dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitMove(Register src, String dest) {
|
||||||
|
emitMove(src.repr, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitMove(Register src, Register dest) {
|
||||||
|
emitMove(src.repr, dest.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitMove(String src, Register dest) {
|
||||||
|
emitMove(src, dest.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitMove(String src, String dest) {
|
||||||
|
if (!src.equals(dest))
|
||||||
|
emit("movl", src, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitLoad(int srcOffset, Register src, Register dest) {
|
||||||
|
emitMove(registerOffset(srcOffset, src), dest.repr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitStore(Register src, int destOffset, Register dest) {
|
||||||
|
emitStore(src.repr, destOffset, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitStore(String src, int destOffset, Register dest) {
|
||||||
|
emitMove(src, registerOffset(destOffset, dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitConstantData(String data) {
|
||||||
|
emitRaw(String.format("%s %s", Config.DOT_INT, data));
|
||||||
|
}
|
||||||
|
|
||||||
|
String uniqueLabel() {
|
||||||
|
String labelName = "label" + counter++;
|
||||||
|
return labelName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitLabel(String label) {
|
||||||
|
try {
|
||||||
|
out.write(label + ":" + "\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void emitRaw(String op) {
|
||||||
|
try {
|
||||||
|
out.write(indent.toString());
|
||||||
|
out.write(op);
|
||||||
|
out.write("\n");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/cd/backend/codegen/AssemblyFailedException.java
Normal file
14
src/cd/backend/codegen/AssemblyFailedException.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
public class AssemblyFailedException extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = -5658502514441032016L;
|
||||||
|
|
||||||
|
public final String assemblerOutput;
|
||||||
|
public AssemblyFailedException(
|
||||||
|
String assemblerOutput) {
|
||||||
|
super("Executing assembler failed.\n"
|
||||||
|
+ "Output:\n"
|
||||||
|
+ assemblerOutput);
|
||||||
|
this.assemblerOutput = assemblerOutput;
|
||||||
|
}
|
||||||
|
}
|
58
src/cd/backend/codegen/AstCodeGenerator.java
Normal file
58
src/cd/backend/codegen/AstCodeGenerator.java
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import cd.Main;
|
||||||
|
import cd.ir.Ast.ClassDecl;
|
||||||
|
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AstCodeGenerator {
|
||||||
|
|
||||||
|
protected ExprGenerator eg;
|
||||||
|
protected StmtGenerator sg;
|
||||||
|
|
||||||
|
protected final Main main;
|
||||||
|
|
||||||
|
protected final AssemblyEmitter emit;
|
||||||
|
protected final RegisterManager rm = new RegisterManager();
|
||||||
|
|
||||||
|
AstCodeGenerator(Main main, Writer out) {
|
||||||
|
this.emit = new AssemblyEmitter(out);
|
||||||
|
this.main = main;
|
||||||
|
this.eg = new ExprGenerator(this);
|
||||||
|
this.sg = new StmtGenerator(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void debug(String format, Object... args) {
|
||||||
|
this.main.debug(format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AstCodeGenerator createCodeGenerator(Main main, Writer out) {
|
||||||
|
return new AstCodeGenerator(main, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main method. Causes us to emit x86 assembly corresponding to {@code ast}
|
||||||
|
* into {@code file}. Throws a {@link RuntimeException} should any I/O error
|
||||||
|
* occur.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* The generated file will be divided into two sections:
|
||||||
|
* <ol>
|
||||||
|
* <li>Prologue: Generated by {@link #emitPrefix()}. This contains any
|
||||||
|
* introductory declarations and the like.
|
||||||
|
* <li>Body: Generated by {@link ExprGenerator}. This contains the main
|
||||||
|
* method definitions.
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
public void go(List<? extends ClassDecl> astRoots) {
|
||||||
|
for (ClassDecl ast : astRoots) {
|
||||||
|
sg.gen(ast);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void initMethodData() {
|
||||||
|
}
|
||||||
|
}
|
307
src/cd/backend/codegen/ExprGenerator.java
Normal file
307
src/cd/backend/codegen/ExprGenerator.java
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import cd.backend.codegen.RegisterManager.Register;
|
||||||
|
import cd.ir.Ast.*;
|
||||||
|
import cd.ir.Ast.BinaryOp.BOp;
|
||||||
|
import cd.ir.ExprVisitor;
|
||||||
|
import cd.util.debug.AstOneLine;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates code to evaluate expressions. After emitting the code, returns a
|
||||||
|
* String which indicates the register where the result can be found.
|
||||||
|
*/
|
||||||
|
class ExprGenerator extends ExprVisitor<Register, Void> {
|
||||||
|
protected final AstCodeGenerator cg;
|
||||||
|
|
||||||
|
ExprGenerator(AstCodeGenerator astCodeGenerator) {
|
||||||
|
cg = astCodeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Register gen(Expr ast) {
|
||||||
|
return visit(ast, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register visit(Expr ast, Void arg) {
|
||||||
|
try {
|
||||||
|
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||||
|
return super.visit(ast, null);
|
||||||
|
} finally {
|
||||||
|
cg.emit.decreaseIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register binaryOp(BinaryOp ast, Void arg) {
|
||||||
|
String op;
|
||||||
|
if (ast.operator == BOp.B_TIMES)
|
||||||
|
op = "imul";
|
||||||
|
else if (ast.operator == BOp.B_PLUS)
|
||||||
|
op = "add";
|
||||||
|
else if (ast.operator == BOp.B_MINUS)
|
||||||
|
op = "sub";
|
||||||
|
else if (ast.operator == BOp.B_DIV)
|
||||||
|
op = "div";
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Not required (optional)");
|
||||||
|
op += "l"; // long = 4B = 32b
|
||||||
|
|
||||||
|
Register left, right;
|
||||||
|
int leftNeeded = ast.left().registersNeeded();
|
||||||
|
int rightNeeded = ast.right().registersNeeded();
|
||||||
|
if (leftNeeded > rightNeeded) {
|
||||||
|
left = cg.eg.visit(ast.left(), arg);
|
||||||
|
right = cg.eg.visit(ast.right(), arg);
|
||||||
|
} else {
|
||||||
|
right = cg.eg.visit(ast.right(), arg);
|
||||||
|
left = cg.eg.visit(ast.left(), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast.operator == BOp.B_DIV) {
|
||||||
|
// The dividend (left) will be replaced with the quotient
|
||||||
|
// So free the divisor register (right) and return the quotient (left)
|
||||||
|
division(left, right);
|
||||||
|
cg.rm.releaseRegister(right);
|
||||||
|
return left;
|
||||||
|
} else {
|
||||||
|
// All operators take the form OP A, B --> B = B OP A --> B is left, A is right
|
||||||
|
// Therefore the result is stored in B (left) and A (right) can be freed
|
||||||
|
cg.emit.emit(op, right, left);
|
||||||
|
cg.rm.releaseRegister(right);
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Division with divide by zero check incorporated
|
||||||
|
*/
|
||||||
|
private void division(Register dividend, Register divisor) {
|
||||||
|
division(dividend, divisor, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special implementation for divisions, as it requires specific registers to be used
|
||||||
|
* @param dividend Maximum 32bit (even though the division is implemented as 64bit)
|
||||||
|
* @param divisor Maximum 32bit
|
||||||
|
* @param checkZero Whether to generate code that checks for division by zero or not
|
||||||
|
* @return The quotient will be in place of the dividend and the remainder in the divisor
|
||||||
|
*/
|
||||||
|
protected void division(Register dividend, Register divisor, boolean checkZero) {
|
||||||
|
if (checkZero) {
|
||||||
|
String beginDivision = cg.emit.uniqueLabel();
|
||||||
|
cg.emit.emit("cmp", 0, divisor);
|
||||||
|
cg.emit.emit("jne", beginDivision);
|
||||||
|
// Exit with error code in case of div/0
|
||||||
|
Interrupts.exit(cg, Interrupts.EXIT_DIV_0);
|
||||||
|
cg.emit.emitLabel(beginDivision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// D = d * q + r (dividend, divisor, quotient, remainder)
|
||||||
|
// EDX:EAX = d * EAX + EDX (before and after the div operation)
|
||||||
|
|
||||||
|
// Take care of register use: move EAX to memory if necessary
|
||||||
|
// The stack is not needed because the division operator is not recursive
|
||||||
|
// After the division is performed, move the data back to dividend and divisor
|
||||||
|
Register d, D = Register.EAX;
|
||||||
|
if (divisor.equals(D)) {
|
||||||
|
d = cg.rm.getRegister();
|
||||||
|
cg.emit.emitMove(divisor, d);
|
||||||
|
cg.emit.emitMove(dividend, Register.EAX);
|
||||||
|
} else {
|
||||||
|
if (!dividend.equals(D) && cg.rm.isInUse(D))
|
||||||
|
cg.emit.emitMove(D, "(" + Interrupts.Mem.EAX.pos + ")");
|
||||||
|
cg.emit.emitMove(dividend, D);
|
||||||
|
if (divisor.equals(Register.EDX)) {
|
||||||
|
d = cg.rm.getRegister();
|
||||||
|
cg.emit.emitMove(divisor, d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Division
|
||||||
|
cg.emit.emit("cdq");
|
||||||
|
cg.emit.emit("idiv", divisor);
|
||||||
|
cg.emit.emitMove(Register.EAX, dividend);
|
||||||
|
cg.emit.emitMove(Register.EDX, divisor);
|
||||||
|
|
||||||
|
Register q = Register.EAX, r = Register.EDX;
|
||||||
|
if (divisor.equals(D)) {
|
||||||
|
cg.emit.emitMove(q, dividend);
|
||||||
|
cg.emit.emitMove(r, divisor);
|
||||||
|
} else {
|
||||||
|
if (!dividend.equals(D) && cg.rm.isInUse(D))
|
||||||
|
cg.emit.emitMove(Interrupts.Mem.EAX.pos, D);
|
||||||
|
cg.emit.emitMove(dividend, D);
|
||||||
|
if (!divisor.equals(r)) {
|
||||||
|
cg.emit.emitMove(r, divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register booleanConst(BooleanConst ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register builtInRead(BuiltInRead ast, Void arg) {
|
||||||
|
// Holds the first digit and then is used to accumulate the total value
|
||||||
|
Register value = cg.rm.getRegister();
|
||||||
|
// Used to mark for positive (0) or negative (1) value
|
||||||
|
Register negative = cg.rm.getRegister();
|
||||||
|
// Holds the next digit read, once the first has been read
|
||||||
|
Register nextDigit = cg.rm.getRegister();
|
||||||
|
|
||||||
|
// Labels for the read structure
|
||||||
|
String removeWhitespace = cg.emit.uniqueLabel();
|
||||||
|
String negativeFirstDigit = cg.emit.uniqueLabel();
|
||||||
|
String inputError = cg.emit.uniqueLabel();
|
||||||
|
String moreDigits = cg.emit.uniqueLabel();
|
||||||
|
String endRead = cg.emit.uniqueLabel();
|
||||||
|
String checkSign = cg.emit.uniqueLabel();
|
||||||
|
|
||||||
|
// By default, variable is positive (negative == 0)
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(0), negative);
|
||||||
|
|
||||||
|
// Skip whitespace ( \t\n\r) at the beginning
|
||||||
|
cg.emit.emitLabel(removeWhitespace);
|
||||||
|
Interrupts.readChar(cg, value);
|
||||||
|
cg.emit.emit("cmp", '\n', value);
|
||||||
|
cg.emit.emit("je", removeWhitespace);
|
||||||
|
cg.emit.emit("cmp", ' ', value);
|
||||||
|
cg.emit.emit("je", removeWhitespace);
|
||||||
|
cg.emit.emit("cmp", '\t', value);
|
||||||
|
cg.emit.emit("je", removeWhitespace);
|
||||||
|
cg.emit.emit("cmp", '\r', value);
|
||||||
|
cg.emit.emit("je", removeWhitespace);
|
||||||
|
|
||||||
|
// Recognize first digit, if negative read another digit to place at value
|
||||||
|
cg.emit.emit("cmp", '-', value);
|
||||||
|
cg.emit.emit("je", negativeFirstDigit);
|
||||||
|
charToIntOrJump(value, inputError);
|
||||||
|
cg.emit.emit("jmp", moreDigits);
|
||||||
|
|
||||||
|
// Negative first digit
|
||||||
|
cg.emit.emitLabel(negativeFirstDigit);
|
||||||
|
Interrupts.readChar(cg, value);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(1), negative);
|
||||||
|
charToIntOrJump(value, inputError);
|
||||||
|
|
||||||
|
// All other digits:
|
||||||
|
// while ( (nextDigit = read()).isDigit())
|
||||||
|
// value = value * 10 + nextDigit
|
||||||
|
cg.emit.emitLabel(moreDigits);
|
||||||
|
Interrupts.readChar(cg, nextDigit);
|
||||||
|
charToIntOrJump(nextDigit, checkSign);
|
||||||
|
cg.emit.emit("imul", 10, value);
|
||||||
|
cg.emit.emit("add", nextDigit, value);
|
||||||
|
cg.emit.emit("jmp", moreDigits);
|
||||||
|
|
||||||
|
// No number can be read (non-digit first or following the '-')
|
||||||
|
cg.emit.emitLabel(inputError);
|
||||||
|
Interrupts.exit(cg, Interrupts.EXIT_INPUT_MISMATCH);
|
||||||
|
|
||||||
|
// Sign check and end
|
||||||
|
cg.emit.emitLabel(checkSign);
|
||||||
|
cg.emit.emit("cmp", 0, negative);
|
||||||
|
cg.emit.emit("je", endRead);
|
||||||
|
cg.emit.emit("negl", value);
|
||||||
|
cg.emit.emitLabel(endRead);
|
||||||
|
|
||||||
|
// Register bookkeeping and return read value
|
||||||
|
cg.rm.releaseRegister(negative);
|
||||||
|
cg.rm.releaseRegister(nextDigit);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void charToIntOrJump(Register reg, String jumpLabel) {
|
||||||
|
cg.emit.emit("cmp", '0', reg);
|
||||||
|
cg.emit.emit("jl", jumpLabel);
|
||||||
|
cg.emit.emit("cmp", '9', reg);
|
||||||
|
cg.emit.emit("jg", jumpLabel);
|
||||||
|
cg.emit.emit("sub", '0', reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register cast(Cast ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register index(Index ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register intConst(IntConst ast, Void arg) {
|
||||||
|
Register r = cg.rm.getRegister();
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(ast.value), r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register field(Field ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register newArray(NewArray ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register newObject(NewObject ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register nullConst(NullConst ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register thisRef(ThisRef ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register unaryOp(UnaryOp ast, Void arg) {
|
||||||
|
Register aux = cg.eg.visit(ast.arg(), arg);
|
||||||
|
switch (ast.operator) {
|
||||||
|
case U_PLUS:
|
||||||
|
return aux;
|
||||||
|
case U_MINUS:
|
||||||
|
cg.emit.emit("negl", aux);
|
||||||
|
return aux;
|
||||||
|
case U_BOOL_NOT:
|
||||||
|
cg.emit.emit("notl", aux);
|
||||||
|
return aux;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register var(Var ast, Void arg) {
|
||||||
|
Register r = cg.rm.getRegister();
|
||||||
|
cg.emit.emitMove(ast.getLabel(), r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
97
src/cd/backend/codegen/Interrupts.java
Normal file
97
src/cd/backend/codegen/Interrupts.java
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import cd.backend.codegen.RegisterManager.Register;
|
||||||
|
|
||||||
|
import static cd.backend.codegen.Interrupts.Mem.BUFFER;
|
||||||
|
|
||||||
|
public class Interrupts {
|
||||||
|
|
||||||
|
protected static final int EXIT_OK = 0, EXIT_DIV_0 = 7, EXIT_INPUT_MISMATCH = 8;
|
||||||
|
protected static final int STDIN = 0, STDOUT = 1, STDERR = 2;
|
||||||
|
protected static final int I_EXIT = 1, I_READ = 3, I_WRITE = 4;
|
||||||
|
|
||||||
|
protected enum Mem {
|
||||||
|
BUFFER("io_buffer"), EAX("eax");
|
||||||
|
|
||||||
|
String pos;
|
||||||
|
Mem(String pos) {
|
||||||
|
this.pos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write auxiliary data variables needed for I/O and division
|
||||||
|
* @param cg AstCodeGenerator to print instructions
|
||||||
|
*/
|
||||||
|
protected static void reserveMemory(AstCodeGenerator cg) {
|
||||||
|
for (Mem m : Mem.values()) {
|
||||||
|
cg.emit.emitLabel(m.pos);
|
||||||
|
cg.emit.emitConstantData("0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a single character passed as parameter
|
||||||
|
* @param cg AstCodeGenerator to print instructions
|
||||||
|
* @param i Character code in ASCII (or single casted character)
|
||||||
|
*/
|
||||||
|
protected static void printChar(AstCodeGenerator cg, int i) {
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(i), BUFFER.pos);
|
||||||
|
printChar(cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes a single character from the BUFFER memory pos to stdout
|
||||||
|
* @param cg AstCodeGenerator to print instructions
|
||||||
|
*/
|
||||||
|
protected static void printChar(AstCodeGenerator cg) {
|
||||||
|
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
|
||||||
|
for (int i = 0; i < needed.length; i++)
|
||||||
|
if (cg.rm.isInUse(needed[i]))
|
||||||
|
cg.emit.emit("push", needed[i]);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(I_WRITE), Register.EAX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(STDOUT), Register.EBX);
|
||||||
|
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
|
||||||
|
cg.emit.emit("int", 0x80);
|
||||||
|
for (int i = needed.length - 1; i >= 0; i--)
|
||||||
|
if (cg.rm.isInUse(needed[i]))
|
||||||
|
cg.emit.emit("pop", needed[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a single character from stdin and places it in the
|
||||||
|
* memory pos BUFFER
|
||||||
|
*/
|
||||||
|
protected static void readChar(AstCodeGenerator cg, Register destination) {
|
||||||
|
Register[] needed = {Register.EAX, Register.EBX, Register.ECX, Register.EDX};
|
||||||
|
for (int i = 0; i < needed.length; i++)
|
||||||
|
if (cg.rm.isInUse(needed[i]))
|
||||||
|
cg.emit.emit("push", needed[i]);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(I_READ), Register.EAX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(STDIN), Register.EBX);
|
||||||
|
cg.emit.emitMove("$" + BUFFER.pos, Register.ECX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(1), Register.EDX);
|
||||||
|
cg.emit.emit("int", 0x80);
|
||||||
|
for (int i = needed.length - 1; i >= 0; i--)
|
||||||
|
if (cg.rm.isInUse(needed[i]))
|
||||||
|
cg.emit.emit("pop", needed[i]);
|
||||||
|
cg.emit.emitMove(BUFFER.pos, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a exit interrupt with the code provided
|
||||||
|
* @param cg AstCodeGenerator to print instructions
|
||||||
|
* @param exitCode Number to use as exit code (can use constants in this class)
|
||||||
|
*/
|
||||||
|
protected static void exit(AstCodeGenerator cg, int exitCode) {
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(I_EXIT), Register.EAX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(exitCode), Register.EBX);
|
||||||
|
cg.emit.emit("int", 0x80);
|
||||||
|
}
|
||||||
|
}
|
123
src/cd/backend/codegen/RegisterManager.java
Normal file
123
src/cd/backend/codegen/RegisterManager.java
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple class that manages the set of currently used
|
||||||
|
* and unused registers
|
||||||
|
*/
|
||||||
|
public class RegisterManager {
|
||||||
|
private List<Register> registers = new ArrayList<Register>();
|
||||||
|
|
||||||
|
// lists of register to save by the callee and the caller
|
||||||
|
public static final Register CALLEE_SAVE[] = new Register[]{Register.ESI,
|
||||||
|
Register.EDI, Register.EBX};
|
||||||
|
public static final Register CALLER_SAVE[] = new Register[]{Register.EAX,
|
||||||
|
Register.ECX, Register.EDX};
|
||||||
|
|
||||||
|
// list of general purpose registers
|
||||||
|
public static final Register GPR[] = new Register[]{Register.EAX, Register.EBX,
|
||||||
|
Register.ECX, Register.EDX, Register.ESI, Register.EDI};
|
||||||
|
|
||||||
|
// special purpose registers
|
||||||
|
public static final Register BASE_REG = Register.EBP;
|
||||||
|
public static final Register STACK_REG = Register.ESP;
|
||||||
|
|
||||||
|
public static final int SIZEOF_REG = 4;
|
||||||
|
|
||||||
|
|
||||||
|
public enum Register {
|
||||||
|
EAX("%eax", ByteRegister.EAX), EBX("%ebx", ByteRegister.EBX), ECX(
|
||||||
|
"%ecx", ByteRegister.ECX), EDX("%edx", ByteRegister.EDX), ESI(
|
||||||
|
"%esi", null), EDI("%edi", null), EBP("%ebp", null), ESP(
|
||||||
|
"%esp", null);
|
||||||
|
|
||||||
|
public final String repr;
|
||||||
|
private final ByteRegister lowByteVersion;
|
||||||
|
|
||||||
|
private Register(String repr, ByteRegister bv) {
|
||||||
|
this.repr = repr;
|
||||||
|
this.lowByteVersion = bv;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return repr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* determines if this register has an 8bit version
|
||||||
|
*/
|
||||||
|
public boolean hasLowByteVersion() {
|
||||||
|
return lowByteVersion != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a register like {@code %eax} returns {@code %al}, but doesn't
|
||||||
|
* work for {@code %esi} and {@code %edi}!
|
||||||
|
*/
|
||||||
|
public ByteRegister lowByteVersion() {
|
||||||
|
assert hasLowByteVersion();
|
||||||
|
return lowByteVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ByteRegister {
|
||||||
|
EAX("%al"), EBX("%bl"), ECX("%cl"), EDX("%dl");
|
||||||
|
|
||||||
|
public final String repr;
|
||||||
|
|
||||||
|
private ByteRegister(String repr) {
|
||||||
|
this.repr = repr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return repr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all general purpose registers to free
|
||||||
|
*/
|
||||||
|
public void initRegisters() {
|
||||||
|
registers.clear();
|
||||||
|
registers.addAll(Arrays.asList(GPR));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a free register and marks it as used
|
||||||
|
*/
|
||||||
|
public Register getRegister() {
|
||||||
|
int last = registers.size() - 1;
|
||||||
|
if (last < 0)
|
||||||
|
throw new AssemblyFailedException(
|
||||||
|
"Program requires too many registers");
|
||||||
|
|
||||||
|
return registers.remove(last);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* marks a currently used register as free
|
||||||
|
*/
|
||||||
|
public void releaseRegister(Register reg) {
|
||||||
|
assert !registers.contains(reg);
|
||||||
|
registers.add(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the register is currently non-free
|
||||||
|
*/
|
||||||
|
public boolean isInUse(Register reg) {
|
||||||
|
return !registers.contains(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the number of free registers
|
||||||
|
*/
|
||||||
|
public int availableRegisters() {
|
||||||
|
return registers.size();
|
||||||
|
}
|
||||||
|
}
|
206
src/cd/backend/codegen/StmtGenerator.java
Normal file
206
src/cd/backend/codegen/StmtGenerator.java
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
package cd.backend.codegen;
|
||||||
|
|
||||||
|
import cd.backend.codegen.RegisterManager.Register;
|
||||||
|
import cd.ir.Ast;
|
||||||
|
import cd.ir.Ast.*;
|
||||||
|
import cd.ir.AstVisitor;
|
||||||
|
import cd.util.debug.AstOneLine;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates code to process statements and declarations.
|
||||||
|
*/
|
||||||
|
class StmtGenerator extends AstVisitor<Register, Void> {
|
||||||
|
protected final AstCodeGenerator cg;
|
||||||
|
|
||||||
|
StmtGenerator(AstCodeGenerator astCodeGenerator) {
|
||||||
|
cg = astCodeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void gen(Ast ast) {
|
||||||
|
visit(ast, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register visit(Ast ast, Void arg) {
|
||||||
|
try {
|
||||||
|
cg.emit.increaseIndent("Emitting " + AstOneLine.toString(ast));
|
||||||
|
return super.visit(ast, arg);
|
||||||
|
} finally {
|
||||||
|
cg.emit.decreaseIndent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares a variable in the data segment. This method was implemented because it was necessary
|
||||||
|
* to create the data segment
|
||||||
|
* @param ast Variable declaration node
|
||||||
|
* @param arg Extra arguments
|
||||||
|
* @return null
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Register varDecl(Ast.VarDecl ast, Void arg) {
|
||||||
|
cg.emit.emitLabel(ast.getLabel());
|
||||||
|
cg.emit.emitConstantData("0");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register methodCall(MethodCall ast, Void dummy) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register methodDecl(MethodDecl ast, Void arg) {
|
||||||
|
// Prologue: in future version a more complex prologue that
|
||||||
|
// takes care of parameters and return values is necessary
|
||||||
|
// Method code
|
||||||
|
cg.rm.initRegisters();
|
||||||
|
cg.emit.emitRaw(".section .data");
|
||||||
|
Interrupts.reserveMemory(cg);
|
||||||
|
// Variables
|
||||||
|
cg.sg.visit(ast.decls(), arg);
|
||||||
|
cg.emit.emitRaw(".section .text");
|
||||||
|
cg.emit.emitRaw(".globl " + ast.name);
|
||||||
|
cg.emit.emitLabel(ast.name);
|
||||||
|
Register result = cg.sg.visit(ast.body(), arg);
|
||||||
|
// Epilogue: not needed in the simple HW1
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register ifElse(IfElse ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register whileLoop(WhileLoop ast, Void arg) {
|
||||||
|
{
|
||||||
|
throw new RuntimeException("Not required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register assign(Assign ast, Void arg) {
|
||||||
|
// Because we only handle very simple programs in HW1,
|
||||||
|
// you can just emit the prologue here!
|
||||||
|
Register value = cg.eg.visit(ast.right(), arg);
|
||||||
|
Ast.Var variable = (Var) ast.left();
|
||||||
|
cg.emit.emitMove(value, variable.getLabel());
|
||||||
|
cg.rm.releaseRegister(value);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register builtInWrite(BuiltInWrite ast, Void arg) {
|
||||||
|
String printNonZero = cg.emit.uniqueLabel();
|
||||||
|
String end = cg.emit.uniqueLabel();
|
||||||
|
String printPositive = cg.emit.uniqueLabel();
|
||||||
|
String stackLoop = cg.emit.uniqueLabel();
|
||||||
|
String printLoop = cg.emit.uniqueLabel();
|
||||||
|
|
||||||
|
Register value = cg.eg.visit(ast.arg(), arg);
|
||||||
|
// Begin: decide if print 0 or print number
|
||||||
|
cg.emit.emit("cmp", 0, value);
|
||||||
|
cg.emit.emit("jne", printNonZero); // value != 0
|
||||||
|
|
||||||
|
// Print 0
|
||||||
|
Interrupts.printChar(cg, '0'); // print 0
|
||||||
|
cg.emit.emit("jmp", end);
|
||||||
|
|
||||||
|
// Not 0: positive or negative?
|
||||||
|
cg.emit.emitLabel(printNonZero);
|
||||||
|
cg.emit.emit("jg", printPositive); // value > 0
|
||||||
|
|
||||||
|
// Number is negative, print '-' then number (with sign changed)
|
||||||
|
Interrupts.printChar(cg, '-');
|
||||||
|
cg.emit.emit("negl", value);
|
||||||
|
|
||||||
|
// Print number: extract and print all digits of number
|
||||||
|
cg.emit.emitLabel(printPositive);
|
||||||
|
|
||||||
|
// In order to avoid using EAX and EDX, we are going to guarantee
|
||||||
|
// that there will be at least 2 other registers available
|
||||||
|
List<Register> auxRegs = Arrays.asList(Register.EDI, Register.ESI);
|
||||||
|
List<Register> divRegs = Arrays.asList(Register.EAX, Register.EDX);
|
||||||
|
Map<Register, Boolean> isPushed = new HashMap<>();
|
||||||
|
boolean usingAuxRegs = cg.rm.availableRegisters() < 4;
|
||||||
|
if (usingAuxRegs)
|
||||||
|
for (int i = 0; i < auxRegs.size(); i++)
|
||||||
|
isPushed.put(auxRegs.get(i), cg.rm.isInUse(auxRegs.get(i)));
|
||||||
|
|
||||||
|
// Free EAX and EDX for div operand use
|
||||||
|
isPushed.put(Register.EAX, cg.rm.isInUse(Register.EAX) && !value.equals(Register.EAX));
|
||||||
|
isPushed.put(Register.EDX, cg.rm.isInUse(Register.EDX));
|
||||||
|
|
||||||
|
// Push all elements
|
||||||
|
for (Register r : isPushed.keySet())
|
||||||
|
if (isPushed.get(r))
|
||||||
|
cg.emit.emit("push", r);
|
||||||
|
|
||||||
|
// Force counter and divisor to not be EDX/EAX, as they need to coexist
|
||||||
|
Register counter = cg.rm.getRegister();
|
||||||
|
Register divisor = cg.rm.getRegister();
|
||||||
|
while (divRegs.contains(counter)) {
|
||||||
|
Register aux = cg.rm.getRegister();
|
||||||
|
cg.rm.releaseRegister(counter);
|
||||||
|
counter = aux;
|
||||||
|
}
|
||||||
|
while (divRegs.contains(divisor)) {
|
||||||
|
Register aux = cg.rm.getRegister();
|
||||||
|
cg.rm.releaseRegister(divisor);
|
||||||
|
divisor = aux;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide by 10 to extract the remainders and push them to the stack
|
||||||
|
// Registers used: EAX is the remaining digits (initially the value)
|
||||||
|
// in div it is used for the lower half of the dividend and for quotient
|
||||||
|
// divisor
|
||||||
|
// EDX is used as the high half of the dividend and as remainder
|
||||||
|
cg.emit.emitMove(value, Register.EAX);
|
||||||
|
cg.rm.releaseRegister(value);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(0), counter);
|
||||||
|
|
||||||
|
cg.emit.emitLabel(stackLoop);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(0), Register.EDX);
|
||||||
|
cg.emit.emitMove(AssemblyEmitter.constant(10), divisor);
|
||||||
|
cg.emit.emit("div", divisor);
|
||||||
|
cg.emit.emit("add", '0', Register.EDX);
|
||||||
|
cg.emit.emit("push", Register.EDX);
|
||||||
|
cg.emit.emit("inc", counter);
|
||||||
|
cg.emit.emit("cmp", 0, Register.EAX);
|
||||||
|
cg.emit.emit("je", printLoop);
|
||||||
|
cg.emit.emit("jmp", stackLoop);
|
||||||
|
// Release divisor register
|
||||||
|
cg.rm.releaseRegister(divisor);
|
||||||
|
|
||||||
|
// Print digits from the stack
|
||||||
|
cg.emit.emitLabel(printLoop);
|
||||||
|
cg.emit.emit("cmp", 0, counter);
|
||||||
|
cg.emit.emit("jz", end);
|
||||||
|
cg.emit.emit("dec", counter);
|
||||||
|
cg.emit.emit("pop", Interrupts.Mem.BUFFER.pos);
|
||||||
|
Interrupts.printChar(cg);
|
||||||
|
cg.emit.emit("jmp", printLoop);
|
||||||
|
cg.emit.emitLabel(end);
|
||||||
|
|
||||||
|
cg.rm.releaseRegister(counter);
|
||||||
|
|
||||||
|
// Restore original registers
|
||||||
|
for (Register r : isPushed.keySet())
|
||||||
|
if (isPushed.get(r))
|
||||||
|
cg.emit.emit("pop", r);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Register builtInWriteln(BuiltInWriteln ast, Void arg) {
|
||||||
|
Interrupts.printChar(cg, '\n');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
6
src/cd/frontend/parser/.gitignore
vendored
Normal file
6
src/cd/frontend/parser/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/Javali.tokens
|
||||||
|
/JavaliBaseVisitor.java
|
||||||
|
/JavaliLexer.java
|
||||||
|
/JavaliLexer.tokens
|
||||||
|
/JavaliParser.java
|
||||||
|
/JavaliVisitor.java
|
9
src/cd/frontend/parser/ParseFailure.java
Normal file
9
src/cd/frontend/parser/ParseFailure.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package cd.frontend.parser;
|
||||||
|
|
||||||
|
public class ParseFailure extends RuntimeException {
|
||||||
|
private static final long serialVersionUID = -949992757679367939L;
|
||||||
|
public ParseFailure(int line, String string) {
|
||||||
|
super(String.format("Parse failure on line %d: %s",
|
||||||
|
line, string));
|
||||||
|
}
|
||||||
|
}
|
752
src/cd/ir/Ast.java
Normal file
752
src/cd/ir/Ast.java
Normal file
|
@ -0,0 +1,752 @@
|
||||||
|
package cd.ir;
|
||||||
|
|
||||||
|
import cd.util.Pair;
|
||||||
|
import cd.util.debug.AstOneLine;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class Ast {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of children AST nodes. Typically, this list is of a fixed size: its contents
|
||||||
|
* can also be accessed using the various accessors defined on the Ast subtypes.
|
||||||
|
*
|
||||||
|
* <p><b>Note:</b> this list may contain null pointers!
|
||||||
|
*/
|
||||||
|
public final List<Ast> rwChildren;
|
||||||
|
final String VAR_LABEL_FORMAT = "var_%s";
|
||||||
|
|
||||||
|
protected Ast(int fixedCount) {
|
||||||
|
if (fixedCount == -1)
|
||||||
|
this.rwChildren = new ArrayList<Ast>();
|
||||||
|
else
|
||||||
|
this.rwChildren = Arrays.asList(new Ast[fixedCount]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the list of children for this node. The result will
|
||||||
|
* never contain null pointers.
|
||||||
|
*/
|
||||||
|
public List<Ast> children() {
|
||||||
|
ArrayList<Ast> result = new ArrayList<Ast>();
|
||||||
|
for (Ast n : rwChildren) {
|
||||||
|
if (n != null) result.add(n);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new list containing all children AST nodes
|
||||||
|
* that are of the given type.
|
||||||
|
*/
|
||||||
|
public <A> List<A> childrenOfType(Class<A> C) {
|
||||||
|
List<A> res = new ArrayList<A>();
|
||||||
|
for (Ast c : children()) {
|
||||||
|
if (C.isInstance(c))
|
||||||
|
res.add(C.cast(c));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Accept method for the pattern Visitor. */
|
||||||
|
public abstract <R,A> R accept(AstVisitor<R, A> visitor, A arg);
|
||||||
|
|
||||||
|
/** Convenient debugging printout */
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format(
|
||||||
|
"(%s)@%x",
|
||||||
|
AstOneLine.toString(this),
|
||||||
|
System.identityHashCode(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
// _________________________________________________________________
|
||||||
|
// Expressions
|
||||||
|
|
||||||
|
/** Base class for all expressions */
|
||||||
|
public static abstract class Expr extends Ast {
|
||||||
|
|
||||||
|
int needed = -1;
|
||||||
|
|
||||||
|
protected Expr(int fixedCount) {
|
||||||
|
super(fixedCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return this.accept((ExprVisitor<R, A>) visitor, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract <R, A> R accept(ExprVisitor<R, A> visitor, A arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers needed to perform the operation represented by this Expr node
|
||||||
|
* If not already run, the cost is visiting all subnodes, else the cost is constant
|
||||||
|
* @return Minimum number of registers needed
|
||||||
|
*/
|
||||||
|
public abstract int registersNeeded();
|
||||||
|
|
||||||
|
/** Copies any non-AST fields. */
|
||||||
|
protected <E extends Expr> E postCopy(E item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class used for exprs with left/right operands.
|
||||||
|
* We use this for all expressions that take strictly two operands,
|
||||||
|
* such as binary operators or array indexing. */
|
||||||
|
public static abstract class LeftRightExpr extends Expr {
|
||||||
|
|
||||||
|
public LeftRightExpr(Expr left, Expr right) {
|
||||||
|
super(2);
|
||||||
|
assert left != null;
|
||||||
|
assert right != null;
|
||||||
|
setLeft(left);
|
||||||
|
setRight(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registersNeeded() {
|
||||||
|
if (needed != -1)
|
||||||
|
return needed;
|
||||||
|
int leftNeed = left().registersNeeded();
|
||||||
|
int rightNeed = right().registersNeeded();
|
||||||
|
if (leftNeed > rightNeed)
|
||||||
|
return needed = leftNeed;
|
||||||
|
else if (leftNeed < rightNeed)
|
||||||
|
return needed = rightNeed;
|
||||||
|
else
|
||||||
|
return needed = ++rightNeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr left() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setLeft(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
public Expr right() { return (Expr) this.rwChildren.get(1); }
|
||||||
|
public void setRight(Expr node) { this.rwChildren.set(1, node); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class used for expressions with a single argument */
|
||||||
|
public static abstract class ArgExpr extends Expr {
|
||||||
|
|
||||||
|
public ArgExpr(Expr arg) {
|
||||||
|
super(1);
|
||||||
|
assert arg != null;
|
||||||
|
setArg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registersNeeded() {
|
||||||
|
return arg().registersNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Base class used for things with no arguments */
|
||||||
|
protected static abstract class LeafExpr extends Expr {
|
||||||
|
public LeafExpr() {
|
||||||
|
super(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registersNeeded() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents {@code this}, the current object */
|
||||||
|
public static class ThisRef extends LeafExpr {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.thisRef(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A binary operation combining a left and right operand,
|
||||||
|
* such as "1+2" or "3*4" */
|
||||||
|
public static class BinaryOp extends LeftRightExpr {
|
||||||
|
|
||||||
|
public static enum BOp {
|
||||||
|
|
||||||
|
B_TIMES("*"),
|
||||||
|
B_DIV("/"),
|
||||||
|
B_MOD("%"),
|
||||||
|
B_PLUS("+"),
|
||||||
|
B_MINUS("-"),
|
||||||
|
B_AND("&&"),
|
||||||
|
B_OR("||"),
|
||||||
|
B_EQUAL("=="),
|
||||||
|
B_NOT_EQUAL("!="),
|
||||||
|
B_LESS_THAN("<"),
|
||||||
|
B_LESS_OR_EQUAL("<="),
|
||||||
|
B_GREATER_THAN(">"),
|
||||||
|
B_GREATER_OR_EQUAL(">=");
|
||||||
|
|
||||||
|
public String repr;
|
||||||
|
private BOp(String repr) { this.repr = repr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that this method ignores short-circuit evaluation of boolean
|
||||||
|
* AND/OR.
|
||||||
|
*
|
||||||
|
* @return <code>true</code> iff <code>A op B == B op A</code> for this
|
||||||
|
* operator.
|
||||||
|
*/
|
||||||
|
public boolean isCommutative() {
|
||||||
|
switch(this) {
|
||||||
|
case B_PLUS:
|
||||||
|
case B_TIMES:
|
||||||
|
case B_AND:
|
||||||
|
case B_OR:
|
||||||
|
case B_EQUAL:
|
||||||
|
case B_NOT_EQUAL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public BOp operator;
|
||||||
|
|
||||||
|
public BinaryOp(Expr left, BOp operator, Expr right) {
|
||||||
|
super(left, right);
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.binaryOp(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A Cast from one type to another: {@code (typeName)arg} */
|
||||||
|
public static class Cast extends ArgExpr {
|
||||||
|
|
||||||
|
public String typeName;
|
||||||
|
|
||||||
|
public Cast(Expr arg, String typeName) {
|
||||||
|
super(arg);
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.cast(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IntConst extends LeafExpr {
|
||||||
|
|
||||||
|
public final int value;
|
||||||
|
public IntConst(int value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.intConst(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BooleanConst extends LeafExpr {
|
||||||
|
|
||||||
|
public final boolean value;
|
||||||
|
public BooleanConst(boolean value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.booleanConst(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NullConst extends LeafExpr {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.nullConst(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Field extends ArgExpr {
|
||||||
|
|
||||||
|
public final String fieldName;
|
||||||
|
|
||||||
|
public Field(Expr arg, String fieldName) {
|
||||||
|
super(arg);
|
||||||
|
assert arg != null && fieldName != null;
|
||||||
|
this.fieldName = fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.field(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Index extends LeftRightExpr {
|
||||||
|
|
||||||
|
public Index(Expr array, Expr index) {
|
||||||
|
super(array, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.index(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NewObject extends LeafExpr {
|
||||||
|
|
||||||
|
/** Name of the type to be created */
|
||||||
|
public String typeName;
|
||||||
|
|
||||||
|
public NewObject(String typeName) {
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.newObject(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class NewArray extends ArgExpr {
|
||||||
|
|
||||||
|
/** Name of the type to be created: must be an array type */
|
||||||
|
public String typeName;
|
||||||
|
|
||||||
|
public NewArray(String typeName, Expr capacity) {
|
||||||
|
super(capacity);
|
||||||
|
this.typeName = typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.newArray(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnaryOp extends ArgExpr {
|
||||||
|
|
||||||
|
public static enum UOp {
|
||||||
|
U_PLUS("+"),
|
||||||
|
U_MINUS("-"),
|
||||||
|
U_BOOL_NOT("!");
|
||||||
|
public String repr;
|
||||||
|
private UOp(String repr) { this.repr = repr; }
|
||||||
|
};
|
||||||
|
|
||||||
|
public final UOp operator;
|
||||||
|
|
||||||
|
public UnaryOp(UOp operator, Expr arg) {
|
||||||
|
super(arg);
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.unaryOp(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Var extends LeafExpr {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this constructor to build an instance of this AST
|
||||||
|
* in the parser.
|
||||||
|
*/
|
||||||
|
public Var(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return String.format(VAR_LABEL_FORMAT, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.var(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuiltInRead extends LeafExpr {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.builtInRead(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MethodCallExpr extends Expr {
|
||||||
|
|
||||||
|
public String methodName;
|
||||||
|
|
||||||
|
public MethodCallExpr(Expr rcvr, String methodName, List<Expr> arguments) {
|
||||||
|
super(-1);
|
||||||
|
assert rcvr != null && methodName != null && arguments != null;
|
||||||
|
this.methodName = methodName;
|
||||||
|
this.rwChildren.add(rcvr);
|
||||||
|
this.rwChildren.addAll(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the receiver of the method call.
|
||||||
|
* i.e., for a method call {@code a.b(c,d)} returns {@code a}. */
|
||||||
|
public Expr receiver() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
|
||||||
|
/** Changes the receiver of the method call.
|
||||||
|
* i.e., for a method call {@code a.b(c,d)} changes {@code a}. */
|
||||||
|
public void setReceiver(Expr rcvr) { this.rwChildren.set(0, rcvr); }
|
||||||
|
|
||||||
|
/** Returns all arguments to the method, <b>including the receiver.</b>
|
||||||
|
* i.e, for a method call {@code a.b(c,d)} returns {@code [a, c, d]} */
|
||||||
|
public List<Expr> allArguments()
|
||||||
|
{
|
||||||
|
ArrayList<Expr> result = new ArrayList<Expr>();
|
||||||
|
for (Ast chi : this.rwChildren)
|
||||||
|
result.add((Expr) chi);
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns all arguments to the method, without the receiver.
|
||||||
|
* i.e, for a method call {@code a.b(c,d)} returns {@code [c, d]} */
|
||||||
|
public List<Expr> argumentsWithoutReceiver()
|
||||||
|
{
|
||||||
|
ArrayList<Expr> result = new ArrayList<Expr>();
|
||||||
|
for (int i = 1; i < this.rwChildren.size(); i++)
|
||||||
|
result.add((Expr) this.rwChildren.get(i));
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(ExprVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.methodCall(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registersNeeded() {
|
||||||
|
throw new RuntimeException("Not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// _________________________________________________________________
|
||||||
|
// Statements
|
||||||
|
|
||||||
|
/** Interface for all statements */
|
||||||
|
public static abstract class Stmt extends Ast {
|
||||||
|
protected Stmt(int fixedCount) {
|
||||||
|
super(fixedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Represents an empty statement: has no effect. */
|
||||||
|
public static class Nop extends Stmt {
|
||||||
|
|
||||||
|
public Nop() {
|
||||||
|
super(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.nop(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An assignment from {@code right()} to the location
|
||||||
|
* represented by {@code left()}.
|
||||||
|
*/
|
||||||
|
public static class Assign extends Stmt {
|
||||||
|
|
||||||
|
public Assign(Expr left, Expr right) {
|
||||||
|
super(2);
|
||||||
|
assert left != null && right != null;
|
||||||
|
setLeft(left);
|
||||||
|
setRight(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr left() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setLeft(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
public Expr right() { return (Expr) this.rwChildren.get(1); }
|
||||||
|
public void setRight(Expr node) { this.rwChildren.set(1, node); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.assign(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class IfElse extends Stmt {
|
||||||
|
|
||||||
|
public IfElse(Expr cond, Ast then, Ast otherwise) {
|
||||||
|
super(3);
|
||||||
|
assert cond != null && then != null && otherwise != null;
|
||||||
|
setCondition(cond);
|
||||||
|
setThen(then);
|
||||||
|
setOtherwise(otherwise);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr condition() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setCondition(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
public Ast then() { return this.rwChildren.get(1); }
|
||||||
|
public void setThen(Ast node) { this.rwChildren.set(1, node); }
|
||||||
|
|
||||||
|
public Ast otherwise() { return this.rwChildren.get(2); }
|
||||||
|
public void setOtherwise(Ast node) { this.rwChildren.set(2, node); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.ifElse(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ReturnStmt extends Stmt {
|
||||||
|
|
||||||
|
public ReturnStmt(Expr arg) {
|
||||||
|
super(1);
|
||||||
|
setArg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.returnStmt(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuiltInWrite extends Stmt {
|
||||||
|
|
||||||
|
public BuiltInWrite(Expr arg) {
|
||||||
|
super(1);
|
||||||
|
assert arg != null;
|
||||||
|
setArg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr arg() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setArg(Expr node) { this.rwChildren.set(0, node); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.builtInWrite(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BuiltInWriteln extends Stmt {
|
||||||
|
|
||||||
|
public BuiltInWriteln() {
|
||||||
|
super(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.builtInWriteln(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MethodCall extends Stmt {
|
||||||
|
|
||||||
|
public MethodCall(MethodCallExpr mce) {
|
||||||
|
super(1);
|
||||||
|
this.rwChildren.set(0, mce);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodCallExpr getMethodCallExpr() {
|
||||||
|
return (MethodCallExpr)this.rwChildren.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.methodCall(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class WhileLoop extends Stmt {
|
||||||
|
|
||||||
|
public WhileLoop(Expr condition, Ast body) {
|
||||||
|
super(2);
|
||||||
|
assert condition != null && body != null;
|
||||||
|
setCondition(condition);
|
||||||
|
setBody(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Expr condition() { return (Expr) this.rwChildren.get(0); }
|
||||||
|
public void setCondition(Expr cond) { this.rwChildren.set(0, cond); }
|
||||||
|
|
||||||
|
public Ast body() { return this.rwChildren.get(1); }
|
||||||
|
public void setBody(Ast body) { this.rwChildren.set(1, body); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.whileLoop(this, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// _________________________________________________________________
|
||||||
|
// Declarations
|
||||||
|
|
||||||
|
/** Interface for all declarations */
|
||||||
|
public static abstract class Decl extends Ast {
|
||||||
|
protected Decl(int fixedCount) {
|
||||||
|
super(fixedCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class VarDecl extends Decl {
|
||||||
|
|
||||||
|
public String type;
|
||||||
|
public String name;
|
||||||
|
public VarDecl(String type, String name) {
|
||||||
|
this(0, type, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected VarDecl(int num, String type, String name) {
|
||||||
|
super(num);
|
||||||
|
this.type = type;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLabel() {
|
||||||
|
return String.format(VAR_LABEL_FORMAT, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.varDecl(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used in {@link MethodDecl} to group together declarations
|
||||||
|
* and method bodies. */
|
||||||
|
public static class Seq extends Decl {
|
||||||
|
|
||||||
|
public Seq(List<Ast> nodes) {
|
||||||
|
super(-1);
|
||||||
|
if (nodes != null) this.rwChildren.addAll(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Grant access to the raw list of children for seq nodes */
|
||||||
|
public List<Ast> rwChildren() {
|
||||||
|
return this.rwChildren;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.seq(this, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MethodDecl extends Decl {
|
||||||
|
|
||||||
|
public String returnType;
|
||||||
|
public String name;
|
||||||
|
public List<String> argumentTypes;
|
||||||
|
public List<String> argumentNames;
|
||||||
|
public MethodDecl(
|
||||||
|
String returnType,
|
||||||
|
String name,
|
||||||
|
List<Pair<String>> formalParams,
|
||||||
|
Seq decls,
|
||||||
|
Seq body) {
|
||||||
|
this(returnType, name, Pair.unzipA(formalParams), Pair.unzipB(formalParams), decls, body);
|
||||||
|
}
|
||||||
|
public MethodDecl(
|
||||||
|
String returnType,
|
||||||
|
String name,
|
||||||
|
List<String> argumentTypes,
|
||||||
|
List<String> argumentNames,
|
||||||
|
Seq decls,
|
||||||
|
Seq body) {
|
||||||
|
super(2);
|
||||||
|
this.returnType = returnType;
|
||||||
|
this.name = name;
|
||||||
|
this.argumentTypes = argumentTypes;
|
||||||
|
this.argumentNames = argumentNames;
|
||||||
|
setDecls(decls);
|
||||||
|
setBody(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Seq decls() { return (Seq) this.rwChildren.get(0); }
|
||||||
|
public void setDecls(Seq decls) { this.rwChildren.set(0, decls); }
|
||||||
|
|
||||||
|
public Seq body() { return (Seq) this.rwChildren.get(1); }
|
||||||
|
public void setBody(Seq body) { this.rwChildren.set(1, body); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.methodDecl(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ClassDecl extends Decl {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String superClass;
|
||||||
|
public ClassDecl(
|
||||||
|
String name,
|
||||||
|
String superClass,
|
||||||
|
List<? extends Ast> members) {
|
||||||
|
super(-1);
|
||||||
|
this.name = name;
|
||||||
|
this.superClass = superClass;
|
||||||
|
this.rwChildren.addAll(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Ast> members() {
|
||||||
|
return Collections.unmodifiableList(this.rwChildren);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<VarDecl> fields() {
|
||||||
|
return childrenOfType(VarDecl.class);
|
||||||
|
} // includes constants!
|
||||||
|
|
||||||
|
public List<MethodDecl> methods() {
|
||||||
|
return childrenOfType(MethodDecl.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <R, A> R accept(AstVisitor<R, A> visitor, A arg) {
|
||||||
|
return visitor.classDecl(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
106
src/cd/ir/AstVisitor.java
Normal file
106
src/cd/ir/AstVisitor.java
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
package cd.ir;
|
||||||
|
|
||||||
|
import cd.ir.Ast.Decl;
|
||||||
|
import cd.ir.Ast.Expr;
|
||||||
|
import cd.ir.Ast.Stmt;
|
||||||
|
|
||||||
|
/** A visitor that visits any kind of node */
|
||||||
|
public class AstVisitor<R,A> extends ExprVisitor<R,A> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recurse and process {@code ast}. It is preferred to
|
||||||
|
* call this rather than calling accept directly, since
|
||||||
|
* it can be overloaded to introduce memoization,
|
||||||
|
* for example. */
|
||||||
|
public R visit(Ast ast, A arg) {
|
||||||
|
return ast.accept(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handy function which visits the children of {@code ast},
|
||||||
|
* providing "arg" to each of them. It returns the result of
|
||||||
|
* the last child in the list. It is invoked by the method
|
||||||
|
* {@link #dflt(Ast, Object)} by default.
|
||||||
|
*/
|
||||||
|
public R visitChildren(Ast ast, A arg) {
|
||||||
|
R lastValue = null;
|
||||||
|
for (Ast child : ast.children())
|
||||||
|
lastValue = visit(child, arg);
|
||||||
|
return lastValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default action for default actions is to call this,
|
||||||
|
* which simply recurses to any children. Also called
|
||||||
|
* by seq() by default. */
|
||||||
|
protected R dflt(Ast ast, A arg) {
|
||||||
|
return visitChildren(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default action for statements is to call this */
|
||||||
|
protected R dfltStmt(Stmt ast, A arg) {
|
||||||
|
return dflt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default action for expressions is to call this */
|
||||||
|
protected R dfltExpr(Expr ast, A arg) {
|
||||||
|
return dflt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default action for AST nodes representing declarations
|
||||||
|
* is to call this function */
|
||||||
|
protected R dfltDecl(Decl ast, A arg) {
|
||||||
|
return dflt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R assign(Ast.Assign ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R builtInWrite(Ast.BuiltInWrite ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R builtInWriteln(Ast.BuiltInWriteln ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R classDecl(Ast.ClassDecl ast, A arg) {
|
||||||
|
return dfltDecl(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R methodDecl(Ast.MethodDecl ast, A arg) {
|
||||||
|
return dfltDecl(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R varDecl(Ast.VarDecl ast, A arg) {
|
||||||
|
return dfltDecl(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R ifElse(Ast.IfElse ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R returnStmt(Ast.ReturnStmt ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R methodCall(Ast.MethodCall ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R nop(Ast.Nop ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R seq(Ast.Seq ast, A arg) {
|
||||||
|
return dflt(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R whileLoop(Ast.WhileLoop ast, A arg) {
|
||||||
|
return dfltStmt(ast, arg);
|
||||||
|
}
|
||||||
|
}
|
92
src/cd/ir/ExprVisitor.java
Normal file
92
src/cd/ir/ExprVisitor.java
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
package cd.ir;
|
||||||
|
|
||||||
|
import cd.ir.Ast.Expr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A visitor that only visits {@link Expr} nodes.
|
||||||
|
*/
|
||||||
|
public class ExprVisitor<R,A> {
|
||||||
|
/**
|
||||||
|
* Recurse and process {@code ast}. It is preferred to
|
||||||
|
* call this rather than calling accept directly, since
|
||||||
|
* it can be overloaded to introduce memoization,
|
||||||
|
* for example. */
|
||||||
|
public R visit(Expr ast, A arg) {
|
||||||
|
return ast.accept(this, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits all children of the expression. Relies on the fact
|
||||||
|
* that {@link Expr} nodes only contain other {@link Expr} nodes.
|
||||||
|
*/
|
||||||
|
public R visitChildren(Expr ast, A arg) {
|
||||||
|
R lastValue = null;
|
||||||
|
for (Ast child : ast.children())
|
||||||
|
lastValue = visit((Expr)child, arg);
|
||||||
|
return lastValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default action for default actions is to call this,
|
||||||
|
* which simply recurses to any children. Also called
|
||||||
|
* by seq() by default. */
|
||||||
|
protected R dfltExpr(Expr ast, A arg) {
|
||||||
|
return visitChildren(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R binaryOp(Ast.BinaryOp ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R booleanConst(Ast.BooleanConst ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R builtInRead(Ast.BuiltInRead ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R cast(Ast.Cast ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R field(Ast.Field ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R index(Ast.Index ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R intConst(Ast.IntConst ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R methodCall(Ast.MethodCallExpr ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R newObject(Ast.NewObject ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R newArray(Ast.NewArray ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R nullConst(Ast.NullConst ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R thisRef(Ast.ThisRef ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R unaryOp(Ast.UnaryOp ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public R var(Ast.Var ast, A arg) {
|
||||||
|
return dfltExpr(ast, arg);
|
||||||
|
}
|
||||||
|
}
|
121
src/cd/util/FileUtil.java
Normal file
121
src/cd/util/FileUtil.java
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package cd.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class FileUtil {
|
||||||
|
|
||||||
|
public static String readAll(Reader ubReader) throws IOException {
|
||||||
|
try (BufferedReader bReader = new BufferedReader(ubReader)) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int ch = bReader.read();
|
||||||
|
if (ch == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sb.append((char) ch);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String read(File file) throws IOException {
|
||||||
|
return readAll(new FileReader(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(File file, String text) throws IOException {
|
||||||
|
try (FileWriter writer = new FileWriter(file)) {
|
||||||
|
writer.write(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String runCommand(File dir, String[] command,
|
||||||
|
String[] substs, String input, boolean detectError)
|
||||||
|
throws IOException {
|
||||||
|
// Substitute the substitution strings $0, $1, etc
|
||||||
|
String newCommand[] = new String[command.length];
|
||||||
|
for (int i = 0; i < command.length; i++) {
|
||||||
|
String newItem = command[i];
|
||||||
|
for (int j = 0; j < substs.length; j++)
|
||||||
|
newItem = newItem.replace("$" + j, substs[j]);
|
||||||
|
newCommand[i] = newItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the command in the specified directory
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(newCommand);
|
||||||
|
pb.directory(dir);
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
final Process p = pb.start();
|
||||||
|
if (input != null && !input.equals("")) {
|
||||||
|
try (OutputStreamWriter osw = new OutputStreamWriter(p.getOutputStream())) {
|
||||||
|
osw.write(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
final StringBuffer result = new StringBuffer();
|
||||||
|
// thread to read stdout from child so that p.waitFor() is interruptible
|
||||||
|
// by JUnit's timeout mechanism. Otherwise it would block in readAll()
|
||||||
|
Thread t = new Thread () {
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
result.append(readAll(new InputStreamReader(p.getInputStream())));
|
||||||
|
} catch (IOException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
t.start();
|
||||||
|
|
||||||
|
if (detectError) {
|
||||||
|
int err = p.waitFor();
|
||||||
|
|
||||||
|
// hack: same as ReferenceServer returns when
|
||||||
|
// a dynamic error occurs running the interpreter
|
||||||
|
if (err != 0)
|
||||||
|
return "Error: " + err + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
t.join();
|
||||||
|
return result.toString();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
return "Error: execution of " + command[0] + " got interrupted (probably timed out)\n";
|
||||||
|
} finally {
|
||||||
|
// in case child is still running, destroy
|
||||||
|
p.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds all .javali under directory {@code testDir}, adding File objects
|
||||||
|
* into {@code result} for each one.
|
||||||
|
*/
|
||||||
|
public static void findJavaliFiles(File testDir, List<Object[]> result) {
|
||||||
|
for (File testFile : testDir.listFiles()) {
|
||||||
|
if (testFile.getName().endsWith(".javali"))
|
||||||
|
result.add(new Object[] { testFile });
|
||||||
|
else if (testFile.isDirectory())
|
||||||
|
findJavaliFiles(testFile, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds all .javali under directory {@code testDir} and returns them. */
|
||||||
|
public static List<File> findJavaliFiles(File testDir) {
|
||||||
|
List<File> result = new ArrayList<File>();
|
||||||
|
for (File testFile : testDir.listFiles()) {
|
||||||
|
if (testFile.getName().endsWith(".javali"))
|
||||||
|
result.add(testFile);
|
||||||
|
else if (testFile.isDirectory())
|
||||||
|
result.addAll(findJavaliFiles(testFile));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
53
src/cd/util/Pair.java
Normal file
53
src/cd/util/Pair.java
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package cd.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/** Simple class for joining two objects of the same type */
|
||||||
|
public class Pair<T> {
|
||||||
|
public T a;
|
||||||
|
public T b;
|
||||||
|
|
||||||
|
public Pair(T a, T b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<Pair<T>> zip(List<T> listA, List<T> listB) {
|
||||||
|
List<Pair<T>> res = new ArrayList<Pair<T>>();
|
||||||
|
for (int i = 0; i < Math.min(listA.size(), listB.size()); i++) {
|
||||||
|
res.add(new Pair<T>(listA.get(i), listB.get(i)));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> unzipA(List<Pair<T>> list) {
|
||||||
|
List<T> res = new ArrayList<T>();
|
||||||
|
for (Pair<T> p : list)
|
||||||
|
res.add(p.a);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> unzipB(List<Pair<T>> list) {
|
||||||
|
List<T> res = new ArrayList<T>();
|
||||||
|
for (Pair<T> p : list)
|
||||||
|
res.add(p.b);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String join(
|
||||||
|
List<Pair<?>> pairs,
|
||||||
|
String itemSep,
|
||||||
|
String pairSep) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (Pair<?> pair : pairs) {
|
||||||
|
if (!first) sb.append(pairSep);
|
||||||
|
sb.append(pair.a);
|
||||||
|
sb.append(itemSep);
|
||||||
|
sb.append(pair.b);
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
16
src/cd/util/Tuple.java
Normal file
16
src/cd/util/Tuple.java
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package cd.util;
|
||||||
|
|
||||||
|
/** Simple class for joining two objects of different type */
|
||||||
|
public class Tuple<A,B> {
|
||||||
|
public A a;
|
||||||
|
public B b;
|
||||||
|
|
||||||
|
public Tuple(A a, B b) {
|
||||||
|
this.a = a;
|
||||||
|
this.b = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "(" + a.toString() + ", " + b.toString() + ")";
|
||||||
|
}
|
||||||
|
}
|
125
src/cd/util/debug/AstDump.java
Normal file
125
src/cd/util/debug/AstDump.java
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
package cd.util.debug;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import cd.ir.Ast;
|
||||||
|
import cd.ir.AstVisitor;
|
||||||
|
import cd.util.Pair;
|
||||||
|
|
||||||
|
public class AstDump {
|
||||||
|
|
||||||
|
public static String toString(Ast ast) {
|
||||||
|
return toString(ast, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(List<? extends Ast> astRoots) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (Ast a : astRoots) {
|
||||||
|
sb.append(toString(a));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(Ast ast, String indent) {
|
||||||
|
AstDump ad = new AstDump();
|
||||||
|
ad.dump(ast, indent);
|
||||||
|
return ad.sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toStringFlat(Ast ast) {
|
||||||
|
AstDump ad = new AstDump();
|
||||||
|
ad.dumpFlat(ast);
|
||||||
|
return ad.sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private StringBuilder sb = new StringBuilder();
|
||||||
|
private Visitor vis = new Visitor();
|
||||||
|
|
||||||
|
protected void dump(Ast ast, String indent) {
|
||||||
|
// print out the overall class structure
|
||||||
|
sb.append(indent);
|
||||||
|
String nodeName = ast.getClass().getSimpleName();
|
||||||
|
List<Pair<?>> flds = vis.visit(ast, null);
|
||||||
|
sb.append(String.format("%s (%s)\n",
|
||||||
|
nodeName,
|
||||||
|
Pair.join(flds, ": ", ", ")));
|
||||||
|
|
||||||
|
// print out any children
|
||||||
|
String newIndent = indent + "| ";
|
||||||
|
for (Ast child : ast.children()) {
|
||||||
|
dump(child, newIndent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void dumpFlat(Ast ast) {
|
||||||
|
String nodeName = ast.getClass().getSimpleName();
|
||||||
|
List<Pair<?>> flds = vis.visit(ast, null);
|
||||||
|
sb.append(String.format("%s(%s)[",
|
||||||
|
nodeName,
|
||||||
|
Pair.join(flds, ":", ",")));
|
||||||
|
|
||||||
|
// print out any children
|
||||||
|
for (int child = 0; child < ast.children().size(); child++) {
|
||||||
|
dumpFlat(ast.children().get(child));
|
||||||
|
if (child < ast.children().size() - 1)
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected class Visitor extends AstVisitor<List<Pair<?>>, Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<Pair<?>> dflt(Ast ast, Void arg) {
|
||||||
|
ArrayList<Pair<?>> res = new ArrayList<Pair<?>>();
|
||||||
|
|
||||||
|
// Get the list of fields and sort them by name:
|
||||||
|
java.lang.Class<? extends Ast> rclass = ast.getClass();
|
||||||
|
List<java.lang.reflect.Field> rflds =
|
||||||
|
Arrays.asList(rclass.getFields());
|
||||||
|
Collections.sort(rflds, new Comparator<java.lang.reflect.Field> () {
|
||||||
|
public int compare(Field o1, Field o2) {
|
||||||
|
return o1.getName().compareTo(o2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create pairs for each one that is not of type Ast:
|
||||||
|
for (java.lang.reflect.Field rfld : rflds) {
|
||||||
|
rfld.setAccessible(true);
|
||||||
|
|
||||||
|
// ignore various weird fields that show up from
|
||||||
|
// time to time:
|
||||||
|
if (rfld.getName().startsWith("$"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ignore fields of AST type, and rwChildren (they should be
|
||||||
|
// uncovered using the normal tree walk)
|
||||||
|
if (rfld.getType().isAssignableFrom(Ast.class))
|
||||||
|
continue;
|
||||||
|
if (rfld.getName().equals("rwChildren"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ignore NULL fields, but add others to our list of pairs
|
||||||
|
try {
|
||||||
|
Object value = rfld.get(ast);
|
||||||
|
if (value != null)
|
||||||
|
res.add(new Pair<Object>(rfld.getName(), value));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
160
src/cd/util/debug/AstOneLine.java
Normal file
160
src/cd/util/debug/AstOneLine.java
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package cd.util.debug;
|
||||||
|
|
||||||
|
import cd.ir.Ast;
|
||||||
|
import cd.ir.Ast.Assign;
|
||||||
|
import cd.ir.Ast.BinaryOp;
|
||||||
|
import cd.ir.Ast.BooleanConst;
|
||||||
|
import cd.ir.Ast.BuiltInRead;
|
||||||
|
import cd.ir.Ast.BuiltInWrite;
|
||||||
|
import cd.ir.Ast.BuiltInWriteln;
|
||||||
|
import cd.ir.Ast.Cast;
|
||||||
|
import cd.ir.Ast.ClassDecl;
|
||||||
|
import cd.ir.Ast.Field;
|
||||||
|
import cd.ir.Ast.IfElse;
|
||||||
|
import cd.ir.Ast.Index;
|
||||||
|
import cd.ir.Ast.IntConst;
|
||||||
|
import cd.ir.Ast.MethodDecl;
|
||||||
|
import cd.ir.Ast.NewArray;
|
||||||
|
import cd.ir.Ast.NewObject;
|
||||||
|
import cd.ir.Ast.Nop;
|
||||||
|
import cd.ir.Ast.NullConst;
|
||||||
|
import cd.ir.Ast.Seq;
|
||||||
|
import cd.ir.Ast.ThisRef;
|
||||||
|
import cd.ir.Ast.UnaryOp;
|
||||||
|
import cd.ir.Ast.Var;
|
||||||
|
import cd.ir.Ast.VarDecl;
|
||||||
|
import cd.ir.Ast.WhileLoop;
|
||||||
|
import cd.ir.AstVisitor;
|
||||||
|
|
||||||
|
public class AstOneLine {
|
||||||
|
|
||||||
|
public static String toString(Ast ast) {
|
||||||
|
return new Visitor().visit(ast, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static class Visitor extends AstVisitor<String, Void> {
|
||||||
|
|
||||||
|
public String str(Ast ast) {
|
||||||
|
return ast.accept(this, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String assign(Assign ast, Void arg) {
|
||||||
|
return String.format("%s = %s", str(ast.left()), str(ast.right()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String binaryOp(BinaryOp ast, Void arg) {
|
||||||
|
return String.format("(%s %s %s)",
|
||||||
|
str(ast.left()), ast.operator.repr, str(ast.right()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String booleanConst(BooleanConst ast, Void arg) {
|
||||||
|
return Boolean.toString(ast.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String builtInRead(BuiltInRead ast, Void arg) {
|
||||||
|
return String.format("read()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String builtInWrite(BuiltInWrite ast, Void arg) {
|
||||||
|
return String.format("write(%s)", str(ast.arg()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String builtInWriteln(BuiltInWriteln ast, Void arg) {
|
||||||
|
return String.format("writeln()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String cast(Cast ast, Void arg) {
|
||||||
|
return String.format("(%s)(%s)", ast.typeName, str(ast.arg()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String classDecl(ClassDecl ast, Void arg) {
|
||||||
|
return String.format("class %s {...}", ast.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String field(Field ast, Void arg) {
|
||||||
|
return String.format("%s.%s", str(ast.arg()), ast.fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String ifElse(IfElse ast, Void arg) {
|
||||||
|
return String.format("if (%s) {...} else {...}", str(ast.condition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String index(Index ast, Void arg) {
|
||||||
|
return String.format("%s[%s]", str(ast.left()), str(ast.right()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String intConst(IntConst ast, Void arg) {
|
||||||
|
return Integer.toString(ast.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String methodDecl(MethodDecl ast, Void arg) {
|
||||||
|
return String.format("%s %s(...) {...}", ast.returnType, ast.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String newArray(NewArray ast, Void arg) {
|
||||||
|
return String.format("new %s[%s]", ast.typeName, str(ast.arg()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String newObject(NewObject ast, Void arg) {
|
||||||
|
return String.format("new %s()", ast.typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nop(Nop ast, Void arg) {
|
||||||
|
return "nop";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String nullConst(NullConst ast, Void arg) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String seq(Seq ast, Void arg) {
|
||||||
|
return "(...)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String thisRef(ThisRef ast, Void arg) {
|
||||||
|
return "this";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String unaryOp(UnaryOp ast, Void arg) {
|
||||||
|
return String.format("%s(%s)", ast.operator.repr, str(ast.arg()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String var(Var ast, Void arg) {
|
||||||
|
{
|
||||||
|
return ast.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String varDecl(VarDecl ast, Void arg) {
|
||||||
|
return String.format("%s %s", ast.type, ast.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String whileLoop(WhileLoop ast, Void arg) {
|
||||||
|
return String.format("while (%s) {...}", str(ast.condition()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
223
test/cd/test/AbstractTestAgainstFrozenReference.java
Normal file
223
test/cd/test/AbstractTestAgainstFrozenReference.java
Normal file
|
@ -0,0 +1,223 @@
|
||||||
|
package cd.test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import cd.Config;
|
||||||
|
import cd.Main;
|
||||||
|
import cd.backend.codegen.AssemblyFailedException;
|
||||||
|
import cd.frontend.parser.ParseFailure;
|
||||||
|
import cd.ir.Ast.ClassDecl;
|
||||||
|
import cd.util.FileUtil;
|
||||||
|
import cd.util.debug.AstDump;
|
||||||
|
|
||||||
|
abstract public class AbstractTestAgainstFrozenReference {
|
||||||
|
|
||||||
|
public static final String SEMANTIC_OK = "OK";
|
||||||
|
public static final String PARSE_FAILURE = "ParseFailure";
|
||||||
|
|
||||||
|
public File file, sfile, binfile, infile;
|
||||||
|
public File parserreffile, semanticreffile, execreffile, cfgreffile, optreffile;
|
||||||
|
public File errfile;
|
||||||
|
public Main main;
|
||||||
|
|
||||||
|
public static int counter = 0;
|
||||||
|
|
||||||
|
@Test(timeout=10000)
|
||||||
|
public void test() throws Throwable {
|
||||||
|
System.err.println("[" + counter++ + " = " + file + "]");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Delete intermediate files from previous runs:
|
||||||
|
if (sfile.exists())
|
||||||
|
sfile.delete();
|
||||||
|
if (binfile.exists())
|
||||||
|
binfile.delete();
|
||||||
|
|
||||||
|
runReference();
|
||||||
|
|
||||||
|
List<ClassDecl> astRoots = testParser();
|
||||||
|
if (astRoots != null) {
|
||||||
|
{
|
||||||
|
testCodeGenerator(astRoots);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (org.junit.ComparisonFailure cf) {
|
||||||
|
throw cf;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
PrintStream err = new PrintStream(errfile);
|
||||||
|
err.println("Debug information for file: " + this.file);
|
||||||
|
err.println(this.main.debug.toString());
|
||||||
|
err.println("Test failed because an exception was thrown:");
|
||||||
|
err.println(" " + e.getLocalizedMessage());
|
||||||
|
err.println("Stack trace:");
|
||||||
|
e.printStackTrace(err);
|
||||||
|
System.err.println(FileUtil.read(errfile));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here, then the test passed, so delete the errfile:
|
||||||
|
// (which has been accumulating debug output etc)
|
||||||
|
if (errfile.exists())
|
||||||
|
errfile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runReference() throws IOException, InterruptedException {
|
||||||
|
String slash = File.separator;
|
||||||
|
String colon = File.pathSeparator;
|
||||||
|
String javaExe = System.getProperty("java.home") + slash + "bin" + slash + Config.JAVA_EXE;
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
|
javaExe, "-Dcd.meta_hidden.Version=" + referenceVersion(),
|
||||||
|
"-cp", "lib/frozenReferenceObf.jar" + colon + " lib/junit-4.12.jar" + colon + "lib/antlr-4.4-complete.jar",
|
||||||
|
"cd.FrozenReferenceMain", file.getAbsolutePath());
|
||||||
|
|
||||||
|
Process proc = pb.start();
|
||||||
|
proc.waitFor();
|
||||||
|
try (InputStream err = proc.getErrorStream()) {
|
||||||
|
if (err.available() > 0) {
|
||||||
|
byte b[] = new byte[err.available()];
|
||||||
|
err.read(b, 0, b.length);
|
||||||
|
System.err.println(new String(b));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String referenceVersion() {
|
||||||
|
{
|
||||||
|
return "CD_HW_CODEGEN_SIMPLE_SOL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Run the parser and compare the output against the reference results */
|
||||||
|
private List<ClassDecl> testParser() throws Exception {
|
||||||
|
String parserRef = findParserRef();
|
||||||
|
List<ClassDecl> astRoots = null;
|
||||||
|
String parserOut;
|
||||||
|
|
||||||
|
try {
|
||||||
|
astRoots = main.parse(new FileReader(this.file));
|
||||||
|
|
||||||
|
parserOut = AstDump.toString(astRoots);
|
||||||
|
} catch (ParseFailure pf) {
|
||||||
|
{
|
||||||
|
// For this assignment, we expect all tests to parse!
|
||||||
|
throw pf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
assertEquals("parser", parserRef, parserOut);
|
||||||
|
}
|
||||||
|
return astRoots;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findParserRef() throws IOException {
|
||||||
|
// Check for a .ref file
|
||||||
|
if (parserreffile.exists() && parserreffile.lastModified() >= file.lastModified()) {
|
||||||
|
return FileUtil.read(parserreffile);
|
||||||
|
}
|
||||||
|
throw new RuntimeException("ERROR: could not find parser .ref");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the code generator, assemble the resulting .s file, and (if the output
|
||||||
|
* is well-defined) compare against the expected output.
|
||||||
|
*/
|
||||||
|
private void testCodeGenerator(List<ClassDecl> astRoots)
|
||||||
|
throws IOException {
|
||||||
|
// Determine the input and expected output.
|
||||||
|
String inFile = (infile.exists() ? FileUtil.read(infile) : "");
|
||||||
|
String execRef = findExecRef();
|
||||||
|
|
||||||
|
// Run the code generator:
|
||||||
|
try (FileWriter fw = new FileWriter(this.sfile)) {
|
||||||
|
main.generateCode(astRoots, fw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we have generated a .s file and we have to compile
|
||||||
|
// it to a binary file. We need to call out to GCC or something
|
||||||
|
// to do this.
|
||||||
|
String asmOutput = FileUtil.runCommand(
|
||||||
|
Config.ASM_DIR,
|
||||||
|
Config.ASM,
|
||||||
|
new String[] { binfile.getAbsolutePath(),
|
||||||
|
sfile.getAbsolutePath() }, null, false);
|
||||||
|
|
||||||
|
// To check if gcc succeeded, check if the binary file exists.
|
||||||
|
// We could use the return code instead, but this seems more
|
||||||
|
// portable to other compilers / make systems.
|
||||||
|
if (!binfile.exists())
|
||||||
|
throw new AssemblyFailedException(asmOutput);
|
||||||
|
|
||||||
|
// Execute the binary file, providing input if relevant, and
|
||||||
|
// capturing the output. Check the error code so see if the
|
||||||
|
// code signaled dynamic errors.
|
||||||
|
String execOut = FileUtil.runCommand(new File("."),
|
||||||
|
new String[] { binfile.getAbsolutePath() }, new String[] {},
|
||||||
|
inFile, true);
|
||||||
|
|
||||||
|
// Compute the output to what we expected to see.
|
||||||
|
if (!execRef.equals(execOut))
|
||||||
|
assertEqualOutput("exec", execRef, execOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findExecRef() throws IOException {
|
||||||
|
// Check for a .ref file
|
||||||
|
if (execreffile.exists() && execreffile.lastModified() > file.lastModified()) {
|
||||||
|
return FileUtil.read(execreffile);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("ERROR: could not find execution .ref");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertEquals(String phase, String exp, String act_) {
|
||||||
|
String act = act_.replace("\r\n", "\n"); // for windows machines
|
||||||
|
if (!exp.equals(act)) {
|
||||||
|
warnAboutDiff(phase, exp, act);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare the output of two executions
|
||||||
|
*/
|
||||||
|
private void assertEqualOutput(String phase, String exp, String act_) {
|
||||||
|
String act = act_.replace("\r\n", "\n"); // for windows machines
|
||||||
|
if (!exp.equals(act)) {
|
||||||
|
warnAboutDiff(phase, exp, act);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void warnAboutDiff(String phase, String exp, String act) {
|
||||||
|
try {
|
||||||
|
try (PrintStream err = new PrintStream(errfile)) {
|
||||||
|
err.println("Debug information for file: " + this.file);
|
||||||
|
err.println(this.main.debug.toString());
|
||||||
|
err.println(String.format(
|
||||||
|
"Phase %s failed because we expected to see:", phase));
|
||||||
|
err.println(exp);
|
||||||
|
err.println("But we actually saw:");
|
||||||
|
err.println(act);
|
||||||
|
err.println("The difference is:");
|
||||||
|
err.println(Diff.computeDiff(exp, act));
|
||||||
|
}
|
||||||
|
} catch (FileNotFoundException exc) {
|
||||||
|
System.err.println("Unable to write debug output to " + errfile
|
||||||
|
+ ":");
|
||||||
|
exc.printStackTrace();
|
||||||
|
}
|
||||||
|
Assert.assertEquals(
|
||||||
|
String.format("Phase %s for %s failed!", phase,
|
||||||
|
file.getPath()), exp, act);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
87
test/cd/test/Diff.java
Normal file
87
test/cd/test/Diff.java
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package cd.test;
|
||||||
|
|
||||||
|
// NOTE: This code was adapted from the code found at
|
||||||
|
// http://www.cs.princeton.edu/introcs/96optimization/Diff.java.html
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Compilation: javac Diff
|
||||||
|
* Execution: java Diff filename1 filename2
|
||||||
|
* Dependencies: In.java
|
||||||
|
*
|
||||||
|
* Reads in two files and compute their diff.
|
||||||
|
* A bare bones version.
|
||||||
|
*
|
||||||
|
* % java Diff input1.txt input2.txt
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Limitations
|
||||||
|
* -----------
|
||||||
|
* - Could hash the lines to avoid potentially more expensive
|
||||||
|
* string comparisons.
|
||||||
|
*
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
public class Diff {
|
||||||
|
|
||||||
|
public static String computeDiff(String in0, String in1) {
|
||||||
|
StringBuffer out = new StringBuffer();
|
||||||
|
|
||||||
|
// break into lines of each file
|
||||||
|
String[] x = in0.split("\\n");
|
||||||
|
String[] y = in1.split("\\n");
|
||||||
|
|
||||||
|
// number of lines of each file
|
||||||
|
int M = x.length;
|
||||||
|
int N = y.length;
|
||||||
|
|
||||||
|
// opt[i][j] = length of LCS of x[i..M] and y[j..N]
|
||||||
|
int[][] opt = new int[M+1][N+1];
|
||||||
|
|
||||||
|
// compute length of LCS and all subproblems via dynamic programming
|
||||||
|
for (int i = M-1; i >= 0; i--) {
|
||||||
|
for (int j = N-1; j >= 0; j--) {
|
||||||
|
if (x[i].equals(y[j]))
|
||||||
|
opt[i][j] = opt[i+1][j+1] + 1;
|
||||||
|
else
|
||||||
|
opt[i][j] = Math.max(opt[i+1][j], opt[i][j+1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recover LCS itself and print out non-matching lines to standard output
|
||||||
|
boolean lineNumber = false;
|
||||||
|
int i = 0, j = 0;
|
||||||
|
while(i < M && j < N) {
|
||||||
|
if (x[i].equals(y[j])) {
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
lineNumber = false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!lineNumber) {
|
||||||
|
out.append(String.format(
|
||||||
|
"At line %3d / %3d:\n", i+1, j+1));
|
||||||
|
lineNumber = true;
|
||||||
|
}
|
||||||
|
assert lineNumber;
|
||||||
|
if (opt[i+1][j] >= opt[i][j+1])
|
||||||
|
out.append("< " + x[i++] + "\n");
|
||||||
|
else
|
||||||
|
out.append("> " + y[j++] + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// dump out one remainder of one string if the other is exhausted
|
||||||
|
if (!lineNumber) {
|
||||||
|
out.append(String.format("Line %3d / %3d:\n", i+1, j+1));
|
||||||
|
}
|
||||||
|
while(i < M || j < N) {
|
||||||
|
if (i == M)
|
||||||
|
out.append("> " + y[j++] + "\n");
|
||||||
|
else if (j == N)
|
||||||
|
out.append("< " + x[i++] + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
74
test/cd/test/TestSamplePrograms.java
Normal file
74
test/cd/test/TestSamplePrograms.java
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package cd.test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.junit.runners.Parameterized;
|
||||||
|
import org.junit.runners.Parameterized.Parameters;
|
||||||
|
|
||||||
|
import cd.Config;
|
||||||
|
import cd.Main;
|
||||||
|
import cd.util.FileUtil;
|
||||||
|
|
||||||
|
@RunWith(Parameterized.class)
|
||||||
|
public class TestSamplePrograms extends AbstractTestAgainstFrozenReference {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If you want to run the test on just one file, then initialize this
|
||||||
|
* variable like:
|
||||||
|
* {@code justFile = new File("javali_tests/HW2/Inheritance.javali")}.
|
||||||
|
*/
|
||||||
|
// public static final File justFile = new File("javali_tests/HW1/HelloWorld.javali");
|
||||||
|
public static final File justFile = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory in which to search for test files. If null, then the default is
|
||||||
|
* the current directory (to include all files). To run only tests in a
|
||||||
|
* particular directory, use sth. like:
|
||||||
|
* {@code testDir = new File("javali_tests/HW2/")}.
|
||||||
|
*/
|
||||||
|
// public static final File testDir = new File("javali_tests/HW1");
|
||||||
|
public static final File testDir = new File("javali_tests");
|
||||||
|
|
||||||
|
@Parameters(name="{index}:{0}")
|
||||||
|
public static Collection<Object[]> testFiles() {
|
||||||
|
List<Object[]> result = new ArrayList<Object[]>();
|
||||||
|
if (justFile != null)
|
||||||
|
result.add(new Object[] { justFile });
|
||||||
|
else if (testDir != null) {
|
||||||
|
for (File file : FileUtil.findJavaliFiles(testDir)) {
|
||||||
|
result.add(new Object[] { file });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (File file : FileUtil.findJavaliFiles(new File("."))) {
|
||||||
|
result.add(new Object[] { file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param file
|
||||||
|
* The javali file to test.
|
||||||
|
*/
|
||||||
|
public TestSamplePrograms(File file) {
|
||||||
|
this.file = file;
|
||||||
|
this.sfile = new File(file.getPath() + Config.ASMEXT);
|
||||||
|
this.binfile = new File(file.getPath() + Config.BINARYEXT);
|
||||||
|
this.infile = new File(file.getPath() + ".in");
|
||||||
|
this.parserreffile = new File(file.getPath() + ".parser.ref");
|
||||||
|
this.semanticreffile = new File(file.getPath() + ".semantic.ref");
|
||||||
|
this.execreffile = new File(file.getPath() + ".exec.ref");
|
||||||
|
this.cfgreffile = new File(file.getPath() + ".cfg.dot.ref");
|
||||||
|
this.optreffile = new File(file.getPath() + ".opt.ref");
|
||||||
|
this.errfile = new File(String.format("%s.err", file.getPath()));
|
||||||
|
this.main = new Main();
|
||||||
|
this.main.debug = new StringWriter();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue