290 likes | 481 Views
Topic 5 -Semantic Analysis. Dr. William A. Maniatty Assistant Prof. Dept. of Computer Science University At Albany CSI 511 Programming Languages and Systems Concepts Fall 2002 Monday Wednesday 2:30-3:50 LI 99. Introduction to Semantic Analysis. Semantics are what a program means
E N D
Topic 5 -Semantic Analysis Dr. William A. Maniatty Assistant Prof. Dept. of Computer Science University At Albany CSI 511 Programming Languages and Systems Concepts Fall 2002 Monday Wednesday 2:30-3:50 LI 99
Introduction to Semantic Analysis • Semantics are what a program means • Decorating the parse tree/syntax tree is done to generate code and analyze semantics. • Actions are inserted into the parser to generate the semantic analysis when language constructs are recognized. • Analysis means • Determining correct behavior • Detecting Dangerous or Nonsensical Behavior
When are Semantics Analyzed? • Semantic analysis done at compile time and run time • Can be done at most binding times. • Compilers split into front end/back end • Back end does semantic analysis • Front end does syntax analysis
Some notes on correctness • Semantic errors are hard to catch at compile time • Late binding makes static analysis hard • Correct programs can be derived • But this is hard and typically done for short heavily used code • In practice, combine static analysis and run time checking to help the programmer.
So, Why does my software crash? • Engineers know how to make working machines, why does software crash? • Software is discrete. • Failure not gradual or but discontinuous. • Analysis/understanding is hard • Bad Tools, Bad Techniques or Bad Paradigm? • Like doing calculus with roman numerals • Cost and performance trade offs • Last to market loses, Slow performance loses.
The Role of Testing and Verification • Verification - Derive correct solution • Useful but hard, expensive and not feasible for large pieces of software. • Testing - Checks for the presence of errors. • But cannot rule out errors. • Cheaper, and practical. • Catching errors late increases repair costs.
Assertions, Invariants, Pre/Post Conditions • Hardware provides some runtime checks • Languages can support testing: • Assertion - A condition which must be true at a particular point during execution. • Invariant -A condition which must hold true at all "clean points" of execution • Precondition - True before a statement • Postcondition - Ture after a statement
Attribute Grammars 1 • Recall that context free grammars specify programming language syntax. • An Attribute grammar (AG) extends the CFG to specify semantics of the language. • Each nonterminal is associated with a set of attributes (semantic information) • Each production has a set of semantic rules. • Terminals can supply needed information.
Attribute Grammars 2 • Semantic Rules are either: • Copy rules -Copy the RHS Attribute to LHS • Semantic Functions -Perform some function on the RHS attributes. • The Semantic Rules are simple • Refer to information available in the RHS of the production. • Avoid using non-local information
Attribute Grammars 3 • Closely related to: • Denotational Semantics -Focus on machine independent details. • Axiomatic Semantics - Focuses on verification/theorem proving.
Attribute Tree Construction • CFGs and Attribute Grammars do not specify: • Shape of the parse/attribute tree • Order of parse/attribute tree construction • Attribute Trees can be constructed: • On the fly - At the same time as parse tree • After the parse tree is built (in another pass)
Attribute Trees • Attribute Trees are annotated parse trees • Attributes flow between nodes • Values initialized by leaf nodes • Synthesized Attributes are computed only in productions where the nonterminal appears on the RHS • S-Attributed grammars synthesize all their attributes. • Scanner gives initial attributes for tokens
Attribute Flow • Attribute Trees are annotated parse trees • Attribute flow is the pattern of information movement in the attribute grammar tree. • Synthesized Attributes are computed in productions with the nonterminal on the LHS • S-Attributed grammars synthesize all their attributes. • Inherited attributes are calculated when a symbol is on the RHS of a production.
Attribute Flow LR Example • LR Parsers have • Rightmost Trees • Bottom-Up Construction • Attributes Flow Along Parse Tree Edges. • In Bottom-Up Direction • Can Augment Parser Stack • E.g. AG for (3 + 1) * 2
Attribute Flow LR Example • Attributes initialized by scanner. • Transitive closure of copied pointers makes attributes available when evaluated.
Attribute Flow in LL Grammars • Notice that some productions have multiple rules. • TT and FT use an extra "subtotal" attribute. • If a symbol is on LHS and RHS add a subscript.
Attribute Flow in LL Grammars • Minor errata, the RHS in expression 3: should be • TT1} - T TT2
Attribute Flow in LL Grammars • LL Grammars need to work with • Leftmost parse tree derivations • Top to bottom • L-Attributed grammars do as follows: • Each LHS symbol's attributes depend on • The symbol's own attributes • Attributes of a symbol on the RHS • Each inherited attribute depends only on inherited attributes of the LHS grammars.
So Why are Attribute Flows Important? • A translation scheme is an algorithm that invokes the attributes of a parse tree in an order consistent with its attribute flow. • This is how compilers generate code! • One Pass compilers can evaluate attributes during parse tree construction using a stack. • LR Parsers can mirror or augment the parse stack • Since The Attribute Flow Mimics the parse tree • LL Parsers need a separate attribute stack.
Some Notation • An AG is well-defined if every parse tree has a unique set of attributes. • An AG is noncircular if no attribute ever depends (transitively) on itself.
Action Routines • Action routines realize the AG: • What Semantic Rule to apply • When (during what phase of parsing) • Can be after parsing the entire RHS (typical) • Can be in the middle of parsing the RHS • Sometimes convenient • Does LL differ from LR? • Yes, LL can evaluate any time, with LR you may get conflicts (when to shift/reduce?)
An LL Action Routine Example • Given the same LL AG as earlier • Insert action routines • Predictive nature of LL allows actions at arbitrary times
YACC and Bison Handling of Attributes 1 • YACC and BISON are LALR(1) parsers • Attributes are managed with a stack • Augment the parse stack • To access attributes in a production • The attributes on the LHS is denoted $$ • The symbol's attributes on the RHS are denoted $1, $2, ..., $n where $i is the ith term from the left
YACC and BisonSemantic Values • Symbol attributes are pushed on the stack • Want to change the stack's element type. • To select a stack element type: • For single type stacks, define YYTYPE: • #define YYTYPE typename // A macro • For multi-type stacks, use %union • Each field in the union becomes is a type • Access them using say $<fieldtype>1
YACC and BisonSemantic Values • Attribute initialization: • Synthesized Attributes are assigned, using a statement like: • $$ = f($1, $2, ..., $n); /* n = |RHS|, f is a function */ • Sometimes Inherited Attributes are useful • e.g. A grammar has productions: • Decl } Type Id_List; and, Id_List } Id , Id_List | Id; • So need to set an Id's type need a statement like • $1.type = $$.type; $2.type = $$.type
YACC and BisonSemantic Values • Symbol attributes are pushed on the stack • Want to change the stack's element type. • To select a stack element type: • For single type stacks, define YYTYPE: • #define YYTYPE typename // A macro • For multi-type stacks, use %union • Each field in the union becomes is a type