Module: C-lexer-internal Author: Toby Weinberg Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // hack!! define constant = ; ignorable(has-exponent?-setter); // Tokens support the following protocols: // // lexer-string => // // This is the string returned from the lexer. The string is the // same as the input after the pre-lexer has run. string and character // literals include enclosing quote marks and escapes are not interpreted. // This raw form of input is needed to support token merging via the ## // operator in cpp expansions. // // quoted-string => // // String as returned from the # string-ization operator in cpp. Initial // double quotes, embedded double quotes and embedded \ characters in both // character and string literals are escaped. Otherwise the same as // lexer-string. // // "a \n string" => "\" \\n string\"" // // dylan-value => type-union (, , // ) // // Literals are converted to dylan equivalents, number literals to // or , character literals to and string literals // to . Lexer-strings for character and string literals are // converted to dylan-values by removing surrounding quotations and // interpreting escape sequences. // // constant-value type-union(, ) => // // returns compile time constant values for character and integer literals // and 0 for not #define'd identifiers. this is used to evaluate // compile time constant expansions in #if expressions. // // source-line (token :: ) => (result :: false-or()); // // parser-tag (token :: ) => (result :: ); // // token-print-string (token :: ) => (result :: ); // // Used to implement print-object in the emulator. // define sealed generic source-line (token :: ) => (result :: false-or()); // someday maybe make the source-line be a push down stack so that source // lines for tokens which are created during macro expansion can keep track // of locations in the macro definitions as well. define open abstract primary class () slot source-line :: false-or(), init-keyword: source-line:, init-value: #f; end; // lexer-string and parser-tag are sometimes slots and sometimes attributes // of classes define sealed generic lexer-string (token :: ) => (result :: ); define open generic parser-tag (token :: ) => (result :: ); define sealed generic dylan-value (token :: ) => (result :: type-union(, , , )); define sealed generic quoted-string (token :: ) => (result :: ); // catch all for tokens without more specific methods define method quoted-string (token :: ) => (result :: ); token.lexer-string end method; // white space tokens are needed for macro expansion, especially in the // expansion of # and in the recognition of function macros. white space // tokens are discarded before parsing. indeed it is possible that there // is only one actual white space token... define sealed generic copy-token (source-token :: , #key /* source-line: the-source-line :: false-or() */) => (result-token :: ); // this method is all that is needed for tokens with only constant slots // (other than the source-line). define method copy-token (source-token :: , #key source-line: the-source-line :: false-or() = #f) => (result-token :: ) make(type-for-copy(source-token), source-line: if(the-source-line) the-source-line else source-token.source-line end if) end method; define method print-object (the-token :: , the-stream :: ) => (); format(the-stream, "(instance of (%s, source-line: %=))", token-print-string(the-token), the-token.source-line); end method; define method token-print-string (the-token :: ) => (result :: ) let the-class-string = print-to-string(object-class(the-token)); copy-sequence(the-class-string, start: 7, end: the-class-string.size - 1) end method; define abstract class () end class; // mixin class for new-line and eoi tokens both of which terminate macro // definitions. define abstract class () end class; define class () constant slot lexer-string :: = " "; constant slot parser-tag :: = #"space"; end class; // empty tokens are used during macro expansion to represent null arguments // to function macros. define class () constant slot lexer-string :: = ""; constant slot parser-tag :: = #"empty-token"; end class; // the lexer-string for new-line tokens is space. this is correct for the // behavior of the # operator during macro expansion. any sequence of // white-space character is reduced to a single space. define class (, ) constant slot lexer-string :: = " "; // not a mistake! constant slot parser-tag :: = #"new-line"; end class; // and tokens are also discarded during macro // expansion which is the only context in which they have meaning. define class () constant slot lexer-string :: = "#"; constant slot parser-tag :: = #"pound"; end class; define class () constant slot lexer-string :: = "##"; constant slot parser-tag :: = #"pound-pound"; end class; // Because of the separate name spaces for enum, union and struct tags and // other names, tags are valid for names appearing either as // or as tokens. The class is // needed for dispatch on the rules which parse tags. define abstract class () end class; define class () slot lexer-string :: , init-keyword: lexer-string:; constant slot parser-tag :: = #"identifier"; end class; // this method is correct only if it is called on tokens which have been // fully macro expanded. the rule here is that in #if compile time // constant expression an identifier is given the value 0 if it isn't a // defined macro. therefore any identifier present in a fully expanded // expression must have the value 0. define sealed generic constant-value (token :: type-union(, , )) => (result :: ); define method constant-value (token :: ) => (result :: ) as(, 0) end method; define method copy-token (source-token :: , #key source-line: the-source-line :: false-or()) => (result-token :: ) let result-token = next-method(); result-token.lexer-string := copy-sequence(source-token.lexer-string); result-token end method; define method token-print-string (the-token :: ) => (result :: ); concatenate(next-method(), " lexer-string: \"", the-token.lexer-string, "\"") end method; // tokens don't support either dylan-value or constant-value define abstract class () end class; define sealed generic precedence (token :: type-union(, )) => (result :: ); define constant = one-of(#"right", #"left"); define sealed generic associativity (token :: type-union(, )) => (result :: ); define sealed concrete class () constant slot lexer-string :: = "("; constant slot parser-tag :: = #"("; constant slot precedence :: = 0; constant slot associativity :: = #"right"; end class; define sealed concrete class () constant slot lexer-string :: = ")"; constant slot parser-tag :: = #")"; constant slot precedence :: = 0; constant slot associativity :: = #"right"; end class; define sealed concrete class () constant slot lexer-string :: = "["; constant slot parser-tag :: = #"["; end class; define sealed concrete class () constant slot lexer-string :: = "]"; constant slot parser-tag :: = #"]"; end class; define sealed concrete class () constant slot lexer-string :: = "{"; constant slot parser-tag :: = #"{"; end class; define sealed concrete class () constant slot lexer-string :: = "}"; constant slot parser-tag :: = #"}"; end class; define sealed concrete class () constant slot lexer-string :: = ","; constant slot parser-tag :: = #","; end class; define sealed concrete class () constant slot lexer-string :: = ";"; constant slot parser-tag :: = #";"; end class; define class () constant slot lexer-string :: = "..."; constant slot parser-tag :: = #"..."; end class; define sealed concrete class (, ) constant slot lexer-string :: = ""; constant slot parser-tag :: = #"eoi"; end class; define constant $eoi-token = make(); define method quoted-string (token :: ) => (result :: ); error("attempt to quote the end of input token"); end method; define abstract class () end class; define method copy-token (source-token :: , #key source-line: the-source-line :: false-or() = #f) => (result-token :: ) let result-token = next-method(); result-token.lexer-string := copy-sequence(source-token.lexer-string); result-token end method; define method token-print-string (the-token :: ) => (result :: ); concatenate(next-method(), " lexer-string: \"", the-token.quoted-string, "\"") end method; define sealed concrete class () constant slot parser-tag :: = #"character-literal"; slot lexer-string :: , init-keyword: lexer-string:; end class; // machine integer value for constant expression evaluation define method constant-value (token :: ) => (result :: ); as(, as(, token.dylan-value)) end; define method quoted-string (token :: ) => (result :: ); let result = make(); for(character in token.lexer-string) select(character) '"' => add!(result, '\\'); add!(result, '"'); '\\' => add!(result, '\\'); add!(result, '\\'); otherwise => add!(result, character); end select; end for; as(, result) end method; define method dylan-value (token :: ) => (result :: ); let index :: = 1; // skip the '\'' let (result, new-index) = if (token.lexer-string[index] = '\\') escape-sequence-to-character(token.lexer-string, index + 1) else values(token.lexer-string[index], index + 1) end if; if (new-index + 1 > size(token.lexer-string)) error("dylan-value: multi-byte characters not supported, input = %=", as(, token.lexer-string)); end; result end method dylan-value; // escape scanner used by dylan value for both and // . define method escape-sequence-to-character (input-string :: , index :: ) => (result :: , index :: ); if (clex-out-of-range-character?(input-string[index])) error("unexpected escape character in literal string %= ", input-string[index]); end if; let category = clex-escape-category(input-string[index]); case clex-hex-escape-category?(category) // hex escapes -- "\x" followed by any number of hex digits => index := index + 1; // skip the 'x' let result = 0; while(clex-hex-escape-digit?(input-string[index])) result := result * 16 + clex-digit-to-integer(input-string[index]); index := index + 1; end while; values(as(, result), index); clex-octal-escape-digit-category?(category) // octal escapes -- "\" followed by 1 to 3 octal digits => let result = clex-digit-to-integer(input-string[index]); index := index + 1; if (clex-octal-escape-digit?(input-string[index])) result := result * 8 + clex-digit-to-integer(input-string[index]); index := index + 1; if (clex-octal-escape-digit?(input-string[index])) result := result * 8 + clex-digit-to-integer(input-string[index]); index := index + 1; end if; end if; values(as(, result), index); clex-character-escape-category?(category) => values(character-escape-value(input-string[index]), index + 1); otherwise => error("unexpected escape character in literal string %= ", input-string[index]); end case; end method escape-sequence-to-character; define sealed concrete class () constant slot parser-tag :: = #"string-literal"; slot internal-lexer-string-value :: type-union(, ), init-keyword: lexer-string:; end class; define method lexer-string-setter(value, token :: ) token.internal-lexer-string-value := value; end method; // Quoted string shouldn't ever be called on a lexer string value // which isn't a since expansion of # and ## should happen // before adjacent strings are concatenated so there won't be any // beasties yet. define method lexer-string(token :: ) => (result :: ); select (token.internal-lexer-string-value by instance?) => token.internal-lexer-string-value; => block() let list-of-strings = as(, token.internal-lexer-string-value); let result = list-of-strings.head; for (another-string in list-of-strings.tail) result := concatenate(result, " ", another-string); end for; result end; otherwise => error("unrecognized class for internal-lexer-string-value"); end select; end method; // Quoted string shouldn't ever be called on a String literal where // the internal-lexer-string-value isn't a since expansion of // # and ## should happen before adjacent strings are concatenated so // there won't be any beasties yet. define method quoted-string (token :: ) => (result :: ); let result = make(); for(index from 0 below token.lexer-string.size) select(token.lexer-string[index]) '"' => add!(result, '\\'); add!(result, '"'); '\\' => add!(result, '\\'); add!(result, '\\'); otherwise => add!(result, token.lexer-string[index]); end select; end for; as(, result) end method; // Turn the parsed characters of a c string into a dylan string. This // method removes the surrounding double quotes and converts C escape // sequences into s. For internal lexer strings Which are // really sequences (s) of lists this concatenate the lists // after doing the escape character expansion so that characters at // the beginning of a concatenated string don't get subsumed into // escape sequences at the end of a preceding string. define method dylan-value (token :: ) => (result :: ); let value-for-one-string = method(input-string, result) let index = 1; // skip the '"' while(index < size(input-string) - 1) if (input-string[index] = '\\') let(new-character, new-index) = escape-sequence-to-character(input-string, index + 1); add!(result, new-character); index := new-index; else add!(result, input-string[index]); index := index + 1; end if; end while; result end method; let result = make(); select (token.internal-lexer-string-value by instance?) => value-for-one-string(token.internal-lexer-string-value, result); => for (string in token.internal-lexer-string-value) value-for-one-string(string, result); end for; otherwise => error("unexpected type for lexer-string"); end select; as(, result) end method dylan-value; define abstract primary class () slot lexer-string :: , init-keyword: lexer-string:; end; define sealed concrete class () constant slot parser-tag :: = #"ordinary-filename"; end; define sealed concrete class () constant slot parser-tag :: = #"standard-filename"; end; // remove the enclosing double quotes or angle brackets define method dylan-value (token :: ) => (result :: ); copy-sequence(token.lexer-string, start: 1, end: size(token.lexer-string) - 1) end method dylan-value; // need maybe unsigned/long discrimination for integer literals define abstract class () constant slot parser-tag :: = #"integer-literal"; slot lexer-string :: , init-keyword: lexer-string:; end class; define sealed concrete class () end; // machine integer value for constant expression evaluation define method constant-value (token :: ) => (result :: ); as-decimal-machine-word(token.lexer-string) end; define method as-decimal-machine-word (string :: ) => (number :: ); let number :: = clex-digit-to-machine-word(string[0]); let start-index = 1; for (i from start-index below string.size) number := number * as(, 10) + clex-digit-to-machine-word(string[i]); end for; number end method; define method dylan-value (token :: ) => (result :: stupid-); as-decimal-integer(token.lexer-string) end; define method as-decimal-integer (string :: ) => (number :: stupid-) let number :: stupid- = clex-digit-to-integer(string[0]); let start-index = 1; for (i from start-index below string.size) number := stupid-+(stupid-*(number, 10), clex-digit-to-integer(string[i])); end for; number end method; define sealed concrete class () end; // machine integer value for constant expression evaluation define method constant-value (token :: ) => (result :: ); as-octal-machine-word(token.lexer-string) end; define method as-octal-machine-word (string :: ) => (number :: ); let number :: = clex-digit-to-machine-word(string[0]); let start-index = 1; for (i from start-index below string.size) number := number * as(, 8) + clex-digit-to-machine-word(string[i]); end for; number end method; define method dylan-value (token :: ) => (result :: stupid-); as-octal-integer(token.lexer-string) end; define method as-octal-integer (string :: ) => (number :: stupid-); let number :: stupid- = clex-digit-to-integer(string[0]); let start-index = 1; for (i from start-index below string.size) number := stupid-+(stupid-*(number, 8), clex-digit-to-integer(string[i])); end for; number end method; define sealed concrete class () end; // machine integer value for constant expression evaluation define method constant-value (token :: ) => (result :: ); as-hexadecimal-machine-word(token.lexer-string) end; define method as-hexadecimal-machine-word (string :: ) => int :: ; let number :: = clex-digit-to-machine-word(string[0]); let start-index = 1; for (i from start-index below string.size) number := number * as(, 16) + clex-digit-to-machine-word(string[i]); end for; number end method; // machine integer value for constant expression evaluation define method dylan-value (token :: ) => (result :: stupid-); as-hexadecimal-integer(token.lexer-string) end; define method as-hexadecimal-integer (string :: ) => (number :: stupid-); let number :: stupid- = clex-digit-to-integer(string[0]); let start-index = 1; for (i from start-index below string.size) number := stupid-+(stupid-*(number, 16), clex-digit-to-integer(string[i])); end for; number end method; define concrete class () constant slot parser-tag :: = #"float-literal"; slot lexer-string :: , init-keyword: lexer-string:; slot has-exponent? :: , required-init-keyword: has-exponent?:; end class; define method dylan-value (token :: ) => (result :: ); let string :: = token.lexer-string; // strip leading zeros let i = 0; while(string[i] == '0') i := i + 1; end while; if (i > 0) string := copy-sequence(string, start: i); end if; i := string.size - 1; let float-size = #"double"; let exponent = 0; if (token.has-exponent?) // collect the exponent as a decimal integer let multiplier = 1; let done = #f; until(done) select (string[i] by \==) 'e', 'E' => done := #t; '+', 'l', 'L' => #f; // do nothing, just skip it 'f', 'F' => float-size := #"single"; '-' => exponent := exponent * -1; otherwise => exponent := exponent + (clex-digit-to-integer(string[i]) * multiplier); multiplier := multiplier * 10; end select; i := i - 1; end until; end if; // Now collect the constant together with number of digits left of the // decimal point so we can convert to a normalized float (all digits // to the right of the decimal point). let constant-part = 0.d0; let digits-to-the-left = 0; until (i < 0) select (string[i] by \==) '.' => digits-to-the-left := 0; // Everything so far was to the right 'l', 'L' => #f; // do nothing, just skip it 'f', 'F' => float-size := #"single"; otherwise => constant-part := (constant-part + clex-digit-to-integer(string[i])) * 0.1; digits-to-the-left := digits-to-the-left + 1; end select; i := i - 1; end until; exponent := exponent + digits-to-the-left; let result = constant-part * (10.d0 ^ exponent); if (float-size == #"single") as(, result) else result end end method; // no method for constant-value of a float literal needed since floats can't be // part of constants in #if conditions. define abstract class () end class; // at this point the only symbol tokens identified as either unary or // binary operators are those which can be used in compile time constant // expressions in #if cpp directives. define abstract class () end class; define abstract class () end class; define method lexer-string (token :: ) => (result :: ); as(, token.parser-tag) end method; define sealed concrete class () constant slot parser-tag :: = #".*"; end; define sealed concrete class () constant slot parser-tag :: = #"."; end; define sealed concrete class () constant slot parser-tag :: = #"&&"; constant slot precedence :: = 5; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"&="; end; define sealed concrete class () constant slot parser-tag :: = #"&"; constant slot precedence :: = 8; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"*="; end; define sealed concrete class () constant slot parser-tag :: = #"*"; constant slot precedence :: = 13; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"+="; end; define sealed concrete class () constant slot parser-tag :: = #"+"; constant slot precedence :: = 12; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"+"; constant slot precedence :: = 15; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #"++"; end; define sealed concrete class () constant slot parser-tag :: = #"-="; end; define sealed concrete class () constant slot parser-tag :: = #"-"; constant slot precedence :: = 12; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"-"; constant slot precedence :: = 15; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #"--"; end; define sealed concrete class () constant slot parser-tag :: = #"->"; end; define sealed concrete class () constant slot parser-tag :: = #"->*"; end; define sealed concrete class () constant slot parser-tag :: = #"~"; constant slot precedence :: = 15; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #"!="; constant slot precedence :: = 9; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"!"; constant slot precedence :: = 15; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #"/="; end; define sealed concrete class () constant slot parser-tag :: = #"/"; constant slot precedence :: = 13; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"%="; end; define sealed concrete class () constant slot parser-tag :: = #"%"; constant slot precedence :: = 13; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"<<="; end; define sealed concrete class () constant slot parser-tag :: = #"<<"; constant slot precedence :: = 11; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"<="; constant slot precedence :: = 10; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"<"; constant slot precedence :: = 10; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #">>="; end; define sealed concrete class () constant slot parser-tag :: = #">>"; constant slot precedence :: = 11; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #">="; constant slot precedence :: = 10; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #">"; constant slot precedence :: = 10; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"^="; end; define sealed concrete class () constant slot parser-tag :: = #"^"; constant slot precedence :: = 7; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"|="; end; define sealed concrete class () constant slot parser-tag :: = #"|"; constant slot precedence :: = 6; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"||"; constant slot precedence :: = 4; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"=="; constant slot precedence :: = 9; constant slot associativity :: = #"left"; end; define sealed concrete class () constant slot parser-tag :: = #"="; end; define sealed concrete class () constant slot parser-tag :: = #"?"; constant slot precedence :: = 3; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #":"; constant slot precedence :: = 3; constant slot associativity :: = #"right"; end; define sealed concrete class () constant slot parser-tag :: = #"::"; end; // reserved word tokens. define open abstract class () end; define method lexer-string (token :: ) => (result :: ); as(, token.parser-tag) end method; // s are Microsoft specific extensions. They are // documented as "attributes" in the Microsoft BNF but in the code they // appear as modifiers to function declarators immediately preceding the // declarator for the function and following any stars which indicate // pointer to function types. define abstract class () end; define sealed concrete class <__fastcall> () constant slot parser-tag :: = #"__fastcall"; end; // Also they use _cdecl end __cdecl interchangeably define sealed concrete class <__cdecl> () constant slot parser-tag :: = #"__cdecl"; end; define sealed concrete class <_cdecl> () constant slot parser-tag :: = #"__cdecl"; end; define sealed concrete class <__stdcall> () constant slot parser-tag :: = #"__stdcall"; end; define abstract class () end; // Yes Virginia, they really use both _inline and __inline // interchangeably and they didn't even #define one to the other. // Note that the parser-tags are the same even though the classes and // lexer-strings are different. define sealed concrete class <_inline> () constant slot lexer-string :: = "_inline"; constant slot parser-tag :: = #"_inline"; end; define sealed concrete class <__inline> () constant slot lexer-string :: = "__inline"; constant slot parser-tag :: = #"_inline"; end; define sealed concrete class <__asm> () constant slot parser-tag :: = #"__asm"; end; define sealed concrete class <__based> () constant slot parser-tag :: = #"__based"; end; define abstract primary class () end; define abstract primary class () end; define abstract primary class () end; define sealed concrete class () constant slot parser-tag :: = #"auto"; end; define sealed concrete class () constant slot parser-tag :: = #"register"; end; define sealed concrete class () constant slot parser-tag :: = #"static"; end; define sealed concrete class () constant slot parser-tag :: = #"extern"; end; define sealed concrete class () constant slot parser-tag :: = #"typedef"; end; define sealed concrete class <__declspec> () constant slot parser-tag :: = #"__declspec"; end; // extended-decl-modifier -- microsoft specific // these are context sensitive reserved words so they are just identifiers // during parsing. // // extended-decl-modifier : // thread // naked // dllimport // dllexport define abstract class () end; define abstract class () end; define abstract class () end; define sealed concrete class () constant slot parser-tag :: = #"void"; end; define sealed concrete class () constant slot parser-tag :: = #"char"; end; define sealed concrete class () constant slot parser-tag :: = #"short"; end; define sealed concrete class () constant slot parser-tag :: = #"int"; end; define sealed concrete class <__int8> () constant slot parser-tag :: = #"__int8"; end; define sealed concrete class <__int16> () constant slot parser-tag :: = #"__int16"; end; define sealed concrete class <__int32> () constant slot parser-tag :: = #"__int32"; end; define sealed concrete class <__int64> () constant slot parser-tag :: = #"__int64"; end; define sealed concrete class () constant slot parser-tag :: = #"long"; end; // can't use because it conflicts with the dylan class define sealed concrete class () constant slot parser-tag :: = #"float"; end; define sealed concrete class () constant slot parser-tag :: = #"double"; end; define sealed concrete class () constant slot parser-tag :: = #"signed"; end; define sealed concrete class () constant slot parser-tag :: = #"unsigned"; end; define abstract class () end; define abstract class () end; define abstract class () end; // __unaligned is a reserved word only in certain versions of the Microsoft C // compilers. Usually they chop up the headers with conditional // compilation. I am assuming that it is reserved for all Microsoft header // files. That might be a problem but only if somebody defines __unaligned // as a macro or typedef. They deserve what they get if they do that. define sealed concrete class <__unaligned> () constant slot parser-tag :: = #"__unaligned"; end; define sealed concrete class () constant slot parser-tag :: = #"const"; end; define sealed concrete class () constant slot parser-tag :: = #"volatile"; end; define abstract class () end; define sealed concrete class () constant slot parser-tag :: = #"struct"; end; define sealed concrete class () constant slot parser-tag :: = #"union"; end; define sealed concrete class () constant slot parser-tag :: = #"enum"; end; define sealed concrete class () constant slot parser-tag :: = #"break"; end; define sealed concrete class () constant slot parser-tag :: = #"case"; end; define sealed concrete class () constant slot parser-tag :: = #"continue"; end; define sealed concrete class () constant slot parser-tag :: = #"default"; end; define sealed concrete class () constant slot parser-tag :: = #"do"; end; define sealed concrete class () constant slot parser-tag :: = #"else"; end; define sealed concrete class () constant slot parser-tag :: = #"for"; end; define sealed concrete class () constant slot parser-tag :: = #"goto"; end; define sealed concrete class () constant slot parser-tag :: = #"if"; end; define sealed concrete class () constant slot parser-tag :: = #"return"; end; define sealed concrete class () constant slot parser-tag :: = #"sizeof"; end; define sealed concrete class () constant slot parser-tag :: = #"switch"; end; define sealed concrete class () constant slot parser-tag :: = #"while"; end; define sealed concrete class () slot lexer-string :: , init-keyword: lexer-string:; constant slot parser-tag :: = #"typedef-name"; end; define method copy-token (source-token :: , #key source-line: the-source-line :: false-or()) => (result-token :: ) let result-token = next-method(); result-token.lexer-string := copy-sequence(source-token.lexer-string); result-token end method; define method token-print-string (the-token :: ) => (result :: ); concatenate(next-method(), " lexer-string: \"", the-token.lexer-string, "\"") end method;