Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
6 views35 pages

CD Module3

The document covers YACC, a tool for generating parsers from LALR(1) grammars, and discusses error recovery strategies in bottom-up parsers, including panic mode and phrase-level recovery. It also explains Syntax Directed Translation (SDT) and Syntax Directed Definitions (SDD), detailing their roles in semantic analysis and code generation, along with their advantages and disadvantages. Additionally, it describes the types of attributes used in SDT and SDD, such as synthesized and inherited attributes, and their applications in evaluating expressions and type checking.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views35 pages

CD Module3

The document covers YACC, a tool for generating parsers from LALR(1) grammars, and discusses error recovery strategies in bottom-up parsers, including panic mode and phrase-level recovery. It also explains Syntax Directed Translation (SDT) and Syntax Directed Definitions (SDD), detailing their roles in semantic analysis and code generation, along with their advantages and disadvantages. Additionally, it describes the types of attributes used in SDT and SDD, such as synthesized and inherited attributes, and their applications in evaluating expressions and type checking.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 35

Module 3

YACC, and Error recovery in bottom up parsers, Syntax-


directed Translation, Syntax Directed Definition, Evaluation
Order for SDD’s, Application of Syntax Directed Translation,
Syntax Directed Translation Schemes, and Implementing L
attributed SDD’s

YACC
o YACC stands for Yet Another Compiler Compiler.
o YACC provides a tool to produce a parser for a given grammar.
o YACC is a program designed to compile a LALR (1) grammar.
o It is used to produce the source code of the syntactic analyzer of the language
produced by LALR (1) grammar.
o The input of YACC is the rule or grammar and the output is a C program.
These are some points about YACC:
Input: A CFG- file.y
Output: A parser y.tab.c (yacc)
o The output file "file.output" contains the parsing tables.
o The file "file.tab.h" contains declarations.
o The parser called the yyparse ().
o Parser expects to use a function called yylex () to get tokens.
The basic operational sequence is as follows:
This file contains the desired grammar in YACC format.

It shows the YACC program.

It is the c source program created by YACC.

C Compiler
Executable file that will parse grammar given in gram.Y

Error Recovery in Bottom up Parser


The error may occur at various levels of compilation, so error handling is important for the
correct execution of code. There are mainly five error recovery strategies, which are as
follows:
1. Panic mode
2. Phrase level recovery
3. Error production
4. Global correction
5. Symbol table
Panic Mode:
This strategy is used by most parsing methods. In this method of discovering the error, the
parser discards input symbols one at a time. This process is continued until one of the
designated sets of synchronizing tokens is found. Synchronizing tokens are delimiters such
as semicolons or ends. These tokens indicate an end of the input statement.
Thus, in panic mode recovery a considerable amount of input checking is for additional
errors. If there is less number of errors in the same statement, then this strategy is best choice.
Example:
int a, 5abcd, sum, $2;
Advantage:
1. It’s easy to use.
2. The program never falls into the loop.
Disadvantage:
1. This technique may lead to semantic error or runtime error in further stages.
Phrase Level Recovery:
In this strategy, on discovering an error, parser performs local correction on the remaining
input. It can replace a prefix of the remaining input with some string. This actually helps the
parser to continue its job. The local correction can be replacing the comma with semicolons,
omission of semicolons, or, fitting missing semicolons. This type of local correction is
decided by the compiler developer.
Examples:
int a,b
// AFTER RECOVERY:
int a,b; //Semicolon is added by the compiler

Advantages: This method is used in many errors repairing compilers.


Disadvantages: While doing the replacement the program should be prevented from falling
into an infinite loop.
Error Production:
It requires good knowledge of common errors that might get encountered, then we can
augment the grammar for the corresponding language with error productions that generate the
erroneous constructs.
If error production is used during parsing, we can generate an appropriate error message to
indicate the error that has been recognized in the input. This method is extremely difficult to
maintain, because if we change grammar, then it becomes necessary to change the
corresponding productions.
Example: Suppose the input string is abcd.
Grammar: S-> A
A-> aA | bA | a | b
B-> cd
The input string is not obtainable by the above grammar, so we need to add Augmented
Grammar.
Grammar: E->SB // AUGMENT THE GRAMMAR
S-> A
A-> aA| bA | a | b
B-> cd
Now, string abcd is possible to obtain.
Advantages:
1. Syntactic phase errors are generally recovered by error productions.
Disadvantages:
1. The method is very difficult to maintain because if we change the grammar then it
becomes necessary to change the corresponding production.
2. It is difficult to maintain by the developers.

