CMPE 152: Compiler Design February 19 Class Meeting

Slides:



Advertisements
Similar presentations
CPSC 388 – Compiler Design and Construction
Advertisements

Semantics Static semantics Dynamic semantics attribute grammars
Intermediate Code Generation
Environments and Evaluation
2.2 A Simple Syntax-Directed Translator Syntax-Directed Translation 2.4 Parsing 2.5 A Translator for Simple Expressions 2.6 Lexical Analysis.
Interpretation Environments and Evaluation. CS 354 Spring Translation Stages Lexical analysis (scanning) Parsing –Recognizing –Building parse tree.
CS 153: Concepts of Compiler Design September 9 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design October 5 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 14 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 152: Programming Language Paradigms April 2 Class Meeting Department of Computer Science San Jose State University Spring 2014 Instructor: Ron Mak
CS 153: Concepts of Compiler Design August 26 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 16 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 21 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 30 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 152: Programming Language Paradigms April 7 Class Meeting Department of Computer Science San Jose State University Spring 2014 Instructor: Ron Mak
CS 153: Concepts of Compiler Design October 12 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 23 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 28 Class Meeting Department of Computer Science San Jose State University Fall 2015 Instructor: Ron Mak
CS 153: Concepts of Compiler Design September 14 Class Meeting
A Simple Syntax-Directed Translator
CS 153: Concepts of Compiler Design August 29 Class Meeting
CS 153: Concepts of Compiler Design September 12 Class Meeting
CS 153: Concepts of Compiler Design October 17 Class Meeting
CS 153: Concepts of Compiler Design October 5 Class Meeting
CS 432: Compiler Construction Lecture 10
CS 153: Concepts of Compiler Design September 7 Class Meeting
Ch. 4 – Semantic Analysis Errors can arise in syntax, static semantics, dynamic semantics Some PL features are impossible or infeasible to specify in grammar.
CS 153: Concepts of Compiler Design October 3 Class Meeting
CS 153: Concepts of Compiler Design December 5 Class Meeting
CS 153: Concepts of Compiler Design November 30 Class Meeting
CMPE 152: Compiler Design December 5 Class Meeting
CMPE 152: Compiler Design February 6 Class Meeting
CMPE 152: Compiler Design September 25 Class Meeting
CMPE 152: Compiler Design September 4 Class Meeting
CS 3304 Comparative Languages
CMPE 152: Compiler Design September 6 Class Meeting
CS 432: Compiler Construction Lecture 7
CMPE 152: Compiler Design September 11 Class Meeting
CMPE 152: Compiler Design September 18 Class Meeting
CMPE 152: Compiler Design September 13 Class Meeting
CMPE 152: Compiler Design October 2 Class Meeting
CMPE 152: Compiler Design October 4 Class Meeting
CMPE 152: Compiler Design September 11/13 Lab
CMPE 152: Compiler Design October 4 Class Meeting
CMPE 152: Compiler Design August 23 Class Meeting
CMPE 152: Compiler Design August 21/23 Lab
CMPE 152: Compiler Design September 20 Class Meeting
Compiling Control Statements
CMPE 152: Compiler Design September 27 Class Meeting
CS 153: Concepts of Compiler Design November 6 Class Meeting
CS 432: Compiler Construction Lecture 11
CMPE 152: Compiler Design February 14 Class Meeting
CMPE 152: Compiler Design February 28 Class Meeting
CMPE 152: Compiler Design March 7 Class Meeting
CMPE 152: Compiler Design January 29 Class Meeting
CMPE 152: Compiler Design February 12 Class Meeting
CMPE 152: Compiler Design February 7 Class Meeting
CMPE 152: Compiler Design February 21/26 Lab
CMPE 152: Compiler Design February 21 Class Meeting
CMPE 152: Compiler Design February 26 Class Meeting
CMPE 152: Compiler Design March 7 Class Meeting
CMPE 152: Compiler Design March 5 Class Meeting
CMPE 152: Compiler Design April 16 Class Meeting
CMPE 152: Compiler Design December 4 Class Meeting
CMPE 152: Compiler Design September 3 Class Meeting
CMPE 152: Compiler Design August 27 Class Meeting
CMPE 152: Compiler Design September 17 Class Meeting
CMPE 152: Compiler Design February 7 Class Meeting
CMPE 152: Compiler Design September 26 Class Meeting
CMPE 152: Compiler Design September 19 Class Meeting
Presentation transcript:

