flexc++(1)
π flexc++(1) β flexc++ scanner generator π
okay bestie, listen UP, this is the flexc++ manual page (that's the title and the manual-section bit, btw, in case you were wondering why I'm screaming numbers) and it's literally the glow-up of your whole tokenizing era β¨
π NAME π
flexc++ β Generate a C++ scanner class and parsing function π«Ά
πΈ SYNOPSIS πΈ
flexc++ [options] rules-file
β¨ DESCRIPTION β¨
So flexc++(1) was designed after flex(1) and flex++(1). Like those two, flexc++ generates code that does pattern-matching on text, and possibly executes actions when certain regular expressions get recognized. iconic behavior, no notes π
But here's the tea π: flexc++, contrary to flex and flex++, generates code that is explicitly intended for use by C++ programs. The well-known flex(1) program generates C source-code, and flex++(1) merely offers a C++-like shell around the yylex function generated by flex(1) and hardly supports present-day ideas about C++ software development. like... it's giving 1993, bestie π
Contrary to this, flexc++ creates a C++ class offering a predefined member function lex matching input against regular expressions and possibly executing C++ code once regular expressions were matched. The code generated by flexc++ is pure C++, allowing its users to apply all of the features offered by that language. pure C++. unmatched. periodt π
Not every aspect of flexc++ is covered by the man-pages. In addition to what's summarized by the man-pages, the flexc++ manual offers a chapter covering pre-loading of input lines (allowing you to, e.g, display lines in which errors are observed even though not all of the line's tokens have already been scanned), as well as a chapter covering technical documentation about the inner working of flexc++. deep lore, iykyk π§
Before version 2.08.00 the lexical scanner's specification file (e.g., lexer) could be split into several files using //include directives, but //include directives required that all files were specified relative to the location of the lexer file itself. E.g., if inc/part1 had to include part2, available in the same (inc) directory as part1, then part1 had to specify //include inc/part2 instead of merely //include part2. okay that was lowkey chaotic of them π
Starting with version 2.08.00 //include directives use the directories of the files containing these directories as the current directory. In the provided example part1 should simply contain //include part2. See the flexc++ flexc++api(3) and flexc++input(7) man-pages for details. slay, fixed it π
Flexc++ offers several man-pages. These man-pages contain the following main sections:
π This man-page
This man-page offers the following sections:
- 1. QUICK START: a quick start overview about how to use
flexc++; - 2. QUICK START: FLEXC++ and BISONC++: a quick start overview about how to use
flexc++in combination withbisonc++(1); - 3. GENERATED FILES: files generated by
flexc++and their purposes; - 4. OPTIONS: options available for
flexc++.
π The flexc++api(3) man-page
This man-page describes the classes generated by flexc++, describing flexc++'s actions from the programmer's point of view.
- 1. INTERACTIVE SCANNERS: how to create an interactive scanner
- 2. THE CLASS INTERFACE: SCANNER.H: Constructors and members of the scanner class generated by
flexc++ - 3. NAMING CONVENTION: symbols defined by
flexc++in the scanner class. - 4. CONSTRUCTORS: constructors defined in the scanner class.
- 5. PUBLIC MEMBER FUNCTION: public member declared in the scanner class.
- 6. PRIVATE MEMBER FUNCTIONS: private members declared in the scanner class.
- 7. SCANNER CLASS HEADER EXAMPLE: an example of a generated scanner class header
- 8. THE SCANNER BASE CLASS: the scanner class is derived from a base class. The base class is described in this section
- 9. PUBLIC ENUMS AND -TYPES: enums and types declared by the base class
- 10. PROTECTED ENUMS AND -TYPES: enumerations and types used by the scanner and scanner base classes
- 11. NO PUBLIC CONSTRUCTORS: the scanner base class does not offer public constructors.
- 12. PUBLIC MEMBER FUNCTIONS: several members defined by the scanner base class have public access rights.
- 13. PROTECTED CONSTRUCTORS: the base class can be constructed by a derived class. Usually this is the scanner class generated by
flexc++. - 14. PROTECTED MEMBER FUNCTIONS: this section covers the base class member functions that can only be used by scanner class or scanner base class members
- 15. PROTECTED DATA MEMBERS: this section covers the base class data members that can only be used by scanner class or scanner base class members
- 16. FLEX++ TO FLEXC++ MEMBERS: a short overview of frequently used
flex(1)members that received different names inflexc++. - 17. THE CLASS INPUT: the scanner's job is completely decoupled from the actual input stream. The class
Input, nested within the scanner base class handles the communication with the input streams. The classInput, is described in this section. - 18. INPUT CONSTRUCTORS: the class
Inputcan easily be replaced by another class. The constructor-requirements are described in this section. - 19. REQUIRED PUBLIC MEMBER FUNCTIONS: this section covers the required public members of a self-made
Inputclass
π The flexc++input(7) man-page
This man-page describes how flexc++'s input s should be organized. It contains the following sections:
- 1. SPECIFICATION FILE(S): the format and contents of
flexc++input files, specifying the Scanner's characteristics - 2. FILE SWITCHING: how to switch to another input specification file
- 3. DIRECTIVES: directives that can be used in input specification files
- 4. MINI SCANNERS: how to declare mini-scanners
- 5. DEFINITIONS: how to define symbolic names for regular expressions
- 6. %% SEPARATOR: the separator between the input specification sections
- 7. REGULAR EXPRESSIONS: regular expressions supported by
flexc++ - 8. SPECIFICATION EXAMPLE: an example of a specification file
π 1. QUICK START π
okay so you want a bare-bones, no-frills scanner? say less bestie, here's the whole recipe π
- First define a subdirectory
scanner, and change-dir toscanner. This directory is going to contain all scanner-related files, created next. - Create a file
lexerdefining the regular expressions to recognize, and the tokens to return. Use token values exceeding0xffwhen plain ascii character values could also be used as token values. Example (assume capitalized words are token-symbols defined in an enum defined by the scanner class):
%%
[ \t\n]+ // skip white space chars.
[0-9]+ return NUMBER;
[[:alpha:]_][[:alpha:][:digit:]_]* return IDENTIFIER;
. return matched()[0];
- Execute:
flexc++ lexer
This generates four files: Scanner.h, Scanner.ih, Scannerbase.h, and lex.cc. four whole files, we love a productive queen π
- Edit
Scanner.hto add the enum defining the token-symbols in (usually) the public section of the classScanner. E.g.,
class Scanner: public ScannerBase
{
public:
enum Tokens
{
IDENTIFIER = 0x100,
NUMBER
};
// ... (etc, as generated by flexc++)
}
- Change-dir to
scanner's base directory, and there create a filemain.ccdefiningint main:
#include <iostream>
#include "scanner/Scanner.h"
using namespace std;
int main()
{
Scanner scanner; // define a Scanner object
while (int token = scanner.lex()) // get all tokens
{
string const &text = scanner.matched();
switch (token)
{
case Scanner::IDENTIFIER:
cout << "identifier: " << text << β\nβ;
break;
case Scanner::NUMBER:
cout << "number: " << text << β\nβ;
break;
default:
cout << "char. token: `" << text << "β\n";
break;
}
}
}
- Compile all
.ccfiles, creatinga.out:
g++ *.cc scanner/*.cc
- To `tokenize'
main.cc, execute:
a.out < main.cc
and that's only part 1 bestie, we're not even done yet π
π 2. QUICK START: FLEXC++ and BISONC++ π
okay bestie, so now we're wiring flexc++ up to the bisonc++(1) parser generator, and honestly? it's giving choreography. Do the steps in order and you'll be fine π«Ά
- Start from the directory containing
main.ccused in the previous section; the lexical scanner developed there is also used here. (no cap, we're reusing her π) - Create a directory
parserand change-dir to that directory. - Define the following grammar in the file
grammar:
%scanner ../scanner/Scanner.h
%token-path ../scanner/tokens.h
%token IDENTIFIER NUMBER CHAR
%%
startrule:
startrule tokenshow
|
tokenshow
;
tokenshow:
token
{
std::cout << "matched: " << d_scanner.matched() << '\n';
}
;
token:
IDENTIFIER
|
NUMBER
|
CHAR
;
- Create the parser by executing:
bisonc++ grammar
This generates five files: parse.cc, Parserbase.h, Parser.h, Parser.ih and ../scanner/tokens.h, where that last file contains the class Tokens defining the enumeration Tokens_ specifying the symbolic token names. five files bestie, all of them iconic β¨
- Now that the parser has been defined, define the directory
scannerin the directoryparser's parent directory, and modify the abovementionedscanner/lexerfile so that it contains the%token-pathdirective, and edit the (three) lines in the file../scanner/lexerso that they return tokens defined intokens/tokens.h:
%token-path = "../tokens/tokens.h"
%%
[0-9]+ return Tokens::NUMBER;
[[:alpha:]_][[:alpha:][:digit:]_]* return Tokens::IDENTIFIER;
. return Tokens::CHAR;
- Generate the
Scannerclass, thescanner.ihinternal header file and the filelex.ccby executing in the directoryscannerthe command:
flexc++ lexer
- It's not very likely, but if the scanner ever has to use members of the parser's base class then add
#include "../parser/Parserbase.h"
to the file scanner/Scanner.ih. (edge case behavior, but we're ready for her π
)
- Change-dir to the scanner's parent directory and rewrite the
main.ccfile defined in the previous section as follows:
#include "parser/Parser.h"
int main(int argc, char **argv)
{
Parser parser;
parser.parse();
}
- Compile all sources:
g++ *.cc */*.cc
- Execute the program, providing it with some source file to be processed:
a.out < main.cc
and that's the whole pipeline queen, flexc++ out here feeding bisonc++ across directories and it just works π¦
π 3. GENERATED FILES π
Flexc++ generates four files from a well-formed input file, bestie, count 'em, four:
- A file containing the implementation of the
lexmember function and its support functions. By default this file is namedlex.cc. - A file containing the scanner's class interface. By default this file is named
Scanner.h. The scanner class itself is generated once and is thereafterownedby the programmer, who may change it ad-lib. Newly added members (data members, function members) will survive future flexc++ runs, because flexc++ will never rewrite an existing scanner class interface file, unless you explicitly order it to. she's respectful like that π - A file containing the interface of the scanner class's base class. The scanner class is publicly derived from this base class. It's used to minimize the size of the scanner interface itself. The scanner base class is
ownedby flexc++ and should never be hand-modified π. By default the scanner's base class is provided in the fileScannerbase.h. At each new flexc++ run this file is rewritten, unless flexc++ is explicitly ordered not to do so. - A file containing the implementation header. This file should contain includes and declarations that are only required when compiling the members of the scanner class. By default this file is named
Scanner.ih. This file, like the file containing the scanner class's interface, is never rewritten by flexc++ unless flexc++ is explicitly ordered to do so.
β οΈ tl;dr vibe check:Scanner.handScanner.ihare yours,Scannerbase.his hers. Touch the base class by hand at your own risk, no cap.
π 4. OPTIONS π
Where available, single letter options are listed between parentheses following their associated long-option variants. Single letter options require arguments if their associated long options require arguments as well. Options affecting the class header or implementation header file are ignored if these files already exist. Options accepting a filename do not accept path names, i.e., they cannot contain directory separators (/); options accepting a pathname may contain directory separators.
Some options may generate errors. This happens when an option conflicts with the contents of an existing file which flexc++ cannot modify (e.g., a scanner class header file exists, but doesn't define a name space, but a --namespace option was provided). To solve the error the offending option could be omitted, the existing file could be removed, or the existing file could be hand-edited according to the option's specification. Note that flexc++ currently does not handle the opposite error condition: if a previously used option is omitted, then flexc++ does not detect the inconsistency. In those cases you may encounter compilation errors π
--baseclass-header=filename(-b) β Use filename as the name of the file to contain the scanner class's base class. Defaults to the name of the scanner class plusbase.h.
it is an error if this option is used and an already existing scanner-class header file does not include filename.
--baseclass-preinclude=pathname(-H) β pathname defines the path to the file preincluded in the scanner's base-class header. This option (or the corresponding directive) may be required when using a user-definedInputclass using types etc. which aren't yet provided by the scanner's base class header. E.g., if a user-definedInputclass uses a stack, then#include <stack>must have been declared when compiling that class (see the description of the%input-...declarations in theflexc++input(7)man-page for details β¨). By specifying this option (or the corresponding directive) the required header(s) are included when compiling the files generated by flexc++. By default the pathname argument is surrounded by double quotes (which can also explicitly be provided). Alternatively, if pathname is surrounded by pointed brackets then those are used.--baseclass-skeleton=pathname(-C) β Use pathname as the path to the file containing the skeleton of the scanner class's base class. Its filename defaults toflexc++base.h.--case-insensitiveβ Use this option to generate a scanner case insensitively matching regular expressions. All regular expressions specified in flexc++'s input file are interpreted case insensitively and the resulting scanner object will case insensitively interpret its input. π
When this option is specified the resulting scanner does not distinguish between the following rules:
First // initial F is transformed to f
first
FIRST // all capitals are transformed to lower case chars
With a case-insensitive scanner only the first rule can be matched, and flexc++ will issue warnings for the second and third rule about rules that cannot be matched.
Input processed by a case-insensitive scanner is also handled case insensitively. The above mentioned First rule is matched for all of the following input words: first First FIRST firST.
Although the matching process proceeds case insensitively, the matched text (as returned by the scanner's matched() member) always contains the original, unmodified text. So, with the above input matched() returns, respectively, first, First, FIRST and firST, while matching the rule First. the way she preserves your original casing?? we love a respectful queen π
--class-header=filename(-c) β Use filename as the name of the file to contain the scanner class. Defaults to the name of the scanner class plus the suffix.h.--class-name=classNameβ Use className (rather thanScanner) as the name of the scanner class. Unless overridden by other options, generated files will be given the (transformed to lower case)className*name instead ofscanner*.
it is an error if this option is used and an already existing scanner-class header file does not define class className.
--class-skeleton=pathname(-C) β Use pathname as the path to the file containing the skeleton of the scanner class. Its filename defaults toflexc++.h.--construction(-K) β Write details about the lexical scanner to the filerules-file.output. Details cover the used character ranges, information about the regexes, the raw NFA states, and the final DFAs. lowkey the receipts of how your scanner got built π--debug(-d) β Providelexand its support functions with debugging code, showing the actual parsing process on the standard output stream. When included, the debugging output is active by default, but its activity may be controlled using thesetDebug(bool on-off)member. Note that#ifdef DEBUGmacros are not used anymore. By rerunning flexc++ without the--debugoption an equivalent scanner is generated not containing the debugging code. This option does not provide debug information about flexc++ itself. For that use the options--own-parserand/or--own-tokens(see below). two very different kinds of debugging bestie, don't mix them up π--filenames=genericName(-f) β Generic name of generated files (header files, not the lex-function source file, see the--lex-sourceoption for that). By default the header file names will be equal to the name of the generated class.--help(-h) β Write basic usage information to the standard output stream and terminate. β¨--implementation-header=filename,-iβ sets filename as the name of the file that holds the implementation header. By default it's the generated scanner class name plus the suffix.ihπ . What even is this file? It's the spot for all the directives and declarations that only the implementations of the scanner's member functions care about β and it's the only header file included by the source file containinglex()'s implementation. So if you write your own implementations of other class members, you can use the same convention, and then all the directives and declarations needed to compile every other source file belonging to the scanner class live together in one cozy little header π .
π¨ It is an error if you use this option and an already existing filename file does not include the scanner class header file.
--implementation-skeleton=pathname,-Iβ use pathname as the path to the file containing the skeleton of the implementation header. Its filename defaults toflexc++.ihβ¨.--lex-skeleton=pathname,-Lβ use pathname as the path to the file containing the skeleton of thelex()member function. Its filename defaults toflexc++.ccπ.--lex-function-name=funnameβ use funname instead oflexas the name of the member function that does the lexical scanning. Renaming your main character, honestly π.--lex-source=filename,-lβ define filename as the name of the source file that contains the scanner member functionlex. Defaults tolex.ccπΈ.--matched-rules,-Rβ the generated scanner writes the numbers of the matched rules to the standard output. It's implied by the--debugoption. Wanna shut it up? Call the generated scanner's membersetDebug(false)β or, of course, just regenerate the scanner without specifying--matched-rulesπ«Ά.--max-depth=depth,-mβ sets the maximum inclusion depth of the lexical scanner's specification files to depth. Default is10. Use more than depth specification files and the scanner throws aMax stream stack size exceededstd::length_errorexception π.--namespace=identifierβ defines the scanner class inside the namespace identifier. By default no namespace is used. When you use this option, the implementation header gets a commented-outusing namespacedeclaration for the requested namespace. Also, the scanner and scanner base class header files use the specified namespace to define their include guard directives π.
π¨ It is an error if you use this option and an already existing scanner-class header file does not define namespace identifier.
--no-baseclass-headerβ don't write the file containing the scanner's base class interface, even if it doesn't exist yet. By default that file is (re)written every single timeflexc++is called. So this is you telling it "bestie, hands off" π.--no-linesβ no#linepreprocessor directives in the file containing the scanner'slexfunction. By default#linedirectives are entered at the beginning of the action statements in the generatedlex.ccfile, which lets the compiler and debuggers blame errors on lines in your grammar specification file instead of on the source file containing thelexfunction itself. Honestly iconic error-mapping behaviour β¨.--no-lex-sourceβ don't write the file containing the scanner's predefined scanner member functions, even if that file doesn't exist yet. By default the file containing the scanner'slexmember function is (re)written each timeflexc++is called.
π This option should normally be avoided, because that file contains the parsing tables, and those get altered whenever the grammar definition is modified.
--own-parser,-Pβ the actions performed byflexc++'s own parser are written to the standard output stream.- Heads up: this does not make the generated program optionally display the actions of its
lexfunction. If that's the vibe you want, use--debuginstead π.
- Heads up: this does not make the generated program optionally display the actions of its
--own-tokens,-Tβ the tokens returned as well as the text matched byflexc++are written to the standard output stream when this option is used.- Heads up again: this does not make the generated program display returned tokens and matched text. If that's what you want, use
--print-tokensπ.
- Heads up again: this does not make the generated program display returned tokens and matched text. If that's what you want, use
--print-tokens,-tβ the tokens returned as well as the text matched by the generatedlexfunction are displayed on the standard output stream, right before returning the token tolex's caller. Displaying tokens and matched text gets suppressed again when thelex.ccfile is generated without using this option. The function showing the tokens (ScannerBase::print_) is called fromScanner::printTokens, which is defined in-line inScanner.h. So callingScannerBase::print_can also easily be controlled by an option controlled by the program using the scanner object π.- And once more: this does not show the tokens returned and text matched by
flexc++itself when it reads its input. If that's what you want, use--own-tokensπ.
- And once more: this does not show the tokens returned and text matched by
--regex-callsβ show the function call order when parsing regular expressions. Normal people do not need this one β its main purpose is helping developers understand what's happening when regular expressions are parsed π§.--show-filenames,-Fβ write the names of the files that are generated to the standard error stream π.--skeleton-directory=pathname,-Sβ specifies the directory containing the skeleton files. This option can be overridden by the specific skeleton-specifying options (-B,-C,-H, and-I) π .--target-directory=pathnameβ specifies the directory where generated files should be written. By default that's the directory whereflexc++was called π.--token-path=pathname,-Oβ specifies the location of the file containing thestruct Tokens, which defines the (parser's) symbolic tokens that can be returned by the scanner'slex()function. By default (i.e. if token_path isn't specified) symbolic tokens to be returned by the scanner'slex()function must otherwise be made available to the scanner β say, by defining them in the scanner class. Thebisonc++(1)parser generator has a similar option generating the file containing thestruct Tokensπ―.--usage,-hβ write basic usage information to the standard output stream and terminate. The polite lil "how do I even" button π«Ά.--verbose,-Vβ the verbose option generates various pieces of additional information on the standard output stream, stuff not covered by the--constructionand--show-filenamesoptions π£οΈ.--version,-vβ displayflexc++'s version number and terminate β.
πΈ FILES πΈ
Flexc++'s default skeleton files live in /usr/share/flexc++ π‘.
By default, flexc++ generates the following files:
Scanner.h: the header file containing the scanner class's interface.Scannerbase.h: the header file containing the interface of the scanner class's base class.Scanner.ih: the internal header file that's meant to be included by the scanner class's source files (e.g. it's included bylex.cc, see the next item's file), and that should contain all declarations required for compiling the scanner class's sources.lex.cc: the source file implementing the scanner class member functionlex(and support functions), performing the lexical scan π«.
π SEE ALSO π
bisonc++(1), flexc++api(3), flexc++input(7)
π¦ BUGS π¦
None reported π β she's flawless as far as anyone's saying. Periodt.
π ABOUT flexc++ π
Flexc++ was originally started as a programming project by Jean-Paul van Oosten and Richard Berendsen in the 2007-2008 academic year π. After graduating, Richard left the project and moved to Amsterdam. Jean-Paul remained in Groningen, and after on-and-off activities on the project, in close cooperation with Frank B. Brokken, Frank undertook a rewrite of the project's code around 2010 π οΈ.
During the development of flexc++, the lookahead-operator handling continuously threatened the completion of the project π. But in version 2.00.00 the lookahead operator received a completely new implementation (with a bug fix in version 2.04.00), which solved the previously encountered problems with the lookahead-operator. Character development arc, honestly β¨.
β COPYRIGHT β
This is free software, distributed under the terms of the GNU General Public License (GPL) π«Ά.
π AUTHOR π
Frank B. Brokken (f.b.brokken@rug.nl), Jean-Paul van Oosten (j.p.van.oosten@rug.nl), Richard Berendsen (richardberendsen@xs4all.nl) (until 2010).
okay bestie that's the whole page, you're officially a flexc++ girlie now π
β¨ this was flexc++.2.19.00, dated 2008-2026, from the flexc++(1) manual β go forth and lex, no cap πππ¦
π¦ where this page comes from
- ubuntu stonking: flexc++ 2.19.00-1build1 Β· GPL-3.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms