What is a Compiler Design? Types, Construction Tools, Example

โšก Smart Summary

Phases of Compiler describe the six stages that transform source code into machine code: lexical analysis, syntax analysis, semantic analysis, intermediate code generation, code optimization, and code generation, supported by symbol table management and error handling.

  • ๐Ÿ”ค Lexical Analysis: The scanner groups characters into tokens and records them in the symbol table.
  • ๐ŸŒณ Syntax Analysis: The parser checks grammar and builds a hierarchical parse tree from tokens.
  • โœ”๏ธ Semantic Analysis: This phase verifies type compatibility and meaning using the syntax tree.
  • โš™๏ธ Code Generation: Intermediate code is generated, optimized, and finally translated into target machine code.
  • ๐Ÿ“‹ Support Routines: A symbol table and an error-handling routine interact with every compilation phase.

Phases of Compiler with Example: Compilation Process and Steps

What are the Phases of Compiler Design?

A Compiler operates in various phases, and each phase transforms the source program from one representation to another. Every phase takes inputs from its previous stage and feeds its output to the next phase of the compiler. There are 6 phases in a compiler. Each of these phases helps in converting the high-level language into machine code. The phases of a compiler are:

  1. Lexical analysis
  2. Syntax analysis
  3. Semantic analysis
  4. Intermediate code generator
  5. Code optimizer
  6. Code generator

Phases of Compiler

Phases of Compiler

All these phases convert the source code by dividing it into tokens, creating parse trees, and optimizing the source code through different phases.

Phase 1: Lexical Analysis

Lexical Analysis is the first phase, when the compiler scans the source code. This process can be left to right, character by character, and groups these characters into tokens.

Here, the character stream from the source program is grouped in meaningful sequences by identifying the tokens. It makes the entry of the corresponding tokens into the symbol table and passes that token to the next phase.

The primary functions of this phase are:

  • Identify the lexical units in a source code.
  • Classify lexical units into classes like constants, reserved words, and enter them in different tables. It will ignore comments in the source program.
  • Identify a token which is not a part of the language.

Example: x = y + 10

Token Type
X identifier
= Assignment operator
Y identifier
+ Addition operator
10 Number

Phase 2: Syntax Analysis

Syntax analysis is all about discovering structure in code. It determines whether or not a text follows the expected format. The main aim of this phase is to make sure that the source code written by the programmer is correct or not.

Syntax analysis is based on the rules of the specific programming language by constructing the parse tree with the help of tokens. It also determines the structure of the source language and the grammar or syntax of the language.

Here is a list of tasks performed in this phase:

  • Obtain tokens from the lexical analyzer.
  • Check if the expression is syntactically correct or not.
  • Report all syntax errors.
  • Construct a hierarchical structure which is known as a parse tree.

Example

Any identifier/number is an expression. If x is an identifier and y+10 is an expression, then x = y+10 is a statement. Consider the parse tree for the following example:

(a+b)*c

Example of Syntax Analysis

In the Parse Tree:

  • Interior node: record with an operator field and two fields for children.
  • Leaf: records with 2 or more fields; one for the token and other information about the token.
  • Ensure that the components of the program fit together meaningfully.
  • Gathers type information and checks for type compatibility.
  • Checks operands are permitted by the source language.

Phase 3: Semantic Analysis

Semantic analysis checks the semantic consistency of the code. It uses the syntax tree of the previous phase along with the symbol table to verify that the given source code is semantically consistent. It also checks whether the code is conveying an appropriate meaning.

The Semantic Analyzer will check for type mismatches, incompatible operands, a function called with improper arguments, an undeclared variable, etc.

Functions of the semantic analysis phase are:

  • Helps you to store type information gathered and save it in the symbol table or syntax tree.
  • Allows you to perform type checking.
  • In the case of a type mismatch, where there are no exact type correction rules which satisfy the desired operation, a semantic error is shown.
  • Collects type information and checks for type compatibility.
  • Checks if the source language permits the operands or not.

Example

float x = 20.2;
float y = x*30;

