Initial commit

master
github-classroom[bot] 4 years ago
commit 9f1681bb23

149
.gitignore vendored

@ -0,0 +1,149 @@
.DS_Store
__pycache__
target
# Created by https://www.gitignore.io/api/java,eclipse,intellij,emacs,vim
### Java ###
*.class
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
# Eclipse Core
.project
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# JDT-specific (Eclipse Java Development Tools)
.classpath
# Java annotation processor (APT)
.factorypath
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:
# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml
# Gradle:
# .idea/gradle.xml
# .idea/libraries
# Mongo Explorer plugin:
# .idea/mongoSettings.xml
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
/out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
### Vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~

@ -0,0 +1,26 @@
Copyright (c) 2017-2018 The Regents of the University of California
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -0,0 +1,52 @@
# NYU Compiler Construction CSCI-GA.2130/Spring 2021: Programming Assignment 3
This assignment is adapted from https://github.com/cs164berkeley/pa3-chocopy-code-generation with the authors' permission.
See the PA3 document on Piazza for a detailed specification.
## Quickstart
Run the following commands to compile your code generator and run the tests:
```
mvn clean package
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \
--pass=..s --test --run --dir src/test/data/pa3/sample/
```
The dots in `--pass` make the compiler skip parsing and semantic analysis and go straight to code generation.
`--pass=..s` uses your (`s` for `student`) generator to generate code from an annotated AST (the `.ast.typed` files under `src/test/data/pa3/sample/`).
With the starter code, only one test should pass.
Your main objective is to build a code generator that passes all the provided tests.
`--pass=..r` uses the reference (`r` for `reference`) generator, which should pass all tests.
In addition to running in test mode with `--test`, you can also observe the actual output of your (or reference) generator with:
```
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \
--pass=..s src/test/data/pa3/sample/op_add.py.ast.typed
```
You can also run all passes on the original `.py` file:
```
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \
--pass=rrr src/test/data/pa3/sample/op_add.py
```
Once you merge your semantic analysis code from assignment 2, you should be able to use `--pass=sss`.
## Generating vs Running
The above should be familiar from previous assignments.
A new aspect of PA3 is the distinction between generating and running the code.
The following outputs the generated RISC-V assembly (with the usual option to save to a file with `--out`):
```
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \
--pass=rrr src/test/data/pa3/sample/op_add.py
```
The following (note the `--run` option) generates the assembly and then _runs it on the bundled RISC-V emulator_:
```
java -cp "chocopy-ref.jar:target/assignment.jar" chocopy.ChocoPy \
--pass=rrr --run src/test/data/pa3/sample/op_add.py
```

Binary file not shown.

