* oct
* o-----------o
* | dec |
* | |
* v |
* -------------- |
* o------->| s03 | ---o
* | --------------
* -------------- oct | |
* | s00 |-------------o |
* -------------- dec |
* ^ |
* | |
* | ~oct |
* o-----------------------------------o
* ~dec
*
*
* where the arc transitions stand for 0 1 2 3 4 5 6 7 7 8 not, as in, not a octal number or decimal number
*/
static const Descriptor GOTO[][16] = {
// STATES
// . +/- S "S" chr ' U U
// 0 OCT DEC HEX +/- DBL exp exp str dqu squ squ uin uchr
//events s00 s01 s02 s03 s04 s05 s06 s07 s08 s09 s10 s11 s12 s13 s14 s15
/*EOF*/ {s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s00}
/*spc*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*zro*/,{s01, s02, s02, s03, s04, s01, s06, s07, s08, s09, s10, s12, s09, s00, s09, s09}
/*oct*/,{s03, s02, s02, s03, s04, s03, s06, s07, s08, s09, s10, s12, s09, s00, s09, s09}
/*dec*/,{s03, s03, s00, s03, s04, s03, s06, s07, s08, s09, s10, s12, s09, s00, s09, s09}
/*hex*/,{s09, s09, s00, s00, s04, s09, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*alp*/,{s09, s09, s00, s09, s09, s09, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*lpr*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*rpr*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*lcr*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*rcr*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*lng*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*rng*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*cln*/,{s00, s00, s00, s00, s00, s00, s00, s00, s00, s00, s10, s12, s00, s00, s00, s00}
/*squ*/,{s11, s00, s00, s00, s00, s00, s00, s00, s00, s09, s10, s00, s13, s00, s00, s00}
/*dqu*/,{s10, s00, s00, s00, s00, s00, s00, s00, s00, s09, s00, s12, s09, s00, s00, s00}
/*sls*/,{s09, s00, s00, s00, s00, s00, s00, s00, s09, s09, s10, s12, s09, s00, s09, s09}
/*esc*/,{s09, s00, s00, s00, s00, s00, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*ast*/,{s09, s00, s00, s00, s00, s00, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*plu*/,{s05, s00, s00, s00, s00, s00, s08, s00, s08, s09, s10, s12, s09, s00, s09, s09}
/*min*/,{s05, s09, s00, s00, s00, s00, s00, s00, s08, s09, s10, s12, s09, s00, s09, s09}
/*per*/,{s06, s06, s06, s06, s00, s06, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*x */,{s09, s04, s00, s00, s09, s00, s00, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*d */,{s09, s09, s00, s00, s04, s00, s07, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*e */,{s09, s09, s00, s00, s04, s00, s07, s00, s00, s09, s10, s12, s09, s00, s09, s09}
/*u */,{s09, s09, s00, s14, s00, s00, s00, s00, s00, s09, s10, s12, s09, s15, s09, s09}
}; // Descriptor GOTO[][13]
/**
* Arc functions.
*
* At each FSM transition a function is executed. The values given stand for
* the switch statement value which selects a case branch statement. This
* brach statement performs the indicated operation.
*/
// FSM function definition
static const Descriptor& END= { 0, "EOF"}; //!< END OF FILE
static const Descriptor& NUL= { 1, "NUL"}; //!< do nothing
static const Descriptor& STP= { 2, "END"}; //!< generate an End of File Token
static const Descriptor& DEC= { 3, "DEC"}; //!< generate an integer Token
static const Descriptor& ESC= { 4, "ESC"}; //!< Escape sequence processing
static const Descriptor& BIN= { 5, "BIN"}; //!< process a decimal Token
static const Descriptor& CHR= { 6, "CHR"}; //!< character token, get next chr
static const Descriptor& CTT= { 7, "CTT"}; //!< character token, keep chr
static const Descriptor& DBL= { 8, "DBL"}; //!< generate a double Token
static const Descriptor& HEX= { 9, "HEX"}; //!< process a hexadecimal Token
static const Descriptor& OCT= {10, "OCT"}; //!< process an octal Token
static const Descriptor& SGN= {11, "SGN"}; //!< set negative sign
static const Descriptor& STR= {12, "STR"}; //!< generate a string Token
static const Descriptor& STT= {13, "STT"}; //!< string token, get next chr
static const Descriptor& TOK= {14, "TOK"}; //!< return delimiter Token
static const Descriptor& UCK= {15, "UCK"}; //!< unsigned char token, keep chr
static const Descriptor& UCH= {16, "UCH"}; //!< unsigned char token, get next chr
static const Descriptor& UIK= {17, "UIK"}; //!< unsigned int token, keep chr
static const Descriptor& UIN= {18, "UIN"}; //!< unsigned int token, get next chr
// diagnostic output
static const Descriptor& TOO= {19, "TOO"}; //!< too many +|- signs
static const Descriptor& EXP= {20, "EXP"}; //!< too many +|- signs in exponent
static const Descriptor& NUM= {21, "NUM"}; //!< Illegal number - converting to string
/**
* FSM arc transition executable function.
*
* At each entry in the table, a function is executed (via a switch statement)
* to execute. This function is the FSM arc transition function.
*
* For example in:
*
*
* oct
* o-----------o
* | dec |
* | | --NUL
* v |
* -------------- |
* o------->| s03 | ---o
* | --------------
* -------------- | |
* | s00 |--- dec -----o -- NUL |
* -------------- | -- BIN
* ^ |
* | |
* | ~oct |
* o-----------------------------------o
* ~dec
*
*
* where the arc transition functions are: do nothing generate a decimal number Token
* --------------
* o------->| state/node |
* | --------------
* -------------- label |
* | state/node |--- arc -----o
* -------------- event
*
*
* The following definitions apply to the example:
* table[event][state], where the 'event' is the arc
* transition trigger, and the 'state' is the current state.
*
* Events in the lexer are alphabet classes, where a class is a grouping of
* letters in the input alphabet whUCH are functionally related. For example,
* the letters 0 1 2 3 4 5 6 7 8 9 A B C D E F are members of the
* hexadecimal class.
*
* >b>Lexical tokens
* Comments are not returned to the parser. There are two types of comments://: line commentblock comment| integer | *[+|-][0-9]+U? * 0[0-7]+ * 0(x|X)[0-9a-fA-F]+ |
* decimal number * octal number * hexadecimal number |
*
| floating point | *[+|-][0-9]+.[[e|E][ |+|-][0-9]+ * [+|-][0-9]*.[0-9]+[[e|E][ |+|-][0-9]+ * [+|-][0-9]+.[[d|D][ |+|-][0-9]+ * [+|-][0-9]*.[0-9]+[[d|D][ |+|-][0-9]+ |
* single precision * single precision * double precision * double precision |
*
| boolean | *true * false |
* case ignored | *
| character | *c * 'c'U? * '\''U? |
* simple char * quoted char * escaped char |
*
| string | *abc * "a.." * "... \c\" ... |
* more than 1 char * quoted string |
* quoted "
| escape sequence | *\' * \" * \\ * \b * \f * \n * \r * \t * \uFFFF |
* single quote * double quote * backslash * backspace * form feed * line feed * carriage return * horizontal tab * unicode character |
*
* space
* ( ) left/right parenthesis
* { } left/right curly braces
* < > left/right angle brackets
* spc space
* BOL beginning of line
* EOL end of line
* EOF end of file/input
*
*
* Text handling:
*
* 0257 a octal number, decimal value 175
* 0x257 a hexadecimal number, decimal value 599
* 257 a decimal number, decimal value 257
* A character
* 'A' character
* "A" string
* 0123A string
* ++ string
* \n character (line feed)
* '\n' character (line feed)
* "\n" string (line feed)
* "str\012" string ("str\n")
* 'ab' string ("ab")
*
*
* @return token containing recognized type and value
*
* @author A. Schwarz
*/
/**
* Finite State Machine States.
*
* The values represent an index into the FSM tables, the GOTO and the
* EXE tables, where GOTO[alphabetic class][state] represents
* the transition arc to the next state in the FSM, and
* EXE[alphabetic class][state] represents the function to be
* performed at the transition.
*/
SlipToken& SlipLexer::lexer() {
static const Descriptor& ENDLOOP = s00; //!< lexer loop end
/******************* lexer code *******************/
if (in.isEOF()) {
token.start(in.getLine(), in.getCol());
token.token(SlipRead::END);
return token;
};
// remove all white space and comments before tokenization of input
do {
if (map[chr].value() == spc.value())
while((chr = in.getChar()) == ' '); // remove leading space
// Remove Comments
if (chr == '/') {
char peek = in.peek();
if (peek == '/') { //!< line comment ("//")
while( (chr = in.getChar()) != '\n' &&
(chr != in.END));
} else if (peek == '*') { //!< block comment ("/* */")
in.getChar(); // skip over '*'
do {
while( (chr = in.getChar()) != '*' &&
(chr != in.END));
chr = in.getChar();
if ((chr == '/') || (chr == in.END)) break;
} while (chr != in.END);
chr = in.getChar();
}
}
} while(map[chr].value() == spc.value());
// Token processing: Note we always start at s00
Descriptor state = s00; // lexer FSM start state
int sign = 1; //!< integer sign count
unsigned int value = 0; //!< integer value
string str; //!< local token copy
token.start(in.getLine(), in.getCol()); // create Token
do { // FSM loop
mapClass clss = (chr > 0xFF)? alp: map[chr]; //!< character class
Descriptor curState = state;
str.push_back(chr);
state = GOTO[clss.value()][curState.value()];
Descriptor exe = EXE[clss.value()][curState.value()];
if (debug) {
char fchr = (chr == in.END)? ' ': (char)(chr & 0x00FF);
static const string str1 = "lex delta(\'";
static const string str2 = "\', ";
string debug = str1 + fchr + str2 + curState.geteName() + ") => <"
+ exe.geteName() + ", " + state.geteName()
+ ">\n";
out.print(debug);
out.flush();
}
switch (exe.value()) {
case 0: //!< STP: endo of file found
break;
case 1: //!< NUL: do nothing
break;
case 2: //!< END: generate an End of File Token
token.token(SlipRead::END);
continue;
case 3: //!< DEC: decimal number token
token.token(SlipRead::INTEGER, new SlipDatum((LONG)(sign*value)));
sign = 1;
continue;
case 4: { //!< ESC: escape sequence processing
chr = escapeSequenceProcessing(str);
continue; }
case 5: //!< BIN: process a decimal number
value = value * 10 + (chr - '0');
break;
case 6: //!< CHR: generate a character Token
token.token(SlipRead::CHR, new SlipDatum((CHAR)str.at(1)));
break;
case 7: //!< CTT:generate a character Token
token.token(SlipRead::CHR, new SlipDatum((CHAR)str.at(1)));
continue;
case 8: { //!< DBL: generate a double Token
try {
double dbl = (double)stod(str);
token.token(SlipRead::DOUBL, new SlipDatum((DOUBLE)dbl));
} catch (invalid_argument const& ex) {
cout << "invalid argument " << "\"" << str << "\"" << ex.what() << endl;
token.token(SlipRead::DOUBL, new SlipDatum((DOUBLE)0.0));
}
continue; }
case 9: { //!< HEX: process a hexadecimal number
int hexvalue = ((chr - 'A') < 0)? (chr - '0')
: ((chr | 0x20) - 'a' + 10);
value = value * 16 + hexvalue;
break; }
case 10: { //!< OCT: generate an octal Token
value = value * 8 + (chr - '0');
break; }
case 11: //!< SGN: sign (+|-) processing
if (chr == '-') sign = -1;
break;
case 12: { //!< STR: generate a string Token
int length = str.length() - 1;
string sub = str.substr(0, length); // substring("true" or "false")
if (sub == "false") {
token.token(SlipRead::BOOLEAN, new SlipDatum((bool)false));
} else if (sub == "true") {
token.token(SlipRead::BOOLEAN, new SlipDatum((bool)true));
} else if (length == 1){
token.token(SlipRead::CHR, new SlipDatum((CHAR)str.at(0)));
} else {
token.token(SlipRead::STR, new SlipDatum(sub));
}
continue; }
case 13: //!< STT: generate a string Token
token.token(SlipRead::STR, new SlipDatum(str.substr(1, str.length()-2)));
break;
case 14: //!< TOK: return delimiter Token
token.token(clss.type(), new SlipDatum((CHAR)str.at(0)));
break;
case 15: //!< UIK: unsigned char token, keep chr
token.token(SlipRead::CHR, new SlipDatum((UCHAR)str.at(1)));
continue;
case 16: //!< UCH: unsigned char token, get next chr
token.token(SlipRead::CHR, new SlipDatum((UCHAR)str.at(1)));
break;
case 17: //!< UIK: unsigned int token, keep chr
token.token(SlipRead::CHR, new SlipDatum((CHAR)value));
continue;
case 18: //!< UIN: unsigned int token, get next chr
token.token(SlipRead::INTEGER, new SlipDatum((ULONG)value));
break;
// diagnostic statements
case 19: //!< TOO: too many +|- signs
postError(__FILE__, __LINE__, E5001, str);
break;
case 20: //!< EXP: too many +|- signs in exponents
postError(__FILE__, __LINE__, E5002, str);
break;
case 21: //!< NUM: Illegal number - converting to string
postError(__FILE__, __LINE__, E5003, str);
break;
default:
break;
};
chr = in.getChar(); //!< get next character
} while (state.value() != s00.value());
return token;
}; // SlipRead::SlipToken SlipLexer::lexer()
/**
* Escape sequence processing.
*
* Escape sequence processing yields a single unicode character.
* This character can be one of the special characters {\b, \f,
* \n, \r, \t}, a unicode character, '\uxxxx', a hexadecimal or
* an octal number.
*
* Processing uses an FSM, similar to the lexer FSM, to determine
* what to return.
*
* Using a separate function to process escape sequences simplifies
* the lexer FSM.
*
* On exit, the next character to be processed is input.
*
* @param[in] lexer recognized string.
*/
char SlipLexer::escapeSequenceProcessing(StringBuilder str) {
/**
* Finite State Machine States.
*
* The values represent an index into the FSM tables, the GOTO and the
* EXE tables, where GOTO[alphabetic class][state] represents
* the transition arc to the next state in the FSM, and
* EXE[alphabetic class][state] represents the function to be
* performed at the transition.
*/
// states
static Descriptor s00= { 0, "s00:start "}; //!< start
static Descriptor s01= { 1, "s01:zero "}; //!< zero found, could be octal/hex
static Descriptor s02= { 2, "s02:oct "}; //!< octal number
static Descriptor s03= { 3, "s03:dec "}; //!< decimal number
static Descriptor s04= { 4, "s04:hex "}; //!< hexadecimal number
/**
* GOTO table shows Finite State Machine transfer arcs.
*
* The global state Descriptor values are used.
*
*/
static const Descriptor GOTO[][6] = {
// STATES
// 0 OCT DEC HEX
//events s00 s01 s02 s03 s04
/*EOF*/ {s00, s00, s00, s00, s00}
/*spc*/,{s00, s00, s00, s00, s00}
/*zro*/,{s01, s02, s02, s03, s04}
/*oct*/,{s03, s02, s02, s03, s04}
/*dec*/,{s03, s00, s00, s03, s04}
/*hex*/,{s00, s00, s00, s00, s04}
/*alp*/,{s00, s00, s00, s00, s00}
/*lpr*/,{s00, s00, s00, s00, s00}
/*rpr*/,{s00, s00, s00, s00, s00}
/*lcr*/,{s00, s00, s00, s00, s00}
/*rcr*/,{s00, s00, s00, s00, s00}
/*lng*/,{s00, s00, s00, s00, s00}
/*rng*/,{s00, s00, s00, s00, s00}
/*cln*/,{s00, s00, s00, s00, s00}
/*squ*/,{s00, s00, s00, s00, s00}
/*dqu*/,{s00, s00, s00, s00, s00}
/*sls*/,{s00, s00, s00, s00, s00}
/*esc*/,{s00, s00, s00, s00, s00}
/*ast*/,{s00, s00, s00, s00, s00}
/*plu*/,{s00, s00, s00, s00, s00}
/*min*/,{s00, s00, s00, s00, s00}
/*per*/,{s00, s00, s00, s00, s00}
/*x */,{s00, s04, s00, s00, s00}
/*d */,{s00, s00, s00, s00, s04}
/*e */,{s00, s00, s00, s00, s04}
}; // Descriptor GOTO[][6]
/**
* Arc functions.
*
* At each FSM transition a function is executed. The values given stand for
* the switch statement value whUCH selects a case branch statement. This
* brach statement performs the indicated operation.
*/
// FSM function definition
static Descriptor nul= { 0, "nul "}; //!< do nothing
static Descriptor end= { 1, "stop "}; //!< terminate processing
static Descriptor OCT= { 2, "octal "}; //!< terminate octal processing
static Descriptor DEC= { 3, "dec "}; //!< terminate decimal processing
static Descriptor HEX= { 4, "hex "}; //!< terminate hexadecimal processing
static Descriptor RMV= { 5, "remove"}; //!< remove "\0x" from input
static const Descriptor EXE[][6] = {
// STATES
// 0 OCT DEC HEX
//events end s01 s02 s03 s04
/*EOF*/ {end, end, end, end, end}
/*spc*/,{end, end, end, end, end}
/*zro*/,{nul, nul, OCT, DEC, HEX}
/*oct*/,{nul, OCT, OCT, DEC, HEX}
/*dec*/,{nul, DEC, end, DEC, HEX}
/*hex*/,{end, end, end, end, HEX}
/*alp*/,{end, end, end, end, end}
/*lpr*/,{end, end, end, end, end}
/*rpr*/,{end, end, end, end, end}
/*lcr*/,{end, end, end, end, end}
/*rcr*/,{end, end, end, end, end}
/*lng*/,{end, end, end, end, end}
/*rng*/,{end, end, end, end, end}
/*cln*/,{end, end, end, end, end}
/*squ*/,{end, end, end, end, end}
/*dqu*/,{end, end, end, end, end}
/*sls*/,{end, end, end, end, end}
/*esc*/,{end, end, end, end, end}
/*ast*/,{end, end, end, end, end}
/*plu*/,{end, end, end, end, end}
/*min*/,{end, end, end, end, end}
/*per*/,{end, end, end, end, end}
/*x */,{end, nul, end, end, end}
/*d */,{end, end, end, end, HEX}
/*e */,{end, end, end, end, HEX}
}; // Descriptor EXE[][6]
Descriptor state = s00; //!< current FSM state
int value = 0; //!< excaped value to be inserted
char c_string[2] = {'\0', '\0'};
/*********** escapeSequenceProcessing code **********/
chr = in.getChar(); //!< Escape (\\) recognized, get next character
if (chr > 0x00FF) return chr; //!< unicode character
if ((chr | 0x0020) == 'u') { //!< unicode character must be hexadecimal
state = s04;
chr = in.getChar();
} else if (map[chr].value() == alp.value()) { //!< Alphabetic character
if (chr == 'b') chr = '\b';
else if (chr == 'f') chr = '\f';
else if (chr == 'n') chr = '\n';
else if (chr == 'r') chr = '\r';
else if (chr == 't') chr = '\t';
str.append(&(c_string[0] = chr));
return in.getChar();
} else if (map[chr].value() == esc.value()) {
str.append(&(c_string[0] = chr));
return in.getChar();
}
do {
Descriptor clss = (chr > 0x00FF)? alp: map[chr]; //!< character class
Descriptor curState = state;
state = GOTO[clss.value()][curState.value()];
Descriptor exe = EXE[clss.value()][curState.value()];
if (debug) {
char fchr = (chr == in.END)? ' ': chr;
static const string str1 = "escape delta(\'";
static const string str2 = "\', ";
string debug = str1 + fchr + str2 + curState.geteName() + ") => <"
+ exe.geteName() + ", " + state.geteName() + ">\n" ;
out.print(debug);
out.flush();
}
try {
switch(exe.value()) {
case 0: //!< nul: do nothing
break;
case 1: //!< stop
continue;
case 2: //!< octal
value = (value * 8) + (chr - '0');
break;
case 3: //!< decimal
value = (value * 10) + (chr - '0');
break;
case 4: //!< hexadecimal
int hexvalue = ((chr - 'A') < 0)? (chr - '0')
: ((chr | 0x20) - 'a' + 10);
value = (value * 16) + hexvalue;
break;
};
} catch(exception e) {
state = s00;
}
chr = in.getChar();
} while (state.value() != s00.value());
str.append(&(c_string[0] = (char)value));
return chr;
}; // char SlipLexer::escapeSequenceProcessing(SlipInputInterface in)
}; // namespace slip