In the above code, the semantic analyzer will typecast the integer 30 to float 30.0 before multiplication.

Phase 4: Intermediate Code Generation

Once the semantic analysis phase is over, the compiler generates intermediate code for the target machine. It represents a program for some abstract machine.

Intermediate code is between the high-level and machine-level language. This intermediate code needs to be generated in such a manner that makes it easy to translate it into the target machine code.

Functions of Intermediate Code generation:

  • It should be generated from the semantic representation of the source program.
  • Holds the values computed during the process of translation.
  • Helps you to translate the intermediate code into the target language.
  • Allows you to maintain precedence ordering of the source language.
  • It holds the correct number of operands of the instruction.

Example

For example:

total = count + rate * 5

Intermediate code with the help of the address code method is:

t1 := int_to_float(5)
t2 := rate * t1
t3 := count + t2
total := t3

Phase 5: Code Optimization

The next phase is code optimization of the intermediate code. This phase removes unnecessary code lines and arranges the sequence of statements to speed up the execution of the program without wasting resources. The main goal of this phase is to improve on the intermediate code to generate a code that runs faster and occupies less space.

The primary functions of this phase are:

  • It helps you to establish a trade-off between execution and compilation speed.
  • Improves the running time of the target program.
  • Generates streamlined code still in intermediate representation.
  • Removing unreachable code and getting rid of unused variables.
  • Removing statements which are not altered from the loop.

Example: Consider the following code:

a = intofloat(10)
b = c * a
d = e + b
f = d

Can become:

b = c * 10.0
f = e + b

Phase 6: Code Generation

Code generation is the last and final phase of a compiler. It gets inputs from the code optimization phase and produces the page code or object code as a result. The objective of this phase is to allocate storage and generate relocatable machine code.

It also allocates memory locations for the variables. The instructions in the intermediate code are converted into machine instructions. This phase converts the optimized or intermediate code into the target language.

The target language is the machine code. Therefore, all the memory locations and registers are also selected and allotted during this phase. The code generated by this phase is executed to take inputs and generate expected outputs.

Example

a = b + 60.0 would be possibly translated to registers as:

MOVF a, R1
MULF #60.0, R2
ADDF R1, R2

Symbol Table Management

A symbol table contains a record for each identifier with fields for the attributes of the identifier. This component makes it easier for the compiler to search the identifier record and retrieve it quickly. The symbol table also helps you with scope management. The symbol table and error handler interact with all the phases, and the symbol table is updated correspondingly.

Error Handling Routine

In the compiler design process, errors may occur in all the below-given phases:

  • Lexical analyzer: Wrongly spelled tokens.
  • Syntax analyzer: Missing parenthesis.
  • Intermediate code generator: Mismatched operands for an operator.
  • Code Optimizer: When the statement is not reachable.
  • Code Generator: When the memory is full or proper registers are not allocated.
  • Symbol tables: Error of multiple declared identifiers.

The most common errors are invalid character sequences in scanning, invalid token sequences in type, scope error, and parsing in semantic analysis.

The error may be encountered in any of the above phases. After finding errors, the phase needs to deal with the errors to continue with the compilation process. These errors need to be reported to the error handler, which handles the error to perform the compilation process. Generally, the errors are reported in the form of a message.

FAQs

Yes. AI assistants can explain each phase, trace how sample code turns into tokens and parse trees, and point out where a syntax or semantic error arises. They are helpful study aids, but verify explanations against course material.

Machine learning can predict which optimization passes produce the fastest code for a given program and machine, tuning choices that were once hand-crafted heuristics. The compiler must still guarantee the optimized program behaves exactly like the original.

The front end handles lexical, syntax, and semantic analysis plus intermediate code, and depends on the source language. The back end handles optimization and code generation, and depends on the target machine. This split simplifies retargeting.

No. Lexical analysis (scanning) groups characters into tokens, while parsing (syntax analysis) arranges those tokens into a parse tree according to grammar rules. Lexical analysis runs first and feeds its tokens to the parser.

Summarize this post with: