Initial commit

This commit is contained in:
github-classroom[bot]
2021-03-03 14:08:57 +00:00
commit b758a071dc
233 changed files with 25009 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
package chocopy.common;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
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, StandardCharsets.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,55 @@
package chocopy.pa2;
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.*;
/** Analyzes declarations to create a top-level symbol table. */
public class DeclarationAnalyzer extends AbstractNodeAnalyzer<Type> {
/** Current symbol table. Changes with new declarative region. */
private final SymbolTable<Type> sym = new SymbolTable<>();
/** Global symbol table. */
private final SymbolTable<Type> globals = sym;
/** Receiver for semantic error messages. */
private final Errors errors;
/** A new declaration analyzer sending errors to ERRORS0. */
public DeclarationAnalyzer(Errors errors0) {
errors = errors0;
}
public SymbolTable<Type> getGlobals() {
return globals;
}
@Override
public Type analyze(Program program) {
for (Declaration decl : program.declarations) {
Identifier id = decl.getIdentifier();
String name = id.name;
Type type = decl.dispatch(this);
if (type == null) {
continue;
}
if (sym.declares(name)) {
errors.semError(
id, "Duplicate declaration of identifier in same " + "scope: %s", name);
} else {
sym.put(name, type);
}
}
return null;
}
@Override
public Type analyze(VarDef varDef) {
return ValueType.annotationToValueType(varDef.var.type);
}
}
@@ -0,0 +1,30 @@
package chocopy.pa2;
import chocopy.common.analysis.SymbolTable;
import chocopy.common.analysis.types.Type;
import chocopy.common.astnodes.Program;
/** Top-level class for performing semantic analysis. */
public class StudentAnalysis {
/**
* Perform semantic analysis on PROGRAM, adding error messages and type annotations. Provide
* debugging output iff DEBUG. Returns modified tree.
*/
public static Program process(Program program, boolean debug) {
if (program.hasErrors()) {
return program;
}
DeclarationAnalyzer declarationAnalyzer = new DeclarationAnalyzer(program.errors);
program.dispatch(declarationAnalyzer);
SymbolTable<Type> globalSym = declarationAnalyzer.getGlobals();
if (!program.hasErrors()) {
TypeChecker typeChecker = new TypeChecker(globalSym, program.errors);
program.dispatch(typeChecker);
}
return program;
}
}
@@ -0,0 +1,94 @@
package chocopy.pa2;
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 static chocopy.common.analysis.types.Type.INT_TYPE;
import static chocopy.common.analysis.types.Type.OBJECT_TYPE;
/**
* Analyzer that performs ChocoPy type checks on all nodes. Applied after collecting declarations.
*/
public class TypeChecker extends AbstractNodeAnalyzer<Type> {
/** The current symbol table (changes depending on the function being analyzed). */
private final SymbolTable<Type> sym;
/** Collector for errors. */
private final Errors errors;
/**
* Creates a type checker using GLOBALSYMBOLS for the initial global symbol table and ERRORS0 to
* receive semantic errors.
*/
public TypeChecker(SymbolTable<Type> globalSymbols, Errors errors0) {
sym = globalSymbols;
errors = errors0;
}
/**
* Inserts an error message in NODE if there isn't one already. The message is constructed with
* MESSAGE and ARGS as for String.format.
*/
private void err(Node node, String message, Object... args) {
errors.semError(node, message, args);
}
@Override
public Type analyze(Program program) {
for (Declaration decl : program.declarations) {
decl.dispatch(this);
}
for (Stmt stmt : program.statements) {
stmt.dispatch(this);
}
return null;
}
@Override
public Type analyze(ExprStmt s) {
s.expr.dispatch(this);
return null;
}
@Override
public Type analyze(IntegerLiteral i) {
return i.setInferredType(Type.INT_TYPE);
}
@Override
public Type analyze(BinaryExpr e) {
Type t1 = e.left.dispatch(this);
Type t2 = e.right.dispatch(this);
switch (e.operator) {
case "-":
case "*":
case "//":
case "%":
if (INT_TYPE.equals(t1) && INT_TYPE.equals(t2)) {
return e.setInferredType(INT_TYPE);
} else {
err(e, "Cannot apply operator `%s` on types `%s` and `%s`", e.operator, t1, t2);
return e.setInferredType(INT_TYPE);
}
default:
return e.setInferredType(OBJECT_TYPE);
}
}
@Override
public Type analyze(Identifier id) {
String varName = id.name;
Type varType = sym.get(varName);
if (varType != null && varType.isValueType()) {
return id.setInferredType(varType);
}
err(id, "Not a variable: %s", varName);
return id.setInferredType(ValueType.OBJECT_TYPE);
}
}