@ -0,0 +1,330 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>chocopy</groupId>
<artifactId>chocopy</artifactId>
<packaging>jar</packaging>
<version>2.2-SNAPSHOT</version>
<name>chocopy</name>
<url>http://maven.apache.org</url>
<!-- Set this property to true on the command-line for very verbose output -->
<properties>
<chocopy.debug>false</chocopy.debug>
</properties>
<build>
<!-- Specify JFlex and CUP plugins here; execute in profiles -->
<pluginManagement>
<plugins>
<plugin>
<groupId>de.jflex</groupId>
<artifactId>jflex-maven-plugin</artifactId>
<version>1.6.1</version>
</plugin>
<plugin>
<groupId>com.github.vbmacher</groupId>
<artifactId>cup-maven-plugin</artifactId>
<version>11b-20160615</version>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>assignment</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase> <!-- bind to the packaging phase -->
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<encoding>UTF-8</encoding>
<source>1.8</source>
<target>1.8</target>
<parameters>true</parameters>
<debug>true</debug>
</configuration>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/asm</directory>
<includes>
<include>**/*.s</include>
<include>**/*.os</include>
</includes>
<excludes>
<exclude>**/reference/*.s</exclude>
</excludes>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>reference</id>
<activation>
<!-- This profile is activated whenever we have the reference sources available -->
<file>
<exists>src/main/java/chocopy/reference/</exists>
</file>
</activation>
<build>
<plugins>
<!-- Do not include student skeletons in the reference JAR -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/pa1/*</exclude>
<exclude>**/pa2/*</exclude>
<exclude>**/pa3/*</exclude>
</excludes>
</configuration>
</plugin>
<!-- Name the generated JAR differently so that it is different from the student version -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>chocopy-ref</finalName>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
</plugin>
<!-- Run JFlex to generate reference lexer -->
<plugin>
<groupId>de.jflex</groupId>
<artifactId>jflex-maven-plugin</artifactId>
<executions>
<execution>
<id>jflex-reference</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<lexDefinitions>
<lexDefinition>src/main/jflex/chocopy/reference/ChocoPy.jflex</lexDefinition>
</lexDefinitions>
<dump>${chocopy.debug}</dump>
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
<!-- Run CUP to generate reference parser -->
<plugin>
<groupId>com.github.vbmacher</groupId>
<artifactId>cup-maven-plugin</artifactId>
<executions>
<execution>
<id>cup-reference</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<cupDefinition>src/main/cup/chocopy/reference/ChocoPy.cup</cupDefinition>
<packageName>chocopy.reference</packageName>
<className>ChocoPyParser</className>
<symbolsName>ChocoPyTokens</symbolsName>
<dumpTables>${chocopy.debug}</dumpTables>
<dumpStates>${chocopy.debug}</dumpStates>
<dumpGrammar>${chocopy.debug}</dumpGrammar>
<locations>true</locations>
</configuration>
</execution>
</executions>
</plugin>
<!-- No debug -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<debug>true</debug>
<debuglevel>none</debuglevel>
</configuration>
</plugin>
<!-- Copy dependencies to target/ -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>pa1</id>
<activation>
<!-- This profile is activated whenever we are in a PA1 distribution -->
<file>
<exists>src/main/java/chocopy/pa1</exists>
</file>
</activation>
<build>
<plugins>
<!-- Run JFlex on the student version -->
<plugin>
<groupId>de.jflex</groupId>
<artifactId>jflex-maven-plugin</artifactId>
<executions>
<execution>
<id>jflex-pa1</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<lexDefinitions>
<lexDefinition>src/main/jflex/chocopy/pa1/ChocoPy.jflex</lexDefinition>
</lexDefinitions>
<dump>${chocopy.debug}</dump>
<verbose>true</verbose>
</configuration>
</execution>
</executions>
</plugin>
<!-- Run CUP on the student version -->
<plugin>
<groupId>com.github.vbmacher</groupId>
<artifactId>cup-maven-plugin</artifactId>
<executions>
<execution>
<id>cup-pa1</id>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<cupDefinition>src/main/cup/chocopy/pa1/ChocoPy.cup</cupDefinition>
<packageName>chocopy.pa1</packageName>
<className>ChocoPyParser</className>
<symbolsName>ChocoPyTokens</symbolsName>
<dumpTables>${chocopy.debug}</dumpTables>
<dumpStates>${chocopy.debug}</dumpStates>
<dumpGrammar>${chocopy.debug}</dumpGrammar>
<locations>true</locations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>pa2</id>
<activation>
<!-- This profile is activated whenever we are in a PA2 distribution -->
<file>
<exists>src/main/java/chocopy/pa2</exists>
</file>
</activation>
</profile>
<profile>
<id>pa3</id>
<activation>
<!-- This profile is activated whenever we are in a PA3 distribution -->
<file>
<exists>src/main/java/chocopy/pa3</exists>
</file>
</activation>
</profile>
</profiles>
<repositories>
<repository>
<id>venus164-repo</id>
<name>Repository for Venus164</name>
<url>https://raw.githubusercontent.com/chocopy/venus/maven-repository/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>net.sourceforge.argparse4j</groupId>
<artifactId>argparse4j</artifactId>
<version>0.8.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
<version>2.9.10</version>
</dependency>
<dependency>
<groupId>com.github.vbmacher</groupId>
<artifactId>java-cup-runtime</artifactId>
<version>11b-20160615</version>
</dependency>
<!-- https://mvnrepository.com/artifact/de.jflex/jflex -->
<dependency>
<groupId>de.jflex</groupId>
<artifactId>jflex</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.2.71</version>
</dependency>
<dependency>
<groupId>edu.berkeley.eecs.venus164</groupId>
<artifactId>venus164</artifactId>
<version>0.2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sf.proguard</groupId>
<artifactId>proguard-base</artifactId>
<version>6.0.3</version>
</dependency>
</dependencies>
</project>

@ -0,0 +1,12 @@
# Runtime support function abort (does not return).
mv t0, a0 # Save exit code in temp
li a0, @print_string # Code for print_string ecall
ecall # Print error message in a1
li a1, 10 # Load newline character
li a0, @print_char # Code for print_char ecall
ecall # Print newline
mv a1, t0 # Move exit code to a1
li a0, @exit2 # Code for exit2 ecall
ecall # Exit with code
abort_17: # Infinite loop
j abort_17 # Prevent fallthrough

@ -0,0 +1,4 @@
# Runtime support function alloc.
# Prototype address is in a0.
lw a1, 4(a0) # Get size of object in words
j alloc2 # Allocate object with exact size

@ -0,0 +1,27 @@
# Runtime support function alloc2 (realloc).
# Prototype address is in a0.
# Number of words to allocate is in a1.
li a2, 4 # Word size in bytes
mul a2, a1, a2 # Calculate number of bytes to allocate
add a2, gp, a2 # Estimate where GP will move
bgeu a2, s11, alloc2_15 # Go to OOM handler if too large
lw t0, @.__obj_size__(a0) # Get size of object in words
mv t2, a0 # Initialize src ptr
mv t3, gp # Initialize dest ptr
alloc2_16: # Copy-loop header
lw t1, 0(t2) # Load next word from src
sw t1, 0(t3) # Store next word to dest
addi t2, t2, 4 # Increment src
addi t3, t3, 4 # Increment dest
addi t0, t0, -1 # Decrement counter
bne t0, zero, alloc2_16 # Loop if more words left to copy
mv a0, gp # Save new object's address to return
sw a1, @.__obj_size__(a0) # Set size of new object in words
# (same as requested size)
mv gp, a2 # Set next free slot in the heap
jr ra # Return to caller
alloc2_15: # OOM handler
li a0, @error_oom # Exit code for: Out of memory
la a1, STRING["Out of memory"] # Load error message as str
addi a1, a1, @.__str__ # Load address of attribute __str__
j abort # Abort

@ -0,0 +1,5 @@
# Runtime support function heap.init.
mv a1, a0 # Move requested size to A1
li a0, @sbrk # Code for ecall: sbrk
ecall # Request A1 bytes
jr ra # Return to caller

@ -0,0 +1,40 @@
# Function input
addi sp, sp, -16 # Reserve stack
sw ra, 12(sp) # Save registers
sw fp, 8(sp)
sw s1, 4(sp)
addi fp, sp, 16 # Set fp
li a0, @fill_line_buffer # Fill the internal line buffer.
ecall
bgez a0, input_nonempty # More input found
la a0, $str$prototype # EOF: Return empty string.
j input_done
input_nonempty:
mv s1, a0
addi t0, s1, 5 # Compute bytes for string (+NL+NUL),
addi t0, t0, @.__str__ # Including header.
srli a1, t0, 2 # Convert to words.
la a0, $str$prototype # Load address of string prototype.
jal ra, alloc2 # Allocate string.
sw s1, @.__len__(a0) # Store string length.
mv a2, s1 # Pass length.
mv s1, a0 # Save string object address.
addi a1, a0, @.__str__ # Pass address of string data.
li a0, @read_string # ecall to read from internal buffer.
ecall
addi a0, a0, 1 # Actual length (including NL).
sw a0, @.__len__(s1) # Store actual length.
add t0, a0, s1
li t1, 10 # Store newline and null byte
sb t1, @.__str__-1(t0)
sb zero, @.__str__(t0) # Store null byte at end.
mv a0, s1 # Return string object.
input_done:
lw s1, -12(fp)
lw ra, -4(fp)
lw fp, -8(fp)
addi sp, sp, 16
jr ra

@ -0,0 +1,20 @@
# Function len
# We do not save/restore fp/ra for this function
# because we know that it does not use the stack or does not
# call other functions.
lw a0, 0(sp) # Load arg
beq a0, zero, len_12 # None is an illegal argument
lw t0, 0(a0) # Get type tag of arg
li t1, 3 # Load type tag of `str`
beq t0, t1, len_13 # Go to len(str)
li t1, -1 # Load type tag for list objects
beq t0, t1, len_13 # Go to len(list)
len_12: # Invalid argument
li a0, @error_arg # Exit code for: Invalid argument
la a1, STRING["Invalid argument"] # Load error message as str
addi a1, a1, @.__str__ # Load address of attribute __str__
j abort # Abort
len_13: # Get length of string
lw a0, @.__len__(a0) # Load attribute: __len__
jr ra # Return to caller

@ -0,0 +1,3 @@
# Init method for type object.
mv a0, zero # `None` constant
jr ra # Return

@ -0,0 +1,52 @@
# Function print
lw a0, 0(sp) # Load arg
beq a0, zero, print_6 # None is an illegal argument
lw t0, 0(a0) # Get type tag of arg
li t1, 1 # Load type tag of `int`
beq t0, t1, print_7 # Go to print(int)
li t1, 3 # Load type tag of `str`
beq t0, t1, print_8 # Go to print(str)
li t1, 2 # Load type tag of `bool`
beq t0, t1, print_9 # Go to print(bool)
print_6: # Invalid argument
li a0, 1 # Exit code for: Invalid argument
la a1, STRING["Invalid argument"] # Load error message as str
addi a1, a1, @.__str__ # Load address of attribute __str__
j abort # Abort
# Printing bools
print_9: # Print bool object in A0
lw a0, @.__bool__(a0) # Load attribute __bool__
beq a0, zero, print_10 # Go to: print(False)
la a0, STRING["True"] # String representation: True
j print_8 # Go to: print(str)
print_10: # Print False object in A0
la a0, STRING["False"] # String representation: False
j print_8 # Go to: print(str)
# Printing strs.
print_8: # Print str object in A0
addi a1, a0, @.__str__ # Load address of attribute __str__
j print_11 # Print the null-terminated string is now in A1
mv a0, zero # Load None
j print_5 # Go to return
print_11: # Print null-terminated string in A1
li a0, @print_string # Code for ecall: print_string
ecall # Print string
li a1, 10 # Load newline character
li a0, @print_char # Code for ecall: print_char
ecall # Print character
j print_5 # Go to return
# Printing ints.
print_7: # Print int object in A0
lw a1, @.__int__(a0) # Load attribute __int__
li a0, @print_int # Code for ecall: print_int
ecall # Print integer
li a1, 10 # Load newline character
li a0, 11 # Code for ecall: print_char
ecall # Print character
print_5: # End of function
mv a0, zero # Load None
jr ra # Return to caller

@ -0,0 +1,54 @@
package chocopy.common;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.stream.Collectors;
/** Utility functions for general use. */
public class Utils {
/**
* Return resource file FILENAME's contents as a string. FILENAME can refer to a file within the
* class hierarchy, so that a text resource in file resource.txt in the chocopy.common.codegen
* package, for example, could be referred to with FILENAME chocopy/common/codegen/resource.txt.
*
* <p>Credit: Lucio Paiva.
*/
public static String getResourceFileAsString(String fileName) {
InputStream is = Utils.class.getClassLoader().getResourceAsStream(fileName);
if (is != null) {
BufferedReader reader =
new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8")));
return reader.lines().collect(Collectors.joining(System.lineSeparator()));
}
return null;
}
/**
* Return an exception signalling a fatal error having a message formed from MSGFORMAT and ARGS,
* as for String.format.
*/
public static Error fatal(String msgFormat, Object... args) {
return new Error(String.format(msgFormat, args));
}
/**
* Return the string S padded with FILL to TOLEN characters. Padding is on the left if
* PADONLEFT, and otherwise on the right. If S is already at least TOLEN characters, returns S.
*/
public static String pad(String s, Character fill, int toLen, boolean padOnLeft) {
StringBuilder result = new StringBuilder(toLen);
if (!padOnLeft) {
result.append(s);
}
for (int n = s.length(); n < toLen; n += 1) {
result.append(fill);
}
if (padOnLeft) {
result.append(s);
}
return result.toString();
}
}

@ -0,0 +1,174 @@
package chocopy.common.analysis;
import chocopy.common.astnodes.*;
/**
* An empty implementation of the {@link NodeAnalyzer} that simply returns does nothing and returns
* null for every AST node type.
*
* <p>T is the type of analysis result.
*/
public class AbstractNodeAnalyzer<T> implements NodeAnalyzer<T> {
@Override
public T analyze(AssignStmt node) {
return defaultAction(node);
}
@Override
public T analyze(BinaryExpr node) {
return defaultAction(node);
}
@Override
public T analyze(BooleanLiteral node) {
return defaultAction(node);
}
@Override
public T analyze(CallExpr node) {
return defaultAction(node);
}
@Override
public T analyze(ClassDef node) {
return defaultAction(node);
}
@Override
public T analyze(ClassType node) {
return defaultAction(node);
}
@Override
public T analyze(CompilerError node) {
return defaultAction(node);
}
@Override
public T analyze(Errors node) {
return defaultAction(node);
}
@Override
public T analyze(ExprStmt node) {
return defaultAction(node);
}
@Override
public T analyze(ForStmt node) {
return defaultAction(node);
}
@Override
public T analyze(FuncDef node) {
return defaultAction(node);
}
@Override
public T analyze(GlobalDecl node) {
return defaultAction(node);
}
@Override
public T analyze(Identifier node) {
return defaultAction(node);
}
@Override
public T analyze(IfExpr node) {
return defaultAction(node);
}
@Override
public T analyze(IfStmt node) {
return defaultAction(node);
}
@Override
public T analyze(IndexExpr node) {
return defaultAction(node);
}
@Override
public T analyze(IntegerLiteral node) {
return defaultAction(node);
}
@Override
public T analyze(ListExpr node) {
return defaultAction(node);
}
@Override
public T analyze(ListType node) {
return defaultAction(node);
}
@Override
public T analyze(MemberExpr node) {
return defaultAction(node);
}
@Override
public T analyze(MethodCallExpr node) {
return defaultAction(node);
}
@Override
public T analyze(NoneLiteral node) {
return defaultAction(node);
}
@Override
public T analyze(NonLocalDecl node) {
return defaultAction(node);
}
@Override
public T analyze(Program node) {
return defaultAction(node);
}
@Override
public T analyze(ReturnStmt node) {
return defaultAction(node);
}
@Override
public T analyze(StringLiteral node) {
return defaultAction(node);
}
@Override
public T analyze(TypedVar node) {
return defaultAction(node);
}
@Override
public T analyze(UnaryExpr node) {
return defaultAction(node);
}
@Override
public T analyze(VarDef node) {
return defaultAction(node);
}
@Override
public T analyze(WhileStmt node) {
return defaultAction(node);
}
@Override
public void setDefault(T value) {
defaultValue = value;
}
@Override
public T defaultAction(Node node) {
return defaultValue;
}
/** Default value for non-overridden methods. */
private T defaultValue = null;
}

@ -0,0 +1,93 @@
package chocopy.common.analysis;
import chocopy.common.astnodes.*;
/**
* This interface can be used to separate logic for various concrete classes in the AST class
* hierarchy.
*
* <p>The idea is that a phase of the analysis is encapsulated in a class that implements this
* interface, and contains an overriding of the analyze method for each concrete Node class that
* needs something other than default processing. Each concrete node class, C, implements a generic
* dispatch method that takes a NodeAnalyzer<T> argument and calls the overloading of analyze that
* takes an argument of type C. The effect is that anode.dispatch(anAnalyzer) executes the method
* anAnalyzer.analyze that is appropriate to aNode's dynamic type. As a result each NodeAnalyzer
* subtype encapsulates all implementations of a particular action on Nodes. Thus, it inverts the
* usual OO pattern in which the implementations of analysis A for each different class are
* scattered among the class bodies themselves as overridings of a method A on the Node class.
*
* <p>The class AbstractNodeAnalyzer provides empty default implementations for these methods.
*
* <p>The type T is the type of result returned by the encapsulated analysis.
*/
public interface NodeAnalyzer<T> {
T analyze(AssignStmt node);
T analyze(BinaryExpr node);
T analyze(BooleanLiteral node);
T analyze(CallExpr node);
T analyze(ClassDef node);
T analyze(ClassType node);
T analyze(CompilerError node);
T analyze(Errors node);
T analyze(ExprStmt node);
T analyze(ForStmt node);
T analyze(FuncDef node);
T analyze(GlobalDecl node);
T analyze(Identifier node);
T analyze(IfExpr node);
T analyze(IfStmt node);
T analyze(IndexExpr node);
T analyze(IntegerLiteral node);
T analyze(ListExpr node);
T analyze(ListType node);
T analyze(MemberExpr node);
T analyze(MethodCallExpr node);
T analyze(NoneLiteral node);
T analyze(NonLocalDecl node);
T analyze(Program node);
T analyze(ReturnStmt node);
T analyze(StringLiteral node);
T analyze(TypedVar node);
T analyze(UnaryExpr node);
T analyze(VarDef node);
T analyze(WhileStmt node);
/**
* Set the default value returned by calls to analyze that are not overridden to VALUE. By
* default, this is null.
*/
void setDefault(T value);
/** Default value for non-overridden methods. */
T defaultAction(Node node);
}

@ -0,0 +1,62 @@
package chocopy.common.analysis;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* A block-structured symbol table a mapping identifiers to information about them of type T in a
* given declarative region.
*/
public class SymbolTable<T> {
/** Contents of the current (innermost) region. */
private final Map<String, T> tab = new HashMap<>();
/** Enclosing block. */
private final SymbolTable<T> parent;
/** A table representing a region nested in that represented by PARENT0. */
public SymbolTable(SymbolTable<T> parent0) {
parent = parent0;
}
/** A top-level symbol table. */
public SymbolTable() {
this.parent = null;
}
/** Returns the mapping of NAME in the innermost nested region containing this one. */
public T get(String name) {
if (tab.containsKey(name)) {
return tab.get(name);
} else if (parent != null) {
return parent.get(name);
} else {
return null;
}
}
/**
* Adds a new mapping of NAME -> VALUE to the current region, possibly shadowing mappings in the
* enclosing parent. Returns modified table.
*/
public SymbolTable<T> put(String name, T value) {
tab.put(name, value);
return this;
}
/** Returns whether NAME has a mapping in this region (ignoring enclosing regions. */
public boolean declares(String name) {
return tab.containsKey(name);
}
/** Returns all the names declared this region (ignoring enclosing regions). */
public Set<String> getDeclaredSymbols() {
return tab.keySet();
}
/** Returns the parent, or null if this is the top level. */
public SymbolTable<T> getParent() {
return this.parent;
}
}

