Index: src/ast_stmt.cpp =================================================================== --- src/ast_stmt.cpp (revision 33) +++ src/ast_stmt.cpp (working copy) @@ -11,6 +11,16 @@ (decls=d)->setParentAll(this); } +void Program::check() { + /* pp3: here is where the semantic analyzer is kicked off. + * The general idea is perform a tree traversal of the + * entire program, examining all constructs for compliance + * with the semantic rules. Each node can have its own way of + * checking itself, which makes for a great use of inheritance + * and polymorphism in the node classes. + */ +} + void Program::printChildren(int indentLevel) { decls->printAll(indentLevel+1); printf("\n"); Index: src/hashtable.h =================================================================== --- src/hashtable.h (revision 0) +++ src/hashtable.h (revision 0) @@ -0,0 +1,104 @@ +/* File: hashtable.h + * + * This is a simple table for storing values associated with a string key, + * supporting simple operations for put and get. It is not much more than a + * thin cover over the STL associative map container, but hides the awkward C++ + * template syntax and provides a more familiar interface. + * + * The keys are always strings, but the values can be of any type (ok, that's + * actually kind of a fib, it expects the type to be some sort of pointer to + * conform to using NULL for "not found"). The typename for a Hashtable + * includes the value type in angle brackets, e.g. if the table is storing char + * *as values, you would use the type name Hashtable. If storing values + * that are of type Decl*, it would be Hashtable. The same notation is + * used on the matching iterator for the table, i.e. a Hashtable supports + * an Iterator. + * + * An iterator is provided for iterating over the entries in a table. The + * iterator walks through the values, one by one, in alphabetical order by the + * key. Sample iteration usage: + * + * void PrintNames(Hashtable *table) + * { + * Iterator iter = table-iterator(); + * Decl *decl; + * while ((decl = iter.next()) != NULL) { + * printf("%s\n", decl->GetName()); + * } + * } + */ + +#ifndef HASHTABLE_T +#define HASHTABLE_T + +#include +#include +using namespace std +; +struct ltstr { + bool operator()(const char* s1, const char* s2) const + { return strcmp(s1, s2) < 0; } +}; + + +template class Iterator; + +template class Hashtable { + private: + multimap mmap; + + public: + // ctor creates a new empty hashtable + Hashtable() {} + + // Returns number of entries currently in table + int size() const; + + // Associates value with key. If a previous entry for + // key exists, the bool parameter controls whether + // new value overwrites the previous (removing it from + // from the table entirely) or just shadows it (keeps previous + // and adds additional entry). The lastmost entered one for an + // key will be the one returned by get. + void put(const char *key, Value val, bool overwriteInsteadOfShadow = true); + + // Removes a given key->value pair. Any other values + // for that key are not affected. If this is the last + // remaining value for that key, the key is removed + // entirely. + void remove(const char *key, Value value); + + // Returns value stored under key or NULL if no match. + // If more than one value for key (ie shadow feature was + // used during put), returns the lastmost entered one. + Value get(const char *key); + + // Returns an Iterator object (see below) that can be used to + // visit each value in the table in alphabetical order. + Iterator iterator(); +}; + +/* Don't worry too much about how the Iterator is implemented, see sample usage + * above for how to iterate over a hashtable using an iterator. + */ +template +class Iterator { + friend class Hashtable; + + private: + typename multimap::iterator cur, end; + Iterator(multimap& t) + : cur(t.begin()), end(t.end()) {} + + public: + // Returns current value and advances iterator to next. + // Returns NULL when there are no more values in table + // Visits every value, even those that are shadowed. + Value next(); + const char * nextkey(); + const char * key(); //doesn't advance the iterator +}; + +#include "hashtable.cpp" // icky, but allows implicit template instantiation + +#endif Index: src/options.h =================================================================== --- src/options.h (revision 33) +++ src/options.h (working copy) @@ -17,7 +17,7 @@ const char* output_type_to_string( output_type_t o ); -#define DEFAULT_OUTPUT_TYPE OUTPUT_PARSE_TREE +#define DEFAULT_OUTPUT_TYPE OUTPUT_CHECK_ONLY #define DEFAULT_ERRORS_BEFORE_BAIL 0 class Options Index: src/scanner.l =================================================================== --- src/scanner.l (revision 33) +++ src/scanner.l (working copy) @@ -92,11 +92,8 @@ "for" { return T_For; } "if" { return T_If; } "else" { return T_Else; } -"switch" { return T_Switch; } -"case" { return T_Case; } "return" { return T_Return; } "break" { return T_Break; } -"default" { return T_Default; } "New" { return T_New; } "NewArray" { return T_NewArray; } "ReadInteger" { return T_ReadInteger; } @@ -112,8 +109,6 @@ "&&" { return T_And; } "||" { return T_Or; } "[]" { return T_Dims; } -"++" { return T_PlusPlus; } -"--" { return T_MinusMinus; } {OPERATOR} { return yytext[0]; } /* -------------------- Constants ------------------------------ */ Index: src/ast_stmt.h =================================================================== --- src/ast_stmt.h (revision 33) +++ src/ast_stmt.h (working copy) @@ -4,8 +4,8 @@ * parse tree. For each statment in the language (for, if, return, etc.) there * is a corresponding node class for that construct. * - * pp2: You will need to add new expression and statement node classes for the - * additional grammar elements (Switch/Postfix) + * pp3: You will need to extend the Stmt classes to implement + * semantic analysis for rules pertaining to statements. */ #ifndef AST_STMT_H @@ -27,6 +27,7 @@ Program(List *declList); const char *getPrintNameForNode() { return "Program"; } void printChildren(int indentLevel); + void check(); }; class Stmt : public Node Index: src/errors.cpp =================================================================== --- src/errors.cpp (revision 33) +++ src/errors.cpp (working copy) @@ -8,6 +8,10 @@ #include #include using namespace std; +#include "ast_type.h" +#include "ast_expr.h" +#include "ast_stmt.h" +#include "ast_decl.h" int ReportError::numErrors = 0; @@ -70,6 +74,107 @@ OutputError(loc, s.str()); } +void ReportError::DeclConflict(Decl *decl, Decl *prevDecl) { + ostringstream s; + s << "Declaration of '" << decl << "' here conflicts with declaration on line " + << prevDecl->getLocation()->first_line; + OutputError(decl->getLocation(), s.str()); +} + +void ReportError::OverrideMismatch(Decl *fnDecl) { + ostringstream s; + s << "Method '" << fnDecl << "' must match inherited type signature"; + OutputError(fnDecl->getLocation(), s.str()); +} + +void ReportError::InterfaceNotImplemented(Decl *cd, Type *interfaceType) { + ostringstream s; + s << "Class '" << cd << "' does not implement entire interface '" << interfaceType << "'"; + OutputError(interfaceType->getLocation(), s.str()); +} + +void ReportError::IdentifierNotDeclared(Identifier *ident, reasonT whyNeeded) { + ostringstream s; + static const char *names[] = {"type", "class", "interface", "variable", "function"}; + assert(whyNeeded >= 0 && ((size_t)whyNeeded) <= sizeof(names)/sizeof(names[0])); + s << "No declaration found for "<< names[whyNeeded] << " '" << ident << "'"; + OutputError(ident->getLocation(), s.str()); +} + +void ReportError::IncompatibleOperands(Operator *op, Type *lhs, Type *rhs) { + ostringstream s; + s << "Incompatible operands: " << lhs << " " << op << " " << rhs; + OutputError(op->getLocation(), s.str()); +} + +void ReportError::IncompatibleOperand(Operator *op, Type *rhs) { + ostringstream s; + s << "Incompatible operand: " << op << " " << rhs; + OutputError(op->getLocation(), s.str()); +} + +void ReportError::ThisOutsideClassScope(This *th) { + OutputError(th->getLocation(), "'this' is only valid within class scope"); +} + +void ReportError::BracketsOnNonArray(Expr *baseExpr) { + OutputError(baseExpr->getLocation(), "[] can only be applied to arrays"); +} + +void ReportError::SubscriptNotInteger(Expr *subscriptExpr) { + OutputError(subscriptExpr->getLocation(), "Array subscript must be an integer"); +} + +void ReportError::NewArraySizeNotInteger(Expr *sizeExpr) { + OutputError(sizeExpr->getLocation(), "Size for NewArray must be an integer"); +} + +void ReportError::NumArgsMismatch(Identifier *fnIdent, int numExpected, int numGiven) { + ostringstream s; + s << "Function '"<< fnIdent << "' expects " << numExpected << " argument" << (numExpected==1?"":"s") + << " but " << numGiven << " given"; + OutputError(fnIdent->getLocation(), s.str()); +} + +void ReportError::ArgMismatch(Expr *arg, int argIndex, Type *given, Type *expected) { + ostringstream s; + s << "Incompatible argument " << argIndex << ": " << given << " given, " << expected << " expected"; + OutputError(arg->getLocation(), s.str()); +} + +void ReportError::ReturnMismatch(ReturnStmt *rStmt, Type *given, Type *expected) { + ostringstream s; + s << "Incompatible return: " << given << " given, " << expected << " expected"; + OutputError(rStmt->getLocation(), s.str()); +} + +void ReportError::FieldNotFoundInBase(Identifier *field, Type *base) { + ostringstream s; + s << base << " has no such field '" << field <<"'"; + OutputError(field->getLocation(), s.str()); +} + +void ReportError::InaccessibleField(Identifier *field, Type *base) { + ostringstream s; + s << base << " field '" << field << "' only accessible within class scope"; + OutputError(field->getLocation(), s.str()); +} + +void ReportError::PrintArgMismatch(Expr *arg, int argIndex, Type *given) { + ostringstream s; + s << "Incompatible argument " << argIndex << ": " << given + << " given, int/bool/string expected"; + OutputError(arg->getLocation(), s.str()); +} + +void ReportError::TestNotBoolean(Expr *expr) { + OutputError(expr->getLocation(), "Test expression must have boolean type"); +} + +void ReportError::BreakOutsideLoop(BreakStmt *bStmt) { + OutputError(bStmt->getLocation(), "break is only allowed inside a loop"); +} + /** * Standard error-reporting function expected by yacc. Our version merely calls * into the error reporter above, passing the location of the last token Index: src/manual_token.c =================================================================== --- src/manual_token.c (revision 33) +++ src/manual_token.c (working copy) @@ -57,15 +57,6 @@ case T_ReadLine: return "T_ReadLine"; case T_Malloc: return "T_Malloc"; - /* tokens you'll need for the switch statement for PP2 */ - case T_Switch: return "T_Switch"; - case T_Case: return "T_Case"; - - /* tokens you'll need for post increment and decrement */ - case T_PlusPlus: return "T_PlusPlus"; - case T_MinusMinus: return "T_MinusMinus"; - - default: static char buf[32]; snprintf( buf, 32, "Token %d", token ); Index: src/errors.h =================================================================== --- src/errors.h (revision 33) +++ src/errors.h (working copy) @@ -14,7 +14,17 @@ #include #include "location.h" using namespace std; +class Type; +class Identifier; +class Expr; +class BreakStmt; +class ReturnStmt; +class This; +class Decl; +class Operator; +typedef enum {LookingForType, LookingForClass, LookingForInterface, LookingForVariable, LookingForFunction} reasonT; + /* General notes on using this class * * Each of the methods in thie class matches one of the standard Decaf errors @@ -44,6 +54,38 @@ // Parser errors static void NonStandardDecaf(yyltype *loc, const char* str); + // Errors used by semantic analyzer for declarations + static void DeclConflict(Decl *newDecl, Decl *prevDecl); + static void OverrideMismatch(Decl *fnDecl); + static void InterfaceNotImplemented(Decl *classDecl, Type *intfType); + + // Errors used by semantic analyzer for identifiers + static void IdentifierNotDeclared(Identifier *ident, reasonT whyNeeded); + + // Errors used by semantic analyzer for expressions + static void IncompatibleOperand(Operator *op, Type *rhs); // unary + static void IncompatibleOperands(Operator *op, Type *lhs, Type *rhs); // binary + static void ThisOutsideClassScope(This *th); + + // Errors used by semantic analyzer for array acesss & NewArray + static void BracketsOnNonArray(Expr *baseExpr); + static void SubscriptNotInteger(Expr *subscriptExpr); + static void NewArraySizeNotInteger(Expr *sizeExpr); + + // Errors used by semantic analyzer for function/method calls + static void NumArgsMismatch(Identifier *fnIdentifier, int numExpected, int numGiven); + static void ArgMismatch(Expr *arg, int argIndex, Type *given, Type *expected); + static void PrintArgMismatch(Expr *arg, int argIndex, Type *given); + + // Errors used by semantic analyzer for field access + static void FieldNotFoundInBase(Identifier *field, Type *base); + static void InaccessibleField(Identifier *field, Type *base); + + // Errors used by semantic analyzer for control structures + static void TestNotBoolean(Expr *testExpr); + static void ReturnMismatch(ReturnStmt *rStmt, Type *given, Type *expected); + static void BreakOutsideLoop(BreakStmt *bStmt); + // Returns number of errors encountered static int NumErrors() { return numErrors; } static void OutputFormattedError(yyltype *loc, const char *format, ...); Index: src/parser.y =================================================================== --- src/parser.y (revision 35) +++ src/parser.y (working copy) @@ -61,7 +61,6 @@ %token T_And T_Or T_Null T_Extends T_This T_Interface T_Implements %token T_While T_For T_If T_Else T_Return T_Break %token T_Print T_ReadInteger T_ReadLine T_New T_NewArray T_Malloc -%token T_Switch T_Default T_Case T_PlusPlus T_MinusMinus %token T_IntConst %token T_BoolConst Index: src/ast_decl.h =================================================================== --- src/ast_decl.h (revision 33) +++ src/ast_decl.h (working copy) @@ -3,6 +3,10 @@ * Decl nodes are used to represent and manage declarations. There are 4 * subclasses of the base class, specialized for declarations of variables, * functions, classes, and interfaces. + * + * pp3: You will need to extend the Decl classes to implement + * semantic processing including detection of declaration conflicts + * and managing scoping issues. */ #ifndef AST_DECL_H @@ -23,6 +27,7 @@ public: Decl(Identifier *name); + friend ostream& operator<<(ostream& out, Decl *d) { return out << d->id; } }; class VarDecl : public Decl Index: src/main.cpp =================================================================== --- src/main.cpp (revision 33) +++ src/main.cpp (working copy) @@ -45,26 +45,29 @@ options.parse( argc, argv ); init(); - switch( options.getOutputType() ) { - case OUTPUT_LEX_TOKENS: + if( options.getOutputType() == OUTPUT_LEX_TOKENS ) print_all_tokens(); - break; - - case OUTPUT_PARSE_TREE: - Program* prgm; + else { yyparse(); - if( (prgm=parser_get_result()) ) - prgm->print(0); - break; + if( parser_get_result() ) { + switch( options.getOutputType() ) { + case OUTPUT_PARSE_TREE: + parser_get_result()->print(0); + break; - case OUTPUT_CHECK_ONLY: - case OUTPUT_TAC: - case OUTPUT_MIPS: - case OUTPUT_CPP: - default: - fprintf( stderr, "Output mode '%s' is not yet implemented.\n", - output_type_to_string(options.getOutputType()) ); - exit( 1 ); + case OUTPUT_CHECK_ONLY: + parser_get_result()->check(); + break; + + case OUTPUT_TAC: + case OUTPUT_MIPS: + case OUTPUT_CPP: + default: + fprintf( stderr, "Output mode '%s' is not yet implemented.\n", + output_type_to_string(options.getOutputType()) ); + exit( 1 ); + } + } } return( ReportError::NumErrors()==0 ? 0 : -1 ); Index: src/ast_expr.h =================================================================== --- src/ast_expr.h (revision 33) +++ src/ast_expr.h (working copy) @@ -3,6 +3,9 @@ * The Expr class and its subclasses are used to represent expressions in the * parse tree. For each expression in the language (add, call, New, etc.) there * is a corresponding node class for that construct. + * + * pp3: You will need to extend the Expr classes to implement + * semantic analysis for rules pertaining to expressions. */ #ifndef AST_EXPR_H @@ -91,6 +94,7 @@ Operator(yyltype loc, const char *tok); const char *getPrintNameForNode() { return "Operator"; } void printChildren(int indentLevel); + friend ostream& operator<<(ostream& out, Operator *o) { return out << o->tokenString; } }; class CompoundExpr : public Expr Index: src/hashtable.cpp =================================================================== --- src/hashtable.cpp (revision 0) +++ src/hashtable.cpp (revision 0) @@ -0,0 +1,76 @@ +/* File: hashtable.cpp */ + +template +void Hashtable::put(const char *key, Value val, bool overwrite) +{ + Value prev; + if (overwrite && (prev = get(key))) + remove(key, prev); + mmap.insert(make_pair(strdup(key), val)); +} + +template void Hashtable::remove(const char *key, Value val) +{ + if (mmap.count(key) == 0) // no matches at all + return; + + typename multimap::iterator itr; + itr = mmap.find(key); // start at first occurrence + while (itr != mmap.upper_bound(key)) { + if (itr->second == val) { // iterate to find matching pair + mmap.erase(itr); + break; + } + ++itr; + } +} + +template +Value Hashtable::get(const char *key) +{ + Value found = NULL; + + if (mmap.count(key) > 0) { + typename multimap::iterator cur, last, prev; + cur = mmap.find(key); // start at first occurrence + last = mmap.upper_bound(key); + while (cur != last) { // iterate to find last entered + prev = cur; + if (++cur == mmap.upper_bound(key)) { // have to go one too far + found = prev->second; // one before last was it + break; + } + } + } + return found; +} + +template +int Hashtable::size() const +{ + return mmap.size(); +} + +template +Iterator Hashtable::iterator() +{ + return Iterator(mmap); +} + +template +Value Iterator::next() +{ + return (cur == end ? NULL : (*cur++).second); +} + +template +const char* Iterator::nextkey() +{ + return (cur == end ? NULL : (*cur++).first); +} + +template +const char* Iterator::key() +{ + return (cur == end ? NULL : (*cur).first); +} Index: src/ast.h =================================================================== --- src/ast.h (revision 33) +++ src/ast.h (working copy) @@ -11,7 +11,11 @@ * PrintChildren() and getPrintNameForNode() methods. All the classes we provide * already implement these methods, so your job is to construct the nodes and * wire them up during parsing. Once that's done, printing is a snap! - + * + * Semantic analysis: For pp3 you are adding "Check" behavior to the ast + * node classes. Your semantic analyzer should do an inorder walk on the + * parse tree, and when visiting each node, verify the particular + * semantic rules that apply to that construct. */ #ifndef AST_H @@ -19,6 +23,8 @@ #include // for NULL #include "location.h" +#include +using namespace std; class Node { @@ -56,6 +62,7 @@ Identifier(yyltype loc, const char *name); const char *getPrintNameForNode() { return "Identifier"; } void printChildren(int indentLevel); + friend ostream& operator<<(ostream& out, Identifier *id) { return out << id->name; } }; Index: src/ast_type.h =================================================================== --- src/ast_type.h (revision 33) +++ src/ast_type.h (working copy) @@ -3,6 +3,9 @@ * In our parse tree, Type nodes are used to represent and store type * information. The base Type class is used for built-in types, the NamedType * for classes and interfaces, and the ArrayType for arrays of other types. + * + * pp3: You will need to extend the Type classes to implement + * the type system and rules for type equivalency and compatibility. */ #ifndef AST_TYPE_H @@ -10,6 +13,8 @@ #include "ast.h" #include "list.h" +#include +using namespace std; class Type : public Node { @@ -25,6 +30,10 @@ const char *getPrintNameForNode() { return "Type"; } void printChildren(int indentLevel); + + virtual void PrintToStream(ostream& out) { out << typeName; } + friend ostream& operator<<(ostream& out, Type *t) { t->PrintToStream(out); return out; } + virtual bool IsEquivalentTo(Type *other) { return this == other; } }; class NamedType : public Type @@ -37,6 +46,7 @@ const char *getPrintNameForNode() { return "NamedType"; } void printChildren(int indentLevel); + void PrintToStream(ostream& out) { out << id; } }; class ArrayType : public Type @@ -49,6 +59,7 @@ const char *getPrintNameForNode() { return "ArrayType"; } void printChildren(int indentLevel); + void PrintToStream(ostream& out) { out << elemType << "[]"; } }; #endif /* AST_TYPE_H */