Global Correction:

We often want such a compiler that makes very few changes in processing an incorrect
input string to the correct input string. Given an incorrect input string x and grammar G, the
algorithm itself can find a parse tree for a related string y (Expected output string); such
that a number of insertions, deletions, and changes of token require to transform x into y is
as low as possible. Global correction methods increase time & space requirements at
parsing time. This is simply a theoretical concept.

Advantages: It makes very few changes in processing an incorrect input string.

Disadvantages: It is simply a theoretical concept, which is unimplementable.

Symbol Table:

In semantic errors, errors are recovered by using a symbol table for the corresponding
identifier and if data types of two operands are not compatible, automatically type
conversion is done by the compiler.

Advantages: It allows basic type conversion, which we generally do in real-life


calculations.

Disadvantages: Only Implicit type conversion is possible.

Syntax Directed Translation

Parser uses a CFG(Context-free-Grammar) to validate the input string and produce output
for the next phase of the compiler. Output could be either a parse tree or an abstract syntax
tree. Now to interleave semantic analysis with the syntax analysis phase of the compiler, we
use Syntax Directed Translation.

Conceptually, with both syntax-directed definition and translation schemes, we parse the
input token stream, build the parse tree, and then traverse the tree as needed to evaluate the
semantic rules at the parse tree nodes. Evaluation of the semantic rules may generate code,
save information in a symbol table, issue error messages, or perform any other activities.
The translation of the token stream is the result obtained by evaluating the semantic rules.

Definition
Syntax Directed Translation has augmented rules to the grammar that facilitate semantic
analysis. SDT involves passing information bottom-up and/or top-down to the parse tree in
form of attributes attached to the nodes. Syntax-directed translation rules use 1) lexical
values of nodes, 2) constants & 3) attributes associated with the non-terminals in their
definitions.

The general approach to Syntax-Directed Translation is to construct a parse tree or syntax


tree and compute the values of attributes at the nodes of the tree by visiting them in some
order. In many cases, translation can be done during parsing without building an explicit
tree.
Example

E -> E+T | T

T -> T*F | F

F -> INTLIT

This is a grammar to syntactically validate an expression having additions and


multiplications in it. Now, to carry out semantic analysis we will augment SDT rules to this
grammar, in order to pass some information up the parse tree and check for semantic errors,
if any. In this example, we will focus on the evaluation of the given expression, as we don’t
have any semantic assertions to check in this very basic example.

E -> E+T { E.val = E.val + T.val } PR#1

E -> T { E.val = T.val } PR#2

T -> T*F { T.val = T.val * F.val } PR#3

T -> F { T.val = F.val } PR#4

F -> INTLIT { F.val = INTLIT.lexval } PR#5

For understanding translation rules further, we take the first SDT augmented to [ E ->
E+T ] production rule. The translation rule in consideration has val as an attribute for both
the non-terminals – E & T. Right-hand side of the translation rule corresponds to attribute
values of the right-side nodes of the production rule and vice-versa. Generalizing, SDT are
augmented rules to a CFG that associate 1) set of attributes to every node of the grammar
and 2) a set of translation rules to every production rule using attributes, constants, and
lexical values.

Let’s take a string to see how semantic analysis happens – S = 2+3*4. Parse tree
corresponding to S would be

To evaluate translation rules, we can employ one depth-first search traversal on the parse
tree. This is possible only because SDT rules don’t impose any specific order on evaluation
until children’s attributes are computed before parents for a grammar having all synthesized
attributes. Otherwise, we would have to figure out the best-suited plan to traverse through
the parse tree and evaluate all the attributes in one or more traversals. For better
understanding, we will move bottom-up in the left to right fashion for computing the
translation rules of our example.

