/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the functions of the scanner, which provides a very ** important abstraction, namely the concept that an input file is a stream ** of symbols, while it hides such nasty things as <space>, <tab>, <newline> ** characters, comments (they are worst :-), characters making up ** identifiers or digits that make up integers from the rest of GAP.
*/
/**************************************************************************** ** *F SyntaxErrorOrWarning( <msg> ) . . . . . . raise a syntax error or warning ** ** Helper function used by 'SyntaxError' and 'SyntaxWarning'. **
*/ staticvoid SyntaxErrorOrWarning(ScannerState * s, constChar * msg,
UInt error, Int tokenoffset)
{
GAP_ASSERT(tokenoffset >= 0 && tokenoffset <= 2); // do not print a message if we found one already on the current line if (s->input->lastErrorLine != s->input->number) {
// print the message ... if (error)
Pr("Syntax error: %s", (Int)msg, 0); else
Pr("Syntax warning: %s", (Int)msg, 0);
// ... and the filename + line, unless it is '*stdin*'
Obj name = GetCachedFilename(GetInputFilenameID(s->input)); if (!streq("*stdin*", CONST_CSTR_STRING(name)))
Pr(" in %g:%d", (Int)name, GetInputLineNumber(s->input));
Pr("\n", 0, 0);
// print the current line constchar * line = GetInputLineBuffer(s->input); const UInt len = strlen(line); if (len > 0 && line[len-1] != '\n')
Pr("%s\n", (Int)line, 0); else
Pr("%s", (Int)line, 0);
// print a '^' pointing to the current position Int startPos = s->SymbolStartPos[tokenoffset]; Int pos; if (tokenoffset == 0)
pos = GetInputLinePosition(s->input); else
pos = s->SymbolStartPos[tokenoffset - 1];
/**************************************************************************** ** *F AppendBufToString() ** ** Append 'bufsize' bytes from the string buffer 'buf' to the string object ** 'string'. If 'string' is 0, then a new string object is allocated first. ** ** The string object is returned at the end, regardless of whether it was ** given as an argument, or created from scratch. **
*/ static Obj AppendBufToString(Obj string, constchar * buf, UInt bufsize)
{ char *s; if (string == 0) {
string = NEW_STRING(bufsize);
s = CSTR_STRING(string);
} else { const UInt len = GET_LEN_STRING(string);
GROW_STRING(string, len + bufsize);
SET_LEN_STRING(string, len + bufsize);
s = CSTR_STRING(string) + len;
}
memcpy(s, buf, bufsize);
s[bufsize] = '\0'; return string;
}
/**************************************************************************** ** *F Match( <symbol>, <msg>, <skipto> ) . match current symbol and fetch next ** ** 'Match' is the main interface between the scanner and the parser. It ** performs the four most common actions in the scanner with just one call. ** First it checks that the current symbol stored in the variable 'Symbol' ** is the expected symbol as passed in the argument <symbol>. If it is, ** 'Match' reads the next symbol from input and returns. Otherwise 'Match' ** first prints the current input line followed by the syntax error message: ** '^ syntax error, <msg> expected' with '^' pointing to the current symbol. ** It then skips symbols up to one in the resynchronisation set <skipto>. ** Actually 'Match' calls 'SyntaxError' so its comments apply here too. ** ** One kind of typical 'Match' call has the form ** ** 'Match( Symbol, "", 0 );'. ** ** This is used if the parser knows that the current symbol is correct, for ** example in 'ReadReturn' the first symbol must be 'S_RETURN', otherwise ** 'ReadReturn' would not have been called. Called this way 'Match' will of ** course never raise a syntax error, therefore <msg> and <skipto> are of no ** concern. The effect of this call is merely to read the next symbol from ** input. ** ** Another typical 'Match' call is in 'ReadIf' after we read the if symbol ** and the condition following, and now expect to see the 'then' symbol: ** ** Match( S_THEN, "then", STATBEGIN|S_ELIF|S_ELSE|S_FI|follow ); ** ** If the current symbol is 'S_THEN' it is matched and the next symbol is ** read. Otherwise 'Match' prints the current line followed by the error ** message: '^ syntax error, then expected'. Then 'Match' skips all symbols ** until finding either a symbol that can begin a statement, an 'elif' or ** 'else' or 'fi' symbol, or a symbol that is contained in the set <follow> ** which is passed to 'ReadIf' and contains all symbols allowing one of the ** calling functions to resynchronize, for example 'S_OD' if 'ReadIf' has ** been called from 'ReadFor'. <follow> always contain 'S_EOF', which 'Read' ** uses to resynchronise. ** ** If 'Match' needs to read a new line from '*stdin*' or '*errin*' to get ** the next symbol it prints the string pointed to by 'Prompt'.
*/ void Match(ScannerState * s,
UInt symbol, constChar * msg,
TypSymbolSet skipto)
{ Char errmsg [256];
// if 's->Symbol' is the expected symbol match it away if (symbol == s->Symbol) {
s->Symbol = NextSymbol(s);
}
// else generate an error message and skip to a symbol in <skipto> else {
gap_strlcpy( errmsg, msg, sizeof(errmsg) );
gap_strlcat( errmsg, " expected", sizeof(errmsg) );
SyntaxError(s, errmsg); while (!IS_IN(s->Symbol, skipto))
s->Symbol = NextSymbol(s);
}
}
/**************************************************************************** ** *F GetIdent() . . . . . . . . . . . . . get an identifier or keyword, local ** ** 'GetIdent' reads an identifier from the current input file into the ** variable 's->Value' and sets 'Symbol' to 'S_IDENT'. The first ** character of the identifier is the current character pointed to by 'In'. ** If the characters make up a keyword 'GetIdent' will set 'Symbol' to the ** corresponding value. The parser will ignore 's->Value' in this case. ** ** An identifier consists of a letter followed by more letters, digits and ** underscores '_'. An identifier is terminated by the first character not ** in this class. The backslash '\' can be used to include special ** characters like '(' in identifiers. For example 'G\(2\,5\)' is an ** identifier not a call to a function 'G'. ** ** The size of 's->Value' limits the number of significant characters in ** an identifier. If an identifier has more characters 'GetIdent' truncates ** it and signal a syntax error. ** ** After reading the identifier 'GetIdent' looks at the first and the last ** character of 's->Value' to see if it could possibly be a keyword. For ** example 'test' could not be a keyword because there is no keyword ** starting and ending with a 't'. After that test either 'GetIdent' knows ** that 's->Value' is not a keyword, or there is a unique possible ** keyword that could match, because no two keywords have identical first ** and last characters. For example if 's->Value' starts with 'f' and ** ends with 'n' the only possible keyword is 'function'. Thus in this case ** 'GetIdent' can decide with one string comparison if 's->Value' holds ** a keyword or not.
*/ static UInt GetIdent(ScannerState * s, Int i, Char c)
{ // initially it could be a keyword Int isQuoted = 0;
// read all characters into 's->Value' for (; IsIdent(c) || c == '\\'; i++) {
// handle escape sequences if (c == '\\') {
c = GET_NEXT_CHAR(); switch(c) { case'n': c = '\n'; break; case't': c = '\t'; break; case'r': c = '\r'; break; case'b': c = '\b'; break; default:
isQuoted = 1;
}
}
/// put char into 's->Value' but only if there is room if (i < MAX_VALUE_LEN - 1)
s->Value[i] = c;
// read the next character
c = GET_NEXT_CHAR();
}
// reject overlong identifiers if (i > MAX_VALUE_LEN - 1) {
SyntaxError(
s, "Identifiers in GAP must consist of at most 1023 characters.");
i = MAX_VALUE_LEN - 1;
}
// terminate the identifier
s->Value[i] = '\0';
// if it is quoted then it is not a keyword if (isQuoted) return S_IDENT;
// now check if 's->Value' holds a keyword constChar * v = s->Value; switch ( 256*v[0]+v[i-1] ) { case 256*'a'+'d': if(streq(v,"and")) return S_AND; case 256*'a'+'c': if(streq(v,"atomic")) return S_ATOMIC; case 256*'b'+'k': if(streq(v,"break")) return S_BREAK; case 256*'c'+'e': if(streq(v,"continue")) return S_CONTINUE; case 256*'d'+'o': if(streq(v,"do")) return S_DO; case 256*'e'+'f': if(streq(v,"elif")) return S_ELIF; case 256*'e'+'e': if(streq(v,"else")) return S_ELSE; case 256*'e'+'d': if(streq(v,"end")) return S_END; case 256*'f'+'e': if(streq(v,"false")) return S_FALSE; case 256*'f'+'i': if(streq(v,"fi")) return S_FI; case 256*'f'+'r': if(streq(v,"for")) return S_FOR; case 256*'f'+'n': if(streq(v,"function")) return S_FUNCTION; case 256*'i'+'f': if(streq(v,"if")) return S_IF; case 256*'i'+'n': if(streq(v,"in")) return S_IN; case 256*'l'+'l': if(streq(v,"local")) return S_LOCAL; case 256*'m'+'d': if(streq(v,"mod")) return S_MOD; case 256*'n'+'t': if(streq(v,"not")) return S_NOT; case 256*'o'+'d': if(streq(v,"od")) return S_OD; case 256*'o'+'r': if(streq(v,"or")) return S_OR; case 256*'r'+'e': if(streq(v,"readwrite")) return S_READWRITE; case 256*'r'+'y': if(streq(v,"readonly")) return S_READONLY; case 256*'r'+'c': if(streq(v,"rec")) return S_REC; case 256*'r'+'t': if(streq(v,"repeat")) return S_REPEAT; case 256*'r'+'n': if(streq(v,"return")) return S_RETURN; case 256*'t'+'n': if(streq(v,"then")) return S_THEN; case 256*'t'+'e': if(streq(v,"true")) return S_TRUE; case 256*'u'+'l': if(streq(v,"until")) return S_UNTIL; case 256*'w'+'e': if(streq(v,"while")) return S_WHILE; case 256*'q'+'t': if(streq(v,"quit")) return S_QUIT; case 256*'Q'+'T': if(streq(v,"QUIT")) return S_QQUIT; case 256*'I'+'d': if(streq(v,"IsBound")) return S_ISBOUND; case 256*'U'+'d': if(streq(v,"Unbind")) return S_UNBIND; case 256*'T'+'d': if(streq(v,"TryNextMethod")) return S_TRYNEXT; case 256*'I'+'o': if(streq(v,"Info")) return S_INFO; case 256*'A'+'t': if(streq(v,"Assert")) return S_ASSERT;
}
return S_IDENT;
}
/**************************************************************************** ** *F GetNumber() . . . . . . . . . . . . . . get an integer or float literal ** ** 'GetNumber' reads a number from the current input file into the variable ** 's->Value' or 's->ValueObj' and sets 's->Symbol' to 'S_INT' or ** 'S_FLOAT'. The first character of the number is the current character ** pointed to by 'In'. ** ** If the sequence contains characters which do not match the regular ** expression [0-9]+.?[0-9]*([edqEDQ][+-]?[0-9]+)? 'GetNumber' will ** interpret the sequence as an identifier by delegating to 'GetIdent'. ** ** As we read, we keep track of whether we have seen a . or exponent ** notation and so whether we will return 'S_INT' or 'S_FLOAT'. ** ** When 's->Value' is completely filled, then a GAP string object is ** created in 's->ValueObj' and all data is stored there. ** ** The argument is used to signal if a decimal point was already read, ** or whether we are starting from scratch.. **
*/ static UInt AddCharToBuf(Obj * string, Char * buf, UInt bufsize, UInt pos, Char c)
{ if (pos >= bufsize) {
*string = AppendBufToString(*string, buf, pos);
pos = 0;
}
buf[pos++] = c; return pos;
}
static UInt GetNumber(ScannerState * s, Int readDecimalPoint, Char c)
{
UInt symbol = S_ILLEGAL;
UInt i = 0; BOOL seenADigit = FALSE;
s->ValueObj = 0;
if (readDecimalPoint) {
s->Value[i++] = '.';
} else { // read initial sequence of digits into 'Value' while (IsDigit(c)) {
i = AddCharToValue(s, i, c);
seenADigit = TRUE;
c = GET_NEXT_CHAR();
}
// maybe we saw an identifier character and realised that this is an // identifier we are reading if (IsIdent(c) || c == '\\') { // if necessary, copy back from s->ValueObj to s->Value if (s->ValueObj) {
i = GET_LEN_STRING(s->ValueObj);
GAP_ASSERT(i >= MAX_VALUE_LEN - 1);
memcpy(s->Value, CONST_CSTR_STRING(s->ValueObj),
MAX_VALUE_LEN);
s->ValueObj = 0;
} // this looks like an identifier, scan the rest of it return GetIdent(s, i, c);
}
// Or maybe we saw a '.' which could indicate one of three things: // - a float literal: 12.345 // - S_DOT, i.e., '.' used to access a record entry: r.12.345 // - S_DDOT, i.e., '..' in a range expression: [12..345] if (c == '.') {
GAP_ASSERT(i < MAX_VALUE_LEN - 1);
// If the symbol before this integer was S_DOT then we must be in // a nested record element expression, so don't look for a float. // This is a bit fragile if (s->Symbol == S_DOT || s->Symbol == S_BDOT) {
symbol = S_INT; goto finish;
}
// peek ahead to decide if we are looking at a range expression if (PEEK_NEXT_CHAR(s->input) == '.') { // we are looking at '..' and are probably inside a range // expression
symbol = S_INT; goto finish;
}
// Now the '.' must be part of our number; store it and move on
i = AddCharToValue(s, i, '.');
c = GET_NEXT_CHAR();
} else { // Anything else we see tells us that the token is done
symbol = S_INT; goto finish;
}
}
// When we get here we have read possibly some digits, a . and possibly // some more digits, but not an e,E,d,D,q or Q // In any case, from now on, we know we are dealing with a float literal
symbol = S_FLOAT;
// read digits while (IsDigit(c)) {
i = AddCharToValue(s, i, c);
seenADigit = TRUE;
c = GET_NEXT_CHAR();
} if (!seenADigit)
SyntaxError(s, "Badly formed number: need a digit before or after the " "decimal point"); if (c == '\\')
SyntaxError(s, "Badly formed number");
// If the next thing is the start of the exponential notation, read it // now. if (c == 'e' || c == 'E' || c == 'd' || c == 'D' || c == 'q' ||
c == 'Q') {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR(); if (c == '+' || c == '-') {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR();
}
// Here we are into the unsigned exponent of a number in scientific // notation, so we just read digits if (!IsDigit(c))
SyntaxError(s, "Badly formed number: need at least one digit in " "the exponent"); while (IsDigit(c)) {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR();
}
}
// Allow one letter at the end of the number, which is a conversion // marker; e.g. an `i` as in C99, to indicate an imaginary value. if (IsAlpha(c)) {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR();
}
// independently of that, we allow an _ signalling immediate or "eager" // conversion if (c == '_') {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR(); // After which there may be one character signifying the // conversion styles if (IsAlpha(c)) {
i = AddCharToValue(s, i, c);
c = GET_NEXT_CHAR();
}
}
// Now if the next character is an identifier symbol then we have an error if (IsIdent(c)) {
SyntaxError(s, "Badly formed number");
}
finish:
i = AddCharToValue(s, i, '\0'); if (s->ValueObj) { // flush buffer
AppendBufToString(s->ValueObj, s->Value, i - 1);
} return symbol;
}
/**************************************************************************** ** *F GetEscapedChar() . . . . . . . . . . . . . . . . get an escaped character ** ** 'GetEscapedChar' reads an escape sequence from the current input file ** into the variable *dst. **
*/ staticChar GetEscapedChar(ScannerState * s)
{ Char result = 0; Char c = GET_NEXT_CHAR();
if ( c == 'n' ) result = '\n'; elseif ( c == 't' ) result = '\t'; elseif ( c == 'r' ) result = '\r'; elseif ( c == 'b' ) result = '\b'; elseif ( c == '>' ) result = '\01'; elseif ( c == '<' ) result = '\02'; elseif ( c == 'c' ) result = '\03'; elseif ( c == '"' ) result = '"'; elseif ( c == '\\' ) result = '\\'; elseif ( c == '\'' ) result = '\''; elseif ( c == '0' ) { // from here we can either read a hex-escape or three digit octal numbers
c = GET_NEXT_CHAR(); if (c == 'x') {
result = 16 * CharHexDigit(s);
result += CharHexDigit(s);
} elseif (c >= '0' && c <= '7') {
result += GetOctalDigits(s, c);
} else {
SyntaxError(s, "Expecting hexadecimal escape, or two more octal digits");
}
} elseif ( c >= '1' && c <= '7' ) { // escaped three digit octal numbers are allowed in input
result = 64 * (c - '0');
c = GET_NEXT_CHAR();
result += GetOctalDigits(s, c);
} else { // Following discussions on pull-request #612, this warning is currently // disabled for backwards compatibility; some code relies on this behaviour // and tests break with the warning enabled #if 0 if (IsAlpha(c))
SyntaxWarning(s, "Alphabet letter after \\"); #endif
result = c;
} return result;
}
/**************************************************************************** ** *F GetStr() . . . . . . . . . . . . . . . . . . . . . get a string, local ** ** 'GetStr' reads a string from the current input file into the variable ** 's->ValueObj' and sets 'Symbol' to 'S_STRING'. The opening double ** quote '"' of the string is the current character pointed to by 'In'. ** ** A string is a sequence of characters delimited by double quotes '"'. It ** must not include '"' or <newline> characters, but the escape sequences ** '\"' or '\n' can be used instead. The escape sequence '\<newline>' is ** ignored, making it possible to split long strings over multiple lines. ** ** An error is raised if the string includes a <newline> character or if the ** file ends before the closing '"'.
*/ staticChar GetStr(ScannerState * s, Char c)
{
Obj string = 0; Char buf[1024] = "";
UInt i = 0;
while (c != '"' && c != '\n' && c != '\377') { if (c == '\\') {
c = GetEscapedChar(s);
}
i = AddCharToBuf(&string, buf, sizeof(buf), i, c);
// read the next character
c = GET_NEXT_CHAR();
}
// append any remaining data to s->ValueObj
s->ValueObj = AppendBufToString(string, buf, i);
if (c == '\n')
SyntaxError(s, "String must not include ");
if (c == '\377') {
FlushRestOfInputLine(s->input);
SyntaxError(s, "String must end with \" before end of file");
}
return c;
}
staticvoid GetPragma(ScannerState * s, Char c)
{
Obj string = 0; Char buf[1024];
UInt i = 0;
while ( c != '\n' && c != '\r' && c != '\377') {
i = AddCharToBuf(&string, buf, sizeof(buf), i, c);
// read the next character
c = GET_NEXT_CHAR();
}
// append any remaining data to s->ValueObj
s->ValueObj = AppendBufToString(string, buf, i);
if (c == '\377') {
FlushRestOfInputLine(s->input);
}
}
/**************************************************************************** ** *F GetTripStr() . . . . . . . . . . . . . get a triple quoted string, local ** ** 'GetTripStr' reads a triple-quoted string from the current input file ** into the variable 'Value' and sets 'Symbol' to 'S_STRING'. ** The last member of the opening triple quote '"' ** of the string is the current character pointed to by 'In'. ** ** A triple quoted string is any sequence of characters which is terminated ** by """. No escaping is performed. ** ** An error is raised if the file ends before the closing """.
*/ staticChar GetTripStr(ScannerState * s, Char c)
{
Obj string = 0; Char buf[1024];
UInt i = 0;
// print only a partial prompt while reading a triple string
SetPrompt("> ");
while (c != '\377') { // only thing to check for is a triple quote if (c == '"') {
c = GET_NEXT_CHAR(); if (c == '"') {
c = GET_NEXT_CHAR(); if (c == '"') { break;
}
i = AddCharToBuf(&string, buf, sizeof(buf), i, '"');
}
i = AddCharToBuf(&string, buf, sizeof(buf), i, '"');
}
i = AddCharToBuf(&string, buf, sizeof(buf), i, c);
// read the next character
c = GET_NEXT_CHAR();
}
// append any remaining data to s->ValueObj
s->ValueObj = AppendBufToString(string, buf, i);
if (c == '\377') {
FlushRestOfInputLine(s->input);
SyntaxError(s, "String must end with \"\"\" before end of file");
}
return c;
}
/**************************************************************************** ** *F GetString() . . . . . . . . . . . . . . . . . . . . get a string, local ** ** 'GetString' decides if we are reading a single quoted string, or a triple ** quoted string, and then reads it. The opening quote '"' of the string is ** the current character pointed to by 'In'.
*/ staticvoid GetString(ScannerState * s)
{ Int isTripleQuoted = 0; Char c = GET_NEXT_CHAR();
if (c == '"') {
c = GET_NEXT_CHAR(); if (c == '"') {
isTripleQuoted = 1;
c = GET_NEXT_CHAR();
} else { // we read two '"' followed by something else, so this was // just an empty string!
s->ValueObj = NEW_STRING(0); return;
}
}
c = isTripleQuoted ? GetTripStr(s, c) : GetStr(s, c);
// skip trailing '"' if (c == '"')
c = GET_NEXT_CHAR();
}
/**************************************************************************** ** *F GetChar() . . . . . . . . . . . . . . . . . get a single character, local ** ** 'GetChar' reads the next character from the current input file into the ** variable 's->Value' and sets 'Symbol' to 'S_CHAR'. The opening single quote ** '\'' of the character is the current character pointed to by 'In'. ** ** A character is a single character delimited by single quotes '\''. It ** must not be '\'' or <newline>, but the escape sequences '\\\'' or '\n' ** can be used instead.
*/ staticvoid GetChar(ScannerState * s)
{ // skip '\'' Char c = GET_NEXT_CHAR();
// handle escape equences if ( c == '\n' ) {
SyntaxError(s, "Character literal must not include ");
} else { if ( c == '\\' ) {
s->Value[0] = GetEscapedChar(s);
} else { // put normal chars into 's->Value'
s->Value[0] = c;
}
// read the next character
c = GET_NEXT_CHAR();
// check for terminating single quote, and skip if ( c == '\'' ) {
c = GET_NEXT_CHAR();
} else {
SyntaxError(s, "Missing single quote in character constant");
}
}
}
while (c != '\n' && c != '\r' && c != '\377') {
i = AddCharToBuf(&string, buf, sizeof(buf), i, c);
c = GET_NEXT_CHAR();
}
// append any remaining data to s->ValueObj
s->ValueObj = AppendBufToString(string, buf, i);
}
/**************************************************************************** ** *F StoreSymbolPosition() ** ** Store the current position in the input stream, for use in error and ** warning messages. A sequence of positions is stored, which record the start ** and end of each symbol.
*/ staticvoid StoreSymbolPosition(ScannerState * s)
{
s->SymbolStartLine[2] = s->SymbolStartLine[1];
s->SymbolStartPos[2] = s->SymbolStartPos[1];
s->SymbolStartLine[1] = s->SymbolStartLine[0];
s->SymbolStartPos[1] = s->SymbolStartPos[0];
s->SymbolStartLine[0] = GetInputLineNumber(s->input);
s->SymbolStartPos[0] = GetInputLinePosition(s->input);
}
/**************************************************************************** ** *F NextSymbol() . . . . . . . . . . . . . . . . . get the next symbol, local ** ** 'NextSymbol' reads the next symbol from the input, storing it in the ** variable 's->Symbol'. If 's->Symbol' is 'S_IDENT', 'S_INT', ** 'S_FLOAT' or 'S_STRING' the value of the symbol is stored in ** 's->Value' or 's->ValueObj'. 'NextSymbol' first skips all ** <space>, <tab> and <newline> characters and comments. ** ** After reading a symbol the current character is the first character ** beyond that symbol.
*/ static UInt NextSymbol(ScannerState * s)
{ // Record end of previous symbol's position
StoreSymbolPosition(s);
Char c = PEEK_CURR_CHAR(s->input);
// skip over <spaces>, <tabs>, <newlines> and comments while (c == ' ' || c == '\t' || c== '\n' || c== '\r' || c == '\f' || c=='#') { if (c == '#') {
c = GET_NEXT_CHAR_NO_LC(s->input); if (c == '%') { // we have encountered a pragma
GetPragma(s, c); return S_PRAGMA;
}
SKIP_TO_END_OF_LINE(s->input);
}
c = GET_NEXT_CHAR();
}
// Record start of this symbol's position
StoreSymbolPosition(s);
// switch according to the character if (IsAlpha(c)) { return GetIdent(s, 0, c);
}
UInt symbol;
switch (c) { case'.': symbol = S_DOT; c = GET_NEXT_CHAR(); if (c == '.') { symbol = S_DOTDOT; c = GET_NEXT_CHAR(); if (c == '.') { symbol = S_DOTDOTDOT; c = GET_NEXT_CHAR(); }
} break;
case'!': symbol = S_ILLEGAL; c = GET_NEXT_CHAR(); if (c == '.') { symbol = S_BDOT; GET_NEXT_CHAR(); break; } if (c == '[') { symbol = S_BLBRACK; GET_NEXT_CHAR(); break; } break; case'[': symbol = S_LBRACK; GET_NEXT_CHAR(); break; case']': symbol = S_RBRACK; GET_NEXT_CHAR(); break; case'{': symbol = S_LBRACE; GET_NEXT_CHAR(); break; case'}': symbol = S_RBRACE; GET_NEXT_CHAR(); break; case'(': symbol = S_LPAREN; GET_NEXT_CHAR(); break; case')': symbol = S_RPAREN; GET_NEXT_CHAR(); break; case',': symbol = S_COMMA; GET_NEXT_CHAR(); break;
case':': symbol = S_COLON; c = GET_NEXT_CHAR(); if (c == '=') { symbol = S_ASSIGN; GET_NEXT_CHAR(); break; } break;
case';': symbol = S_SEMICOLON; c = GET_NEXT_CHAR(); if (c == ';') { symbol = S_DUALSEMICOLON; GET_NEXT_CHAR(); break; } break;
case'=': symbol = S_EQ; GET_NEXT_CHAR(); break; case'<': symbol = S_LT; c = GET_NEXT_CHAR(); if (c == '=') { symbol = S_LE; GET_NEXT_CHAR(); break; } if (c == '>') { symbol = S_NE; GET_NEXT_CHAR(); break; } break; case'>': symbol = S_GT; c = GET_NEXT_CHAR(); if (c == '=') { symbol = S_GE; GET_NEXT_CHAR(); break; } break;
case'+': symbol = S_PLUS; GET_NEXT_CHAR(); break; case'-': symbol = S_MINUS; c = GET_NEXT_CHAR(); if (c == '>') { symbol = S_MAPTO; GET_NEXT_CHAR(); break; } break; case'*': symbol = S_MULT; GET_NEXT_CHAR(); break; case'/': symbol = S_DIV; GET_NEXT_CHAR(); break; case'^': symbol = S_POW; GET_NEXT_CHAR(); break;
case'~': symbol = S_TILDE; GET_NEXT_CHAR(); break; case'?': symbol = S_HELP; GetHelp(s); break; case'"': symbol = S_STRING; GetString(s); break; case'\'': symbol = S_CHAR; GetChar(s); break; case'\\': return GetIdent(s, 0, c); case'_': return GetIdent(s, 0, c); case'@': return GetIdent(s, 0, c);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.