Topic 5 -Semantic Analysis Dr. William A. Maniatty Assistant Prof. Dept. of Computer Science...
-
date post
21-Dec-2015 -
Category
Documents
-
view
216 -
download
1
Transcript of Topic 5 -Semantic Analysis Dr. William A. Maniatty Assistant Prof. Dept. of Computer Science...
Topic 5 -Semantic Analysis
Dr. William A. ManiattyAssistant Prof.
Dept. of Computer ScienceUniversity At Albany
CSI 511Programming Languages and Systems Concepts
Fall 2002
Monday Wednesday 2:30-3:50LI 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 GrammarsAn Example
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 TT
2
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.
Attribute Flow in LL Grammars
Attribute Flow in LL 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