@ -0,0 +1,53 @@
package chocopy.common.analysis.types;
import chocopy.common.astnodes.ClassType;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
/** Represents the semantic value of a simple class reference. */
public class ClassValueType extends ValueType {
/** The name of the class. */
private final String className;
/** A class type for the class named CLASSNAME. */
@JsonCreator
public ClassValueType(@JsonProperty String className) {
this.className = className;
}
/** A class type for the class referenced by CLASSTYPEANNOTATION. */
public ClassValueType(ClassType classTypeAnnotation) {
this.className = classTypeAnnotation.className;
}
@Override
@JsonProperty
public String className() {
return className;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ClassValueType classType = (ClassValueType) o;
return Objects.equals(className, classType.className);
}
@Override
public int hashCode() {
return Objects.hash(className);
}
@Override
public String toString() {
return className;
}
}

@ -0,0 +1,45 @@
package chocopy.common.analysis.types;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.ArrayList;
import java.util.List;
/** Semantic information for a function or method. */
public class FuncType extends Type {
/** Types of parameters. */
public final List<ValueType> parameters;
/** Function's return type. */
public final ValueType returnType;
/** Create a FuncType returning RETURNTYPE0, initially parameterless. */
public FuncType(ValueType returnType0) {
this(new ArrayList<>(), returnType0);
}
/**
* Create a FuncType for NAME0 with formal parameter types PARAMETERS0, returning type
* RETURNTYPE0.
*/
@JsonCreator
public FuncType(List<ValueType> parameters0, ValueType returnType0) {
this.parameters = parameters0;
this.returnType = returnType0;
}
@Override
public boolean isFuncType() {
return true;
}
/** Return the type of the K-th parameter. */
public ValueType getParamType(int k) {
return parameters.get(k);
}
@Override
public String toString() {
return "<function>";
}
}

@ -0,0 +1,57 @@
package chocopy.common.analysis.types;
import chocopy.common.astnodes.ListType;
import com.fasterxml.jackson.annotation.JsonCreator;
import java.util.Objects;
/** Represents a semantic value of a list type denotation. */
public class ListValueType extends ValueType {
/** This ListValueType represents [ELEMENTTYPE]. */
public final ValueType elementType;
/** Represents [ELEMENTTYPE]. */
@JsonCreator
public ListValueType(Type elementType) {
this.elementType = (ValueType) elementType;
}
/** Represents [<type>], where <type> is that denoted in TYPEANNOTATION. */
public ListValueType(ListType typeAnnotation) {
elementType = ValueType.annotationToValueType(typeAnnotation.elementType);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ListValueType listType = (ListValueType) o;
return Objects.equals(elementType, listType.elementType);
}
@Override
public int hashCode() {
return Objects.hash(elementType);
}
@Override
public String toString() {
return "[" + elementType.toString() + "]";
}
/** Returns true iff I represent [T]. */
@Override
public boolean isListType() {
return true;
}
@Override
public ValueType elementType() {
return elementType;
}
}

