Lexical analysis is a fundamental stage in the compilation process, responsible for breaking down a sequence of characters into meaningful components called tokens. This process is crucial for interpreting both natural and artificial languages. In this context, FLEX (Fast Lexical Analyzer) is a widely used tool that automates the generation of lexical analyzers, simplifying the development of compilers and interpreters.
Lexical Structure
The term lexical refers to the “vocabulary of a language”, distinguishing it from grammatical structures or syntactic rules. Words in natural languages can often be enumerated due to their finite nature, whereas artificial languages—such as programming languages—contain an immense number of possible constructs, making enumeration impractical.
In programming languages, words are classified based on specific structural rules. For instance, C identifiers must consist of alphanumeric characters and underscores but cannot begin with a digit. Unlike words in natural languages, which can have complex and irregular formations, words in artificial languages typically conform to regular languages, a category of formal languages that can be described using regular expressions.
Lexical Analysis
Lexical analysis is the first stage of compilation, responsible for recognizing tokens within a stream of characters.
Definition
A token represents the smallest meaningful unit in a program, such as keywords, identifiers, operators, and literals.
Additionally, lexical analysis may decorate tokens by attaching supplementary information, such as their lexical value or position within the source code.
The process of lexical analysis is typically handled by a scanner. Implementing a scanner manually can be both labor-intensive and error-prone due to the complexity of managing different token patterns and handling input variations. To alleviate this difficulty, scanner generators such as FLEX automate the creation of lexical analyzers by interpreting regular expression descriptions of tokens.
At its core, a scanner functions as a Finite State Automaton (FSA) that transitions through states based on character sequences, identifying token boundaries and classifying input accordingly.
FLEX: The Fast Lexical Analyzer
FLEX is a lexical analyzer generator that produces efficient scanners based on user-defined specifications. It is commonly used in compiler construction and text-processing applications where structured input needs to be analyzed and tokenized.
For certain applications, a scanner alone is sufficient. It can process input streams to identify words and apply semantic actions, such as local transformations or text substitutions. However, in the context of a compiler, the scanner serves a preparatory role for the parser by performing several essential tasks:

- Token recognition: Extracting tokens such as identifiers, constants, keywords, and punctuation marks.
- Input sanitization: Removing comments and unnecessary whitespace.
- Metadata attachment: Assigning additional attributes to tokens, including lexical values and source code locations.
The input to FLEX is a scanner specification file, which defines the patterns and actions for token recognition. FLEX then generates a C source code file that implements the scanner, which can be integrated into a compiler or other text-processing applications.
By automating lexical analysis, FLEX significantly reduces the complexity of scanner development, ensuring efficiency, maintainability, and correctness in the handling of structured text data.
File Format and Structure
A FLEX file follows a well-defined structure divided into three distinct sections, each separated by ` // = Rules =
/* Rules */ // Rules section
{UPPER} { printf(“%c”, tolower(yytext[0])); }
// Rules section [A-Z] { return UPPER; } // Return UPPER token for uppercase letters . { return LOWER; } // Return LOWER token for any other character
// = Rules section = [ \t]+ /* Ignore spaces and tabs */ [0-9]+ { return NUM; } // Match numbers ”+” { return PLUS; } // Match plus operator ”-” { return MINUS; } // Match minus operator “\n” { return END; } // Match end of line
// Rules
”/” { BEGIN(COMMENT); } // Enter COMMENT mode when seeing ”/”
[^] // Consume all characters except '' " "+[^/] // Consume a sequence of '' unless followed by ’/’" "+”/” { BEGIN(INITIAL); } // Exit COMMENT mode when seeing ”*/”%% // User Code int main(int argc, char *argv[]) { return yylex(); }
1. When the scanner detects the opening of a comment (`/*`), it enters the `COMMENT` state using `BEGIN(COMMENT)`. 2. While in the `COMMENT` state: - The scanner consumes all characters **except `*`**, ensuring that it does not prematurely detect the end of the comment. - If a sequence of `*` is found but is **not followed by `/`**, the scanner continues processing without terminating the comment. - When a sequence of `*` is **followed by `/`**, the scanner recognizes the **end of the comment** and returns to `INITIAL` mode, resuming normal token recognition. This approach effectively removes all block comments from a source file while leaving the rest of the content intact.