The above diagram shows how semantic analysis could happen. The flow of information
happens bottom-up and all the children’s attributes are computed before parents, as
discussed above. Right-hand side nodes are sometimes annotated with subscript 1 to
distinguish between children and parents.
Additional Information
Synthesized Attributes are such attributes that depend only on the attribute values of
children nodes.
Thus [ E -> E+T { E.val = E.val + T.val } ] has a synthesized attribute val corresponding to
node E. If all the semantic attributes in an augmented grammar are synthesized, one depth-
first search traversal in any order is sufficient for the semantic analysis phase.
Inherited Attributes are such attributes that depend on parent and/or sibling’s attributes.
Thus [ Ep -> E+T { Ep.val = E.val + T.val, T.val = Ep.val } ], where E & Ep are same
production symbols annotated to differentiate between parent and child, has an inherited
attribute val corresponding to node T.

Advantages of Syntax Directed Translation:

Ease of implementation: SDT is a simple and easy-to-implement method for translating a


programming language. It provides a clear and structured way to specify translation rules
using grammar rules.

Separation of concerns: SDT separates the translation process from the parsing process,
making it easier to modify and maintain the compiler. It also separates the translation
concerns from the parsing concerns, allowing for more modular and extensible compiler
designs.

Efficient code generation: SDT enables the generation of efficient code by optimizing the
translation process. It allows for the use of techniques such as intermediate code generation
and code optimization.

Disadvantages of Syntax Directed Translation:

Limited expressiveness: SDT has limited expressiveness in comparison to other


translation methods, such as attribute grammars. This limits the types of translations that
can be performed using SDT.

Inflexibility: SDT can be inflexible in situations where the translation rules are complex
and cannot be easily expressed using grammar rules.

Limited error recovery: SDT is limited in its ability to recover from errors during the
translation process. This can result in poor error messages and may make it difficult to
locate and fix errors in the input program.
Syntax Directed Definitions (SDD)

Syntax Directed Definitions (SDD) are formal methods of attaching semantic information
to the syntactic structure of a programming language. SDDs improve the means of context-
free high-level by instances, adding a semantic rule set for every production of the
grammar. The rules described in these definitions state how to derive values such as types,
memory locations, or fragments of code from the structure of an input object.

Syntax Directed Translation (SDT) is the action of translating a high-level language


program into an intermediate language or machine language according to the semantic rules
imposed by SDDs. Semantic actions in SDT, act in coordination with the parsing process in
order to provide the translation of the input code. These actions are declarative and they are
triggered during the parsing phase of the message to yield the result.

What is Syntax Directed Translation?

Syntax Directed Translation (SDT), is a technical manner utilized in semantics and


language translation whereby a source language is translated into an intermediate language
or a target language through semantic actions, and attributes attached to the grammar. The
translation process follows the structure of the grammar, besides the performance of
semantic actions is interwoven with the processes of input parsing.

The semantic actions are normally included in the grammatical rules and can be either
performed synchronously or asynchronously with the parsing actions. Integrated
development systems such as SDT offer tools to compilers such as code generation, lexical
analysis, evaluation of expressions, definition, grammar, and checking of types among
other services.

What are Annotated Parse Trees?

The parse tree containing the values of attributes at each node for given input string is
called annotated or decorated parse tree.
Features of Annotated Parse Trees

 High level specification

 Hides implementation details

 Explicit order of evaluation is not specified

Types of Attributes

There are two types of attributes:

1. Synthesized Attributes: These are those attributes which derive their values from their
children nodes i.e. value of synthesized attribute at node is computed from the values of
attributes at children nodes in parse tree.

Example:

E --> E1 + T { E.val = E1.val + T.val}

In this, E.val derive its values from E1.val and T.val

Computation of Synthesized Attributes

 Write the SDD using appropriate semantic rules for each production in given
grammar.

 The annotated parse tree is generated and attribute values are computed in bottom
up manner.The value obtained at root node is the final output.

Example: Consider the following grammar

S --> E

E --> E1 + T

E --> T

T --> T1 * F
T --> F

F --> digit

The SDD for the above grammar can be written as follow

Let us assume an input string 4 * 5 + 6 for computing synthesized attributes. The annotated
parse tree for the input string is
For computation of attributes we start from leftmost bottom node. The rule F –> digit is
used to reduce digit to F and the value of digit is obtained from lexical analyzer which
becomes value of F i.e. from semantic action F.val = digit.lexval. Hence, F.val = 4 and
since T is parent node of F so, we get T.val = 4 from semantic action T.val = F.val. Then,
for T –> T1 * F production, the corresponding semantic action is T.val = T1.val * F.val .
Hence, T.val = 4 * 5 = 20

