100 likes | 170 Views
Using a Single Type of Semantic Value for Nonterminals and Terminals. Semantic values for nonterminals are constructed from the semantic values of other grammar symbols.
E N D
Using a Single Type of Semantic Valuefor Nonterminals and Terminals • Semantic values for nonterminals are constructed from the semantic values of other grammar symbols. • arithmetic_expression : arithmetic_expression '+' term { $$ = new ASTMethodCall($1, $2, new ASTArgListSimple($3)); } • If the type of all semantic values is ASTNode *, then the constructor for ASTMethodCall must be declared as follows: • ASTNode *ASTMethodCall(ASTNode *rcvr, ASTNode *op, ASTNode *args) • Alternatively, one could downcast the arguments to the more specific types: • ASTNode *ASTMethodCall(ASTExpression *rcvr, ASTIdentifier *op, ASTArgList *args) • $$ = new ASTMethodCall(dynamic_cast<ASTExpression*>($1), dynamic_cast<ASTIdentifier*>$2, new ASTArgListSimple($3));
Using Multiple Types of Semantic Valuefor Nonterminals and Terminals • To get a better handle on semantic value type, determine the collection of types that can be used as semantic values and use the bison %union construct to list these: • %union { ASTExpressionList *ExpressionList; ASTExpression *Expression; ASTAssignment *Assignment; ASTIdentifier *Identifier; ...} • Declare a bison %type for each nonterminal and terminal in your grammar • %type <Expression> factor;%type <Identifier> operator;%type <Identifier> IDENTIFIER_TOKEN; • Then each of the semantic values referenced through $1, ... $n, $$ will be declared to have the appopriate type and type-specific constructors can be used.
Project Questions • What is the relation of GarnetObjects to ASTNodes? • Your parser actions will create ASTNodes • When you call eval on the whole program AST Node, you will create GarnetObjects. • start: expression_sequence { $1->ASTPrint(cout); // while debugging $1->eval(); } // to eval the whole program ; expression_sequence: ... • Do we have to implement Arrays in our program? • You will need to have Array GarnetObjects so that you can call methods with multiple arguments. • You do not need to support input of array literals, but it is practically free once you’ve implemented them • factor : ... | ‘[‘ arglist ‘]’ ...
Project Questions • What is the structure of our referencing environment? • You only need to implement a single global frame. • Your frame must contain some representation of the mapping of variable names to their values. I suggest you use a Standard Template Library map for this purpose. In Garnet, each object has a type (its class), no name has a type. • What methods does each built-in class have to contain? • Object: print • Integer: to_s, +, -, !, *, /, <, >, <=, >=, == • Float : to_s, +, -, !, *, /, <, >, <=, >=, == • String: to_s • Array: get_element, set_element, to_s • Method: dispatch, to_s • Class: new, superclass, lookup_method, to_s • TrueClass: to_s, ! • FalseClass: to_s, ! • NilClass: to_s, !
Further Project Questions • What about all that stuff Ashish was asking? • Use this grammar rule for your factors (or something similar) and I think you’ll be happy: • factor : identifier | '[' arglist ']' | INTEGER_TOKEN | FLOAT_TOKEN | factor '(' arglist') '| factor '.' operator /* make some ASTNode subclass for this */ | factor '[' expression '] ' | '(' expression ')' { $$ = $2; } ;operator: identifier | '+' | '-' | '*' | '/' ... ;
: : superclass method_names myclass method_objects slots instance_slot_names Garnet Object Representations and the Class Hierarchy Class The Integer 1 Object Integer “+” String (Class) 1 Method (Class) code for +
How do we represent all those Objects? • Here’s my declaration of the GarnetObject Class: • class GarnetObject {private: GarnetObject *myclass; vector<GarnetPtr> slotsvec; GarnetObject(const GarnetObject& init);public: GarnetObject(); GarnetObject(GarnetObject *cls, vector<GarnetPtr> slots); const GarnetObject& operator= (const GarnetObject& rhs);static void do_init();};
What is a GarnetPtr? • I’ve relented and am now making the GarnetPtr a poor man’s implementation of a discriminated union. Here’s the discriminant type: • typedef enum { d_none, d_gptr, d_strptr, d_intval, d_floatval, d_funcptr }GarnetPtrDiscriminator; • Here’s a union type that matches this discriminant • typedef GarnetPtr (*MethodPtr)(GarnetPtr,GarnetPtr);typedef union { GarnetObject *gptr; string *strptr; int intval; double floatval; MethodPtr funcptr;} primitive;
This is a GarnetPtr • Here’s the GarnetPtr class in all its glory: • class GarnetPtr {private: GarnetPtrDiscriminator d; primitive u;public: GarnetPtr(); GarnetPtr(const GarnetPtr &init); GarnetPtr& operator= (const GarnetPtr &rhs); GarnetPtr(GarnetObject *arg); GarnetPtr(string *arg); GarnetPtr(int arg); GarnetPtr(double arg); GarnetPtr(MethodPtr arg); GarnetObject *gp() const; string *sp() const; int iv() const; double fv() const; MethodPtr fp() const;};
How about the GarnetPtr Constructors and Selectors • The constructors set the discriminant: • GarnetPtr::GarnetPtr(string *arg){ d = d_strptr; u.strptr = arg;} • The selectors query the discriminant: • void discriminant_error(GarnetPtrDiscriminator d1, GarnetPtrDiscriminator d2){ cerr << "Discriminant Error: expected " << d1 << ", got " << d2 << endl; exit(255);}string *GarnetPtr::sp() const{ if (d != d_strptr) { discriminant_error(d_strptr, d); } return u.strptr;}