flexc++api(3)
π NAME π
flexc++api β this is the Application programmerβs interface of flexc++ generated classes page. It's the flexc++ API manpage, section 3, so now you know exactly where to find it again bestie π«Ά
β¨ DESCRIPTION β¨
Okay so. flexc++ was designed after flex(1) and flex++(1). Fr fr. And just like those two, flexc++ generates code that does pattern-matching on text, and it can run actions whenever certain regular expressions get recognized. It's giving lexer factory, no cap.
If you want the big-picture tour, go read flexc++(1). This page is all about the Application Programmerβs Interface of the classes flexc++ spits out, and it's organized into these sections:
- 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 in flexc++. - 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 class Input, is described in this section.
- 18. INPUT CONSTRUCTORS: the class Input can 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 Input class
π¦ UNDERSCORES π¦
Quick lore drop: starting with version 2.07.00, flexc++ reserved identifiers no longer end in two underscore characters β they end in one. Why? Because according to the C++ standard, identifiers with two or more consecutive underscore characters are reserved by the language π so flexc++ had to stop doing that.
In practice this could mean you need some minor edits to existing source files that use flexc++βs facilities. Most likely it's just changing StartCondition__ into StartCondition_, and PostEnum__ into PostEnum_. That's it, that's the whole vibe check.
The complete list of affected names is:
Enums:
ActionType_,Leave_,StartConditon_,PostEnum_;
Member functions:
actionType_,continue_,echoCh_,echoFirst_,executeAction_,getRange_,get_,istreamName_,lex_,lop1_,lop2_,lop3_,lop4_,lopf_,matched_,noReturn_,print_,pushFront_,reset_,return_;
Protected data members:
d_in_d_token_s_finIdx_,s_interactive_,s_maxSizeofStreamStack_,s_nRules_,s_rangeOfEOF_,s_ranges_,s_rf_.
π 1. INTERACTIVE SCANNERS π
An interactive scanner is characterized by the fact that scanning is postponed until an end-of-line character has been received, followed by reading all information on the line read so far. Yes, it waits for the newline like a polite queen. π
Flexc++ supports the %interactive directive), generating an interactive scanner. For everything below we're assuming Scanner is the name of the scanner class generated by flexc++.
Caveat bestie, listen up: generating interactive and non-interactive scanners should not be mixed, because their class organizations fundamentally differ, and several of theScannerclassβs members are only available in the non-interactive scanner. As theScanner.hfile contains theScannerclassβs interface, which is normally left untouched by flexc++, flexc++ cannot adapt theScannerclass when asked to change the interactivity of an existingScannerclass. Because of this, support for the--interactiveoption was discontinued at flexc++βs 1.01.00 release. RIP to that flag, it's gone gone.
The interactive scanner generated by flexc++ has the following characteristics:
- The
Scannerclass is derived privately fromstd::istringstreamand (as usual) publicly fromScannerBase. - The
istringstreambase class is constructed by its default constructor. - The function
lexβs default implementation is removed fromScanner.hand is implemented in the generatedlex.ccsource file. It performs the following tasks:- If the token returned by the scanner is not equal to
0it is returned as then next token; - Otherwise the next line is retrieved from the input stream passed to the
Scannerβs constructor (by defaultstd::cin). If this fails,0is returned. - A
'\n'character is appended to the just read line, and the scannerβsstd::istringstreambase class object is re-initialized with that line; - The member
lex_returns the next token. This implementation allows code callingScanner::lex()to conclude, as usual, that the input is exhausted whenlexreturns0.
- If the token returned by the scanner is not equal to
Here's an example of how such a scanner could be used β look at this, so iconic:
// scanner generated using βflexc++ lexerβ with lexer containing
// the %interactive directive
int main()
{
Scanner scanner; // by default: read from std::cin
while (true)
{
cout << "? "; // prompt at each line
while (true) // process all the lineβs tokens
{
int token = scanner.lex();
if (token == β\nβ) // end of line: new prompt
break;
if (token == 0) // end of input: done
return 0;
// process other tokens
cout << scanner.matched() << β\nβ;
if (scanner.matched()[0] == βqβ)
return 0;
}
}
}
πΈ 2. THE CLASS INTERFACE: SCANNER.H πΈ
By default, flexc++ generates a file Scanner.h containing the initial interface of the scanner class performing the lexical scan according to the specifications given in flexc++βs input file. The name of the file that gets generated can easily be changed using flexc++βs --class-header option. In this man-page weβll stick to using the default name, because consistency is the main character here.
The file Scanner.h is generated only once, unless an explicit request is made to rewrite it (using flexc++βs --force-class-header option).
The provided interface is very light-weight, primarily offering a link to the scannerβs base class (see this manpageβs sections 8 through 16).
Many of the facilities offered by the scanner class are inherited from the ScannerBase base class. Additional facilities offered by the Scanner class are covered below. β¨
π 3. NAMING CONVENTION π
All symbols that are required by the generated scanner class end in an underscore character (e.g., executeAction_). These names should not be redefined, okay? They're spoken for. As they are part of the Scanner and ScannerBase class, their scope is immediately clear and confusion with identically named identifiers elsewhere is unlikely. Ngl that's just good design.
Some member functions do not use the underscore convention. These are the scanner classβs constructors, or names that are similar or equal to names that have historically been used (e.g., length). Also, some functions are offered offering hooks into the implementation (like preCode). The latter category of function also have names that donβt end in underscores. So yes, the underscore thing is the rule but it also has exceptions β it's giving β¨ nuanced β¨.
π 4. CONSTRUCTORS π
explicit Scanner(std::istream &in = std::cin, std::ostream &out = std::cout, bool keepCwd = true)β This constructor by default reads information from the standard input stream and writes to the standard output stream. When theScannerobject goes out of scope the input and output files are closed.
By default (keepCwd == true) the directory that was active when the scanner was constructed is made current again once the scanning process ends (i.e., when lex_(), see below, returns 0).
When keepCwd == false the scanner doesnβt reset its working directory to the directory that was current when the scanner was constructed. This may be convenient in situations where a program repeatedly calls switchStream(), followed by full lexical scans of the switched-to streams. Super useful when you're bouncing between streams fr fr.
With interactive scanners input stream switching or stacking is not available; switching output streams, however, is. So half the party is open π«Ά
Scanner(std::string const &infile, std::string const &outfile, bool keepCwd = true)β This constructor opens the input and output streams whose file names were specified. When theScannerobject goes out of scope the input and output files are closed. Ifoutfile == "-"then the standard output stream is used as the scannerβs output medium; ifoutfile == ""then the standard error stream is used as the scannerβs output medium.
The parameter keepCwd is used as with the previous constructor.
This constructor is not available with interactive scanners. Sad but true.
(more parts coming, don't go anywhere bestie π)
β¨ 5. PUBLIC MEMBER FUNCTIONS β¨
int lex()β okay so this is THE main character function, bestie π«Άlexdoes the lexical scanning of the input file that got specified at construction time (but also peek section 6.1. for the tea on intermediate stream-switching facilities, iykyk). It hands back anintthat represents the token tied to the matched regular expression. If it returns0, that's the signal for end-of-file, no more input, we're done π. Looking at its default implementation, it could be redefined by the user. Fun fact: lex's default implementation is literally just a lil call tolex_:
inline int Scanner::lex()
{
return lex_();
}
β οΈ Caveat, and this one matters: with interactive scanners thelexfunction is defined in the generatedlex.ccfile. Onceflexc++has generated the scanner class header file, this scanner class header file isn't automatically rewritten byflexc++. So if, at some later stage, an interactive scanner must be generated, then that inlineleximplementation has to be removedby handfrom the scanner class header file π. And likewise, alexmember implementation (like the one above) must be providedby handif a non-interactive scanner is required after first having generated files implementing an interactive scanner. Yes it's giving tedious. Yes it's real. Periodt.
πΈ 6. PRIVATE MEMBER FUNCTIONS πΈ
int lex_()β this one is used internally bylex, and should not otherwise be used. Hands off, she's busy πint executeAction_()β also used internally bylex, also should not otherwise be used. Same vibe, same rule πvoid preCode()β by default this function has an empty, inline implementation inScanner.h. It can safely be replaced by a user-defined implementation π. It gets called bylex_, right before it starts matching input characters against its rules:preCodeis called bylex_whenlex_is called, and also after having executed the actions of a rule which did not execute areturnstatement. The outline oflex_'s implementation looks like this:
int Scanner::lex_()
{
...
preCode();
while (true)
{
size_t ch = get_(); // fetch next char
...
switch (actionType_(range)) // determine the action
{
... maybe return
}
... no return, continue scanning
preCode();
} // while
}
void postCode(PostEnum_ type)β by default this function has an empty, inline implementation inScanner.h, and it can safely be replaced by a user-defined implementation π¦. It gets called bylex_just after a rule has been matched. The values of the enum classPostEnum_tell you the characteristic of the matched rule.PostEnum_has four values:PostEnum_::END,PostEnum_::POP,PostEnum_::RETURN, andPostEnum_::WIP. Slide over to section 10 for what each one actually means πvoid print()β when the--print-tokensoption or%print-tokensdirective is used, this function gets called to display, on the standard output stream, the tokens returned and the text matched by the scanner generated byflexc++β¨. Displaying is suppressed when thelex.ccfile is (re)generated without using this directive. The function that actually shows the tokens (ScannerBase::print_) is called fromprint, which is defined in-line inScanner.h. So callingScannerBase::print_can also super easily be controlled by an option controlled by the program that's using the scanner object π.
π 7. SCANNER CLASS HEADER EXAMPLE π
Okay bestie, here's the whole thing laid out, character for character, because honestly seeing it is understanding it:
#ifndef Scanner_H_INCLUDED_
#define Scanner_H_INCLUDED_
// $insert baseclass_h
#include "Scannerbase.h"
// $insert classHead
class Scanner: public ScannerBase
{
public:
explicit Scanner(std::istream &in = std::cin,
std::ostream &out = std::cout);
Scanner(std::string const &infile, std::string const &outfile);
// $insert lexFunctionDecl
int lex();
private:
int lex_();
int executeAction_(size_t ruleNr);
void print();
void preCode(); // re-implement this function for code that must
// be execβed before the patternmatching starts
void postCode(PostEnum_ type);
// re-implement this function for code that must
// be execβed after the rulesβs actions.
};
// $insert scannerConstructors
inline Scanner::Scanner(std::istream &in, std::ostream &out)
:
ScannerBase(in, out)
{}
inline Scanner::Scanner(std::string const &infile, std::string const &outfile)
:
ScannerBase(infile, outfile)
{}
// $insert inlineLexFunction
inline int Scanner::lex()
{
return lex_();
}
inline void Scanner::preCode()
{
// optionally replace by your own code
}
inline void Scanner::postCode(PostEnum_ type)
{
// optionally replace by your own code
}
inline void Scanner::print()
{
print_();
}
#endif // Scanner_H_INCLUDED_
π 8. THE SCANNER BASE CLASS π
By default, flexc++ generates a file Scannerbase.h containing the interface of the base class of the scanner class that flexc++ also generates π«Ά. The name of the file that gets generated is easy to change using flexc++'s --baseclass-header option. In this man-page we're using the default name, for the sake of everyone's sanity.
The file Scannerbase.h is generated at each new flexc++ run. It contains no user-serviceable or extensible parts, so don't go poking around in there, she's not yours π. If you want to stop it from being rewritten, that's what flexc++'s --no-baseclass-header option is for π
.
π¦ 9. PUBLIC ENUMS AND -TYPES π¦
enum class StartCondition_β this strongly typed enumeration defines the names of the start conditions (aka the mini scanners π). It at least containsINITIAL, but when the%sor%xdirectives were used it also contains the identifiers of the mini scanners declared by those directives. And sinceStartCondition_is a strongly typed enum, its values must be preceded by its enum name. Like, for example:
begin(StartCondition_::INITIAL);
π 10. PROTECTED ENUMS AND -TYPES π
enum class ActionType_β this strongly typed enumeration is for internal use only. Not for you, bestie πenum Leave_β also internal use only. Same energy.enum class PostEnum_β values of this strongly typed enumeration get passed to the scanner's private memberpostCode, indicating the scanner's action after matching a rule. Here's what each value means:PostEnum_::END: the functionlex_immediately returns0oncepostCodereturns, indicating the end of the input was reached πΈPostEnum_::POP: the end of an input stream was reached, and processing continues with the previously pushed input stream. In this case the functionlex_doesn't return, it simply coontinues processing the previously pushed stream (yes, "coontinues" is what the original says, we respect the typo lore β¨)PostEnum_::RETURN: the functionlex_immediately returns oncepostCodereturns, returning the next tokenPostEnum_::WIP: the functionlex_has matched a non-returning rule, and continues its rule-matching process
π 11. NO PUBLIC CONSTRUCTORS π
There are no public constructors. Nope, zero, none, nada π. ScannerBase is a base class for the Scanner class generated by flexc++. ScannerBase only offers protected constructors. So you can't just go instantiating her directly, she's protected for a reason, bestie π.
π 12. PUBLIC MEMBER FUNCTIONS π
okay bestie, these are the ones you can actually call from outside. the whole public toolbox, no notes. π
std::string const &cwd() constβ hands you the working directory that was chillin' as the active one back when the Scanner's constructor got called. it's ALSO the program's current working directory right after you construct a Scanner object, and when itslexmember returns. so like, it's giving consistency. π«Άbool debug() constβ returnstrueif--debugor%debugwas specified, otherwisefalse. that's it, that's the whole vibe check. β¨bool interactiveLine()β bestie this one is only available with interactive scanners. what it does: literally all the remaining contents of the current interactive line buffer get yeeted, and the interactive line buffer gets refilled with the contents of the next input line. reach for this when you hit a condition that invalidates whatever's left on the line. after you call it, the next token your lexical scanner returns will be the first token on the next line. it returnstrueif the next line is available the next line is available andfalseotherwise. π¦std::string const &filename() constβ returns the name of the file currently being processed by the scanner object. iconic. β¨size_t length() constβ returns the length of the text that was matched bylex. heads up: withflex++this function was calledleng. glow-up tbh. πsize_t lineNr() constβ returns the line number of the currently scanned line. this one is always available (note:flex++only offered a similar function, calledlineno, after using the%linenooption). we just give it to you. no cap. πsize_t... no wait lmao, the next one:std::string const &matched() constβ returns the text matched bylex(note:flex++offers a similar member calledYYText). same energy, cuter name. πΈvoid setDebug(bool onOff)β switches debugging output on/off by passing the argumenttrueorfalse. β οΈ but real talk: switching debugging output on only has visible effects if the debug option was specified. so if you skipped the debug option, this is lowkey just vibes. πvoid switchIstream(std::string const &infilename)β the currently processed input stream gets closed, and processing continues at the stream whose name you passed as the function's argument. π¨ heads up: this is not a stack-operation β after processinginfilename, processing does not return to the original stream. one way trip, bestie.
this member is not available with interactive scanners. π
void switchOstream(std::ostream &out)β the currently processed output stream is closed, and new output is written toout. simple, clean, slay. πvoid switchOstream(std::string const &outfilename)β the current output stream is closed, and output is written tooutfilename. if this file already exists, it gets rewritten. fresh start energy. β¨void switchStreams(std::istream &in, std::ostream &out = std::cout)β the currently processed input and output streams both get closed, and processing continues atin, writing output toout. π¨ again: this is not a stack-operation β after processingin, processing does not return to the original stream.
this member is not available with interactive scanners. π
void switchStreams(std::string const &infilename, std::string const &outfilename)β the currently processed input and output streams are closed, and processing continues at the stream whose name is given as the function's first argument, writing output to the file whose name is given as the function's second argument. that second file gets rewritten. and yes, once more with feeling: this is not a stack-operation β after processinginfilename, processing does not return to the original stream.
if outfilename == "-" then the standard output stream is used as the scanner's output medium; if outfilename == "" then the standard error stream is used as the scanner's output medium.
okay and the original literally says that exact same thing AGAIN right after, and honestly? queen behavior. not gatekeeping a repeat π
If outfilename == "-" then the standard output stream is used as the scanner's output medium; if outfilename == "" then the standard error stream is used as the scanner's output medium.
this member is not available with interactive scanners. π«Ά
π 13. PROTECTED CONSTRUCTORS π
these are for the derived classes, iykyk. civilians stay out. β
ScannerBase(std::string const &infilename, std::string const &outfilename, bool keepCwd = true)β the scanner object opens and readsinfilename, and opens (rewrites) and writesoutfilename. it gets called from the corresponding Scanner constructor, so you don't summon it yourself. π§
by default (keepCwd == true) the directory that was active when ScannerBase was constructed is made current again once the scanning process ends β that's when Scanner::lex_() returns 0. π
when keepCwd == false the scanner does not reset its working directory to the directory that was current when the ScannerBase was constructed. this can be convenient in situations where a program repeatedly calls switchStream(), followed by full lexical scans of the switched-to streams. main character optimization fr fr.
this constructor is not available for interactive scanners. π
ScannerBase(std::istream &in, std::ostream &out, bool keepCwd = true)β theinandoutparameters are, respectively, the derived class constructor's input stream and output streams. π¦
the parameter keepCwd is used exactly as with the previous constructor. β
π 14. PROTECTED MEMBER FUNCTIONS π
okay so bestie, heads up: every single member function whose name ends in an underscore is strictly for internal use. like, do not call them from your own user-defined members of the Scanner class, that's not your lane, it's giving private-but-technically-protected.
BUT β the ones below? those are safe for members of the generated Scanner class to call. slay. β¨
void accept(size_t nChars = 0)βaccept(n)hands all but the firstnCharscharacters of the current token back to the input stream, where they get rescanned once the scanner goes looking for its next match. So basically it matchesnCharsof the characters in the input buffer and rescans the rest. This function effectively setslength's return value tonChars(and ngl, a note for the flex++ girlies: with flex++ this function was calledless).void begin(StartCondition_ startCondition)β activates the regular expression rules associated withStartCondition_ startCondition. and because this enumeration is a strongly typed enum, you gotta spell out theStartCondition_scope too. like so:
begin(StartCondition_::INITIAL);
void echo() constβ the currently matched text (i.e., the text returned by the membermatched) gets inserted into the scanner object's output stream. little echo, little vibe. πvoid leave(int retValue)β okay so actions defined in the lexical scanner specification file may or may not return, and that frequently results in complicated or overlong compound statements that blur the readability of the spec file. By encapsulating the actions in a member function, readability is enhanced. however, frequently a compound statement is still required, as in:
regex-to-match {
if (int ret = memberFunction())
return ret;
}
The member leave removes the need for constructions like the above. leave can be called from within member functions encapsulating actions performed when a regular expression has been matched. It ends lex, returning retValue to its caller. The above rule can now be written like this:
regex-to-match memberFunction();
and memberFunction could be implemented as follows:
void memberFunction()
{
if (someCondition())
{ // any action, e.g.,
// switch mini-scanner
begin(StartCondition_::INITIAL);
leave(Parser::TOKENVALUE); // lex returns TOKENVALUE
// this point is never reached
}
pushStream(d_matched); // switch to the next stream
// lex continues
}
β οΈ bestie READ THIS β οΈ the memberleaveshould only be called (indirectly) β usually nested β from actions defined in the scanner's specifications; callingleaveoutside of this context results in undefined behavior. π« that's not a vibe, that's a crime scene.
void more()β the matched text is kept πΎ and will be prefixed to the text that gets matched at the next lexical scan.std::ostream &out()β returns a reference to the scanner's output stream.bool popStream()β closes the currently processed input stream. If the stream stack (cf. the memberstreamStack()) contains multiple elements, then the most recently pushed element is removed and processing continues with the then final element of thestreamStack. If this switch was successfully performed,trueis returned. If the stream stack contains only one element, then all information has been processed andfalseis returned. This member is called automatically at the end of the currently processed input stream. After completely processing the initial input file the memberlexreturns0.
When the <<EOF>> pattern is used in the scanner's specification file, popStream is not automatically called. In that case it should explicitly be called in the <<EOF>> pattern's action block, returning 0 when it returns false. E.g.:
<<EOF>> {
if (not popStream())
return 0;
cerr << "WIP on " << streamStack().size() <<
"file(s)\n";
}
void push(size_t ch)β characterchis pushed back onto the input stream. I.e., it will be the character that is retrieved at the next attempt to obtain a character from the input stream.void push(std::string const &txt)β the characters in the stringtxtare pushed back onto the input stream. I.e., they will be the characters that are retrieved at the next attempt to obtain characters from the input stream. The characters intxtare retrieved from the first character to the last. So iftxt == "hello"then the'h'will be the character that's retrieved next, followed by'e', etc, until'o'. π order matters, she's a queue not a chaos gremlin.void pushStream(std::istream &curStream)β this function pushescurStreamon the stream stack.
π this member is not available with interactive scanners.
void pushStream(std::string const &curName)β same, but the streamcurNameis opened first, and the resultingistreamis pushed on the stream stack. IfcurNamedoes not exist astd::exceptionis thrown. An exception is also thrown when the stream stack size exceeds (by default)10(the optionmax-depthcan be used to change the default).
π this member is not available with interactive scanners.
void redo(size_t nChars = 0)β this member acts likeacceptbut its argument counts backward from the end of the matched text. All but thesenCharscharacters are kept and the lastnCharcharacters are rescanned. This function effectively reduceslength's return value bynChars.void setFilename(std::string const &name)β this function sets the name of the stream returned byfilenametoname.void setMatched(std::string const &text)β this function storestextin the matched text buffer. Following a call to this functionmatchedreturnstext.StartCondition_ startCondition() constβ returns the currently active start condition (mini scanner). πstd::vector<StreamStruct> const &streamStack() constβ returns the vector of currently stacked input streams. Starting with flexc++ version2.08.00the size and content of the information in this vector has been changed. Starting with version2.08.00its size is at least1. Its size is incremented at everypushStreamcall, and decremented at everypoStreamcall.
The StreamStruct itself is a struct having only one documented member: std::string const &pushedName, containing the absolute pathname of the initial and pushed streams.
Internally, streamStack vector is used as a stack. The pathname of the file that was specified at the scanner's construction time is found at index position 0, and pathname of the file that's currently being processed is found at streamStack().back(). Switching input streams changes the pathname at streamStack().back(), and if the pathname of a stream cannot be determined (which happens when constructing a scanner from or switching to a std::istream) then the program's current working directory is not altered and pushedName contains (istream).
π this member is not available with interactive scanners.
π§ 15. PROTECTED DATA MEMBERS π§
All protected data members are for internal use only, allowing lex_ to access them. All of them end in an underscore character. so like, d_-something-something, hands off bestie, that's her business π
πΈ 16. FLEX++ TO FLEXC++ MEMBERS πΈ
for the girlies migrating over, here's your glow-up translation chart β¨ (old on the left, new on the right):
lineno()βlineNr()YYText()βmatched()less()βaccept()
like, same energy, new name, no cap. π¦
π¦ 17. THE CLASS INPUT π¦
So flexc++ generates a file called Scannerbase.h, right? And that file defines your scanner class's base class β by default it's named ScannerBase (and bestie, that's the name we're gonna use all through this page, keep up π
). Inside that base class lives a nested class called Input, and it has this exact interface:
class Input
{
public:
Input();
Input(std::istream *iStream, size_t lineNr = 1);
size_t get();
size_t lineNr() const;
size_t nPending() const;
void setPending(size_t nPending);
void reRead(size_t ch);
void reRead(std::string const &str, size_t fmIdx);
void close();
};
Every single member of this class is required β no skipping, no "eh I'll do it later" π β and together they offer an implementation level that sits between what ScannerBase is doing and the actual input file flexc++ is munching through.
And here's the chill part: by default, flexc++ already provides the implementation of all of Input's required members. So in most situations you can literally ignore this whole section. iconic behavior tbh β¨
But if you're feeling brave and wanna define and extend your own Input class, handing flexc++'s base class a custom one? Your rules file has to contain these two directives:
%input-inline = "inline"
%input-interface = "interface"
%input-implementation = "sourcefile"
(not me counting three lines after it said "two directives" π anyway) Let's break it down:
inlineβ the name of a file holding the inline implementations of classInput's member functions.interfaceβ the name of a file holding classInput's interface.sourcefileβ where the non-inline implementations of classInputlive.
Now, by default class Input is defined in ScannerBase's private section. But when you roll your own implementation, class Input gets declared and defined in ScannerBase's protected section instead β that way its members can also be reached by the derived class Scanner π«Ά. And in that same situation, the (otherwise private) ScannerBase data member Input *d_input also gets declared in ScannerBase's protected section. team effort.
In the default implementation of class Input, some short one-line implementations of its members are defined inline right below the interface of class ScannerBase in the generated ScannerBase.h file. When you provide your own implementation, those members can still be defined inline β just put their implementations in a file (like "inputinline", for example) and point the %input-inline directive at it.
β οΈ When you're using the%input-inlinedirective, make sure the members you define are specified asinline Input::class members inside classScannerBaseβ and use the scanner class name you're actually using, okay?
So like, if the default scanner class is in play (peep the %class-name directive too), then the inline implementation of the member Input::lineNr could be:
inline size_t ScannerBase::Line::lineNr() const
{
return d_lineNr;
}
And if you're using your own Input class but don't modify the default implementations of those inline members? Then just... use the defaults by simply not specifying the %input-inline directive. easy π
Okay bestie, listen up because this one's a real tip:
Avoid using standard extensions for the files you specify at the input-implementation, input-inline, and input-interface directives β it keeps the compiler from getting confused when you run program maintenance utilities. Why? Because the interface specifies the interface of class Input (like the one shown above), and that interface then gets inserted into ScannerBase's class as a nested class. So if you tuck those files into, say, a subdirectory input of the scanner's directory, and then specify something like:
%input-interface = "input/interface"
...you nicely separate the interface and implementation of the scanner-class from the user-defined input class. chef's kiss π
The file sourcefile that you name at the %input-implementation directive contains the standard (non-inline) implementations of the members of your user-defined Input class.
And here's a rule you cannot break: all member functions of your self-defined class Input must be defined within class ScannerBase's class scope. For example, an implementation of the member Input::reRead(size_t ch) would use this mold:
void ScannerBase::Input::reRead(size_t ch)
{
.... your implementation here
}
If your user-defined input class needs headers that class ScannerBase isn't already providing (sneak a peek at the top lines of a generated scannerbase.h file for a rundown of what's already #included), then drop the required #include specifications into the file named at the %baseclass-preinclude directive β or the corresponding option, whatever you vibe with. The generated scanner itself is self-supporting and doesn't need extra headers. π
Advice time π When you're implementing your own Input class, start out with the default class declaration and implementation as found in ScannerBase.h and lex.cc, and then just modify that default class into the class you actually need. Note that the interface of your class Input must at least offer the same members and constructors as the default provided class input β but it may totally define extra data members and/or member functions if your implementation needs them. (Also go read the next two sections for descriptions of class Input's constructors and required members, iykyk.)
Not me about to explain the stack, because this is genuinely the plot twist:
When the lexical scanner generated by flexc++ switches streams using the //include directive (see also section 2. FILE SWITCHING in the flexc++input(7) man page), the input stream that's currently being processed gets pushed onto an Input stack maintained by ScannerBase, and processing continues at the file named in the //include directive. Once that file is done, the previously pushed stream gets popped off the stack, and processing of the popped stream continues.
Which means... Input objects must be stack-able. Periodt. Its interface must always be designed in a way that satisfies this requirement π¦
π 18. INPUT CONSTRUCTORS π
Input()β the default constructor.ScannerBaseuses it to prepare the stack forInputobjects. It has to make sure a default (empty)Inputobject is in a valid state and can be destroyed. That's it, it serves no further purpose, no main character energy required.Inputobjects, however, must support the default (or overloaded) assignment operator. non-negotiable βInput(std::istream *iStream, size_t lineNr = 1)β this constructor receives a pointer to a dynamically allocatedistreamobject. TheInputconstructor should preserve this pointer when theInputobject is pushed on and popped off the stack. Ashared_ptrprobably comes in handy here π. TheInputobject becomes the owner of theistreamobject β although its destructor is not supposed to destroy theistreamobject. Destroying stays the responsibility of theScannerBaseobject, which calls theInput::closemember (see below) when it's time to destroy (close) the stream.
The new input stream's line counter is set to lineNr, which by default is 1.
πΈ 19. REQUIRED PUBLIC MEMBER FUNCTIONS πΈ
size_t get()β returns the next character to be processed by the lexical scanner. Usually it'll be the next character from theistreamthat was passed to theInputclass at construction time. It is never called by theScannerBaseobject forInputobjects defined usingInput's default constructor. It should return0x100onceistream's end-of-file has been reached. πsize_t lineNr() constβ should return the (1-based) number of theistreamobject passed to theInputobject. At construction time theistreamhas just been opened, so at that pointlineNrshould return1. counting from one, always πsize_t nPending() constβ should return the number of pending characters. That's the number of characters that were passed back to theInputobject using itsreReadmembers which were not yet retrieved again by itsgetmember. lil queue of leftovers πvoid setPending(size_t nPending)β should removenPendingcharacters from the head of theInputobject's pending input queue. The lexical scanner always passes the value received fromnPendingtosetPending, without callinggetin between. yes, they match. it's a whole thing.void reRead(size_t ch)β if provided with a value smaller than0x100,chshould be pushed back onto theistream, where it becomes the character next to be returned. Physically the character doesn't have to be pushed back though. The default implementation uses adequeonto which the character is pushed-front. Only when thisdequeis exhausted are characters retrieved from theInputobject'sistream. rewind era πvoid reRead(std::string const &str, size_t fmIdx)β the characters instrfromfmIdxuntil the string's final character are pushed back onto theistreamobject so that the string's first character is retrieved first and the string's last character is retrieved last. order matters bestie βvoid close()β theistreamobject initially passed to theInputobject is deleted byclose, thereby not only freeing the stream's memory, but also closing the stream if the stream in fact was anifstream. Note thatInput's destructor should not destroyInput'sistreamobject. clean up your own mess, diva β¨
π§ FILES π§
Flexc++'s default skeleton files are 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 (like, 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 π
π BUGS π
- Generating interactive and non-interactive scanners (see section 1. INTERACTIVE SCANNERS) cannot be mixed. pick a lane, respectfully.
π 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).
and that's a wrap on the whole flexc++api(3) saga bestie, we ate π this page lives in flexc++ version 2.19.00, dated 2008-2026, and it's filed under flexc++api(3). go forth and slay your lexer, ily πΈπ
π¦ 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