1 / 24

CMPE 152: Compiler Design September 17 Class Meeting

Learn about top-down recursive descent parsing, a technique for creating hand-coded parsers, its advantages, disadvantages, and implementation details using a programming language like Pascal. Dive into error handling, runtime error detection, and interpreter back-end functionalities. Explore statement execution and subclass relationships in parsing.

willieb
Download Presentation

CMPE 152: Compiler Design September 17 Class Meeting

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. CMPE 152: Compiler DesignSeptember 17 Class Meeting Department of Computer EngineeringSan Jose State UniversityFall 2019Instructor: Ron Mak www.cs.sjsu.edu/~mak

  2. Top Down Recursive Descent Parsing • The term is very descriptive of how the parser works. • Start by parsing the topmost source language construct. • For now it’s a statement. • Later, it will be the program.

  3. Top Down Recursive Descent Parsing • “Drill down”(descend) by parsing the sub-constructs. statement →assignment statement → expression →variable → etc. • Use recursion on the way down. statement→WHILE statement → statement → etc.

  4. Top Down Recursive Descent Parsing, cont’d • This is the technique for hand-coded parsers. • Very easy to understand and write. • The source language grammar is encoded in the structure of the parser code. • Close correspondence between the parser code and the syntax diagrams. • Disadvantages • Can be tedious coding. • Ad hoc error handling. • Big and slow!

  5. Top Down Recursive Descent Parsing, cont’d • Bottom-up parsers can be smaller and faster. • Error handling can still be tricky. • To be covered later this semester.

  6. What Have We Accomplished So Far? • A working scanner for Pascal. • A set of Pascal token classes. • Symbol table and intermediate code classes. • A parser for Pascal compound, assignment, and control statements and for expressions. • Generate parse trees. • Syntax error handling. • A messaging system with message producers and message listeners. • Placeholder classes for the back end code generator and executor. • So … we are ready to put all this stuff into action!

  7. Temporary Hacks for Now • Only one symbol table in the stack. • Variables are scalars (not records or arrays) but otherwise have no declared type. • We haven’t parsed any Pascal declarations yet! • We consider a variable to be “declared”(and we enter it into the symbol table) the first time it appears on the left-hand-side of an assignment statement (it’s the target of the assignment).

  8. A New Temporary Hack • Today, we’re going to store runtime computed values into the symbol table. • As attribute DATA_VALUE

  9. Chapter 6 Quick Review of the Framework Executing compound statements, assignment statements, and expressions.

  10. The Statement Executor Class • Class StatementExecutor is a subclass of Executor which is a subclass of Backend. • Its execute() method interprets the parse tree whose root node is passed to it. • The return value is either the value of a computed expression, or null.

  11. The Statement Executor Subclasses • StatementExecutoritself has subclasses: • CompoundExecutor • AssignmentExecutor • ExpressionExecutor • The execute() method of each subclass also interprets the parse tree whose root node is passed to it. • Note the dependency relationships among StatementExecutorand its subclasses.

  12. Runtime Error Handling • Just as the front end has an error handler for syntax errors, the interpreter back end has an error handler for runtime errors. • Similar flag() method. • Here, run time means the time when the interpreter is executing the source program. • Runtime error message format • Error message • Source line number where the error occurred

  13. Runtime Error Messages • Here are the errors and their messages that our interpreter will be able to detect and flag at run time. enum class RuntimeErrorCode {     UNINITIALIZED_VALUE,     VALUE_RANGE,     INVALID_CASE_EXPRESSION_VALUE,     DIVISION_BY_ZERO,     INVALID_STANDARD_FUNCTION_ARGUMENT,     INVALID_INPUT,     STACK_OVERFLOW,     UNIMPLEMENTED_FEATURE, }; wci/backend/interpreter/RuntimeError.h

  14. Class StatementExecutor Object StatementExecutor::execute(ICodeNode *node) { ICodeNodeTypeImplnode_type = (ICodeNodeTypeImpl) node->get_type();     ...     switch (node_type)     {         case NT_COMPOUND:         { CompoundExecutorcompound_executor(this);             return compound_executor.execute(node);         }         case NT_ASSIGN:         { AssignmentExecutorassignment_executor(this);             return assignment_executor.execute(node);         } ... } } The node_type tells which executor subclass to use. wci/backend/interpreter/executors/StatementExecutor.cpp

  15. Class CompoundExecutor Object CompoundExecutor::execute(ICodeNode *node) { StatementExecutorstatement_executor(this);     vector<ICodeNode *> children = node->get_children();     for (ICodeNode *child : children) statement_executor.execute(child);     return Object();  // empty } • Get the list of all the child nodes of the COMPOUND node. • Then call statement_executor.execute() on each child. wci/backend/interpreter/executors/CompoundExecutor.cpp

  16. Class AssignmentExecutor Object AssignmentExecutor::execute(ICodeNode *node) {     vector<ICodeNode *> children = node->get_children(); ICodeNode *variable_node = children[0]; ICodeNode *expression_node = children[1]; ExpressionExecutorexpression_executor(this); Object result_value = expression_executor.execute(expression_node); // Set the value as an attribute of the     // target variable's symbol table entry. id->set_attribute((SymTabKey) DATA_VALUE, result_value); send_assignment_message(node, id->get_name(), result_value);     ++execution_count; Object(); // empty } • Temporary hack: Set the computed value into the symbol table. • Send a message about the assignment. wci/backend/interpreter/executors/AssignmentExecutor.cpp

  17. The Assignment Message • Very useful for debugging. • Necessary for now since we don’t have any other way to generate runtime output. • Message format • Source line number • Name of the variable • Value of the expression

  18. Executing Expressions • Recall that Pascal’s operator precedence rules are encoded in the structure of the parse tree. • At run time, we do a postordertree traversal. alpha + 3/(beta - gamma) + 5

  19. Class ExpressionExecutor, cont’d • Does not do type checking. • It’s the job of the language-specific front end to flag any type incompatibilities. • Does not know the operator precedence rules. • The front end must build the parse tree correctly. • The executor simply does a post-order tree traversal.

  20. Class ExpressionExecutor, cont’d • The bridge between the front end and the back end is the symbol table and the intermediate code (parse tree) in the intermediate tier. • Loose coupling (again!)

  21. Simple Interpreter I BEGIN BEGIN {Temperature conversions.} five := -1 + 2 - 3 + 4 + 3; ratio := five/9.0; fahrenheit := 72; centigrade := (fahrenheit - 32)*ratio; centigrade := 25; fahrenheit := centigrade/ratio + 32; centigrade := 25; fahrenheit := 32 + centigrade/ratio END; {Runtime division by zero error.} dze := fahrenheit/(ratio - ratio); continued …

  22. Simple Interpreter I, cont’d • Demo (Chapter 6) • ./Chapter6cpp executeassignments.txt BEGIN {Calculate a square root using Newton's method.} number := 4; root := number; root := (number/root + root)/2; root := (number/root + root)/2; root := (number/root + root)/2; root := (number/root + root)/2; root := (number/root + root)/2; END; ch := 'x'; str := 'hello, world' END.

  23. Lab Assignment #3 • Modify the Pascal parser to parse the new LOOP AGAIN statement. • Your parser should build a parse tree for the new statement. • Use the source files LoopTest.txt and LoopErrors.txt to test your modified parser. • Team assignment, due Wednesday, Sept. 18 Start with the compiler code from Chapter 7

  24. Lab Assignment #3, cont’d BEGIN {main} i := 0; LOOP i := i+ 2; When i = 12 ==>; i := i + 2; agaIn; IF i = 12 THEN BEGIN i := i + 2; LOOP WHEN i = 13 ==>; i := i + 4; WHEN i > 20 ==>; AGAIN; END; END LoopTest.txt LoopErrors.txt BEGIN {main} i := 1; LOOP When i = 10; i := i + 1; agaIn; WHEN i >= 14 ==>; END

More Related