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, boolean value) {
super(left, right);
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,26 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** A function call. */
public class CallExpr extends Expr {
/** The called function. */
public final Identifier function;
/** The actual parameter expressions. */
public final List<Expr> args;
/** AST for FUNCTION(ARGS) at [LEFT..RIGHT]. */
public CallExpr(Location left, Location right, Identifier function, List<Expr> args) {
super(left, right);
this.function = function;
this.args = args;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,39 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** A class definition. */
public class ClassDef extends Declaration {
/** Name of the declared class. */
public final Identifier name;
/** Name of the parent class. */
public final Identifier superClass;
/** Body of the class. */
public final List<Declaration> declarations;
/** An AST for class NAME(SUPERCLASS): DECLARATIONS. spanning source locations [LEFT..RIGHT]. */
public ClassDef(
Location left,
Location right,
Identifier name,
Identifier superClass,
List<Declaration> declarations) {
super(left, right);
this.name = name;
this.superClass = superClass;
this.declarations = declarations;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
@Override
public Identifier getIdentifier() {
return this.name;
}
}

@ -0,0 +1,21 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** A simple class type name. */
public final class ClassType extends TypeAnnotation {
/** The denotation of the class in source. */
public final String className;
/** An AST denoting a type named CLASSNAME0 at [LEFT..RIGHT]. */
public ClassType(Location left, Location right, String className0) {
super(left, right);
className = className0;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,56 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import com.fasterxml.jackson.annotation.JsonInclude;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.Arrays;
import java.util.Objects;
/** Represents a single error. Does not correspond to any Python source construct. */
public class CompilerError extends Node {
/**
* Represents an error with message MESSAGE. Iff SYNTAX, it is a syntactic error. The error
* applies to source text at [LEFT..RIGHT].
*/
public CompilerError(Location left, Location right, String message, boolean syntax) {
super(left, right);
this.message = message;
this.syntax = syntax;
}
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
public boolean isSyntax() {
return syntax;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CompilerError that = (CompilerError) o;
return Objects.equals(message, that.message)
&& Arrays.equals(getLocation(), that.getLocation());
}
@Override
public int hashCode() {
int result = Objects.hash(message);
result = 31 * result + Arrays.hashCode(getLocation());
return result;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
/** The error message. */
public final String message;
/** True if this is a syntax error. */
private final boolean syntax;
}

@ -0,0 +1,17 @@
package chocopy.common.astnodes;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Base of all AST nodes representing definitions or declarations. */
public abstract class Declaration extends Node {
/** A definition or declaration spanning source locations [LEFT..RIGHT]. */
public Declaration(Location left, Location right) {
super(left, right);
}
/** Return the identifier defined by this Declaration. */
@JsonIgnore
public abstract Identifier getIdentifier();
}

@ -0,0 +1,71 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Collects the error messages in a Program. There is exactly one per Program node. */
public class Errors extends Node {
/** The accumulated error messages in the order added. */
public final List<CompilerError> errors;
/** True iff multiple semantic errors allowed on a node. */
@JsonIgnore private boolean allowMultipleErrors;
/**
* An Errors whose list of CompilerErrors is ERRORS. The list should be modified using this.add.
*/
@JsonCreator
public Errors(List<CompilerError> errors) {
super(null, null);
this.errors = errors;
allowMultipleErrors = true;
}
/** Return true iff there are any errors. */
public boolean hasErrors() {
return !this.errors.isEmpty();
}
/** Prevent multiple semantic errors on the same node. */
public void suppressMultipleErrors() {
allowMultipleErrors = false;
}
/**
* Add a new semantic error message attributed to NODE, with message String.format(MESSAGEFORM,
* ARGS).
*/
public void semError(Node node, String messageForm, Object... args) {
if (allowMultipleErrors || !node.hasError()) {
String msg = String.format(messageForm, args);
CompilerError err = new CompilerError(null, null, msg, false);
err.setLocation(node.getLocation());
add(err);
if (!node.hasError()) {
node.setErrorMsg(msg);
}
}
}
/**
* Add a new syntax error message attributed to the source text between LEFT and RIGHT, and with
* message String.format(MESSAGEFORM, ARGS).
*/
public void syntaxError(Location left, Location right, String messageForm, Object... args) {
add(new CompilerError(left, right, String.format(messageForm, args), true));
}
/** Add ERR to the list of errors. */
public void add(CompilerError err) {
errors.add(err);
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,43 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.types.Type;
import com.fasterxml.jackson.annotation.JsonInclude;
import java_cup.runtime.ComplexSymbolFactory.Location;
/**
* Base of all AST nodes representing expressions.
*
* <p>There is nothing in this class, but there will be many AST node types that have fields that
* are *any expression*. For those cases, having a field of this type will encompass all types of
* expressions such as binary expressions and literals that subclass this class.
*/
public abstract class Expr extends Node {
/** A Python expression spanning source locations [LEFT..RIGHT]. */
public Expr(Location left, Location right) {
super(left, right);
}
/**
* The type of the value that this expression evaluates to.
*
* <p>This field is always <tt>null</tt> after the parsing stage, but is populated by the
* typechecker in the semantic analysis stage.
*
* <p>After typechecking this field may be <tt>null</tt> only for expressions that cannot be
* assigned a type. In particular, {@link NoneLiteral} expressions will not have a typed
* assigned to them.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private Type inferredType;
/** Set getInferredType() to TYPE, returning TYPE. */
public Type setInferredType(Type type) {
inferredType = type;
return type;
}
public Type getInferredType() {
return inferredType;
}
}

@ -0,0 +1,21 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Statements consisting of expressions. */
public final class ExprStmt extends Stmt {
/** The expression I evaluate. */
public final Expr expr;
/** The AST for EXPR spanning source locations [LEFT..RIGHT] in a statement context. */
public ExprStmt(Location left, Location right, Expr expr) {
super(left, right);
this.expr = expr;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,29 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** For statements. */
public class ForStmt extends Stmt {
/** Control variable. */
public final Identifier identifier;
/** Source of values of control statement. */
public final Expr iterable;
/** Repeated statements. */
public final List<Stmt> body;
/** The AST for for IDENTIFIER in ITERABLE: BODY spanning source locations [LEFT..RIGHT]. */
public ForStmt(
Location left, Location right, Identifier identifier, Expr iterable, List<Stmt> body) {
super(left, right);
this.identifier = identifier;
this.iterable = iterable;
this.body = body;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,50 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Def statements. */
public class FuncDef extends Declaration {
/** Defined name. */
public final Identifier name;
/** Formal parameters. */
public final List<TypedVar> params;
/** Return type annotation. */
public final TypeAnnotation returnType;
/** Local-variable,inner-function, global, and nonlocal declarations. */
public final List<Declaration> declarations;
/** Other statements. */
public final List<Stmt> statements;
/**
* The AST for def NAME(PARAMS) -> RETURNTYPE: DECLARATIONS STATEMENTS spanning source locations
* [LEFT..RIGHT].
*/
public FuncDef(
Location left,
Location right,
Identifier name,
List<TypedVar> params,
TypeAnnotation returnType,
List<Declaration> declarations,
List<Stmt> statements) {
super(left, right);
this.name = name;
this.params = params;
this.returnType = returnType;
this.declarations = declarations;
this.statements = statements;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
@Override
public Identifier getIdentifier() {
return this.name;
}
}

@ -0,0 +1,26 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Declaration of global variable. */
public class GlobalDecl extends Declaration {
/** The declared variable. */
public final Identifier variable;
/** The AST for the declaration global VARIABLE spanning source locations [LEFT..RIGHT]. */
public GlobalDecl(Location left, Location right, Identifier variable) {
super(left, right);
this.variable = variable;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
@Override
public Identifier getIdentifier() {
return this.variable;
}
}

@ -0,0 +1,24 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** A simple identifier. */
public class Identifier extends Expr {
/** Text of the identifier. */
public final String name;
/**
* An AST for the variable, method, or parameter named NAME, spanning source locations
* [LEFT..RIGHT].
*/
public Identifier(Location left, Location right, String name) {
super(left, right);
this.name = name;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,26 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Conditional expressions. */
public class IfExpr extends Expr {
/** Boolean condition. */
public final Expr condition;
/** True branch. */
public final Expr thenExpr;
/** False branch. */
public final Expr elseExpr;
/** The AST for THENEXPR if CONDITION else ELSEEXPR spanning source locations [LEFT..RIGHT]. */
public IfExpr(Location left, Location right, Expr condition, Expr thenExpr, Expr elseExpr) {
super(left, right);
this.condition = condition;
this.thenExpr = thenExpr;
this.elseExpr = elseExpr;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,35 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Conditional statement. */
public class IfStmt extends Stmt {
/** Test condition. */
public final Expr condition;
/** "True" branch. */
public final List<Stmt> thenBody;
/** "False" branch. */
public final List<Stmt> elseBody;
/**
* The AST for if CONDITION: THENBODY else: ELSEBODY spanning source locations [LEFT..RIGHT].
*/
public IfStmt(
Location left,
Location right,
Expr condition,
List<Stmt> thenBody,
List<Stmt> elseBody) {
super(left, right);
this.condition = condition;
this.thenBody = thenBody;
this.elseBody = elseBody;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,24 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** List-indexing expression. */
public class IndexExpr extends Expr {
/** Indexed list. */
public final Expr list;
/** Expression for index value. */
public final Expr index;
/** The AST for LIST[INDEX]. spanning source locations [LEFT..RIGHT]. */
public IndexExpr(Location left, Location right, Expr list, Expr index) {
super(left, right);
this.list = list;
this.index = index;
}
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;
/** Integer numerals. */
public final class IntegerLiteral extends Literal {
/** Value denoted. */
public final int value;
/** The AST for the literal VALUE, spanning source locations [LEFT..RIGHT]. */
public IntegerLiteral(Location left, Location right, int value) {
super(left, right);
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,23 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** List displays. */
public final class ListExpr extends Expr {
/** List of element expressions. */
public final List<Expr> elements;
/** The AST for [ ELEMENTS ]. spanning source locations [LEFT..RIGHT]. */
public ListExpr(Location left, Location right, List<Expr> elements) {
super(left, right);
this.elements = elements;
}
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;
/** Type denotation for a list type. */
public final class ListType extends TypeAnnotation {
/** The element of list element. */
public final TypeAnnotation elementType;
/** The AST for the type annotation [ ELEMENTTYPE ]. spanning source locations [LEFT..RIGHT]. */
public ListType(Location left, Location right, TypeAnnotation elementType) {
super(left, right);
this.elementType = elementType;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,16 @@
package chocopy.common.astnodes;
import java_cup.runtime.ComplexSymbolFactory.Location;
/**
* Base of all the literal nodes.
*
* <p>There is nothing in this class, but it is useful to isolate expressions that are constant
* literals.
*/
public abstract class Literal extends Expr {
/** A literal spanning source locations [LEFT..RIGHT]. */
public Literal(Location left, Location right) {
super(left, right);
}
}

@ -0,0 +1,24 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Attribute accessor. */
public class MemberExpr extends Expr {
/** Object selected from. */
public final Expr object;
/** Name of attribute (instance variable or method). */
public final Identifier member;
/** The AST for OBJECT.MEMBER. spanning source locations [LEFT..RIGHT]. */
public MemberExpr(Location left, Location right, Expr object, Identifier member) {
super(left, right);
this.object = object;
this.member = member;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,26 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Method calls. */
public class MethodCallExpr extends Expr {
/** Expression for the bound method to be called. */
public final MemberExpr method;
/** Actual parameters. */
public final List<Expr> args;
/** The AST for METHOD(ARGS). spanning source locations [LEFT..RIGHT]. */
public MethodCallExpr(Location left, Location right, MemberExpr method, List<Expr> args) {
super(left, right);
this.method = method;
this.args = args;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,176 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.io.IOException;
/**
* Root of the AST class hierarchy. Every node has a left and right location, indicating the start
* and end of the represented construct in the source text.
*
* <p>Every node can be marked with an error message, which serves two purposes: 1. It indicates
* that an error message has been issued for this Node, allowing tne program to reduce cascades of
* error messages. 2. It aids in debugging by making it convenient to see which Nodes have caused an
* error.
*/
@JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.EXISTING_PROPERTY,
property = "kind")
/* List of all concrete subclasses of Node. */
@JsonSubTypes({
@JsonSubTypes.Type(AssignStmt.class),
@JsonSubTypes.Type(BinaryExpr.class),
@JsonSubTypes.Type(BooleanLiteral.class),
@JsonSubTypes.Type(CallExpr.class),
@JsonSubTypes.Type(ClassDef.class),
@JsonSubTypes.Type(ClassType.class),
@JsonSubTypes.Type(CompilerError.class),
@JsonSubTypes.Type(Errors.class),
@JsonSubTypes.Type(ExprStmt.class),
@JsonSubTypes.Type(ForStmt.class),
@JsonSubTypes.Type(FuncDef.class),
@JsonSubTypes.Type(GlobalDecl.class),
@JsonSubTypes.Type(Identifier.class),
@JsonSubTypes.Type(IfExpr.class),
@JsonSubTypes.Type(IfStmt.class),
@JsonSubTypes.Type(IndexExpr.class),
@JsonSubTypes.Type(IntegerLiteral.class),
@JsonSubTypes.Type(ListExpr.class),
@JsonSubTypes.Type(ListType.class),
@JsonSubTypes.Type(MemberExpr.class),
@JsonSubTypes.Type(MethodCallExpr.class),
@JsonSubTypes.Type(NoneLiteral.class),
@JsonSubTypes.Type(NonLocalDecl.class),
@JsonSubTypes.Type(Program.class),
@JsonSubTypes.Type(ReturnStmt.class),
@JsonSubTypes.Type(StringLiteral.class),
@JsonSubTypes.Type(TypedVar.class),
@JsonSubTypes.Type(UnaryExpr.class),
@JsonSubTypes.Type(VarDef.class),
@JsonSubTypes.Type(WhileStmt.class),
})
public abstract class Node {
/** Node-type indicator for JSON form. */
public final String kind;
/**
* Source position information: 0: line number of start, 1: column number of start, 2: line
* number of end, 3: column number of end.
*/
private final int[] location = new int[4];
/**
* First error message "blamed" on this Node. When non-null, indicates that an error has been
* found in this Node.
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
private String errorMsg;
/** A Node corresponding to source text between LEFT and RIGHT. */
public Node(Location left, Location right) {
if (left != null) {
location[0] = left.getLine();
location[1] = left.getColumn();
}
if (right != null) {
location[2] = right.getLine();
location[3] = right.getColumn();
}
this.kind = getClass().getSimpleName();
this.errorMsg = null;
}
/**
* Return my source location as { <first line>, <first column>, <last line>, <last column> }.
* Result should not be modified, and contents will change after setLocation().
*/
public int[] getLocation() {
return location;
}
/** Copy LOCATION as getLocation(). */
public void setLocation(final int[] location) {
System.arraycopy(location, 0, this.location, 0, 4);
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String msg) {
this.errorMsg = msg;
}
/** Return true iff I have been marked with an error message. */
@JsonIgnore
public boolean hasError() {
return this.errorMsg != null;
}
/**
* Invoke ANALYZER on me as a node of static type T. See the comment on NodeAnalyzer. Returns
* modified Node.
*/
public abstract <T> T dispatch(NodeAnalyzer<T> analyzer);
/** Print out the AST in JSON format. */
@Override
public String toString() {
try {
return toJSON();
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
/** Return a serialization of this node in JSON format. */
public String toJSON() throws JsonProcessingException {
return mapper.writeValueAsString(this);
}
/** Mapper to-and-from serialized JSON. */
private static final ObjectMapper mapper = new ObjectMapper();
static {
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.registerModule(new ParameterNamesModule());
}
/** Returns a T from JSON, a JSON-serialized T value with class CLAS. */
public static <T> T fromJSON(String json, Class<T> clas) throws IOException {
return mapper.readValue(json, clas);
}
/**
* Returns the result of converting JSON, a JSon-serialization of a Node value, into the value
* it serializes.
*/
public static Node fromJSON(String json) throws IOException {
return fromJSON(json, Node.class);
}
/**
* Returns the result of converting TREE to the value of type T that it represents, where CLAS
* reflects T.
*/
public static <T> T fromJSON(JsonNode tree, Class<T> clas) throws IOException {
return mapper.treeToValue(tree, clas);
}
/** Returns the translation of serialized value SRC into the corresponding JSON tree. */
public static JsonNode readTree(String src) throws IOException {
return mapper.readTree(src);
}
}

@ -0,0 +1,26 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Nonlocal declaration. */
public class NonLocalDecl extends Declaration {
/** Name of identifier being declared. */
public final Identifier variable;
/** The AST for nonlocal VARIABLE spanning source locations [LEFT..RIGHT]. */
public NonLocalDecl(Location left, Location right, Identifier variable) {
super(left, right);
this.variable = variable;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
@Override
public Identifier getIdentifier() {
return this.variable;
}
}

@ -0,0 +1,17 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** The expression 'None'. */
public final class NoneLiteral extends Literal {
/** The AST for None, spanning source locations [LEFT..RIGHT]. */
public NoneLiteral(Location left, Location right) {
super(left, right);
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,56 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.ArrayList;
import java.util.List;
/** An entire ChocoPy program. */
public class Program extends Node {
/** Initial variable, class, and function declarations. */
public final List<Declaration> declarations;
/** Trailing statements. */
public final List<Stmt> statements;
/** Accumulated errors. */
public final Errors errors;
/**
* The AST for the program DECLARATIONS STATEMENTS spanning source locations [LEFT..RIGHT].
*
* <p>ERRORS is the container for all error messages applying to the program.
*/
public Program(
Location left,
Location right,
List<Declaration> declarations,
List<Stmt> statements,
Errors errors) {
super(left, right);
this.declarations = declarations;
this.statements = statements;
if (errors == null) {
this.errors = new Errors(new ArrayList<>());
} else {
this.errors = errors;
}
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
/** Returns true iff there is at least one error in the program. */
@JsonIgnore
public boolean hasErrors() {
return errors.hasErrors();
}
/** A convenience method returning the list of all CompilerErrors for this program. */
@JsonIgnore
public List<CompilerError> getErrorList() {
return errors.errors;
}
}

@ -0,0 +1,21 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Return from function. */
public class ReturnStmt extends Stmt {
/** Returned value. */
public final Expr value;
/** The AST for return VALUE spanning source locations [LEFT..RIGHT]. */
public ReturnStmt(Location left, Location right, Expr value) {
super(left, right);
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,18 @@
package chocopy.common.astnodes;
import java_cup.runtime.ComplexSymbolFactory.Location;
/**
* Base of all AST nodes representing statements.
*
* <p>There is nothing in this class, but there will be some AST node types that have fields that
* are *any statement* or a list of statements. For those cases, having a field of this type will
* encompass all types of statements such as expression statements, if statements, while statements,
* etc.
*/
public abstract class Stmt extends Node {
/** A statement spanning source locations [LEFT..RIGHT]. */
public Stmt(Location left, Location right) {
super(left, right);
}
}

@ -0,0 +1,21 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** String constants. */
public final class StringLiteral extends Literal {
/** Contents of the literal, not including quotation marks. */
public final String value;
/** The AST for a string literal containing VALUE, spanning source locations [LEFT..RIGHT]. */
public StringLiteral(Location left, Location right, String value) {
super(left, right);
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,11 @@
package chocopy.common.astnodes;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** Base of all AST nodes representing type annotations (list or class types. */
public abstract class TypeAnnotation extends Node {
/** An annotation spanning source locations [LEFT..RIGHT]. */
public TypeAnnotation(Location left, Location right) {
super(left, right);
}
}

@ -0,0 +1,24 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** An identifier with attached type annotation. */
public class TypedVar extends Node {
/** The typed identifier. */
public final Identifier identifier;
/** The declared type. */
public final TypeAnnotation type;
/** The AST for IDENTIFIER : TYPE. spanning source locations [LEFT..RIGHT]. */
public TypedVar(Location left, Location right, Identifier identifier, TypeAnnotation type) {
super(left, right);
this.identifier = identifier;
this.type = type;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,24 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** An expression applying a unary operator. */
public class UnaryExpr extends Expr {
/** The text representation of the operator. */
public final String operator;
/** The operand to which it is applied. */
public final Expr operand;
/** The AST for OPERATOR OPERAND spanning source locations [LEFT..RIGHT]. */
public UnaryExpr(Location left, Location right, String operator, Expr operand) {
super(left, right);
this.operator = operator;
this.operand = operand;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,32 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
/** A declaration of a variable (i.e., with type annotation). */
public class VarDef extends Declaration {
/** The variable and its assigned type. */
public final TypedVar var;
/** The initial value assigned. */
public final Literal value;
/**
* The AST for VAR = VALUE where VAR has a type annotation, and spanning source locations
* [LEFT..RIGHT].
*/
public VarDef(Location left, Location right, TypedVar var, Literal value) {
super(left, right);
this.var = var;
this.value = value;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
/** The identifier defined by this declaration. */
@Override
public Identifier getIdentifier() {
return this.var.identifier;
}
}

@ -0,0 +1,25 @@
package chocopy.common.astnodes;
import chocopy.common.analysis.NodeAnalyzer;
import java_cup.runtime.ComplexSymbolFactory.Location;
import java.util.List;
/** Indefinite repetition construct. */
public class WhileStmt extends Stmt {
/** Test for whether to continue. */
public final Expr condition;
/** Loop body. */
public final List<Stmt> body;
/** The AST for while CONDITION: BODY spanning source locations [LEFT..RIGHT]. */
public WhileStmt(Location left, Location right, Expr condition, List<Stmt> body) {
super(left, right);
this.condition = condition;
this.body = body;
}
public <T> T dispatch(NodeAnalyzer<T> analyzer) {
return analyzer.analyze(this);
}
}

@ -0,0 +1,16 @@
package chocopy.common.codegen;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.Literal;
/** Information concerning an instance variable. */
public class AttrInfo extends VarInfo {
/**
* A descriptor for an attribute named ATTRNAME of type VARTYPE whose initial value, if any, is
* a constant specified by INITIALVALUE (it is otherwise null).
*/
public AttrInfo(String attrName, ValueType varType, Literal initialValue) {
super(attrName, varType, initialValue);
}
}

@ -0,0 +1,123 @@
package chocopy.common.codegen;
import java.util.ArrayList;
import java.util.List;
/** Information for code generation a class. */
public class ClassInfo extends SymbolInfo {
/** Name of class. */
protected final String className;
/** Information about instance variables of the class. */
public final List<AttrInfo> attributes;
/** Information about methods of the class. */
public final List<FuncInfo> methods;
/**
* Tag indicating type of value: 0: (reserved) 1: int 2: bool 3: str -1: [T] for any T >3:
* User-defined types.
*/
protected final int typeTag;
/** Label of area containing initial instance values. */
protected final Label prototypeLabel;
/** Label of area containing method-dispatching table. */
protected Label dispatchTableLabel;
/**
* A descriptor for a class named CLASSNAME identified by runtime tag TYPETAG, and having the
* class denoted by SUPERCLASSINFO as its superclass. The latter is null iff the class is
* object.
*/
public ClassInfo(String className, int typeTag, ClassInfo superClassInfo) {
this.className = className;
this.typeTag = typeTag;
prototypeLabel = new Label(String.format("$%s$%s", className, "prototype"));
dispatchTableLabel = new Label(String.format("$%s$%s", className, "dispatchTable"));
attributes = new ArrayList<>();
methods = new ArrayList<>();
if (superClassInfo != null) {
attributes.addAll(superClassInfo.attributes);
methods.addAll(superClassInfo.methods);
}
}
/** Add an attribute described by ATTRINFO. */
public void addAttribute(AttrInfo attrInfo) {
this.attributes.add(attrInfo);
}
/**
* Add a method described by FUNCINFO, overriding any inherited method of that name if
* necessary.
*/
public void addMethod(FuncInfo funcInfo) {
String methodName = funcInfo.getBaseName();
int idx = this.getMethodIndex(methodName);
if (idx >= 0) {
this.methods.set(idx, funcInfo);
} else {
this.methods.add(funcInfo);
}
}
/** Return my type tag. */
public int getTypeTag() {
return typeTag;
}
/** Returns the address of this class's prototype object (a label). */
public Label getPrototypeLabel() {
return prototypeLabel;
}
/** Returns the address of this class's dispatch table (a label). */
public Label getDispatchTableLabel() {
return dispatchTableLabel;
}
/**
* Returns the index of the attribute named ATTRNAME in order of definition.
*
* <p>This index takes into account inherited attribute and returns the index of an attribute as
* a slot index in its object layout (excluding the object header). Attributes are numbered from
* 0; the result is an index, and not a byte offset.
*/
public int getAttributeIndex(String attrName) {
for (int i = 0; i < attributes.size(); i++) {
if (attributes.get(i).getVarName().equals(attrName)) {
return i;
}
}
return -1;
}
/**
* Returns the index of the method named METHODNAME in order of definition.
*
* <p>This index takes into account inherited and overridden methods and returns the index of
* the method as a slot number (not a byte offset) in the dispatch table.
*/
public int getMethodIndex(String methodName) {
for (int i = 0; i < methods.size(); i++) {
if (methods.get(i).getBaseName().equals(methodName)) {
return i;
}
}
return -1;
}
public String getClassName() {
return className;
}
/** Returns the list of attributes of this class, in order of the object's layout. */
public List<AttrInfo> getAttributes() {
return attributes;
}
/** Returns the list of methods of this class, in order of the object's dispatch table. */
public List<FuncInfo> getMethods() {
return methods;
}
}

@ -0,0 +1,880 @@
package chocopy.common.codegen;
import chocopy.common.analysis.AbstractNodeAnalyzer;
import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.Type;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static chocopy.common.Utils.*;
import static chocopy.common.codegen.RiscVBackend.Register.*;
/**
* The code generator for a ChocoPy program.
*
* <p>This class implements logic to analyze all declarations in a program and create descriptors
* for classes, functions, methods, variables (global and local), and attributes. This logic also
* builds symbol tables for globals and individual functions.
*
* <p>This class also implements logic to emit global variables, object prototypes and dispatch
* tables, as well as int/str/bool constants.
*
* <p>However, this class is abstract because it does not implement logic for emitting executable
* code in bodies of user-defined functions as well as in top-level statements. This class should be
* extended with implementations for such logic.
*
* <p>All non-public members of this class are `protected`, and can be overridden by sub-classes to
* extend change functionality.
*
* <p>The SymbolInfo classes can also be overridden. If say you want to use your own extended
* FuncInfo called MyFuncInfo (that extends FuncInfo), then override the makeFuncInfo() method of
* this class to `return new MyFuncInfo(...)` instead. This is probably not needed, though.
*/
public abstract class CodeGenBase {
/** The location of the text resources containing common library code. */
protected static final String LIBRARY_CODE_DIR = "chocopy/common/";
/** The backend that emits assembly. */
protected final RiscVBackend backend;
/** Convenience variable: the word size for the current back end. */
protected final int wordSize;
/** A counter for generating unique class type tags. */
protected int nextTypeTag = 0;
/** A counter used to generate unique local label names. */
protected int nextLabelSuffix = 0;
/**
* Predefined classes. The list "class" is a fake class; we use it only to emit a prototype
* object for empty lists.
*/
protected ClassInfo objectClass, intClass, boolClass, strClass, listClass;
/** Predefined functions. */
protected FuncInfo printFunc, lenFunc, inputFunc;
/** A list of global variables, whose initial values are emitted in the backend. */
protected final List<GlobalVarInfo> globalVars = new ArrayList<>();
/**
* A list of program classes, whose prototype objects and dispatch tables are emitted in the
* backend.
*/
protected final List<ClassInfo> classes = new ArrayList<>();
/**
* A list of functions (including methods and nested functions) whose bodies are emitted in the
* backend.
*/
protected final List<FuncInfo> functions = new ArrayList<>();
/** Label for built-in routine: alloc. */
protected final Label objectAllocLabel = new Label("alloc");
/** Label for built-in routine: alloc2. */
protected final Label objectAllocResizeLabel = new Label("alloc2");
/** Label for built-in routine: abort. */
protected final Label abortLabel = new Label("abort");
/** Label for built-in routine: heap.init. */
protected final Label heapInitLabel = new Label("heap.init");
/** Error codes. */
protected final int ERROR_ARG = 1,
ERROR_DIV_ZERO = 2,
ERROR_OOB = 3,
ERROR_NONE = 4,
ERROR_OOM = 5,
ERROR_NYI = 6;
/** Size of heap memory. */
protected final int HEAP_SIZE_BYTES = 1024 * 1024 * 32;
/** Ecall numbers for intrinsic routines. */
protected final int EXIT_ECALL = 10,
EXIT2_ECALL = 17,
PRINT_STRING_ECALL = 4,
PRINT_CHAR_ECALL = 11,
PRINT_INT_ECALL = 1,
READ_STRING_ECALL = 8,
FILL_LINE_BUFFER__ECALL = 18,
SBRK_ECALL = 9;
/**
* The symbol table that maps global names to information about the bound global variables,
* global functions, or classes.
*/
protected final SymbolTable<SymbolInfo> globalSymbols = new SymbolTable<>();
/** A utility for caching constants and generating labels for constants. */
protected final Constants constants = new Constants();
/** The object header size, in words (includes type tag, size, and dispatch table pointer). */
public static final int HEADER_SIZE = 3;
/**
* Initializes a code generator for ChocoPy that uses BACKEND to emit assembly code.
*
* <p>The constructor creates Info objects for predefined functions, classes, methods, and
* built-in routines.
*/
public CodeGenBase(RiscVBackend backend) {
this.backend = backend;
wordSize = backend.getWordSize();
initClasses();
initFunctions();
initAsmConstants();
}
/** Return a fresh type tag. */
protected int getNextTypeTag() {
return nextTypeTag++;
}
/** Returns the next unique label suffix. */
protected int getNextLabelSuffix() {
return nextLabelSuffix++;
}
/**
* Return a fresh label.
*
* <p>This label is guaranteed to be unique amongst labels generated by invoking this method.
* All such labels have a prefix of `label_`.
*
* <p>This is useful to generate local labels in function bodies (e.g. for targets of jumps),
* where the name does not matter in general.
*/
protected Label generateLocalLabel() {
return new Label(String.format("label_%d", getNextLabelSuffix()));
}
/**
* Generates assembly code for PROGRAM.
*
* <p>This is the main driver that calls internal methods for emitting DATA section (globals,
* constants, prototypes, etc) as well as the the CODE section (predefined functions, built-in
* routines, and user-defined functions).
*/
public void generate(Program program) {
analyzeProgram(program);
backend.startData();
for (ClassInfo classInfo : this.classes) {
emitPrototype(classInfo);
}
for (ClassInfo classInfo : this.classes) {
emitDispatchTable(classInfo);
}
for (GlobalVarInfo global : this.globalVars) {
backend.emitGlobalLabel(global.getLabel());
emitConstant(
global.getInitialValue(),
global.getVarType(),
String.format("Initial value of global var: %s", global.getVarName()));
}
backend.startCode();
Label mainLabel = new Label("main");
backend.emitGlobalLabel(mainLabel);
backend.emitLUI(A0, HEAP_SIZE_BYTES >> 12, "Initialize heap size (in multiples of 4KB)");
backend.emitADD(S11, S11, A0, "Save heap size");
backend.emitJAL(heapInitLabel, "Call heap.init routine");
backend.emitMV(GP, A0, "Initialize heap pointer");
backend.emitMV(S10, GP, "Set beginning of heap");
backend.emitADD(S11, S10, S11, "Set end of heap (= start of heap + heap size)");
backend.emitMV(RA, ZERO, "No normal return from main program.");
backend.emitMV(FP, ZERO, "No preceding frame.");
emitTopLevel(program.statements);
for (FuncInfo funcInfo : this.functions) {
funcInfo.emitBody();
}
emitStdFunc("alloc");
emitStdFunc("alloc2");
emitStdFunc("abort");
emitStdFunc("heap.init");
emitCustomCode();
backend.startData();
emitConstants();
}
/** Create descriptors and symbols for builtin classes and methods. */
protected void initClasses() {
FuncInfo objectInit =
makeFuncInfo(
"object.__init__",
0,
Type.NONE_TYPE,
globalSymbols,
null,
this::emitStdFunc);
objectInit.addParam(makeStackVarInfo("self", Type.OBJECT_TYPE, null, objectInit));
functions.add(objectInit);
objectClass = makeClassInfo("object", getNextTypeTag(), null);
objectClass.addMethod(objectInit);
classes.add(objectClass);
globalSymbols.put(objectClass.getClassName(), objectClass);
intClass = makeClassInfo("int", getNextTypeTag(), objectClass);
intClass.addAttribute(makeAttrInfo("__int__", null, null));
classes.add(intClass);
globalSymbols.put(intClass.getClassName(), intClass);
boolClass = makeClassInfo("bool", getNextTypeTag(), objectClass);
boolClass.addAttribute(makeAttrInfo("__bool__", null, null));
classes.add(boolClass);
globalSymbols.put(boolClass.getClassName(), boolClass);
strClass = makeClassInfo("str", getNextTypeTag(), objectClass);
strClass.addAttribute(
makeAttrInfo("__len__", Type.INT_TYPE, new IntegerLiteral(null, null, 0)));
strClass.addAttribute(makeAttrInfo("__str__", null, null));
classes.add(strClass);
globalSymbols.put(strClass.getClassName(), strClass);
listClass = makeClassInfo(".list", -1, objectClass);
listClass.addAttribute(
makeAttrInfo("__len__", Type.INT_TYPE, new IntegerLiteral(null, null, 0)));
classes.add(listClass);
listClass.dispatchTableLabel = null;
}
/** Create descriptors and symbols for builtin functions. */
protected void initFunctions() {
printFunc =
makeFuncInfo("print", 0, Type.NONE_TYPE, globalSymbols, null, this::emitStdFunc);
printFunc.addParam(makeStackVarInfo("arg", Type.OBJECT_TYPE, null, printFunc));
functions.add(printFunc);
globalSymbols.put(printFunc.getBaseName(), printFunc);
lenFunc = makeFuncInfo("len", 0, Type.INT_TYPE, globalSymbols, null, this::emitStdFunc);
lenFunc.addParam(makeStackVarInfo("arg", Type.OBJECT_TYPE, null, lenFunc));
functions.add(lenFunc);
globalSymbols.put(lenFunc.getBaseName(), lenFunc);
inputFunc = makeFuncInfo("input", 0, Type.STR_TYPE, globalSymbols, null, this::emitStdFunc);
functions.add(inputFunc);
globalSymbols.put(inputFunc.getBaseName(), inputFunc);
}
/* Symbolic assembler constants defined here (to add others, override
* initAsmConstants in an extension of CodeGenBase):
* ecalls:
* @sbrk
* @fill_line_buffer
* @read_string
* @print_string
* @print_char
* @print_int
* @exit2
* Exit codes:
* @error_div_zero: Division by 0.
* @error_arg: Bad argument.
* @error_oob: Out of bounds.
* @error_none: Attempt to access attribute of None.
* @error_oom: Out of memory.
* @error_nyi: Unimplemented operation.
* Data-structure byte offsets:
* @.__obj_size__: Offset of size of object.
* @.__len__: Offset of length in chars or words.
* @.__str__: Offset of string data.
* @.__elts__: Offset of first list item.
* @.__int__: Offset of integer value.
* @.__bool__: Offset of boolean (1/0) value.
*/
/** Define @-constants to be used in assembly code. */
protected void initAsmConstants() {
backend.defineSym("sbrk", SBRK_ECALL);
backend.defineSym("print_string", PRINT_STRING_ECALL);
backend.defineSym("print_char", PRINT_CHAR_ECALL);
backend.defineSym("print_int", PRINT_INT_ECALL);
backend.defineSym("exit2", EXIT2_ECALL);
backend.defineSym("read_string", READ_STRING_ECALL);
backend.defineSym("fill_line_buffer", FILL_LINE_BUFFER__ECALL);
backend.defineSym(".__obj_size__", 4);
backend.defineSym(".__len__", 12);
backend.defineSym(".__int__", 12);
backend.defineSym(".__bool__", 12);
backend.defineSym(".__str__", 16);
backend.defineSym(".__elts__", 16);
backend.defineSym("error_div_zero", ERROR_DIV_ZERO);
backend.defineSym("error_arg", ERROR_ARG);
backend.defineSym("error_oob", ERROR_OOB);
backend.defineSym("error_none", ERROR_NONE);
backend.defineSym("error_oom", ERROR_OOM);
backend.defineSym("error_nyi", ERROR_NYI);
}
/*-----------------------------------------------------------*/
/* */
/* FACTORY METHODS TO CREATE INFO OBJECTS */
/* */
/*-----------------------------------------------------------*/
/**
* A factory method that returns a descriptor for function or method FUNCNAME returning type
* RETURNTYPE at nesting level DEPTH in the region corresponding to PARENTSYMBOLTABLE.
*
* <p>PARENTFUNCINFO is a descriptor of the enclosing function and is null for global functions
* and methods.
*
* <p>EMITTER is a method that emits the function's body (usually a generic emitter for
* user-defined functions/methods, and a special emitter for pre-defined functions/methods).
*
* <p>Sub-classes of CodeGenBase can override this method if they wish to use a sub-class of
* FuncInfo with more functionality.
*/
protected FuncInfo makeFuncInfo(
String funcName,
int depth,
ValueType returnType,
SymbolTable<SymbolInfo> parentSymbolTable,
FuncInfo parentFuncInfo,
Consumer<FuncInfo> emitter) {
return new FuncInfo(
funcName, depth, returnType, parentSymbolTable, parentFuncInfo, emitter);
}
/**
* Return a descriptor for a class named CLASSNAME having type tag TYPETAG and superclass
* SUPERCLASSINFO (null for `object' only).
*
* <p>Sub-classes of CodeGenBase can override this method if they wish to use a sub-class of
* ClassInfo with more functionality.
*/
public ClassInfo makeClassInfo(String className, int typeTag, ClassInfo superClassInfo) {
return new ClassInfo(className, typeTag, superClassInfo);
}
/**
* A factory method that returns a descriptor for an attribute named ATTRNAME of type ATTRTYPE
* and with an initial value specified by INITIALVALUE, which may be null to indicate a default
* initialization.
*
* <p>Sub-classes of CodeGenBase can override this method if they wish to use a sub-class of
* AttrInfo with more functionality.
*/
public AttrInfo makeAttrInfo(String attrName, ValueType attrType, Literal initialValue) {
return new AttrInfo(attrName, attrType, initialValue);
}
/**
* A factory method that returns a descriptor for a local variable or parameter named VARNAME of
* type VARTYPE, whose initial value is specified by INITIALVALUE (if non-null) and which is
* defined immediately within the function given by FUNCINFO.
*
* <p>These variables are allocated on the stack in activation frames.
*
* <p>Sub-classes of CodeGenBase can override this method if they wish to use a sub-class of
* StackVarInfo with more functionality.
*/
public StackVarInfo makeStackVarInfo(
String varName, ValueType varType, Literal initialValue, FuncInfo funcInfo) {
return new StackVarInfo(varName, varType, initialValue, funcInfo);
}
/**
* A factory method that returns a descriptor for a global variable with name VARNAME and type
* VARTYPE, whose initial value is specified by INITIALVALUE (if non-null).
*
* <p>Sub-classes of CodeGenBase can override this method if they wish to use a sub-class of
* GlobalVarInfo with more functionality.
*/
public GlobalVarInfo makeGlobalVarInfo(
String varName, ValueType varType, Literal initialValue) {
return new GlobalVarInfo(varName, varType, initialValue);
}
/*-----------------------------------------------------------*
* *
* ANALYSIS OF AST INTO INFO OBJECTS *
* (Students can ignore these methods as all the work has *
* been done and does not need to be modified/extended) *
* *
*-----------------------------------------------------------*/
/** Analyze PROGRAM, creating Info objects for all symbols. Populate the global symbol table. */
protected void analyzeProgram(Program program) {
/* Proceed in phases:
* 1. Analyze all global variable declarations.
* Do this first so that global variables are in the symbol
* table before we encounter `global x` declarations.
* 2. Analyze classes and global functions now that global variables
* are in the symbol table.
*/
for (Declaration decl : program.declarations) {
if (decl instanceof VarDef) {
VarDef varDef = (VarDef) decl;
ValueType varType = ValueType.annotationToValueType(varDef.var.type);
GlobalVarInfo globalVar =
makeGlobalVarInfo(varDef.var.identifier.name, varType, varDef.value);
this.globalVars.add(globalVar);
this.globalSymbols.put(globalVar.getVarName(), globalVar);
}
}
for (Declaration decl : program.declarations) {
if (decl instanceof ClassDef) {
ClassDef classDef = (ClassDef) decl;
ClassInfo classInfo = analyzeClass(classDef);
this.classes.add(classInfo);
this.globalSymbols.put(classInfo.getClassName(), classInfo);
} else if (decl instanceof FuncDef) {
FuncDef funcDef = (FuncDef) decl;
FuncInfo funcInfo = analyzeFunction(null, funcDef, 0, globalSymbols, null);
this.functions.add(funcInfo);
this.globalSymbols.put(funcInfo.getBaseName(), funcInfo);
}
}
}
/**
* Analyze a class definition CLASSDEF and return the resulting Info object. Also creates Info
* objects for attributes/methods and stores them in the ClassInfo. Methods are recursively
* analyzed using analyzeFunction().
*/
protected ClassInfo analyzeClass(ClassDef classDef) {
String className = classDef.name.name;
String superClassName = classDef.superClass.name;
SymbolInfo superSymbolInfo = globalSymbols.get(superClassName);
assert superSymbolInfo instanceof ClassInfo
: "Semantic analysis should ensure that super-class is defined";
ClassInfo superClassInfo = (ClassInfo) superSymbolInfo;
ClassInfo classInfo = makeClassInfo(className, getNextTypeTag(), superClassInfo);
for (Declaration decl : classDef.declarations) {
if (decl instanceof VarDef) {
VarDef attrDef = (VarDef) decl;
ValueType attrType = ValueType.annotationToValueType(attrDef.var.type);
AttrInfo attrInfo =
makeAttrInfo(attrDef.var.identifier.name, attrType, attrDef.value);
classInfo.addAttribute(attrInfo);
} else if (decl instanceof FuncDef) {
FuncDef funcDef = (FuncDef) decl;
FuncInfo methodInfo = analyzeFunction(className, funcDef, 0, globalSymbols, null);
this.functions.add(methodInfo);
classInfo.addMethod(methodInfo);
}
}
return classInfo;
}
/**
* Analyze a function or method definition FUNCDEF at nesting depth DEPTH and return the
* resulting Info object. Analyze any nested functions recursively. The FuncInfo's symbol table
* is completely populated by analyzing all the params, local vars, global and nonlocal var
* declarations.
*
* <p>CONTAINER is the fully qualified name of the containing function/class, or null for global
* functions. PARENTSYMBOLTABLE symbol table contains symbols inherited from outer regions (that
* of the containing function/method for nested function definitions, and the global symbol
* table for global function / method definitions). PARENTFUNCINFO is the Info object for the
* parent function/method if this definition is nested, and otherwise null.
*/
protected FuncInfo analyzeFunction(
String container,
FuncDef funcDef,
int depth,
SymbolTable<SymbolInfo> parentSymbolTable,
FuncInfo parentFuncInfo) {
/* We proceed in three steps.
* 1. Create the FuncInfo object to be returned.
* 2. Populate it by analyzing all the parameters and local var
* definitions.
* 3. Now that the function's symbol table is built up, analyze
* nested function definitions.
* 4. Add the body to the function descriptor for code gen.
*/
String funcBaseName = funcDef.name.name;
String funcQualifiedName =
container != null ? String.format("%s.%s", container, funcBaseName) : funcBaseName;
FuncInfo funcInfo =
makeFuncInfo(
funcQualifiedName,
depth,
ValueType.annotationToValueType(funcDef.returnType),
parentSymbolTable,
parentFuncInfo,
this::emitUserDefinedFunction);
for (TypedVar param : funcDef.params) {
ValueType paramType = ValueType.annotationToValueType(param.type);
StackVarInfo paramInfo =
makeStackVarInfo(param.identifier.name, paramType, null, funcInfo);
funcInfo.addParam(paramInfo);
}
LocalDeclAnalyzer localDefs = new LocalDeclAnalyzer(funcInfo);
for (Declaration decl : funcDef.declarations) {
decl.dispatch(localDefs);
}
NestedFuncAnalyzer nestedFuncs = new NestedFuncAnalyzer(funcInfo);
for (Declaration decl : funcDef.declarations) {
decl.dispatch(nestedFuncs);
}
funcInfo.addBody(funcDef.statements);
return funcInfo;
}
/** Analyzer for local variable declarations in a function. */
protected class LocalDeclAnalyzer extends AbstractNodeAnalyzer<Void> {
/** The descriptor for the function being analyzed. */
private final FuncInfo funcInfo;
/** A new analyzer for a function with descriptor FUNCINFO0. */
protected LocalDeclAnalyzer(FuncInfo funcInfo0) {
funcInfo = funcInfo0;
}
@Override
public Void analyze(VarDef localVarDef) {
ValueType localVarType = ValueType.annotationToValueType(localVarDef.var.type);
StackVarInfo localVar =
makeStackVarInfo(
localVarDef.var.identifier.name,
localVarType,
localVarDef.value,
funcInfo);
funcInfo.addLocal(localVar);
return null;
}
@Override
public Void analyze(GlobalDecl decl) {
SymbolInfo symInfo = globalSymbols.get(decl.getIdentifier().name);
assert symInfo instanceof GlobalVarInfo
: "Semantic analysis should ensure that global var exists";
GlobalVarInfo globalVar = (GlobalVarInfo) symInfo;
funcInfo.getSymbolTable().put(globalVar.getVarName(), globalVar);
return null;
}
@Override
public Void analyze(NonLocalDecl decl) {
assert funcInfo.getSymbolTable().get(decl.getIdentifier().name) instanceof StackVarInfo
: "Semantic analysis should ensure nonlocal var exists";
return null;
}
}
/** Analyzer for nested function declarations in a function. */
protected class NestedFuncAnalyzer extends AbstractNodeAnalyzer<Void> {
/** Descriptor for the function being analyzed. */
private final FuncInfo funcInfo;
/** A new analyzer for a function with descriptor FUNCINFO0. */
protected NestedFuncAnalyzer(FuncInfo funcInfo0) {
funcInfo = funcInfo0;
}
@Override
public Void analyze(FuncDef nestedFuncDef) {
FuncInfo nestedFuncInfo =
analyzeFunction(
funcInfo.getFuncName(),
nestedFuncDef,
funcInfo.getDepth() + 1,
funcInfo.getSymbolTable(),
funcInfo);
functions.add(nestedFuncInfo);
funcInfo.getSymbolTable().put(nestedFuncInfo.getBaseName(), nestedFuncInfo);
return null;
}
}
/*------------------------------------------------------------*
* *
* EMITING DATA SECTION FOR GLOBALS+PROTOTYPES+CONSTANTS *
* (Students can ignore these methods as all the work has *
* been done and does not need to be modified/extended) *
* *
*------------------------------------------------------------*/
/** Emit code to align next data item to word boundary. */
protected void alignObject() {
int wordSizeLog2 = 31 - Integer.numberOfLeadingZeros(wordSize);
backend.alignNext(wordSizeLog2);
}
/** Emit the constant section containing the prototype FOR the class defined by CLASSINFO. */
protected void emitPrototype(ClassInfo classInfo) {
backend.emitGlobalLabel(classInfo.getPrototypeLabel());
backend.emitWordLiteral(
classInfo.getTypeTag(),
String.format("Type tag for class: %s", classInfo.getClassName()));
backend.emitWordLiteral(classInfo.attributes.size() + HEADER_SIZE, "Object size");
backend.emitWordAddress(classInfo.getDispatchTableLabel(), "Pointer to dispatch table");
for (VarInfo attr : classInfo.attributes) {
String cmnt = String.format("Initial value of attribute: %s", attr.getVarName());
emitConstant(attr.getInitialValue(), attr.getVarType(), cmnt);
}
alignObject();
}
/** Emit a word containing a constant int representing VALUE */
protected void emitConstantInt(int value, String comment) {
backend.emitWordLiteral(value, comment);
}
/** Emit a word containing a constant bool representing VALUE */
protected void emitConstantBool(boolean value, String comment) {
backend.emitWordLiteral(value ? 1 : 0, comment);
}
/**
* Emit a word containing a constant representing VALUE, assuming that it will be interpreted as
* a value of static type TYPE. VALUE may be null, indicating None. TYPE may be null, indicating
* object. COMMENT is an optional comment.
*/
protected void emitConstant(Literal value, ValueType type, String comment) {
if (type != null && type.equals(Type.INT_TYPE)) {
this.emitConstantInt(((IntegerLiteral) value).value, comment);
} else if (type != null && type.equals(Type.BOOL_TYPE)) {
this.emitConstantBool(((BooleanLiteral) value).value, comment);
} else {
backend.emitWordAddress(constants.fromLiteral(value), comment);
}
}
/** Emit code for all constants. */
protected void emitConstants() {
backend.emitGlobalLabel(constants.falseConstant);
backend.emitWordLiteral(boolClass.getTypeTag(), "Type tag for class: bool");
backend.emitWordLiteral(boolClass.attributes.size() + HEADER_SIZE, "Object size");
backend.emitWordAddress(boolClass.getDispatchTableLabel(), "Pointer to dispatch table");
backend.emitWordLiteral(0, "Constant value of attribute: __bool__");
alignObject();
backend.emitGlobalLabel(constants.trueConstant);
backend.emitWordLiteral(boolClass.getTypeTag(), "Type tag for class: bool");
backend.emitWordLiteral(boolClass.attributes.size() + HEADER_SIZE, "Object size");
backend.emitWordAddress(boolClass.getDispatchTableLabel(), "Pointer to dispatch table");
backend.emitWordLiteral(1, "Constant value of attribute: __bool__");
alignObject();
for (Map.Entry<String, Label> e : constants.strConstants.entrySet()) {
String value = e.getKey();
Label label = e.getValue();
int numWordsForCharacters = value.length() / wordSize + 1;
backend.emitGlobalLabel(label);
backend.emitWordLiteral(strClass.getTypeTag(), "Type tag for class: str");
backend.emitWordLiteral(3 + 1 + numWordsForCharacters, "Object size");
backend.emitWordAddress(strClass.getDispatchTableLabel(), "Pointer to dispatch table");
this.emitConstantInt(value.length(), "Constant value of attribute: __len__");
backend.emitString(value, "Constant value of attribute: __str__");
alignObject();
}
for (Map.Entry<Integer, Label> e : constants.intConstants.entrySet()) {
Integer value = e.getKey();
Label label = e.getValue();
backend.emitGlobalLabel(label);
backend.emitWordLiteral(intClass.getTypeTag(), "Type tag for class: int");
backend.emitWordLiteral(intClass.attributes.size() + HEADER_SIZE, "Object size");
backend.emitWordAddress(intClass.getDispatchTableLabel(), "Pointer to dispatch table");
backend.emitWordLiteral(value, "Constant value of attribute: __int__");
alignObject();
}
}
/** Emit the method dispatching table for CLASSINFO. */
protected void emitDispatchTable(ClassInfo classInfo) {
Label dispatchTableLabel = classInfo.getDispatchTableLabel();
if (dispatchTableLabel == null) {
return;
}
backend.emitGlobalLabel(dispatchTableLabel);
for (FuncInfo method : classInfo.methods) {
String cmnt =
String.format(
"Implementation for method: %s.%s",
classInfo.getClassName(), method.getBaseName());
backend.emitWordAddress(method.getCodeLabel(), cmnt);
}
}
/*------------------------------------------------------------*
* *
* UTILITY METHODS TO GET BYTE OFFSETS IN OBJECT LAYOUT *
* (Students will find these methods helpful to use in *
* their sub-class when generating code for expressions) *
* *
*------------------------------------------------------------*/
/** Return offset of the type-tag field in an object. */
protected int getTypeTagOffset() {
return 0 * wordSize;
}
/** Return offset of the size field in an object. */
protected int getObjectSizeOffset() {
return 1 * wordSize;
}
/** Return offset of the start of the pointer to the method-dispatching table in an object. */
protected int getDispatchTableOffset() {
return 2 * wordSize;
}
/** Return the offset of the ATTRNAME attribute of an object of type described by CLASSINFO. */
protected int getAttrOffset(ClassInfo classInfo, String attrName) {
int attrIndex = classInfo.getAttributeIndex(attrName);
assert attrIndex >= 0 : "Type checker ensures that attributes are valid";
return wordSize * (HEADER_SIZE + attrIndex);
}
/**
* Return the offset of the method named METHODNAME in the method-dispatching table for the
* class described by CLASSINFO.
*/
protected int getMethodOffset(ClassInfo classInfo, String methodName) {
int methodIndex = classInfo.getMethodIndex(methodName);
assert methodIndex >= 0 : "Type checker ensures that attributes are valid";
return wordSize * methodIndex;
}
/*------------------------------------------------------------*
* *
* UNIMPLEMENTED METHODS (should be extended) *
* *
*------------------------------------------------------------*/
/** Emits code for STATEMENTS, assumed to be at the top level. */
protected abstract void emitTopLevel(List<Stmt> statements);
/** Emits code for the body of user-defined function FUNCINFO. */
protected abstract void emitUserDefinedFunction(FuncInfo funcInfo);
/**
* Emits code outside the ChocoPy program.
*
* <p>Custom assembly routines (that may be jumpable from program statements) can be emitted
* here.
*/
protected abstract void emitCustomCode();
/*------------------------------------------------------------*
* *
* PREDEFINED FUNCTIONS AND ROUTINES *
* (Students may find a cursory read of these methods to *
* be useful to get an idea for how code can be emitted) *
* *
*------------------------------------------------------------*/
/**
* Return Risc V assembler code for function NAME from directory LIB, or null if it does not
* exist. LIB must end in '/'.
*/
protected String getStandardLibraryCode(String name, String lib) {
String simpleName = name.replace("$", "") + ".s";
return getResourceFileAsString(lib + simpleName);
}
/**
* Emit label and body for the function LABEL, taking the source from directory LIB (must end in
* '/').
*/
protected void emitStdFunc(Label label, String lib) {
emitStdFunc(label, label.toString(), lib);
}
/**
* Emit label and body for the function LABEL, taking the source from SOURCEFILE.s in directory
* LIB (must end in '/').
*/
protected void emitStdFunc(Label label, String sourceFile, String lib) {
String source = getStandardLibraryCode(sourceFile, lib);
if (source == null) {
throw fatal("Code for %s is missing.", sourceFile);
}
backend.emitGlobalLabel(label);
backend.emit(convertLiterals(source));
}
/**
* Emit label and body for the function LABEL, taking the source from from the default library
* directory.
*/
protected void emitStdFunc(Label label) {
emitStdFunc(label, LIBRARY_CODE_DIR);
}
/**
* Emit label and body for the function NAME, taking the source from from the default library
* directory.
*/
protected void emitStdFunc(String name) {
emitStdFunc(new Label(name));
}
/**
* Emit label and body for the function described by FUNCINFO, taking the source from from the
* default library directory.
*/
protected void emitStdFunc(FuncInfo funcInfo) {
emitStdFunc(funcInfo.getCodeLabel());
}
/** Pattern matching STRING["..."]. */
private static final Pattern STRING_LITERAL_PATN = Pattern.compile("STRING\\[\"(.*?)\"\\]");
/**
* Return result of converting STRING["..."] notations in SOURCE to labels of string constants,
* adding those constants to the pool.
*/
private String convertLiterals(String source) {
Matcher matcher = STRING_LITERAL_PATN.matcher(source);
StringBuffer result = new StringBuffer();
while (matcher.find()) {
String r = constants.getStrConstant(matcher.group(1)).toString();
matcher.appendReplacement(
result, pad(r, ' ', matcher.end(0) - matcher.start(0), false));
}
return matcher.appendTail(result).toString();
}
}

@ -0,0 +1,116 @@
package chocopy.common.codegen;
import chocopy.common.astnodes.*;
import java.util.HashMap;
import java.util.Map;
/**
* A store for caching and re-using program constants that are represented as immutable objects.
*
* <p>Constants are emitted in assembly in the DATA section, and therefore are represented by their
* labels.
*/
public class Constants {
/** A counter used to generate unique label names for constants. */
protected int nextLabelSuffix = 0;
/** The constant representing the boolean `False`. */
final Label falseConstant = generateConstantLabel();
/**
* The constant representing the boolean `True`. This immediately follows falseConstant in
* static memory.
*/
final Label trueConstant = generateConstantLabel();
/** A cache for integer-valued constants. */
final Map<Integer, Label> intConstants = new HashMap<>();
/** A cache for string-valued constants. */
final Map<String, Label> strConstants = new HashMap<>();
/**
* Returns the next unique label suffix for constants.
*
* @return the next unique label suffix for constants
*/
protected int getNextLabelSuffix() {
return nextLabelSuffix++;
}
/**
* Generates a fresh label for constants.
*
* <p>This label is guaranteed to be unique amongst labels generated by invoking this method.
* All such labels have a prefix of `const_`.
*
* @return a fresh label
*/
public Label generateConstantLabel() {
return new Label(String.format("const_%d", getNextLabelSuffix()));
}
/**
* Returns the label for a `bool` constant.
*
* @param value the boolean value
* @return the label for the boolean value
*/
public Label getBoolConstant(boolean value) {
return value ? trueConstant : falseConstant;
}
/**
* Returns the label for am `int` constant.
*
* @param value the integer value
* @return the label for the integer value
*/
public Label getIntConstant(int value) {
if (intConstants.containsKey(value)) {
return intConstants.get(value);
} else {
Label newLabel = generateConstantLabel();
intConstants.put(value, newLabel);
return newLabel;
}
}
/**
* Returns the label for a `str` constant.
*
* @param value the string value
* @return the label for the string value
*/
public Label getStrConstant(String value) {
if (strConstants.containsKey(value)) {
return strConstants.get(value);
} else {
Label newLabel = generateConstantLabel();
strConstants.put(value, newLabel);
return newLabel;
}
}
/**
* Converts a constant literal in the AST to a constant for code generation.
*
* @param literal the literal expression in the AST
* @return a {@link Label} representing a constant int/str/bool, or `null` representing the None
* literal
*/
public Label fromLiteral(Literal literal) {
if (literal instanceof IntegerLiteral) {
return getIntConstant(((IntegerLiteral) literal).value);
} else if (literal instanceof StringLiteral) {
return getStrConstant(((StringLiteral) literal).value);
} else if (literal instanceof BooleanLiteral) {
return getBoolConstant(((BooleanLiteral) literal).value);
} else {
assert literal == null || literal instanceof NoneLiteral;
return null;
}
}
}

@ -0,0 +1,206 @@
package chocopy.common.codegen;
import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.Stmt;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
* A descriptor for function and method definitions.
*
* <p>This class stores information required for code generation such as the information about a
* function's parameters, local variables, the local symbol table, the function body, and the label
* where the code for the body is generated.
*/
public class FuncInfo extends SymbolInfo {
/**
* The fully-qualified name of the function.
*
* <p>All functions in a ChocoPy program have a unique fully-qualified name. Global functions
* defined with name `f` have fully-qualified name `f`. Methods `m` in a class `C` have
* fully-qualified name `C.m`. Functions `f` nested inside another function with fully-qualified
* name `F` have a fully-qualified name of `F.f`.
*/
protected final String funcName;
/**
* The static depth of a function.
*
* <p>Global functions and class methods have a static depth of 0. Nested functions that are
* defined in the body of a function with static depth `D` have a static depth of `D+1`.
*/
protected final int depth;
/** This function's return type. */
protected final ValueType returnType;
/** A list of parameter names. */
protected final List<String> params = new ArrayList<>();
/** A list of local variable descriptors. */
protected final List<StackVarInfo> locals = new ArrayList<>();
/** The function body. */
protected final List<Stmt> statements = new ArrayList<>();
/** The local symbol table that binds identifiers seen in the function's body. */
protected final SymbolTable<SymbolInfo> symbolTable;
/** The label of the generated code for the function's body. */
protected final Label codeLabel;
/** The descriptor of the enclosing function (this is only non-null for nested functions). */
protected final FuncInfo parentFuncInfo;
/**
* A method that is invoked to emit the function's body.
*
* <p>The method should accept one parameter of type `FuncInfo`.
*/
protected final Consumer<FuncInfo> emitter;
/**
* Creates a descriptor for a function or method with fully qualified name FUNCNAME returning
* type RETURNTYPE that is at nesting depth DEPTH. The code label is formed from FUNCNAME by
* prepending a $ sign to prevent collisions. PARENTSYMBOLTABLE is the symbol table of the
* containing region. PARENTFUNCINFO is the descriptor of the enclosing function (null for
* global functions and methods). EMITTER encapsulates a method that emits the function's body
* (this is usually a generic emitter for user-defined functions/methods, and a special emitter
* for pre-defined functions/methods).
*/
public FuncInfo(
String funcName,
int depth,
ValueType returnType,
SymbolTable<SymbolInfo> parentSymbolTable,
FuncInfo parentFuncInfo,
Consumer<FuncInfo> emitter) {
this.funcName = funcName;
this.codeLabel = new Label(String.format("$%s", funcName));
this.depth = depth;
this.returnType = returnType;
this.symbolTable = new SymbolTable<>(parentSymbolTable);
this.parentFuncInfo = parentFuncInfo;
this.emitter = emitter;
}
/** Adds parameter with descriptor PARAMINFO to this function. */
public void addParam(StackVarInfo paramInfo) {
this.params.add(paramInfo.getVarName());
this.symbolTable.put(paramInfo.getVarName(), paramInfo);
}
/** Adds a local variable with descriptor STACKVARINFO to this function. */
public void addLocal(StackVarInfo stackVarInfo) {
this.locals.add(stackVarInfo);
this.symbolTable.put(stackVarInfo.getVarName(), stackVarInfo);
}
/** Adds STMTS to the function's body. */
public void addBody(List<Stmt> stmts) {
statements.addAll(stmts);
}
/**
* Returns the index of parameter or local variable NAME in the function's activation record.
*
* <p>The convention is that for a function with N params and K local vars, the i`th param is at
* index `i` and the j`th local var is at index `N+j+2`. In all, a function stores N+K+2
* variables contiguously in its activation record, where the N+1st is the frame pointer and the
* N+2nd is the return address.
*
* <p>Caution: this is an index (starting at 0), and not an offset in number of bytes.
*/
public int getVarIndex(String name) {
int idx = params.indexOf(name);
if (idx >= 0) {
return idx;
}
for (int i = 0; i < locals.size(); i++) {
if (locals.get(i).getVarName().equals(name)) {
return i + params.size() + 2;
}
}
String msg = String.format("%s is not a var defined in function %s", name, funcName);
throw new IllegalArgumentException(msg);
}
/** Returns the label corresponding to the function's body in assembly. */
public Label getCodeLabel() {
return codeLabel;
}
/**
* Returns the function's defined name in the program. This is the last component of the
* dot-separated fully-qualified name.
*/
public String getBaseName() {
int rightmostDotIndex = funcName.lastIndexOf('.');
if (rightmostDotIndex == -1) {
return funcName;
} else {
return funcName.substring(rightmostDotIndex + 1);
}
}
/** Returns the function's fully-qualified name. */
public String getFuncName() {
return funcName;
}
/** Returns the function's static nesting depth. */
public int getDepth() {
return depth;
}
/** Returns the function's parameters in order of definition. */
public List<String> getParams() {
return params;
}
/** Returns the return type of this function. */
public ValueType getReturnType() {
return returnType;
}
/**
* Returns the function's explicitly defined local variables, excluding parameters.
*
* <p>This list is mainly used in generating code for initializing local variables that are not
* parameters.
*/
public List<StackVarInfo> getLocals() {
return locals;
}
/** Returns the list of statements in the function's body. */
public List<Stmt> getStatements() {
return statements;
}
/**
* Returns the function's local symbol table.
*
* @return the function's local symbol table
*/
public SymbolTable<SymbolInfo> getSymbolTable() {
return symbolTable;
}
/**
* Returns the parent function's descriptor for nested functions, and null if this function is
* not nested.
*/
public FuncInfo getParentFuncInfo() {
return parentFuncInfo;
}
/** Emits the function's body. */
public void emitBody() {
emitter.accept(this);
}
}

@ -0,0 +1,28 @@
package chocopy.common.codegen;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.Literal;
/** Code-generation related information about a global variable. */
public class GlobalVarInfo extends VarInfo {
/**
* This variable resides in static storage tagged with LABEL. The label is prepended with "$" to
* prevent name clashes.
*/
protected final Label label;
/**
* A descriptor for a global variable named VARNAME of type VARTYPE whose initial value is
* labeled with INITIALVALUE (null if no initialization value).
*/
public GlobalVarInfo(String varName, ValueType varType, Literal initialValue) {
super(varName, varType, initialValue);
this.label = new Label(String.format("$%s", varName));
}
/** Return the code location of this variable. */
public Label getLabel() {
return label;
}
}

@ -0,0 +1,40 @@
package chocopy.common.codegen;
import java.util.Objects;
/** A label in assembly. */
public class Label {
/** The name of the label. */
public final String labelName;
/** A new label with name LABELNAME. */
public Label(String labelName) {
this.labelName = labelName;
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Label label = (Label) o;
return Objects.equals(labelName, label.labelName);
}
/** {@inheritDoc} */
@Override
public int hashCode() {
return Objects.hash(labelName);
}
/** {@inheritDoc} */
@Override
public String toString() {
return labelName;
}
}

@ -0,0 +1,606 @@
package chocopy.common.codegen;
import java.io.PrintWriter;
import java.io.StringWriter;
/** RISC V assembly-language generation utilities. */
public class RiscVBackend {
/** Accumulator for assembly code output. */
protected final StringWriter asmText = new StringWriter();
/** Allows print, println, and printf of assembly code. */
private final PrintWriter out = new PrintWriter(asmText);
/** The word size in bytes for RISC-V 32-bit. */
protected static final int WORD_SIZE = 4;
/** The RISC-V registers. */
public enum Register {
A0("a0"),
A1("a1"),
A2("a2"),
A3("a3"),
A4("a4"),
A5("a5"),
A6("a6"),
A7("a7"),
T0("t0"),
T1("t1"),
T2("t2"),
T3("t3"),
T4("t4"),
T5("t5"),
T6("t6"),
S1("s1"),
S2("s2"),
S3("s3"),
S4("s4"),
S5("s5"),
S6("s6"),
S7("s7"),
S8("s8"),
S9("s9"),
S10("s10"),
S11("s11"),
FP("fp"),
SP("sp"),
GP("gp"),
RA("ra"),
ZERO("zero");
/** The name of the register used in assembly. */
protected final String name;
/** This register's code representation is NAME. */
Register(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
@Override
public String toString() {
return asmText.toString();
}
/**
* Define @NAME to have the value VALUE. Here, NAME is assumed to be an identifier consisting of
* letters, digits, underscores, and any of the characters '$' or '.', and that does not start
* with a digit. Value may be a numeral or another symbol.
*/
public void defineSym(String name, String value) {
if (name.startsWith("@")) {
emitInsn(String.format(".equiv %s, %s", name, value), null);
} else {
emitInsn(String.format(".equiv @%s, %s", name, value), null);
}
}
/**
* Define @NAME to have the value VALUE, where value is converted to a string. See {@link
* #defineSym(java.lang.String, java.lang.String)}.
*/
public void defineSym(String name, int value) {
defineSym(name, Integer.toString(value));
}
/**
* Returns the word size in bytes.
*
* <p>This method is used instead of directly accessing the static field {@link #WORD_SIZE}, so
* that this class may be extended with alternate word sizes.
*/
public int getWordSize() {
return WORD_SIZE;
}
/** Emit the text STR to the output stream verbatim. STR should have no trailing newline. */
protected void emit(String str) {
out.println(str);
}
/** Emit instruction or directive INSN along with COMMENT as a one-line comment, if non-null. */
public void emitInsn(String insn, String comment) {
if (comment != null) {
emit(String.format(" %-40s # %s", insn, comment));
} else {
emitInsn(insn);
}
}
/** Emit instruction or directive INSN without a comment. */
protected void emitInsn(String insn) {
emit(String.format(" %s", insn));
}
/**
* Emit a local label marker for LABEL with one-line comment COMMENT (null if missing). Invoke
* only once per unique label.
*/
public void emitLocalLabel(Label label, String comment) {
if (comment != null) {
emit(String.format("%-42s # %s", label + ":", comment));
} else {
emit(String.format("%s:", label + ":"));
}
}
/** Emit a global label marker for LABEL. Invoke only once per unique label. */
public void emitGlobalLabel(Label label) {
emit(String.format("\n.globl %s", label));
emit(String.format("%s:", label));
}
/**
* Emit a data word containing VALUE as an integer value. COMMENT is a emitted as a one-line
* comment, if non-null.
*/
public void emitWordLiteral(Integer value, String comment) {
emitInsn(String.format(".word %s", value), comment);
}
/**
* Emit a data word containing the address ADDR, or 0 if LABEL is null. COMMENT is a emitted as
* a one-line comment, if non-null.
*/
public void emitWordAddress(Label addr, String comment) {
if (addr == null) {
emitWordLiteral(0, comment);
} else {
emitInsn(String.format(".word %s", addr), comment);
}
}
/**
* Emit VALUE as an ASCII null-terminated string constant, with COMMENT as its one-line comment,
* if non-null.
*/
public void emitString(String value, String comment) {
String quoted =
value.replace("\\", "\\\\")
.replace("\n", "\\n")
.replace("\t", "\\t")
.replace("\"", "\\\"");
emitInsn(String.format(".string \"%s\"", quoted), comment);
}
/** Mark the start of a data section. */
public void startData() {
emit("\n.data");
}
/** Mark the start of a code/text section. */
public void startCode() {
emit("\n.text");
}
/** Align the next instruction/word in memory to a multiple of 2**POW bytes. */
public void alignNext(int pow) {
emitInsn(String.format(".align %d", pow));
}
/** Emit an ecall instruction, with one-line comment COMMENT, if non-null. */
public void emitEcall(String comment) {
emitInsn("ecall", comment);
}
/**
* Emit a load-address instruction with destination RD and source LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitLA(Register rd, Label label, String comment) {
emitInsn(String.format("la %s, %s", rd, label), comment);
}
/**
* Emit a load-immediate pseudo-op to set RD to IMM. COMMENT is an optional one-line comment
* (null if missing).
*/
public void emitLI(Register rd, Integer imm, String comment) {
emitInsn(String.format("li %s, %d", rd, imm), comment);
}
/**
* Emit a load-upper-immediate instruction to set the upper 20 bits of RD to IMM, where 0 <= IMM
* < 2**20. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLUI(Register rd, Integer imm, String comment) {
emitInsn(String.format("lui %s, %d", rd, imm), comment);
}
/**
* Emit a move instruction to set RD to the contents of RS. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitMV(Register rd, Register rs, String comment) {
emitInsn(String.format("mv %s, %s", rd, rs), comment);
}
/**
* Emit a jump-register (computed jump) instruction to the address in RS. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitJR(Register rs, String comment) {
emitInsn(String.format("jr %s", rs), comment);
}
/**
* Emit a jump (unconditional jump) instruction to LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitJ(Label label, String comment) {
emitInsn(String.format("j %s", label), comment);
}
/**
* Emit a jump-and-link instruction to LABEL. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitJAL(Label label, String comment) {
emitInsn(String.format("jal %s", label), comment);
}
/**
* Emit a computed-jump-and-link instruction to the address in RS. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitJALR(Register rs, String comment) {
emitInsn(String.format("jalr %s", rs), comment);
}
/**
* Emit an add-immediate instruction performing RD = RS + IMM. Requires -2048 <= IMM < 2048.
* COMMENT is an optional one-line comment (null if missing).
*/
public void emitADDI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("addi %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit an add-immediate instruction performing RD = RS + IMM. Here, IMM is a string generally
* containing a symbolic assembler constant (see defineSym) representing an integer value, or an
* expression of the form @NAME+NUM or @NAME-NUM. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitADDI(Register rd, Register rs, String imm, String comment) {
emitInsn(String.format("addi %s, %s, %s", rd, rs, imm), comment);
}
/**
* Emit an add instruction performing RD = RS1 + RS2 mod 2**32. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitADD(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("add %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a subtract instruction performing RD = RS1 - RS2 mod 2**32. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSUB(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sub %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a multiply instruction performing RD = RS1 * RS2 mod 2**32. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitMUL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("mul %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a signed integer divide instruction performing RD = RS1 / RS2 mod 2**32, rounding the
* result toward 0. If RS2 == 0, sets RD to -1. If RS1 == -2**31 and RS2 == -1, sets RD to
* -2**31. COMMENT is an optional one-line comment (null if missing).
*/
public void emitDIV(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("div %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a remainder instruction: RD = RS1 rem RS2 defined so that (RS1 / RS2) * RS2 + (RS1 rem
* RS2) == RS1, where / is as for emitDIV. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitREM(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("rem %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an xor instruction: RD = RS1 ^ RS2. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitXOR(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("xor %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an xor-immediate instruction: RD = RS ^ IMM, where -2048 <= IMM < 2048. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitXORI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("xori %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a bitwise and instruction: RD = RS1 & RS2. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitAND(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("and %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a bitwise and-immediate instruction: RD = RS & IMM, where -2048 <= IMM < 2048. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitANDI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("andi %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a bitwise or instruction: RD = RS1 | RS2. COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitOR(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("or %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a bitwise or-immediate instruction: RD = RS | IMM, where -2048 <= IMM < 2048. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitORI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("ori %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a logical left shift instruction: RD = RS1 << (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSLL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sll %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a logical left shift instruction: RD = RS << (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSLLI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("slli %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a logical right shift instruction: RD = RS1 >>> (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRL(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("srl %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a logical right shift instruction: RD = RS >>> (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRLI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("srli %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit an arithmetic right shift instruction: RD = RS1 >> (RS2 & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRA(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("sra %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit an arithmetic right shift instruction: RD = RS >> (IMM & 0x31). COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitSRAI(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("srai %s, %s, %d", rd, rs, imm), comment);
}
/**
* Emit a load-word instruction: RD = MEMORY[RS + IMM]:4, where -2048 <= IMM < 2048. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitLW(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lw %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a load-word instruction: RD = MEMORY[RS + IMM]:4, where -2048 <= IMM < 2048. Here, IMM
* is symbolic constant expression (see emitADDI). COMMENT is an optional one-line comment (null
* if missing).
*/
public void emitLW(Register rd, Register rs, String imm, String comment) {
emitInsn(String.format("lw %s, %s(%s)", rd, imm, rs), comment);
}
/**
* Emit a store-word instruction: MEMORY[RS1 + IMM]:4 = RS2, where -2048 <= IMM < 2048. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitSW(Register rs2, Register rs1, Integer imm, String comment) {
emitInsn(String.format("sw %s, %d(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a store-word instruction: MEMORY[RS1 + IMM]:4 = RS2, where -2048 <= IMM < 2048. Here,
* IMM is symbolic constant expression (see emitADDI). COMMENT is an optional one-line comment
* (null if missing).
*/
public void emitSW(Register rs2, Register rs1, String imm, String comment) {
emitInsn(String.format("sw %s, %s(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a load-word instruction for globals: RD = MEMORY[LABEL]:4. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitLW(Register rd, Label label, String comment) {
emitInsn(String.format("lw %s, %s", rd, label), comment);
}
/**
* Emit a store-word instruction for globals: MEMORY[LABEL]:4 = RS, using TMP as a temporary
* register. COMMENT is an optional one-line comment (null if missing).
*/
public void emitSW(Register rs, Label label, Register tmp, String comment) {
emitInsn(String.format("sw %s, %s, %s", rs, label, tmp), comment);
}
/**
* Emit a load-byte instruction: RD = MEMORY[RS + IMM]:1, where -2048 <= IMM < 2048. Sign
* extends the byte loaded. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLB(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lb %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a load-byte-unsigned instruction: RD = MEMORY[RS + IMM]:1, where -2048 <= IMM < 2048.
* Zero-extends the byte loaded. COMMENT is an optional one-line comment (null if missing).
*/
public void emitLBU(Register rd, Register rs, Integer imm, String comment) {
emitInsn(String.format("lbu %s, %d(%s)", rd, imm, rs), comment);
}
/**
* Emit a store-byte instruction: MEMORY[RS1 + IMM]:1 = RS2, where -2048 <= IMM < 2048. Assigns
* the low-order byte of RS2 to memory. COMMENT is an optional one-line comment (null if
* missing).
*/
public void emitSB(Register rs2, Register rs1, Integer imm, String comment) {
emitInsn(String.format("sb %s, %d(%s)", rs2, imm, rs1), comment);
}
/**
* Emit a branch-if-equal instruction: if RS1 == RS2 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBEQ(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("beq %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-unequal instruction: if RS1 != RS2 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBNE(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bne %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-greater-or-equal (signed) instruction: if RS1 >= RS2 goto LABEL. COMMENT is
* an optional one-line comment (null if missing).
*/
public void emitBGE(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bge %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-greater-or-equal (unsigned) instruction: if RS1 >= RS2 goto LABEL. COMMENT
* is an optional one-line comment (null if missing).
*/
public void emitBGEU(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bgeu %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-less-than (signed) instruction: if RS1 < RS2 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLT(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("blt %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-less-than (unsigned) instruction: if RS1 < RS2 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLTU(Register rs1, Register rs2, Label label, String comment) {
emitInsn(String.format("bltu %s, %s, %s", rs1, rs2, label), comment);
}
/**
* Emit a branch-if-zero instruction: if RS == 0 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBEQZ(Register rs, Label label, String comment) {
emitInsn(String.format("beqz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-not-zero instruction: if RS != 0 goto LABEL. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitBNEZ(Register rs, Label label, String comment) {
emitInsn(String.format("bnez %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-less-than-zero instruction: if RS < 0 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBLTZ(Register rs, Label label, String comment) {
emitInsn(String.format("bltz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-greater-than-zero instruction: if RS > 0 goto LABEL. COMMENT is an optional
* one-line comment (null if missing).
*/
public void emitBGTZ(Register rs, Label label, String comment) {
emitInsn(String.format("bgtz %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-less-than-equal-to-zero instruction: if RS <= 0 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBLEZ(Register rs, Label label, String comment) {
emitInsn(String.format("blez %s, %s", rs, label), comment);
}
/**
* Emit a branch-if-greater-than-equal-to-zero instruction: if RS >= 0 goto LABEL. COMMENT is an
* optional one-line comment (null if missing).
*/
public void emitBGEZ(Register rs, Label label, String comment) {
emitInsn(String.format("bgez %s, %s", rs, label), comment);
}
/**
* Emit a set-less-than instruction: RD = 1 if RS1 < RS2 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSLT(Register rd, Register rs1, Register rs2, String comment) {
emitInsn(String.format("slt %s, %s, %s", rd, rs1, rs2), comment);
}
/**
* Emit a set-if-zero instruction: RD = 1 if RS == 0 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSEQZ(Register rd, Register rs, String comment) {
emitInsn(String.format("seqz %s, %s", rd, rs), comment);
}
/**
* Emit a set-if-not-zero instruction: RD = 1 if RS != 0 else 0. COMMENT is an optional one-line
* comment (null if missing).
*/
public void emitSNEZ(Register rd, Register rs, String comment) {
emitInsn(String.format("snez %s, %s", rd, rs), comment);
}
}

@ -0,0 +1,27 @@
package chocopy.common.codegen;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.Literal;
/** Code-generation information about a local variable or parameter. */
public class StackVarInfo extends VarInfo {
/** Information about the enclosing function. */
protected final FuncInfo funcInfo;
/**
* A descriptor for a local variable or parameter VARNAME of type VARTYPE, whose initial value
* is given by INITIALVALUE (null if no initial value), and which is nested immediately within
* the function described by FUNCINFO.
*/
public StackVarInfo(
String varName, ValueType varType, Literal initialValue, FuncInfo funcInfo) {
super(varName, varType, initialValue);
this.funcInfo = funcInfo;
}
/** Returns the descriptor of the function in which this var is defined. */
public FuncInfo getFuncInfo() {
return funcInfo;
}
}

@ -0,0 +1,7 @@
package chocopy.common.codegen;
/**
* Abstract base class for all the Info classes that store information about a symbol during code
* generation.
*/
public abstract class SymbolInfo {}

@ -0,0 +1,40 @@
package chocopy.common.codegen;
import chocopy.common.analysis.types.ValueType;
import chocopy.common.astnodes.Literal;
/** Information about a variable or attribute. */
public abstract class VarInfo extends SymbolInfo {
/** Name of variable or attribute. */
protected final String varName;
/** Runtime location of initial value for this variable or attribute. */
protected final Literal initialValue;
/** Static type of the variable. */
protected final ValueType varType;
/**
* A descriptor for variable or attribute VARNAME with VARTYPE as its static type and
* INITIALVALUE as its initial value (or null if None).
*/
public VarInfo(String varName, ValueType varType, Literal initialValue) {
this.varName = varName;
this.varType = varType;
this.initialValue = initialValue;
}
/** Returns the name of this variable or attribute. */
public String getVarName() {
return varName;
}
/** Returns the type of this variable or attribute. */
public ValueType getVarType() {
return varType;
}
/** Returns the initial value of this variable or attribute. */
public Literal getInitialValue() {
return initialValue;
}
}

@ -0,0 +1,187 @@
package chocopy.pa3;
import chocopy.common.analysis.AbstractNodeAnalyzer;
import chocopy.common.analysis.SymbolTable;
import chocopy.common.astnodes.ReturnStmt;
import chocopy.common.astnodes.Stmt;
import chocopy.common.codegen.*;
import java.util.List;
import static chocopy.common.codegen.RiscVBackend.Register.*;
/**
* This is where the main implementation of PA3 will live.
*
* <p>A large part of the functionality has already been implemented in the base class, CodeGenBase.
* Make sure to read through that class, since you will want to use many of its fields and utility
* methods in this class when emitting code.
*
* <p>Also read the PDF spec for details on what the base class does and what APIs it exposes for
* its sub-class (this one). Of particular importance is knowing what all the SymbolInfo classes
* contain.
*/
public class CodeGenImpl extends CodeGenBase {
/** A code generator emitting instructions to BACKEND. */
public CodeGenImpl(RiscVBackend backend) {
super(backend);
}
/** Operation on None. */
private final Label errorNone = new Label("error.None");
/** Division by zero. */
private final Label errorDiv = new Label("error.Div");
/** Index out of bounds. */
private final Label errorOob = new Label("error.OOB");
/**
* Emits the top level of the program.
*
* <p>This method is invoked exactly once, and is surrounded by some boilerplate code that: (1)
* initializes the heap before the top-level begins and (2) exits after the top-level ends.
*
* <p>You only need to generate code for statements.
*
* @param statements top level statements
*/
protected void emitTopLevel(List<Stmt> statements) {
StmtAnalyzer stmtAnalyzer = new StmtAnalyzer(null);
backend.emitADDI(
SP, SP, -2 * backend.getWordSize(), "Saved FP and saved RA (unused at top level).");
backend.emitSW(ZERO, SP, 0, "Top saved FP is 0.");
backend.emitSW(ZERO, SP, 4, "Top saved RA is 0.");
backend.emitADDI(FP, SP, 2 * backend.getWordSize(), "Set FP to previous SP.");
for (Stmt stmt : statements) {
stmt.dispatch(stmtAnalyzer);
}
backend.emitLI(A0, EXIT_ECALL, "Code for ecall: exit");
backend.emitEcall(null);
}
/**
* Emits the code for a function described by FUNCINFO.
*
* <p>This method is invoked once per function and method definition. At the code generation
* stage, nested functions are emitted as separate functions of their own. So if function `bar`
* is nested within function `foo`, you only emit `foo`'s code for `foo` and only emit `bar`'s
* code for `bar`.
*/
protected void emitUserDefinedFunction(FuncInfo funcInfo) {
backend.emitGlobalLabel(funcInfo.getCodeLabel());
StmtAnalyzer stmtAnalyzer = new StmtAnalyzer(funcInfo);
for (Stmt stmt : funcInfo.getStatements()) {
stmt.dispatch(stmtAnalyzer);
}
backend.emitMV(A0, ZERO, "Returning None implicitly");
backend.emitLocalLabel(stmtAnalyzer.epilogue, "Epilogue");
// FIXME: {... reset fp etc. ...}
backend.emitJR(RA, "Return to caller");
}
/** An analyzer that encapsulates code generation for statements. */
private class StmtAnalyzer extends AbstractNodeAnalyzer<Void> {
/*
* The symbol table has all the info you need to determine
* what a given identifier 'x' in the current scope is. You can
* use it as follows:
* SymbolInfo x = sym.get("x");
*
* A SymbolInfo can be one the following:
* - ClassInfo: a descriptor for classes
* - FuncInfo: a descriptor for functions/methods
* - AttrInfo: a descriptor for attributes
* - GlobalVarInfo: a descriptor for global variables
* - StackVarInfo: a descriptor for variables allocated on the stack,
* such as locals and parameters
*
* Since the input program is assumed to be semantically
* valid and well-typed at this stage, you can always assume that
* the symbol table contains valid information. For example, in
* an expression `foo()` you KNOW that sym.get("foo") will either be
* a FuncInfo or ClassInfo, but not any of the other infos
* and never null.
*
* The symbol table in funcInfo has already been populated in
* the base class: CodeGenBase. You do not need to add anything to
* the symbol table. Simply query it with an identifier name to
* get a descriptor for a function, class, variable, etc.
*
* The symbol table also maps nonlocal and global vars, so you
* only need to lookup one symbol table and it will fetch the
* appropriate info for the var that is currently in scope.
*/
/** Symbol table for my statements. */
private final SymbolTable<SymbolInfo> sym;
/** Label of code that exits from procedure. */
protected final Label epilogue;
/** The descriptor for the current function, or null at the top level. */
private final FuncInfo funcInfo;
/** An analyzer for the function described by FUNCINFO0, which is null for the top level. */
StmtAnalyzer(FuncInfo funcInfo0) {
funcInfo = funcInfo0;
if (funcInfo == null) {
sym = globalSymbols;
} else {
sym = funcInfo.getSymbolTable();
}
epilogue = generateLocalLabel();
}
// FIXME: Example of statement.
@Override
public Void analyze(ReturnStmt stmt) {
// FIXME: Here, we emit an instruction that does nothing. Clearly,
// this is wrong, and you'll have to fix it.
// This is here just to demonstrate how to emit a
// RISC-V instruction.
backend.emitMV(ZERO, ZERO, "No-op");
return null;
}
// FIXME: More, of course.
}
/**
* Emits custom code in the CODE segment.
*
* <p>This method is called after emitting the top level and the function bodies for each
* function.
*
* <p>You can use this method to emit anything you want outside of the top level or functions,
* e.g. custom routines that you may want to call from within your code to do common tasks. This
* is not strictly needed. You might not modify this at all and still complete the assignment.
*
* <p>To start you off, here is an implementation of three routines that will be commonly needed
* from within the code you will generate for statements.
*
* <p>The routines are error handlers for operations on None, index out of bounds, and division
* by zero. They never return to their caller. Just jump to one of these routines to throw an
* error and exit the program. For example, to throw an OOB error: backend.emitJ(errorOob, "Go
* to out-of-bounds error and abort");
*/
protected void emitCustomCode() {
emitErrorFunc(errorNone, "Operation on None");
emitErrorFunc(errorDiv, "Division by zero");
emitErrorFunc(errorOob, "Index out of bounds");
}
/** Emit an error routine labeled ERRLABEL that aborts with message MSG. */
private void emitErrorFunc(Label errLabel, String msg) {
backend.emitGlobalLabel(errLabel);
backend.emitLI(A0, ERROR_NONE, "Exit code for: " + msg);
backend.emitLA(A1, constants.getStrConstant(msg), "Load error message as str");
backend.emitADDI(
A1, A1, getAttrOffset(strClass, "__str__"), "Load address of attribute __str__");
backend.emitJ(abortLabel, "Abort");
}
}

@ -0,0 +1,34 @@
package chocopy.pa3;
import chocopy.common.astnodes.Program;
import chocopy.common.codegen.CodeGenBase;
import chocopy.common.codegen.RiscVBackend;
/** Interface to code generator. */
public class StudentCodeGen {
/**
* Perform code generation from PROGRAM, assumed to be well-typed, to RISC-V, returning the
* assembly code. DEBUG iff --debug was on the command line.
*/
public static String process(Program program, boolean debug) {
/* Emit code into a ByteOutputStream, and convert to a string.
* If you need instructions not provided by RiscVBackend, simply
* use an extension of it. */
try {
RiscVBackend backend = new RiscVBackend();
CodeGenBase cgen = new CodeGenImpl(backend);
cgen.generate(program);
return backend.toString();
} catch (IllegalStateException | IllegalArgumentException e) {
System.err.println(
"Error performing code generation. "
+ "Re-run with --debug to see stack trace.");
if (debug) {
e.printStackTrace();
}
return null;
}
}
}

@ -0,0 +1,25 @@
# Compute x**y
def exp(x: int, y: int) -> int:
a: int = 0
def f(i: int) -> int:
nonlocal a
def geta() -> int:
return a
if i <= 0:
return geta()
else:
a = a * x
return f(i-1)
a = 1
return f(y)
# Input parameter
n:int = 42
# Run [0, n]
i:int = 0
# Crunch
while i <= n:
print(exp(2, i % 31))
i = i + 1

@ -0,0 +1,562 @@
{
"kind" : "Program",
"location" : [ 2, 1, 26, 1 ],
"declarations" : [ {
"kind" : "FuncDef",
"location" : [ 2, 1, 14, 13 ],
"name" : {
"kind" : "Identifier",
"location" : [ 2, 5, 2, 7 ],
"name" : "exp"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 2, 9, 2, 14 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 2, 9, 2, 9 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 2, 12, 2, 14 ],
"className" : "int"
}
}, {
"kind" : "TypedVar",
"location" : [ 2, 17, 2, 22 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 2, 17, 2, 17 ],
"name" : "y"
},
"type" : {
"kind" : "ClassType",
"location" : [ 2, 20, 2, 22 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 2, 28, 2, 30 ],
"className" : "int"
},
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 3, 2, 3, 11 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 3, 2, 3, 7 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 3, 2, 3, 2 ],
"name" : "a"
},
"type" : {
"kind" : "ClassType",
"location" : [ 3, 5, 3, 7 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 3, 11, 3, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
}, {
"kind" : "FuncDef",
"location" : [ 4, 2, 13, 1 ],
"name" : {
"kind" : "Identifier",
"location" : [ 4, 6, 4, 6 ],
"name" : "f"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 4, 8, 4, 13 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 4, 8, 4, 8 ],
"name" : "i"
},
"type" : {
"kind" : "ClassType",
"location" : [ 4, 11, 4, 13 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 4, 19, 4, 21 ],
"className" : "int"
},
"declarations" : [ {
"kind" : "NonLocalDecl",
"location" : [ 5, 3, 5, 12 ],
"variable" : {
"kind" : "Identifier",
"location" : [ 5, 12, 5, 12 ],
"name" : "a"
}
}, {
"kind" : "FuncDef",
"location" : [ 6, 3, 7, 12 ],
"name" : {
"kind" : "Identifier",
"location" : [ 6, 7, 6, 10 ],
"name" : "geta"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 6, 17, 6, 19 ],
"className" : "int"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ReturnStmt",
"location" : [ 7, 4, 7, 11 ],
"value" : {
"kind" : "Identifier",
"location" : [ 7, 11, 7, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "a"
}
} ]
} ],
"statements" : [ {
"kind" : "IfStmt",
"location" : [ 8, 3, 13, 1 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 8, 6, 8, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "Identifier",
"location" : [ 8, 6, 8, 6 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "<=",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 8, 11, 8, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
},
"thenBody" : [ {
"kind" : "ReturnStmt",
"location" : [ 9, 4, 9, 16 ],
"value" : {
"kind" : "CallExpr",
"location" : [ 9, 11, 9, 16 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 9, 11, 9, 14 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "geta"
},
"args" : [ ]
}
} ],
"elseBody" : [ {
"kind" : "AssignStmt",
"location" : [ 11, 4, 11, 12 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 11, 4, 11, 4 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "a"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 11, 8, 11, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 11, 8, 11, 8 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "a"
},
"operator" : "*",
"right" : {
"kind" : "Identifier",
"location" : [ 11, 12, 11, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
}
}
}, {
"kind" : "ReturnStmt",
"location" : [ 12, 4, 12, 16 ],
"value" : {
"kind" : "CallExpr",
"location" : [ 12, 11, 12, 16 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 12, 11, 12, 11 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "f"
},
"args" : [ {
"kind" : "BinaryExpr",
"location" : [ 12, 13, 12, 15 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 12, 13, 12, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "-",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 12, 15, 12, 15 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
} ]
}
} ]
} ]
} ],
"statements" : [ {
"kind" : "AssignStmt",
"location" : [ 13, 2, 13, 6 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 13, 2, 13, 2 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "a"
} ],
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 13, 6, 13, 6 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}, {
"kind" : "ReturnStmt",
"location" : [ 14, 2, 14, 12 ],
"value" : {
"kind" : "CallExpr",
"location" : [ 14, 9, 14, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 14, 9, 14, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "f"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 14, 11, 14, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "y"
} ]
}
} ]
}, {
"kind" : "VarDef",
"location" : [ 17, 1, 17, 10 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 17, 1, 17, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 17, 1, 17, 1 ],
"name" : "n"
},
"type" : {
"kind" : "ClassType",
"location" : [ 17, 3, 17, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 17, 9, 17, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 42
}
}, {
"kind" : "VarDef",
"location" : [ 20, 1, 20, 9 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 20, 1, 20, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 20, 1, 20, 1 ],
"name" : "i"
},
"type" : {
"kind" : "ClassType",
"location" : [ 20, 3, 20, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 20, 9, 20, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
} ],
"statements" : [ {
"kind" : "WhileStmt",
"location" : [ 23, 1, 26, 1 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 23, 7, 23, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "Identifier",
"location" : [ 23, 7, 23, 7 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "<=",
"right" : {
"kind" : "Identifier",
"location" : [ 23, 12, 23, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "n"
}
},
"body" : [ {
"kind" : "ExprStmt",
"location" : [ 24, 2, 24, 22 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 24, 2, 24, 22 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 24, 2, 24, 6 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "CallExpr",
"location" : [ 24, 8, 24, 21 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 24, 8, 24, 10 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
}, {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "exp"
},
"args" : [ {
"kind" : "IntegerLiteral",
"location" : [ 24, 12, 24, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 2
}, {
"kind" : "BinaryExpr",
"location" : [ 24, 15, 24, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 24, 15, 24, 15 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "%",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 24, 19, 24, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 31
}
} ]
} ]
}
}, {
"kind" : "AssignStmt",
"location" : [ 25, 2, 25, 10 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 25, 2, 25, 2 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 25, 6, 25, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 25, 6, 25, 6 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "+",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 25, 10, 25, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}
} ]
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,43 @@
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
1
2
4
8
16
32
64
128
256
512
1024
2048

@ -0,0 +1,30 @@
# Get the n-th prime starting from 2
def get_prime(n:int) -> int:
candidate:int = 2
found:int = 0
while True:
if is_prime(candidate):
found = found + 1
if found == n:
return candidate
candidate = candidate + 1
return 0 # Never happens
def is_prime(x:int) -> bool:
div:int = 2
while div < x:
if x % div == 0:
return False
div = div + 1
return True
# Input parameter
n:int = 15
# Run [1, n]
i:int = 1
# Crunch
while i <= n:
print(get_prime(i))
i = i + 1

@ -0,0 +1,658 @@
{
"kind" : "Program",
"location" : [ 2, 1, 31, 1 ],
"declarations" : [ {
"kind" : "FuncDef",
"location" : [ 2, 1, 11, 29 ],
"name" : {
"kind" : "Identifier",
"location" : [ 2, 5, 2, 13 ],
"name" : "get_prime"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 2, 15, 2, 19 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 2, 15, 2, 15 ],
"name" : "n"
},
"type" : {
"kind" : "ClassType",
"location" : [ 2, 17, 2, 19 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 2, 25, 2, 27 ],
"className" : "int"
},
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 3, 5, 3, 21 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 3, 5, 3, 17 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 3, 5, 3, 13 ],
"name" : "candidate"
},
"type" : {
"kind" : "ClassType",
"location" : [ 3, 15, 3, 17 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 3, 21, 3, 21 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 2
}
}, {
"kind" : "VarDef",
"location" : [ 4, 5, 4, 17 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 4, 5, 4, 13 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 4, 5, 4, 9 ],
"name" : "found"
},
"type" : {
"kind" : "ClassType",
"location" : [ 4, 11, 4, 13 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 4, 17, 4, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
} ],
"statements" : [ {
"kind" : "WhileStmt",
"location" : [ 5, 5, 11, 4 ],
"condition" : {
"kind" : "BooleanLiteral",
"location" : [ 5, 11, 5, 14 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"value" : true
},
"body" : [ {
"kind" : "IfStmt",
"location" : [ 6, 9, 10, 8 ],
"condition" : {
"kind" : "CallExpr",
"location" : [ 6, 12, 6, 30 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"function" : {
"kind" : "Identifier",
"location" : [ 6, 12, 6, 19 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "bool"
}
},
"name" : "is_prime"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 6, 21, 6, 29 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "candidate"
} ]
},
"thenBody" : [ {
"kind" : "AssignStmt",
"location" : [ 7, 13, 7, 29 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 7, 13, 7, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "found"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 7, 21, 7, 29 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 7, 21, 7, 25 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "found"
},
"operator" : "+",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 7, 29, 7, 29 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}
}, {
"kind" : "IfStmt",
"location" : [ 8, 13, 10, 8 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 8, 16, 8, 25 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "Identifier",
"location" : [ 8, 16, 8, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "found"
},
"operator" : "==",
"right" : {
"kind" : "Identifier",
"location" : [ 8, 25, 8, 25 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "n"
}
},
"thenBody" : [ {
"kind" : "ReturnStmt",
"location" : [ 9, 17, 9, 32 ],
"value" : {
"kind" : "Identifier",
"location" : [ 9, 24, 9, 32 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "candidate"
}
} ],
"elseBody" : [ ]
} ],
"elseBody" : [ ]
}, {
"kind" : "AssignStmt",
"location" : [ 10, 9, 10, 33 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 10, 9, 10, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "candidate"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 10, 21, 10, 33 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 10, 21, 10, 29 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "candidate"
},
"operator" : "+",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 10, 33, 10, 33 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}
} ]
}, {
"kind" : "ReturnStmt",
"location" : [ 11, 5, 11, 12 ],
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 11, 12, 11, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 13, 1, 19, 16 ],
"name" : {
"kind" : "Identifier",
"location" : [ 13, 5, 13, 12 ],
"name" : "is_prime"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 13, 14, 13, 18 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 13, 14, 13, 14 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 13, 16, 13, 18 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 13, 24, 13, 27 ],
"className" : "bool"
},
"declarations" : [ {
"kind" : "VarDef",
"location" : [ 14, 5, 14, 15 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 14, 5, 14, 11 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 14, 5, 14, 7 ],
"name" : "div"
},
"type" : {
"kind" : "ClassType",
"location" : [ 14, 9, 14, 11 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 14, 15, 14, 15 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 2
}
} ],
"statements" : [ {
"kind" : "WhileStmt",
"location" : [ 15, 5, 19, 4 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 15, 11, 15, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "Identifier",
"location" : [ 15, 11, 15, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "div"
},
"operator" : "<",
"right" : {
"kind" : "Identifier",
"location" : [ 15, 17, 15, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
}
},
"body" : [ {
"kind" : "IfStmt",
"location" : [ 16, 9, 18, 8 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 16, 12, 16, 23 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "BinaryExpr",
"location" : [ 16, 12, 16, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 16, 12, 16, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
},
"operator" : "%",
"right" : {
"kind" : "Identifier",
"location" : [ 16, 16, 16, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "div"
}
},
"operator" : "==",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 16, 23, 16, 23 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
},
"thenBody" : [ {
"kind" : "ReturnStmt",
"location" : [ 17, 13, 17, 24 ],
"value" : {
"kind" : "BooleanLiteral",
"location" : [ 17, 20, 17, 24 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"value" : false
}
} ],
"elseBody" : [ ]
}, {
"kind" : "AssignStmt",
"location" : [ 18, 9, 18, 21 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 18, 9, 18, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "div"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 18, 15, 18, 21 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 18, 15, 18, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "div"
},
"operator" : "+",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 18, 21, 18, 21 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}
} ]
}, {
"kind" : "ReturnStmt",
"location" : [ 19, 5, 19, 15 ],
"value" : {
"kind" : "BooleanLiteral",
"location" : [ 19, 12, 19, 15 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"value" : true
}
} ]
}, {
"kind" : "VarDef",
"location" : [ 22, 1, 22, 10 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 22, 1, 22, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 22, 1, 22, 1 ],
"name" : "n"
},
"type" : {
"kind" : "ClassType",
"location" : [ 22, 3, 22, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 22, 9, 22, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 15
}
}, {
"kind" : "VarDef",
"location" : [ 25, 1, 25, 9 ],
"var" : {
"kind" : "TypedVar",
"location" : [ 25, 1, 25, 5 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 25, 1, 25, 1 ],
"name" : "i"
},
"type" : {
"kind" : "ClassType",
"location" : [ 25, 3, 25, 5 ],
"className" : "int"
}
},
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 25, 9, 25, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
} ],
"statements" : [ {
"kind" : "WhileStmt",
"location" : [ 28, 1, 31, 1 ],
"condition" : {
"kind" : "BinaryExpr",
"location" : [ 28, 7, 28, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "bool"
},
"left" : {
"kind" : "Identifier",
"location" : [ 28, 7, 28, 7 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "<=",
"right" : {
"kind" : "Identifier",
"location" : [ 28, 12, 28, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "n"
}
},
"body" : [ {
"kind" : "ExprStmt",
"location" : [ 29, 5, 29, 23 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 29, 5, 29, 23 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 29, 5, 29, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "CallExpr",
"location" : [ 29, 11, 29, 22 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 29, 11, 29, 19 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "get_prime"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 29, 21, 29, 21 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
} ]
} ]
}
}, {
"kind" : "AssignStmt",
"location" : [ 30, 5, 30, 13 ],
"targets" : [ {
"kind" : "Identifier",
"location" : [ 30, 5, 30, 5 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
} ],
"value" : {
"kind" : "BinaryExpr",
"location" : [ 30, 9, 30, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "Identifier",
"location" : [ 30, 9, 30, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "i"
},
"operator" : "+",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 30, 13, 30, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}
}
} ]
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,15 @@
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47

@ -0,0 +1,107 @@
# A resizable list of integers
class Vector(object):
items: [int] = None
size: int = 0
def __init__(self:"Vector"):
self.items = [0]
# Returns current capacity
def capacity(self:"Vector") -> int:
return len(self.items)
# Increases capacity of vector by one element
def increase_capacity(self:"Vector") -> int:
self.items = self.items + [0]
return self.capacity()
# Appends one item to end of vector
def append(self:"Vector", item: int) -> object:
if self.size == self.capacity():
self.increase_capacity()
self.items[self.size] = item
self.size = self.size + 1
# Appends many items to end of vector
def append_all(self:"Vector", new_items: [int]) -> object:
item:int = 0
for item in new_items:
self.append(item)
# Removes an item from the middle of vector
def remove_at(self:"Vector", idx: int) -> object:
if idx < 0:
return
while idx < self.size - 1:
self.items[idx] = self.items[idx + 1]
idx = idx + 1
self.size = self.size - 1
# Retrieves an item at a given index
def get(self:"Vector", idx: int) -> int:
return self.items[idx]
# Retrieves the current size of the vector
def length(self:"Vector") -> int:
return self.size
# A faster (but more memory-consuming) implementation of vector
class DoublingVector(Vector):
doubling_limit:int = 1000
# Overriding to do fewer resizes
def increase_capacity(self:"DoublingVector") -> int:
if (self.capacity() <= self.doubling_limit // 2):
self.items = self.items + self.items
else:
# If doubling limit has been reached, fall back to
# standard capacity increases
self.items = self.items + [0]
return self.capacity()
# Makes a vector in the range [i, j)
def vrange(i:int, j:int) -> Vector:
v:Vector = None
v = DoublingVector()
while i < j:
v.append(i)
i = i + 1
return v
# Sieve of Eratosthenes (not really)
def sieve(v:Vector) -> object:
i:int = 0
j:int = 0
k:int = 0
while i < v.length():
k = v.get(i)
j = i + 1
while j < v.length():
if v.get(j) % k == 0:
v.remove_at(j)
else:
j = j + 1
i = i + 1
# Input parameter
n:int = 50
# Data
v:Vector = None
i:int = 0
# Crunch
v = vrange(2, n)
sieve(v)
# Print
while i < v.length():
print(v.get(i))
i = i + 1

File diff suppressed because it is too large Load Diff

@ -0,0 +1,15 @@
2
3
5
7
11
13
17
19
23
29
31
37
41
43
47

@ -0,0 +1,77 @@
# ChocoPy library functions
def int_to_str(x: int) -> str:
digits:[str] = None
result:str = ""
# Set-up digit mapping
digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
# Write sign if necessary
if x < 0:
result = "-"
x = -x
# Write digits using a recursive call
if x >= 10:
result = result + int_to_str(x // 10)
result = result + digits[x % 10]
return result
def str_to_int(x: str) -> int:
result:int = 0
digit:int = 0
char:str = ""
sign:int = 1
first_char:bool = True
# Parse digits
for char in x:
if char == "-":
if not first_char:
return 0 # Error
sign = -1
elif char == "0":
digit = 0
elif char == "1":
digit = 1
elif char == "2":
digit = 2
elif char == "3":
digit = 3
elif char == "3":
digit = 3
elif char == "4":
digit = 4
elif char == "5":
digit = 5
elif char == "6":
digit = 6
elif char == "7":
digit = 7
elif char == "8":
digit = 8
elif char == "9":
digit = 9
else:
return 0 # On error
first_char = False
result = result * 10 + digit
# Compute result
return result * sign
# Input parameters
c:int = 42
n:int = 10
# Run [-nc, nc] with step size c
s:str = ""
i:int = 0
i = -n * c
# Crunch
while i <= n * c:
s = int_to_str(i)
print(s)
i = str_to_int(s) + c

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
-420
-378
-336
-294
-252
-210
-168
-126
-84
-42
0
42
84
126
168
210
252
294
336
378
420

@ -0,0 +1,83 @@
# Binary-search trees
class TreeNode(object):
value:int = 0
left:"TreeNode" = None
right:"TreeNode" = None
def insert(self:"TreeNode", x:int) -> bool:
if x < self.value:
if self.left is None:
self.left = makeNode(x)
return True
else:
return self.left.insert(x)
elif x > self.value:
if self.right is None:
self.right = makeNode(x)
return True
else:
return self.right.insert(x)
return False
def contains(self:"TreeNode", x:int) -> bool:
if x < self.value:
if self.left is None:
return False
else:
return self.left.contains(x)
elif x > self.value:
if self.right is None:
return False
else:
return self.right.contains(x)
else:
return True
class Tree(object):
root:TreeNode = None
size:int = 0
def insert(self:"Tree", x:int) -> object:
if self.root is None:
self.root = makeNode(x)
self.size = 1
else:
if self.root.insert(x):
self.size = self.size + 1
def contains(self:"Tree", x:int) -> bool:
if self.root is None:
return False
else:
return self.root.contains(x)
def makeNode(x: int) -> TreeNode:
b:TreeNode = None
b = TreeNode()
b.value = x
return b
# Input parameters
n:int = 100
c:int = 4
# Data
t:Tree = None
i:int = 0
k:int = 37813
# Crunch
t = Tree()
while i < n:
t.insert(k)
k = (k * 37813) % 37831
if i % c != 0:
t.insert(i)
i = i + 1
print(t.size)
for i in [4, 8, 15, 16, 23, 42]:
if t.contains(i):
print(i)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,17 @@
def f() -> int:
print("start f")
g()
print("end f")
return 42
def g() -> object:
print("start g")
h()
print("end g")
def h() -> object:
print("start h")
print("end h")
print(f())

@ -0,0 +1,386 @@
{
"kind" : "Program",
"location" : [ 1, 1, 17, 11 ],
"declarations" : [ {
"kind" : "FuncDef",
"location" : [ 1, 1, 5, 14 ],
"name" : {
"kind" : "Identifier",
"location" : [ 1, 5, 1, 5 ],
"name" : "f"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 1, 12, 1, 14 ],
"className" : "int"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 2, 5, 2, 20 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 2, 5, 2, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 2, 5, 2, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 2, 11, 2, 19 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "start f"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 3, 5, 3, 7 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 3, 5, 3, 7 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "object"
},
"function" : {
"kind" : "Identifier",
"location" : [ 3, 5, 3, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "object"
}
},
"name" : "g"
},
"args" : [ ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 4, 5, 4, 18 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 4, 5, 4, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 4, 5, 4, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 4, 11, 4, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "end f"
} ]
}
}, {
"kind" : "ReturnStmt",
"location" : [ 5, 5, 5, 13 ],
"value" : {
"kind" : "IntegerLiteral",
"location" : [ 5, 12, 5, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 42
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 8, 1, 11, 19 ],
"name" : {
"kind" : "Identifier",
"location" : [ 8, 5, 8, 5 ],
"name" : "g"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 8, 12, 8, 17 ],
"className" : "object"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 9, 5, 9, 20 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 9, 5, 9, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 9, 5, 9, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 9, 11, 9, 19 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "start g"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 10, 5, 10, 7 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 10, 5, 10, 7 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "object"
},
"function" : {
"kind" : "Identifier",
"location" : [ 10, 5, 10, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "object"
}
},
"name" : "h"
},
"args" : [ ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 11, 5, 11, 18 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 11, 5, 11, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 11, 5, 11, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 11, 11, 11, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "end g"
} ]
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 13, 1, 15, 19 ],
"name" : {
"kind" : "Identifier",
"location" : [ 13, 5, 13, 5 ],
"name" : "h"
},
"params" : [ ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 13, 12, 13, 17 ],
"className" : "object"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 14, 5, 14, 20 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 14, 5, 14, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 14, 5, 14, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 14, 11, 14, 19 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "start h"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 15, 5, 15, 18 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 15, 5, 15, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 15, 5, 15, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 15, 11, 15, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "end h"
} ]
}
} ]
} ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 17, 1, 17, 10 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 17, 1, 17, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 17, 1, 17, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "CallExpr",
"location" : [ 17, 7, 17, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 17, 7, 17, 7 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "f"
},
"args" : [ ]
} ]
}
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,7 @@
start f
start g
start h
end h
end g
end f
42

@ -0,0 +1,19 @@
def f(x:int) -> int:
print("start f")
print(x)
g(1, x)
print("end f")
return x
def g(y:int, z:int) -> object:
print("start g")
print(y)
print(z)
h("h")
print("end g")
def h(msg: str) -> object:
print(msg)
print(f(4))

@ -0,0 +1,554 @@
{
"kind" : "Program",
"location" : [ 1, 1, 19, 12 ],
"declarations" : [ {
"kind" : "FuncDef",
"location" : [ 1, 1, 6, 13 ],
"name" : {
"kind" : "Identifier",
"location" : [ 1, 5, 1, 5 ],
"name" : "f"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 1, 7, 1, 11 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 1, 7, 1, 7 ],
"name" : "x"
},
"type" : {
"kind" : "ClassType",
"location" : [ 1, 9, 1, 11 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 1, 17, 1, 19 ],
"className" : "int"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 2, 5, 2, 20 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 2, 5, 2, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 2, 5, 2, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 2, 11, 2, 19 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "start f"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 3, 5, 3, 12 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 3, 5, 3, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 3, 5, 3, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 3, 11, 3, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 4, 5, 4, 11 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 4, 5, 4, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "object"
},
"function" : {
"kind" : "Identifier",
"location" : [ 4, 5, 4, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
}, {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "object"
}
},
"name" : "g"
},
"args" : [ {
"kind" : "IntegerLiteral",
"location" : [ 4, 7, 4, 7 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 1
}, {
"kind" : "Identifier",
"location" : [ 4, 10, 4, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 5, 5, 5, 18 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 5, 5, 5, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 5, 5, 5, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 5, 11, 5, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "end f"
} ]
}
}, {
"kind" : "ReturnStmt",
"location" : [ 6, 5, 6, 12 ],
"value" : {
"kind" : "Identifier",
"location" : [ 6, 12, 6, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "x"
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 9, 1, 14, 19 ],
"name" : {
"kind" : "Identifier",
"location" : [ 9, 5, 9, 5 ],
"name" : "g"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 9, 7, 9, 11 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 9, 7, 9, 7 ],
"name" : "y"
},
"type" : {
"kind" : "ClassType",
"location" : [ 9, 9, 9, 11 ],
"className" : "int"
}
}, {
"kind" : "TypedVar",
"location" : [ 9, 14, 9, 18 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 9, 14, 9, 14 ],
"name" : "z"
},
"type" : {
"kind" : "ClassType",
"location" : [ 9, 16, 9, 18 ],
"className" : "int"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 9, 24, 9, 29 ],
"className" : "object"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 10, 5, 10, 20 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 10, 5, 10, 20 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 10, 5, 10, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 10, 11, 10, 19 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "start g"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 11, 5, 11, 12 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 11, 5, 11, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 11, 5, 11, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 11, 11, 11, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "y"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 12, 5, 12, 12 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 12, 5, 12, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 12, 5, 12, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 12, 11, 12, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"name" : "z"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 13, 5, 13, 10 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 13, 5, 13, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "object"
},
"function" : {
"kind" : "Identifier",
"location" : [ 13, 5, 13, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "str"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "object"
}
},
"name" : "h"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 13, 7, 13, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "h"
} ]
}
}, {
"kind" : "ExprStmt",
"location" : [ 14, 5, 14, 18 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 14, 5, 14, 18 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 14, 5, 14, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "StringLiteral",
"location" : [ 14, 11, 14, 17 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"value" : "end g"
} ]
}
} ]
}, {
"kind" : "FuncDef",
"location" : [ 16, 1, 17, 15 ],
"name" : {
"kind" : "Identifier",
"location" : [ 16, 5, 16, 5 ],
"name" : "h"
},
"params" : [ {
"kind" : "TypedVar",
"location" : [ 16, 7, 16, 14 ],
"identifier" : {
"kind" : "Identifier",
"location" : [ 16, 7, 16, 9 ],
"name" : "msg"
},
"type" : {
"kind" : "ClassType",
"location" : [ 16, 12, 16, 14 ],
"className" : "str"
}
} ],
"returnType" : {
"kind" : "ClassType",
"location" : [ 16, 20, 16, 25 ],
"className" : "object"
},
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 17, 5, 17, 14 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 17, 5, 17, 14 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 17, 5, 17, 9 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "Identifier",
"location" : [ 17, 11, 17, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "str"
},
"name" : "msg"
} ]
}
} ]
} ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 19, 1, 19, 11 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 19, 1, 19, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 19, 1, 19, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "CallExpr",
"location" : [ 19, 7, 19, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"function" : {
"kind" : "Identifier",
"location" : [ 19, 7, 19, 7 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "int"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "int"
}
},
"name" : "f"
},
"args" : [ {
"kind" : "IntegerLiteral",
"location" : [ 19, 9, 19, 9 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 4
} ]
} ]
}
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,9 @@
start f
4
start g
1
4
h
end g
end f
4

@ -0,0 +1,65 @@
{
"kind" : "Program",
"location" : [ 1, 1, 1, 15 ],
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 1, 1, 1, 14 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 1, 1, 1, 14 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "BinaryExpr",
"location" : [ 1, 7, 1, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 7, 1, 8 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 42
},
"operator" : "//",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 13, 1, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
} ]
}
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,2 @@
Division by zero
Exited with error code 2

@ -0,0 +1,46 @@
{
"kind" : "Program",
"location" : [ 1, 1, 1, 12 ],
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 1, 1, 1, 11 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 1, 1, 1, 11 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "NoneLiteral",
"location" : [ 1, 7, 1, 10 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
} ]
}
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

@ -0,0 +1,2 @@
Invalid argument
Exited with error code 1

@ -0,0 +1,65 @@
{
"kind" : "Program",
"location" : [ 1, 1, 1, 14 ],
"declarations" : [ ],
"statements" : [ {
"kind" : "ExprStmt",
"location" : [ 1, 1, 1, 13 ],
"expr" : {
"kind" : "CallExpr",
"location" : [ 1, 1, 1, 13 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "<None>"
},
"function" : {
"kind" : "Identifier",
"location" : [ 1, 1, 1, 5 ],
"inferredType" : {
"kind" : "FuncType",
"parameters" : [ {
"kind" : "ClassValueType",
"className" : "object"
} ],
"returnType" : {
"kind" : "ClassValueType",
"className" : "<None>"
}
},
"name" : "print"
},
"args" : [ {
"kind" : "BinaryExpr",
"location" : [ 1, 7, 1, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"left" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 7, 1, 8 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 42
},
"operator" : "%",
"right" : {
"kind" : "IntegerLiteral",
"location" : [ 1, 12, 1, 12 ],
"inferredType" : {
"kind" : "ClassValueType",
"className" : "int"
},
"value" : 0
}
} ]
}
} ],
"errors" : {
"errors" : [ ],
"kind" : "Errors",
"location" : [ 0, 0, 0, 0 ]
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save