Initial commit

This commit is contained in:
github-classroom[bot]
2021-02-09 10:56:17 +00:00
commit 54e35e2b89
114 changed files with 5696 additions and 0 deletions
+214
View File
@@ -0,0 +1,214 @@
import java.util.ArrayList;
import java.util.List;
import java_cup.runtime.*;
import chocopy.common.astnodes.*;
/* The following code section is copied verbatim to the generated
* parser class. */
parser code {:
/* The following fields and methods deal with error reporting
* Avoid changing these unless you know what you are doing. */
/** Node that accumulates error messages to be added to the Program
* node produced as a result. */
public final Errors errors = new Errors(new ArrayList<>());
/** Return the Program node that results from parsing the stream of
* tokens produced by lexical analysis. In the case of syntax errors,
* the program may be empty, but will have error messages. */
public Program parseProgram(boolean debug) {
try {
Symbol result = debug ? debug_parse() : parse();
if (result == null || !(result.value instanceof Program)) {
return new Program(new Location(0, 0), new Location(0, 0),
new ArrayList<Declaration>(),
new ArrayList<Stmt>(),
errors);
} else {
return (Program) result.value;
}
} catch (RuntimeException excp) {
throw excp;
} catch (Exception excp) {
String msg =
String.format("Internal parser error detected: %s%n", excp);
throw new AssertionError(msg);
}
}
@Override
public SymbolFactory getSymbolFactory() {
return ((ChocoPyLexer) getScanner()).symbolFactory;
}
@Override
public void syntax_error(Symbol cur_token) {
String token = symbl_name_from_id(cur_token.sym);
String text = ((ChocoPyLexer) getScanner()).yytext();
errors.syntaxError(
((ComplexSymbolFactory.ComplexSymbol) cur_token).xleft,
((ComplexSymbolFactory.ComplexSymbol) cur_token).xright,
"Parse error near token %s: %s", token, text);
}
@Override
public void unrecovered_syntax_error(Symbol cur_token) {
/* Do not die */
}
:}
/**************************************************************************
* FEEL FREE TO MODIFY ANYTHING BELOW THIS LINE
*
* The rules provided below parse expressions of the form <INT> + <INT> + ...
* You can re-use these rules or edit them as you wish. The start rule
* should return a node of type Program.
*
* Tips: Production rules are usually followed by action code that will be
* copied to the generated parser to be executed immediately after a reduce
* operation; that is, when a production rule has been matched. You can name
* a nonterminal or terminal symbol in a production rule using the colon
* notation, e.g. expr_stmt ::= expr:e, to get the AST node for the matched
* expression. In the action code, `e` will be a variable of whatever type
* has been declared for the corresponding nonterminal, such as `Expr`.
* Therefore, you can construct an AST Node of type `ExprStmt` with `e` in the
* constructor: `new ExprStmt(exleft, exright, e)`
*
* The variables `exleft` and `exright` are automatically generated by CUP
* and contain Location objects for the start and end of the expression `e`.
* You can collect start and line number info for AST nodes by taking the
* location of the left end of the leftmost symbol in a rule and the
* location of the right end of the rightmost symbol. The auto-generated
* variables have names `<sym>xleft` and `<sym>xright`, where <sym> is the
* name given to the symbol using the colon notation.
*
* When you have nonterminals that are lists of things, e.g. List<Stmt> or
* List<Declaration>, it is helpful to get the leftmost and rightmost
* source location from within this list; we have provided some utility
* functions below to do just that.
**************************************************************************/
/* The following code section is copied verbatim to the class that performs
* production-rule actions. */
action code {:
/** Return a mutable list initially containing the single value ITEM. */
<T> List<T> single(T item) {
List<T> list = new ArrayList<>();
if (item != null) {
list.add(item);
}
return list;
}
/** If ITEM is non-null, appends it to the end of LIST. Then returns
* LIST. */
<T> List<T> combine(List<T> list, T item) {
if (item != null) {
list.add(item);
}
return list;
}
/** Return a mutable empty list. */
<T> List<T> empty() {
return new ArrayList<T>();
}
/** Return the leftmost non-whitespace location in NODES, or null if NODES
* is empty. Assumes that the nodes of NODES are ordered in increasing
* order of location, from left to right. */
ComplexSymbolFactory.Location getLeft(List<? extends Node> nodes) {
if (nodes.isEmpty()) {
return null;
}
Node first = nodes.get(0);
return new ComplexSymbolFactory.Location(first.getLocation()[0],
first.getLocation()[1]);
}
:}
/* Terminal symbols (tokens returned by the lexer). The declaration
* terminal <identifier1>, <identifier2>, ...;
* declares each <identifieri> as the denotation of a distinct type terminal
* symbol for use in the grammar. The declaration
* terminal <type> <identifier1>, ...;
* does the same, and in addition indicates that the lexer supplies a
* semantic value of type <type> for these symbols that may be referenced
* in actions ( {: ... :} ).
*/
terminal NEWLINE;
terminal String PLUS;
terminal Integer NUMBER;
/* Returned by the lexer for erroneous tokens. Since it does not appear in
* the grammar, it indicates a syntax error. */
terminal UNRECOGNIZED;
/* Nonterminal symbols (defined in production rules below).
* As for terminal symbols,
* non terminal <type> <identifier1>, ..., <identifiern>;
* defines the listed nonterminal identifier symbols to have semantic values
* of type <type>. */
non terminal Program program;
non terminal List<Declaration> program_head;
non terminal List<Stmt> stmt_list, opt_stmt_list;
non terminal Stmt stmt, expr_stmt;
non terminal Expr expr, binary_expr;
/* Precedences (lowest to highest) for resolving what would otherwise be
* ambiguities in the form of shift/reduce conflicts.. */
precedence left PLUS;
/* The start symbol. */
start with program;
/***** GRAMMAR RULES *****/
program ::= program_head:d opt_stmt_list:s
{: RESULT = new Program(d.isEmpty() ? getLeft(s) : getLeft(d),
sxright, d, s, errors);
:}
;
/* Initial list of declarations. */
program_head ::= /* not implemented; currently matches empty string */
{: RESULT = empty(); :}
;
opt_stmt_list ::= {: RESULT = empty(); :}
| stmt_list:s {: RESULT = s; :}
;
stmt_list ::= stmt:s {: RESULT = single(s); :}
| stmt_list:l stmt:s {: RESULT = combine(l, s); :}
| stmt_list:l error {: RESULT = l; :}
/* If there is a syntax error in the source, this says to discard
* symbols from the parsing stack and perform reductions until
* there is a stmt_list on top of the stack, and then to discard
* input symbols until it is possible to shift again, reporting
* a syntax error. */
;
stmt ::= expr_stmt:s NEWLINE {: RESULT = s; :}
;
expr_stmt ::= expr:e {: RESULT = new ExprStmt(exleft, exright, e); :}
;
expr ::= binary_expr:e {: RESULT = e; :}
| NUMBER:n {: RESULT = new IntegerLiteral(nxleft, nxright, n); :}
;
/* A binary expression, illustrating how to find the left and right
* source position of a phrase. */
binary_expr ::= expr:e1 PLUS:op expr:e2
{: RESULT = new BinaryExpr(e1xleft, e2xright,
e1, op, e2); :}
;
+64
View File
@@ -0,0 +1,64 @@
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,100 @@
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,36 @@
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,40 @@
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,29 @@
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,36 @@
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,50 @@
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,28 @@
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,64 @@
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,23 @@
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,90 @@
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,22 @@
package chocopy.common.astnodes;
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);
}
}
@@ -0,0 +1,29 @@
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,43 @@
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,61 @@
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,34 @@
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,29 @@
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,40 @@
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,45 @@
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,35 @@
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,29 @@
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,32 @@
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,30 @@
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,18 @@
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,36 @@
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,38 @@
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,204 @@
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,34 @@
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,21 @@
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,70 @@
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,30 @@
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,21 @@
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,29 @@
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,16 @@
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,36 @@
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,36 @@
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,42 @@
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,39 @@
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,25 @@
package chocopy.pa1;
import chocopy.common.astnodes.Program;
import java_cup.runtime.ComplexSymbolFactory;
import java.io.StringReader;
/**
* Interface between driver and parser.
*/
public class StudentParser {
/**
* Return the Program AST resulting from parsing INPUT. Turn on
* parser debugging iff DEBUG.
*/
public static Program process(String input, boolean debug) {
ChocoPyLexer lexer = new ChocoPyLexer(new StringReader(input));
ChocoPyParser parser =
new ChocoPyParser(lexer, new ComplexSymbolFactory());
return parser.parseProgram(debug);
}
}
+82
View File
@@ -0,0 +1,82 @@
package chocopy.pa1;
import java_cup.runtime.*;
%%
/*** Do not change the flags below unless you know what you are doing. ***/
%unicode
%line
%column
%class ChocoPyLexer
%public
%cupsym ChocoPyTokens
%cup
%cupdebug
%eofclose false
/*** Do not change the flags above unless you know what you are doing. ***/
/* The following code section is copied verbatim to the
* generated lexer class. */
%{
/* The code below includes some convenience methods to create tokens
* of a given type and optionally a value that the CUP parser can
* understand. Specifically, a lot of the logic below deals with
* embedded information about where in the source code a given token
* was recognized, so that the parser can report errors accurately.
* (It need not be modified for this project.) */
/** Producer of token-related values for the parser. */
final ComplexSymbolFactory symbolFactory = new ComplexSymbolFactory();
/** Return a terminal symbol of syntactic category TYPE and no
* semantic value at the current source location. */
private Symbol symbol(int type) {
return symbol(type, yytext());
}
/** Return a terminal symbol of syntactic category TYPE and semantic
* value VALUE at the current source location. */
private Symbol symbol(int type, Object value) {
return symbolFactory.newSymbol(ChocoPyTokens.terminalNames[type], type,
new ComplexSymbolFactory.Location(yyline + 1, yycolumn + 1),
new ComplexSymbolFactory.Location(yyline + 1,yycolumn + yylength()),
value);
}
%}
/* Macros (regexes used in rules below) */
WhiteSpace = [ \t]
LineBreak = \r|\n|\r\n
IntegerLiteral = 0 | [1-9][0-9]*
%%
<YYINITIAL> {
/* Delimiters. */
{LineBreak} { return symbol(ChocoPyTokens.NEWLINE); }
/* Literals. */
{IntegerLiteral} { return symbol(ChocoPyTokens.NUMBER,
Integer.parseInt(yytext())); }
/* Operators. */
"+" { return symbol(ChocoPyTokens.PLUS, yytext()); }
/* Whitespace. */
{WhiteSpace} { /* ignore */ }
}
<<EOF>> { return symbol(ChocoPyTokens.EOF); }
/* Error fallback. */
[^] { return symbol(ChocoPyTokens.UNRECOGNIZED); }