Similarly, combination of E1.val + T.val becomes E.val i.e. E.val = E1.val + T.val = 26.
Then, the production S –> E is applied to reduce E.val = 26 and semantic action associated
with it prints the result E.val . Hence, the output will be 26.

2. Inherited Attributes: These are the attributes which derive their values from their
parent or sibling nodes i.e. value of inherited attributes are computed by value of parent or
sibling nodes.
Example:

A --> BCD { C.in = A.in, C.type = B.type }

Computation of Inherited Attributes

 Construct the SDD using semantic actions.

 The annotated parse tree is generated and attribute values are computed in top down
manner.

Example: Consider the following grammar

S --> T L

T --> int

T --> float

T --> double

L --> L1, id
L --> id

The SDD for the above grammar can be written as follow

Let us assume an input string int a, c for computing inherited attributes. The annotated
parse tree for the input string is
The value of L nodes is obtained from T.type (sibling) which is basically lexical value
obtained as int, float or double. Then L node gives type of identifiers a and c. The
computation of type is done in top down manner or preorder traversal. Using function
Enter_type the type of identifiers a and c is inserted in symbol table at corresponding
id.entry.

Evaluating Step-by-Step Using Semantic Rules:

1. Leaf Nodes:

o F → number (5): F.val = 5

o F → number (2): F.val = 2

o F → number (3): F.val = 3

2. Bottom-Up Evaluation:

o T → T * F for 5 * 2: T.val = 5 * 2 = 10

o E → E + T for 3 + 10: E.val = 3 + 10 = 13


The final value of E.val is 13, which represents the evaluated result of 3 + 5 * 2.

Types of SDDs

1. Synthesized SDD: Uses only synthesized attributes. It is often evaluated in a single


bottom-up pass.

2. Inherited SDD: Uses inherited attributes, which may require additional passes or
dependencies on parent and sibling attributes.

Applications of Syntax-Directed Definitions

 Evaluating Expressions: Computing values of expressions.

 Type Checking: Ensuring the type compatibility of expressions.

 Code Generation: Generating intermediate or target code based on parsed syntax.

 Intermediate Code Generation: Producing an intermediate representation for further


optimization and final code generation.

Advantages of SDDs

 Modularity: Attributes and semantic rules are associated directly with grammar
productions, making it easier to modify and extend language features.

 Efficiency: Synthesized SDDs can often be evaluated in a single pass through the
parse tree, providing efficient evaluation.

 Flexibility: Supports various semantic analyses, including type checking, scope


management, and code generation.

Evaluation Order for SDD’s


The evaluation order for Syntax-Directed Definitions (SDDs) is crucial to ensure that all
attribute values are computed correctly. Since attributes may depend on one another, the
evaluation order must follow these dependencies to produce the correct values.

The evaluation order of SDDs depends on the types of attributes:


1. Synthesized Attributes: Computed using the attribute values of child nodes in the
parse tree. These can often be evaluated in a single bottom-up pass through the parse
tree.

2. Inherited Attributes: Computed based on the values from parent or sibling nodes in
the parse tree. These may require specific orders to ensure that all dependencies are
satisfied.+

Types of Evaluation Orders

1. Dependency Graph-Based Evaluation

2. Topological Order for Acyclic Dependencies

3. Single-Pass Evaluation (Applicable for S-Attributed Definitions)

4. Multi-Pass Evaluation (Required for L-Attributed Definitions)

Dependency Graph-Based Evaluation

To manage dependencies, we construct a dependency graph:

 Each node in the graph represents an attribute.

 Each edge between nodes indicates a dependency, where the target node’s attribute
depends on the source node’s attribute.

For evaluation, nodes should be computed in an order that respects this dependency graph:

 If the graph is acyclic (no cycles), the attributes can be evaluated in a topological
order.

 If there are cyclic dependencies, the SDD may not have a well-defined evaluation
order, and it might require additional rewriting or adjustments to resolve
dependencies.

2. Topological Order for Acyclic Dependencies

For acyclic dependency graphs, topological sorting is used:

 Identify an ordering of nodes where each attribute is evaluated only after all attributes
it depends on have been evaluated.
 Topological order is often applied for attribute evaluation in parse trees for
synthesized-only SDDs or acyclic SDDs.

3. Single-Pass Evaluation (S-Attributed SDDs)

If an SDD only uses synthesized attributes (also known as S-Attributed Definitions),


evaluation is straightforward and efficient:

 Attributes are evaluated in a bottom-up pass from the leaves of the parse tree to the
root.

 Each node’s synthesized attribute can be computed using its children’s attributes,
which have already been evaluated.

Example:

 For a simple arithmetic expression tree, the values of sub-expressions can be


calculated as the tree is traversed from bottom to top.

4. Multi-Pass Evaluation (L-Attributed SDDs)

When using inherited attributes (in L-Attributed Definitions), a more careful approach is
required:

 L-Attributed Definitions allow each attribute to depend on inherited attributes from


the parent or attributes from the left siblings.

 Evaluation often requires a left-to-right traversal along each level of the tree to
ensure inherited attributes are available when needed.

Example of Evaluation Order in an L-Attributed SDD

Consider a simple expression grammar where each variable in an expression must inherit the
type from its surrounding context.

E → T E'

E' → + T E' | ε
T → num

1. E' inherits the context type from E via inherited attributes.


2. To evaluate this, we first compute the inherited attributes for E, then E' in left-to-right
order.

Example Walkthrough
Suppose we want to evaluate the expression 3 + 4 * 5. Here’s how each approach
would handle it:
Using SDD
1. Parse 3 + 4 * 5 and compute values using the SDD rules.
2. The values of attributes would be computed without any translation output, simply
resulting in a final value of 3 + (4 * 5) = 23.
Using SDT
1. Parse 3 + 4 * 5.
2. Execute translation actions to output postfix notation while computing values.
o After parsing 3, it outputs 3.
o After parsing 4 * 5, it outputs 4 5 *.
o Then it outputs 3 4 5 * +, which is the postfix representation of 3 + 4 * 5.

Differences

SDD (Syntax-Directed
Aspect SDT (Syntax-Directed Translation)
Definition)
Defines attribute relationships Embeds actions directly within grammar
Purpose
without immediate actions rules for immediate execution
Focus Attribute computation Immediate translation actions
Generates intermediate code or
Output Attribute values only
representation
Example Computes E.val = 23 for 3 + 4 Generates postfix notation: 3 4 5 * +
Output * 5

3. In short, SDD focuses on specifying what needs to be computed, while SDT specifies
how and when to compute and translate as part of parsing.

Application of Syntax Directed Translation :


Syntax Directed Translation (SDT) is widely used in compiler design for translating high-
level language syntax into intermediate code, machine code, or another target representation.
SDT attaches semantic rules to grammar productions, helping the compiler perform various
tasks beyond syntax checking, such as type checking, intermediate code generation, and
optimizations.

Here are some key applications of Syntax Directed Translation:

1. Expression Evaluation

 Arithmetic and Boolean Expressions: SDT can evaluate expressions by constructing


a parse tree and computing values using semantic actions attached to each production
rule.
 Syntax-Directed Evaluation: The semantic actions calculate results directly, such as
computing the result of arithmetic expressions (like 3 + 5 * 2).

Example:

 For an expression E → E1 + T, the SDT can evaluate E.val = E1.val + T.val directly
during parsing.

2. Type Checking

 SDT enables type checking by attaching semantic rules to the grammar that enforce
type compatibility.
 The SDT can detect errors such as incompatible type assignments or operations that
violate type rules.

Example:

 For the production E → E1 + T, SDT can include a rule to check if E1 and T have
compatible types before performing addition.
 If incompatible types are detected, the SDT generates an error message instead of
continuing.

3. Intermediate Code Generation


 SDT is commonly used to produce intermediate code for expressions, control
structures, and other language constructs, which can be further optimized before
generating machine code.
 Intermediate representations like three-address code (TAC) or postfix notation are
generated using SDT.

Example:

 For the production E → E1 + T, SDT can generate TAC as:

t1 = E1

t2 = T

t3 = t1 + t2

Here, t3 would be used as a temporary variable for intermediate calculations.

4. Code Optimization

 During intermediate code generation, SDT can also be used to perform basic
optimizations like constant folding (evaluating constant expressions at compile time),
and eliminating unnecessary temporary variables.
 For example, if an SDT detects a constant expression like 3 * 4, it could replace it
with 12 in the intermediate code.

5. Syntax-Directed Translation for Control Flow Statements

 SDT can handle the translation of control flow statements like if, while, and for
loops, which require branching in the generated code.
 SDT rules can generate the labels and jump statements required to implement the
control flow in intermediate code.
Example:

 For an if statement:

if (E) S1 else S2

SDT could generate:

if E.val == false goto L1

S1.code

goto L2

L1: S2.code

L2:

6. Memory Management

 SDT can aid in memory allocation for variables by managing the symbol table and
generating addresses for each variable.
 It helps in keeping track of variable scopes and allocating appropriate memory offsets,
which is essential for code generation.

Example:

 For a variable declaration int a;, SDT might assign an address or offset in the symbol
table for a, which can be used later during code generation.

7. Error Detection and Reporting

 SDT helps in semantic error detection and reporting by including checks for various
conditions, such as undeclared variables or incompatible types, during translation.
 Syntax errors can often be detected at the parsing stage, but semantic errors are
handled by SDT.

Example:

 For a production S → id = E, SDT can check if id has been declared in the current
scope before generating code for the assignment.

8. Translation of Declarations and Data Structures

 SDT can handle the translation of declarations, arrays, structures, and other data
constructs by assigning memory offsets, generating type information, and handling
access patterns.
 This ensures the correct memory layout and alignment, which are essential for correct
code execution.

Example:
 For an array declaration like int arr[10];, SDT can allocate a contiguous block of
memory and compute the offset for each element access.

9. Syntax-Directed Translation for Function Calls and Procedure Invocation

 SDT can manage function calls, parameter passing, and return values, which
involve saving the current state, handling scope changes, and managing function
arguments and return values.
 SDT generates the necessary code to handle function prologues and epilogues, and
ensures arguments are passed correctly (e.g., by value or by reference).

Example:

 For a function call f(a, b), SDT can generate code that pushes a and b onto the stack,
calls f, and retrieves the return value.

10. Code Generation for Object-Oriented Constructs

 SDT is also applied in translating object-oriented constructs such as classes,


inheritance, and polymorphism.
 It can generate code for accessing member functions, virtual function tables, and
handling inheritance hierarchies.

Example:

 For accessing object.method() in an object-oriented language, SDT can generate code


to look up the method in the class's method table and execute it.

Summary

Syntax Directed Translation is essential in compiler design for implementing:

 Expression evaluation
 Type checking
 Intermediate code generation
 Code Optimization
 Control flow handling
 Memory management
 Error detection
 Translation of declaration and Data Structure
 Function and procedure handling
 Object-oriented constructs

SDT is at the core of the translation phase in compilers, turning high-level language
syntax into structured, optimized, and executable intermediate representations or machine
code.

A Syntax Directed Translation Scheme (SDT) is a variation of Syntax Directed


Definitions (SDD) in compiler design, where semantic actions are embedded directly
within the grammar productions. These actions are executed as part of parsing, making
SDTs more procedural than SDDs, where actions are only associated with attributes.

SDTs are useful for implementing compilers or interpreters as they allow code to be
generated, attributes to be evaluated, and actions to be performed in a specific order
during parsing. SDTs are often used to generate intermediate code, perform type
checking, or construct syntax trees.

Types of Syntax Directed Translation Schemes

SDTs can be classified based on where the semantic actions are positioned relative to
grammar symbols in a production rule:

 Postfix SDT: Semantic actions are placed at the end of a production.


 Prefix SDT: Semantic actions are placed at the beginning of a production.
 Embedded SDT: Semantic actions are embedded within the production rule,
interleaved with grammar symbols.

The choice of SDT type depends on whether top-down or bottom-up parsing is used.
Examples of Syntax Directed Translation Schemes

Here are some examples of SDTs for various types of translation tasks:

1. Postfix SDT for Expression Evaluation

For arithmetic expressions, the SDT can evaluate expressions as it parses them by using
postfix notation. Here’s an example:

Grammar:

E → E + T { print('+') }

E→T

T → T * F { print('*') }

T→F

F → (E)

F → num { print(num.val) }

Explanation:

1. This SDT generates postfix code for expressions. For instance, for the expression 3 +
5 * 2, the output would be 3 5 2 * +.
2. Semantic actions are executed in a postfix order, meaning they are executed after
parsing the symbols on the right-hand side of each production.

2. Embedded SDT for Intermediate Code Generation

Suppose we want to generate three-address code (TAC) for expressions with the following
grammar:
Grammar:

E → E + T { E.place = newtemp(); emit(E.place, '=', E1.place, '+', T.place); }

E → T { E.place = T.place; }

T → T * F { T.place = newtemp(); emit(T.place, '=', T1.place, '*', F.place); }

T → F { T.place = F.place; }

F → (E) { F.place = E.place; }

F → num { F.place = num.lexval; }

Explanation:

 This SDT translates expressions into three-address code.


 E.place, T.place, and F.place hold the temporary names for intermediate results.
 Semantic actions generate TAC instructions using emit functions. For instance,
parsing a * (b + c) might generate:

t1 = b + c

t2 = a * t1

3. Embedded SDT for Type Checking

For languages with basic type checking, an SDT can enforce type rules as part of the parsing
process.

Grammar:

S → id = E { if (id.type != E.type) error("Type Mismatch"); }

E → E + T { if (E1.type == int && T.type == int) E.type = int; else E.type = float; }

E → T { E.type = T.type; }

T → num { T.type = int; }

T → real_num { T.type = float; }


Explanation:

 This SDT performs type checking while parsing expressions.


 If there is a type mismatch in assignment (like assigning a float to an integer variable),
an error is raised.
 The type attribute of each symbol is checked at each step, ensuring expressions are
type-consistent.

Types of SDTs Based on Parsing

1. SDT with Bottom-Up Parsing (Postfix SDT)

In bottom-up parsing (used by LR parsers), SDTs typically use postfix actions that execute
once the parser has recognized the right-hand side of a production. This approach is suited to
synthesized attributes.

For example:

E → E + T { print('+') }

E→T

Explanation:

 The semantic action { print('+') } will execute after parsing E + T completely, making
it suitable for postfix operations.

2. SDT with Top-Down Parsing (Prefix and Embedded SDT)

In top-down parsing (used by LL parsers), SDTs may place actions at the beginning (prefix)
or interspersed within the production. Inherited attributes can be handled more naturally in
top-down parsing.

Example using prefix:

E → { E.in = T.in } T { E.out = T.out }


T → { T.in = "int" } num { T.out = T.in }

Explanation:

 Here, E.in and T.in are inherited attributes passed down the parse tree to set or check
types.

Comparison of SDT with Syntax Directed Definitions (SDDs)

Aspect SDT SDD

Attached to grammar as
Semantic Rules Embedded in grammar rules as actions.
attribute rules.

Determined by dependency
Execution Order Procedural; follows parsing sequence.
graph.

Best for on-the-fly code generation and


Suitability Best for attribute evaluation.
translations.

Evaluation Often specific to parsing type (top-down Evaluation order is usually


Strategy or bottom-up). flexible.

Applications of Syntax Directed Translation Schemes

 Intermediate Code Generation: Used to generate code like three-address code.

 Type Checking: Enforces type rules and consistency within expressions and
assignments.

 Code Optimization: Simplifies expressions during parsing (e.g., constant folding).

 Error Checking and Reporting: Provides semantic error checks, such as undeclared
variables or type mismatches.

 Syntax Tree Construction: Builds syntax trees for further analysis or optimization.

 Code Generation for Control Structures: Generates code for if-else, while, for
loops, etc.
L-attributed SDDs (Syntax Directed Definitions) are a specific type of SDD in which
attributes are evaluated following a left-to-right traversal of the parse tree. These SDDs allow
both synthesized attributes (computed based on the attributes of children nodes) and inherited
attributes (computed based on the attributes of parent or left sibling nodes). L-attributed
SDDs are especially suitable for top-down parsing and can be implemented in one left-to-
right pass if the attribute dependencies are set up correctly.

L-attributed SDDs have two main rules:

1. Each synthesized attribute of a non-terminal XXX depends only on attributes of the


symbols in the production (either on itself or its children).

2. Each inherited attribute of a non-terminal depends only on:

1. Attributes of the parent or left siblings (i.e., symbols that appear to the left in
the production rule).

2. No circular dependencies.

Example of L-attributed SDD

Consider a simple grammar for arithmetic expressions:

E → T E'

E' → + T { E'.inh = E.inh + T.val } E' | ε

T → num { T.val = num.val }

In this example:

1. E' inherits the partial result from the preceding computation through E'.inh, which is
based on the left sibling’s value.

2. This can be evaluated in a single left-to-right pass by ensuring that E'.inh is computed
before evaluating the rest of E'.

Steps to Implement L-attributed SDDs


 Define the Grammar and Attributes: Choose attributes that capture the necessary
information for translation or semantic checking. Assign inherited attributes where
dependencies need to pass from left to right, and use synthesized attributes for values
computed based on children nodes.

 Embed Semantic Rules: For each grammar production, write rules to compute
inherited and synthesized attributes.

 Determine the Evaluation Order: Ensure that inherited attributes of a symbol are
computed before any dependent symbols are evaluated in each production. For top-
down parsers, this evaluation typically happens as the parser moves from left to right
within a rule.

 Implement a Recursive Descent Parser or Modify an LL Parser: Since L-


attributed SDDs are compatible with top-down parsing, you can use a recursive
descent parser and evaluate attributes during parsing. When parsing, pass inherited
attributes from parent nodes or left siblings and compute synthesized attributes as
each node completes parsing.

Example: L-Attributed SDD for Expression Translation

Consider translating an expression into postfix notation. Here’s how we might structure this
in an L-attributed SDD.

Grammar for Arithmetic Expression

E → T E' { E.val = E'.syn }

E' → + T { E'.inh = E.inh + T.val } E' | ε { E'.syn = E'.inh }

T → F T' { T.val = T'.syn }

T' → * F { T'.inh = T.inh * F.val } T' | ε { T'.syn = T'.inh }

F → (E) | num { F.val = num.val }

Explanation

1. Attributes:

1. inh: Inherited attribute that passes context from parent to child or from left to
right within a production.
2. syn: Synthesized attribute that combines results from the children and is
returned to the parent.

2. Semantic Rules:

1. E and T synthesize their final values from their child nodes.

2. E' and T' inherit values from left siblings and pass them to the next sibling or
back to the parent.

3. Translation to Postfix Notation:

1. The postfix notation is generated as parsing progresses from left to right.

2. Each production appends its results based on the rules specified in the
attributes.

Example: L-Attributed SDD for Arithmetic Expression Translation

Goal

We want to translate an arithmetic expression (e.g., 3 + 4 * 5) into three-address code (TAC).

Grammar

Let's use a simple grammar for arithmetic expressions:

1. E → E + T

2. E → T

3. T → T * F

4. T → F

5. F → (E)

6. F → id

Where:

 E represents an expression.

 T represents a term.
 F represents a factor.

 id represents an identifier (e.g., variables or constants).

Attributes

1. Synthesized Attribute:

o E.place, T.place, F.place: These attributes store the name of a temporary


variable where the result of evaluating E, T, or F will be placed.

2. Inherited Attribute:

o T.inh, F.inh: These attributes store intermediate values needed for evaluation
(e.g., results from previous operations).

3. Global Temporary Counter:

o tempCount: Used to generate unique temporary variable names (e.g., t1, t2,
…).

Semantic Rules

We'll add semantic rules to each production to translate the expression into three-
address code.

1. E → E1 + T

o E.place = newTemp()

o print(E.place " = " E1.place " + " T.place)

2. E → T

o E.place = T.place

3. T → T1 * F

o T.place = newTemp()

o print(T.place " = " T1.place " * " F.place)

4. T → F

o T.place = F.place
5. F → ( E )

o F.place = E.place

6. F → id

o F.place = lookup(id)

Explanation of the Semantic Rules

 newTemp() is a function that generates a new temporary variable (e.g., t1, t2).

 print(...) outputs the three-address code instructions.

 lookup(id) retrieves the identifier's address or value.

Example Translation

Let's walk through an example: 3 + 4 * 5.

Derivation Steps

1. Start: E → E + T

2. Rewrite: E → T + T

3. Rewrite: E → F + T

4. Rewrite: E → id + T

5. Rewrite: T → T * F

6. Rewrite: T → F * F

7. Rewrite: F → id

Attribute Evaluation and Code Generation

Following the semantic rules:

1. id for 3, 4, and 5 are mapped to t0, t1, and t2.

2. TAC for 4 * 5: t3 = t1 * t2

3. TAC for 3 + (4 * 5): `t4 = t

Tac = t0 + t3
Need for User defined functions, a multifunction program- Elements of user
defined functions Definition of Functions- Return values and their Types-
Function Calls-Function Declaration Category of functions- Nesting of
functions –Recursion.

You might also like