Download presentation
Presentation is loading. Please wait.
1
Recursive Descent (contd)
This is all very, *very* wrong! What would the SW1/SW2 people say if they saw this? Problem: Lack of data abstraction: "client code" is intimately tied to "provider" details What would happen if we changed the PT rep? We need a ParseTree class! Key point: Parser, Printer, Executor functions should not know the internals of the PT class CSE 3341/655; Part 3
2
ParseTree class What are the ParseTree operations?
First: Need -- not just a ParseTree object, but a cursor to track where we are in PT Some ParseTree operations: int currNTNo(); // non-term. at current node int currAlt(); // Alternative no. used at current node void goDownLB(); // move cursor to left child void goDownMB(); //move cursor to second child void goDownRB(); //move cursor to third child How do we go up? void goUp() // move cursor to parent node CSE 3341/655; Part 3
3
Recursive Descent void PrintIf(ParseTree& p) { write( “if” );
p.goDownLB(); PrintCond(p); p.goUp(); p.goDownMB(); PrintSS(p); // bug! p.goUp(); x = p.currAlt(); if (x == 2) then { p.goDownRB(); write (“else”); PrintSS(p); }; write(“end ;”); } // Bug! Compare: void printIf(int n) { write("if); printCond(PT[n,3]); write("then"); printSS(PT[n,4]); if (PT[n,2]==2) … }
4
ParseTree class We didn’t see how the ParseTree is represented or what a node looks like - e.g.: how do we “go back up”? That is the point of data abstraction! You could use an array rep or pointers or whatever you like; the client (Parser/Printer/Executor) will be unaffected! CSE 3341/655; Part 3
5
ParseTree class (contd.)
Need some more operations: If non-term at current node is <id>, need its name, value etc. int currIdVal(); // error if we are not at <id> node void setIdVal(int x); string idName(); void ExecAssign(ParseTree p) { p.goDownMB(); x = EvalExp(p); p.goUp(); p.goDownLB(); p.setIdVal(x); } // Bug! CSE 3341/655; Part 3
6
ParseTree class Problem: What about the parser?
Key point: The parser adds nodes Hence: Need more operations Assumption: Calling procedure will create an “empty” node, set the cursor to that node, then call the appropriate parse procedure. CSE 3341/655; Part 3
7
Parsing void ParseAssign(ParseTree& p) {
p.setNTNo(7); p.setAltNo(1); // why? p.createLB(); p.createMB(); p.goDownLB(); ParseId(p); p.skipToken(); // why? and why p.skipToken? p.goUp(); p.goDownMB(); ParseExp(p); p.skipToken(); p.goUp; } CSE 3341/655; Part 3
8
Object-oriented? That is better than the original version but ...
... is not object-oriented. Object-oriented: Treat each node as a separate, independent object. Have a separate class for each non-terminal Each <if> stmt. is an instance of the If class; each <exp> an instance of the Exp class; ... CSE 3341/655; Part 3
9
Object-oriented approach (contd)
class Prog { private: DS* ds; SS* ss; public: Prog() { ds = nil; ss = nil; } PrintProg() { write(“program”); ds→PrintDS(); write(“begin”); ss→PrintSS(); print(“end”); } ExecProg() { ds→ExecDS(); ss→ExecSS(); } // not quite CSE 3341/655; Part 3
10
Object-oriented approach (contd)
class Prog{ private: DS* ds; SS* ss; public: Prog() { ds = nil; ss = nil; } ParseProg() { skipToken(); // error check? ds = new DS(); ds→ParseDS(); ss = new SS(); ss→ParseSS(); // bug! skipToken(); skipToken();} CSE 3341/655; Part 3
11
Object-oriented approach (contd)
class If{ private: Cond* c; SS* ss1; SS* ss2; // why? public: If() { c = nil; ss1 = nil; ss1 = nil; } ParseIf() { int tokNo; skipToken(); // why? c = new Cond(); c→ParseCond(); skipToken(); ss1 = new SS(); ss1→ParseSS(); tokNo=getToken(); if (tokNo==“end”) {skipToken(); skipToken(); return; } skipToken(); // error check? ss2 = new SS(); ss2→ParseSS(); ..skip tokens.. } CSE 3341/655; Part 3
12
Object-oriented approach (contd)
class If{ // contd. Print If() { write(“if ”); c→PrintCond(); write(“then”); ss1→PrintSS(); ... how to check alt. no? } // Each class can use its own approach ExecIf() { if (c→EvalCond()) then {ss1→ExecSS(); return;} if (!ss2) {ss2→ExecSS();} } CSE 3341/655; Part 3
13
OO Languages During execution of an OO program, it is best to think of memory being organized into three pieces: Code portion: contains the (compiled) code of running progam Stack portion: organized as a collection of "activation records" (or "frames"); a new a.r. is created when a function/method is called … and released when the function/method returns What does an a.r. contain? … Heap: contains objects that are created by calls to "new"; each call to "new" assigns memory as specified in the variables of the class in question … when is this memory released? Back to the Core interpreter … CSE 3341/655; Part 3
14
Object-oriented approach (contd)
class Stmt{ // consider what happens on stack and on heap private: int altNo; Assign* s1; IF* s2; Loop* s3; Input* s4; Output* s5; public: Stmt() { altNo=0; s1 = nil; ... } parseStmt() { int tokNo; tokNo = tok.getToken(); if (tokNo==...) { altNo=1; s1= new Assign(); s1->parseAssign();} if (tokNo==...) { altNo=2; s2= new IF(); s2->parseIF();} if (tokNo==...) { altNo=3; s3= new Loop(); s3->parseLoop();} if (tokNo==...) { altNo=4; s4= new Input(); s4->parseInput();} if (tokNo==...){altNo=5;s5= new Output(); s5->parseOutput();}} printStmt() { if (altNo==1) {s1->printAssign(); return; } } execStmt() { if (altNo==1) {s1->execAssign(); return; } } CSE 3341/655; Part 3
15
Problem with Id class Id class poses some problems: Need to make sure that two occurrences of same Id are not treated as two objects: class Assign { Id* id; Exp* exp; ParseAssign() { id = new Id(); id→ParseId(); // Won’t Work! Need to check if the id in question already exists; and create a new one only if it does not. Solution: Make ParseId() a static method which will decide whether to create a new Id object CSE 3341/655; Part 3
16
Problem with Id class (contd)
class Assign { Id* id; Exp* exp; // nothing new here ParseAssign() { id = Id::ParseId(); // ParseAssign does not create a new // Id object since this Id object may already exist exp = new Exp(); expParseExp(); ... // nothing new} ... Static methods such as ParseId -- also called "class methods" Can be used even if we don't have an instance of the class But: in their bodies *cannot* refer to the normal member variables of the class! (why not?); they can only refer to static variables "static" ≡ single copy that exists for the duration of program
17
Problem with Id class (contd)
class Id { //what happens on stack, heap? string name; int val; bool declared; bool initialized; static Id* eIds[20]; // pointers to existing Id’s; *static* var static int idCount = 0; // also static Id(string n) { // *private* constructor! name = n; declared = false; initialized = false; } public: static Id* ParseId() { // get token; check if name matches eIds[k]→name for // any k = 0, ..., idCount-1; if yes, return eIds[k]; // if not: Id* nId = new Id(n1); ...add to eIds[]... return nId; } int getIdVal() {return val; // check initialized first? setIdVal()? getIdName()? ... CSE 3341/655; Part 3
18
C++ : complications with static variables
C++ needs special syntax for dealing with static variables: class Id { static Id* eIds[20]; static int idCount = 0; That is wrong -- you can't initialize in the header More generally, these are *declarations*; you also need defs; so change above to: static Id* eIds[]; static int idCount; Then outside the class: Id* Id::eIds[20]; // this is the def & allocates space int Id::idCount = 0; // this is the def & allocates space CSE 3341/655; Part 3
19
Similar problem with Tokenizer
We have a number of parse methods: ParseProg(), ParseDS(), ParseDecl(), ParseSS() etc. Each will need to use the tokenizer How to ensure there is only one? In the ParseTree class approach: Include a tokenizer object as part of the parseTree object; and have ParseTree class provide methods, getToken(), skipToken(), etc. These methods will simply call the corresponding methods of Tokenizer class on the tokenizer object Since there is only one ParseTree object, this will work CSE 3341/655; Part 3
20
In the OO approach … Global variable containing the single tokenizer
Pass the single tokenizer around as a parameter Singleton class: Make constructor private (or protected); Have a local static variable (initialized to null) to hold a reference to the single instance; Provide a method Instance() that returns that
21
Singleton class class C { static C* instance = null;
C(){ } // private constructor public: static C* Instance(){ //like ParseId() if (instance == 0) {instance = new C();} return instance; } ... }; //problematic for use for Tokenizer Scope vs life-time: Scope of a variable/object: In which parts of the program do we have access to it? Life-time: When does the object come into existence and when does it go out of existence? Scope, life-time of static variables? Of non-static member variables?
22
Types and Related Issues
(Book chapters: 7, 9, 15(?), …) Key idea: Put as much info as possible into the type of each variable … then system can catch a lot of the errors Strong Typing: All type errors are caught by system Strong vs. weak types must match, only explicit conv., vs. implicit type conversion (Ruby vs. JS) Static vs. dynamic typing (pre-run-time vs. run-time) Duck typing: dynamic, “deduced type” … later Which is best? Why? (Course homepage has link to a brief paper on this) CSE 3341; Part 3
23
OO-approach to interpreter is more strongly typed than the ParseTree class approach (why?) … but type errors that the compiler misses are still possible <exp> ::= <int> | <exp> + <exp> | <exp> * <exp> class Exp { private: int kind; int i; Exp* e1; Exp* e2; public: int evalExp() { if (kind==1) return(i); if (kind==2) return( e1→evalExp() + e2→evalExp() ); if (kind==3) return( e1→evalExp() * e2→evalExp() ); } But ... what if the value of kind is wrong? Isn't this a type problem that the compiler should handle? The problem cannot be solved by simply having three separate types (IntExp, SumExp, ProdExp); why not?
24
Types and Related Issues
Solution: OO polymorphism: Three classes for the three different types of Exp but also include the notion of a generic Exp class Exp { public: virtual int evalExp() = 0; // "abstract" in Java: bool isEven() { // not virtual int k = evalExp(); // on which object? return ((k%2) == 0) ; } }; Note: Every (non-static!) method of a class has an implicit parameter (named this or self) … passed by ref? copy? CSE 3341; Part 3
25
class Exps : public Exp { // sum expression
private: Exp* e1; Exp* e2; // note the types of e1, e2 public: Exps( Exp* e, Exp* f) { e1 = e; e2 = f; } int evalExp() { return(e1→evalExp() + e2→evalExp()); } }; class Expp : public Exp { // product expression private: Exp* e1; Exp* e2; Expp( Exp* e, Exp* f) { e1 = e; e2 = f; } int evalExp() { return(e1→evalExp() * e2→evalExp()); } }; class Expi : public Exp { // integer expression private: int i; public: Expi( int j) { i = j; } int evalExp() { return i; } }; CSE 3341; Part 3
26
Polymorphism: Implementation
Each object carries with it a table of addresses(?) of all virtual methods applicable to it. Exp* ep; ...; ep→evalExp(); ... At compile time, we don't know exact type of <exp> that ep will point to. So produce code that gets the address of evalExp() from the object, then "dispatches" to that addr. This is run-time dispatching. Instead of programmer using "if" statements, the system dispatches. The exact function to be called is not known until run-time ... but it is type-safe! Consider how isEven() works CSE 3341; Part 3
27
Polymorphism So: for each (non-abstract) class, the compiler creates a table with addresses, in a specific order, of implementations of virtual methods that apply to instances of that class; each object, when constructed, includes a pointer to that. Is it possible to carry OO polymorphism too far? We will address that question as a group activity … CSE 3341; Part 3
28
Kinds of typing Strong: types must match, only explicit conversion Weak: implicit type conversion Static typing: types are checked at compile time Dynamic: types are checked at runtime Ideal: Strongly- and statically- typed In practice: not all type checking is at compile time Duck typing is a style of dynamic typing. With duck typing, the set of methods and properties determine the valid semantics of a class, instead of inheritance from a specific class (or impl. of an interface) "Duck Typing" (as in Ruby?): The standard implementation approach does not work for these languages; not only must each object carry addresses of "its" methods, must also carry their names etc. … why? So what? CSE 3341; Part 3
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.