CMPE 152: Compiler Design February 19 Class Meeting Department of Computer Engineering San Jose State University Spring 2019 Instructor: Ron Mak www.cs.sjsu.edu/~mak

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!

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).

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

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

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.

The Statement Executor Subclasses StatementExecutor itself 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 StatementExecutor and its subclasses.

More Architectural Symmetry The statement executor classes in the back end are symmetrical with the statement parser classes in the front end.

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

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

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

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

Class AssignmentExecutor Object AssignmentExecutor::execute(ICodeNode *node) {     vector<ICodeNode *> children = node->get_children();     ICodeNode *variable_node = children[0];     ICodeNode *expression_node = children[1];     ExpressionExecutor expression_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 } wci/backend/interpreter/executors/AssignmentExecutor.cpp Temporary hack: Set the computed value into the symbol table. Send a message about the assignment.

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

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

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.

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!)

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; fahrenheit := 32 + centigrade/ratio END; {Runtime division by zero error.} dze := fahrenheit/(ratio - ratio); continued …

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

Quick Review of the Framework Chapter 7 Quick Review of the Framework FROM: TO: Next topic: Parsing control statements, and generating parse trees.

FOR Statement Example: FOR k := j TO 5 DO n := k Increment/decrement: Node type ADD for TO and SUBTRACT for DOWNTO. Initial assignment. Node type GT for TO and LT for DOWNTO. DO statement.

Pascal Syntax Checker II: FOR Demo. ./Chapter7cpp compile -i for.txt ./Chapter7cpp compile -i forerrors.txt

CASE Statement Example: CASE i+1 OF 1: j := i; 4: j := 4*i; 5, 2, 3: j := 523*i; END Note that Pascal’s CASE statement does not use BREAK statements.

CASE Statement, cont’d Example: CASE i+1 OF 1: j := i; 4: j := 4*i; 5, 2, 3: j := 523*i; END

Pascal Syntax Checker II: CASE Demo. ./Chapter7cpp compile -i case.txt ./Chapter7cpp compile -i caseerrors.txt

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.

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.

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!

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.

Chapter 8 Syntax and Semantics Syntax refers to the grammar rules of a source language. The rules prescribe the proper form of its programs. Rules can be described by syntax diagrams. Syntax checking: Does this sequence of tokens follow the syntax rules?

Syntax and Semantics, cont’d Semantics refers to the meaning of the token sequences according to the source language. Example: Certain sequences of tokens constitute an IF statement according to the syntax rules. The semantics of the statement determine How the interpreter will execute the statement, or How the compiler will generate object code for the statement.

Syntax and Semantics, cont’d Semantic actions by the front end parser: Building symbol tables. Type checking (which we’ll do later). Building proper parse trees. The parse trees encode type checking and operator precedence in their structures. Semantic actions by the back end: Interpreter: The executor runs the program. Compiler: The code generator emits object code.

Interpreter Design Recall the design of our interpreter in the back end:

Control Statement Executor Classes New StatementExecutor subclasses: LoopExecutor IfExecutor SelectExecutor The execute() method of each of these new subclasses executes the parse tree whose root node is passed to it. Each returns null. Only the execute() method of ExpressionExecutor returns a value.

Executing a LOOP Parse Tree Pascal REPEAT loop Pascal WHILE loop Note that we have the flexibility in the LOOP tree to have the TEST child be a middle child. Pascal FOR loop

Executing a LOOP Parse Tree, cont'd Get all the children of the LOOP node. Repeatedly execute all the child subtrees in order. If a child is a TEST node, evaluate the node’s relational expression subtree. If the expression value is true, break out of the loop. If the expression value is false, continue executing the child statement subtrees.

Executing a LOOP Parse Tree, cont’d bool exit_loop = false; ICodeNode *expr_node = nullptr; vector<ICodeNode *> loop_children = node->get_children(); ExpressionExecutor expression_executor(this); StatementExecutor statement_executor(this); while (!exit_loop) {     ++execution_count;  // count the loop statement itself     for (ICodeNode *child : loop_children) {         ICodeNodeTypeImpl child_type =                               (ICodeNodeTypeImpl) child->get_type();         if (child_type == NT_TEST)         {             if (expr_node == nullptr)             {                 expr_node = child->get_children()[0];             }             Object data_value = expression_executor.execute(expr_node);             exit_loop = cast(data_value, bool);         }         else             statement_executor.execute(child);         if (exit_loop) break;     } } wci/backend/interpreter/executors/LoopExecutor.cpp Keep looping until exit_loop becomes true. Execute all the subtrees. TEST node: Evaluate the boolean expression and set exitLoop to its value. Statement subtree: Execute it. Break out of the for loop if exitLoop is true.

Simple Interpreter II: Loops Demos ./Chapter8cpp Pascal execute repeat.txt ./Chapter8cpp Pascal execute while.txt ./Chapter8cpp Pascal execute for.txt

Detailed Code Slides Expression executor

Class ExpressionExecutor Object ExpressionExecutor::execute(ICodeNode *node) {     ICodeNodeTypeImpl node_type = (ICodeNodeTypeImpl) node->get_type();     switch (node_type)     { ... case NT_NEGATE:         {             // Get the NEGATE node's expression node child.             vector<ICodeNode *> children = node->get_children();             ICodeNode *expression_node = children[0];             // Execute the expression and return the negative of its value.            Object result_value = execute(expression_node);            if (instanceof(result_value, int)) return -cast(result_value, int);            else return -cast(result_value, float);         } // Must be a binary operator. default: return execute_binary_operator(node, node_type); } All node types: VARIABLE, INTEGER_CONSTANT, REAL_CONSTANT, STRING_CONSTANT, NEGATE, NOT, and the default. wci/backend/interpreter/executors/ExpressionExecutor.cpp

Method executeBinaryOperator // Set of arithmetic operator node types. set<ICodeNodeTypeImpl> ExpressionExecutor::ARITH_OPS = {     NT_ADD, NT_SUBTRACT, NT_MULTIPLY,     NT_FLOAT_DIVIDE, NT_INTEGER_DIVIDE, NT_MOD, }; Object ExpressionExecutor::execute_binary_operator(                       ICodeNode *node, const ICodeNodeTypeImpl node_type)     // Get the two operand children of the operator node.     vector<ICodeNode *> children = node->get_children();     ICodeNode *operand_node1 = children[0];     ICodeNode *operand_node2 = children[1];     // Operands.     Object operand1 = execute(operand_node1);     Object operand2 = execute(operand_node2);     bool integer_mode = (operand1->type == INTEGER) &&                         (operand2->type == INTEGER); ... } wci/backend/interpreter/executors/ExpressionExecutor.cpp

Method executeBinaryOperator, cont’d if (ARITH_OPS.find(node_type) != ARITH_OPS.end()) {     if (integer_mode)     {         int value1 = operand1->i;         int value2 = operand2->i;         switch (node_type)         {             case NT_ADD:      return value1 + value2;             case NT_SUBTRACT: return value1 - value2;             case NT_MULTIPLY: return value1 * value2;             case NT_FLOAT_DIVIDE:             {                 // Check for division by zero.                 if (value2 != 0)                 {                     return((float) value1)/((float) value2);                 }                 else                     error_handler.flag(node, DIVISION_BY_ZERO, this);                     return 0;             } ... } wci/backend/interpreter/executors/ExpressionExecutor.cpp