@ -0,0 +1,68 @@
package chocopy.common.analysis.types;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
/**
* Representation for the static type of symbols and expressions during type-checking.
*
* <p>Symbols such as variables and attributes will typically map to a {@link ValueType}.
*
* <p>Symbols such as classes will typically map to a more complex Type.
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "kind")
@JsonSubTypes({
@JsonSubTypes.Type(FuncType.class),
@JsonSubTypes.Type(ClassValueType.class),
@JsonSubTypes.Type(ListValueType.class)
})
public abstract class Type {
/** The type object. */
public static final ClassValueType OBJECT_TYPE = new ClassValueType("object");
/** The type int. */
public static final ClassValueType INT_TYPE = new ClassValueType("int");
/** The type str. */
public static final ClassValueType STR_TYPE = new ClassValueType("str");
/** The type bool. */
public static final ClassValueType BOOL_TYPE = new ClassValueType("bool");
/** The type of None. */
public static final ClassValueType NONE_TYPE = new ClassValueType("<None>");
/** The type of []. */
public static final ClassValueType EMPTY_TYPE = new ClassValueType("<Empty>");
/** Returns the name of the class, if this is a class type, Otherwise null. */
public String className() {
return null;
}
/** Return true iff this is a type that does not include the value None. */
@JsonIgnore
public boolean isSpecialType() {
return equals(INT_TYPE) || equals(BOOL_TYPE) || equals(STR_TYPE);
}
@JsonIgnore
public boolean isListType() {
return false;
}
@JsonIgnore
public boolean isFuncType() {
return false;
}
/** Return true iff this type represents a kind of assignable value. */
@JsonIgnore
public boolean isValueType() {
return false;
}
/** For list types, return the type of the elements; otherwise null. */
@JsonIgnore
public ValueType elementType() {
return null;
}
}

@ -0,0 +1,29 @@
package chocopy.common.analysis.types;
import chocopy.common.astnodes.ClassType;
import chocopy.common.astnodes.ListType;
import chocopy.common.astnodes.TypeAnnotation;
/**
* A ValueType references types that are assigned to variables and expressions.
*
* <p>In particular, ValueType can be a {@link ClassValueType} (e.g. "int") or a {@link
* ListValueType} (e.g. "[int]").
*/
public abstract class ValueType extends Type {
/** Returns the type corresponding to ANNOTATION. */
public static ValueType annotationToValueType(TypeAnnotation annotation) {
if (annotation instanceof ClassType) {
return new ClassValueType((ClassType) annotation);
} else {
assert annotation instanceof ListType;
return new ListValueType((ListType) annotation);
}
}
@Override
public boolean isValueType() {
return true;
}
}

@ -0,0 +1,25 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Single and multiple assignments. */
public class AssignStmt extends Stmt {
/** List of left-hand sides. */
public final List<Expr> targets;
/** Right-hand-side value to be assigned. */
public final Expr value;
/** AST for TARGETS[0] = TARGETS[1] = ... = VALUE spanning source locations [LEFT..RIGHT]. */
public AssignStmt(Location left, Location right, List<Expr> targets, Expr value) {
super(left, right);
this.targets = targets;
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,31 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** <operand> <operator> <operand>. */
public class BinaryExpr extends Expr {
/** Left operand. */
public final Expr left;
/** Operator name. */
public final String operator;
/** Right operand. */
public final Expr right;
/**
* An AST for expressions of the form LEFTEXPR OP RIGHTEXPR from text in range
* [LEFTLOC..RIGHTLOC].
*/
public BinaryExpr(
Location leftLoc, Location rightLoc, Expr leftExpr, String op, Expr rightExpr) {
super(leftLoc, rightLoc);
left = leftExpr;
operator = op;
right = rightExpr;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,21 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Literals True or False. */
public final class BooleanLiteral extends Literal {
/** True iff I represent True. */
public final boolean value;
/** An AST for the token True or False at [LEFT..RIGHT], depending on VALUE. */
public BooleanLiteral(Location left, Location right, boo