ORCA-C/Scanner.pas
2024-08-30 13:43:59 -05:00

6070 lines
205 KiB
ObjectPascal

{$optimize 7}
{---------------------------------------------------------------}
{ }
{ Scanner }
{ }
{ External Variables: }
{ }
{ ch - next character to process }
{ printMacroExpansions - print the token list? }
{ reportEOL - report eolsy as a token? }
{ token - next token to process }
{ }
{ External Subroutines: }
{ }
{ Error - flag an error }
{ IsDefined - see if a macro name is in the macro table }
{ InitScanner - initialize the scanner }
{ NextCh - Read the next character from the file, skipping }
{ comments. }
{ NextToken - read the next token from the file }
{ PutBackToken - place a token into the token stream }
{ TermScanner - Shut down the scanner. }
{ }
{---------------------------------------------------------------}
unit Scanner;
interface
{$LibPrefix '0/obj/'}
uses CCommon, Table, CGI, MM, Charset;
{$segment 'SCANNER'}
type
pragmas = {kinds of pragmas}
(p_startofenum,p_cda,p_cdev,p_float,p_keep,
p_nda,p_debug,p_lint,p_memorymodel,p_expand,
p_optimize,p_stacksize,p_toolparms,p_databank,p_rtl,
p_noroot,p_path,p_ignore,p_segment,p_nba,
p_xcmd,p_unix,p_line,p_fenv_access,p_extensions,
p_endofenum);
{preprocessor types}
{------------------}
tokenListRecordPtr = ^tokenListRecord;
tokenListRecord = record {element of a list of tokens}
next: tokenListRecordPtr; {next element in list}
token: tokenType; {token}
expandEnabled: boolean; {can this token be macro expanded?}
suppressPrint: boolean; {suppress printing with #pragma expand?}
tokenStart,tokenEnd: ptr; {token start/end markers}
end;
macroRecordPtr = ^macroRecord;
macroRecord = record {preprocessor macro definition}
next: macroRecordPtr;
saved: boolean;
name: stringPtr;
parameters: integer;
isVarargs: boolean;
tokens: tokenListRecordPtr;
readOnly: boolean;
algorithm: integer;
end;
macroTable = array[0..hashSize] of macroRecordPtr; {preprocessor macro list}
{path name lists}
{---------------}
pathRecordPtr = ^pathRecord;
pathRecord = record
next: pathRecordPtr;
path: stringPtr;
end;
var
ch: char; {next character to process}
macros: ^macroTable; {preprocessor macro list}
pathList: pathRecordPtr; {additional search paths}
printMacroExpansions: boolean; {print the token list?}
preprocessing: boolean; {doing pp directive or macro params?}
suppressMacroExpansions: boolean; {suppress printing even if requested?}
reportEOL: boolean; {report eolsy as a token?}
token: tokenType; {next token to process}
doingFakeFile: boolean; {processing tokens from fake "file" in memory?}
keywordMask: integer; {mask of keyword categories to enable}
{#pragma ignore flags}
{--------------------}
allowLongIntChar: boolean; {allow long int char constants?}
allowSlashSlashComments: boolean; {allow // comments?}
allowTokensAfterEndif: boolean; {allow tokens after #endif?}
skipIllegalTokens: boolean; {skip flagging illegal tokens in skipped code?}
{Note: The following two are set together}
allowMixedDeclarations: boolean; {allow mixed declarations & stmts (C99)?}
c99Scope: boolean; {follow C99 rules for block scopes?}
looseTypeChecks: boolean; {loosen some standard type checks?}
{#pragma extensions flags}
{------------------------}
extendedKeywords: boolean; {recognize ORCA/C-specific keywords?}
extendedParameters: boolean; {change all floating params to extended?}
{---------------------------------------------------------------}
procedure DoDefaultsDotH;
{ Handle the defaults.h file }
procedure Error (err: integer);
{ flag an error }
{ }
{ err - error number }
procedure ErrorWithExtraString (err:integer; extraStr: stringPtr);
{ flag an error, with an extra string to be attached to it }
{ }
{ err - error number }
{ extraStr - extra string to include in error message }
{ }
{ Note: }
{ extraStr must point to a pString allocated with new. }
{ This call transfers ownership of it. }
{procedure Error2 (loc, err: integer); {debug}
{ flag an error }
{ }
{ loc - error location }
{ err - error number }
procedure InitScanner (start, endPtr: ptr);
{ initialize the scanner }
{ }
{ start - pointer to the first character in the file }
{ endPtr - points one byte past the last character in the file }
function IsDefined (name: stringPtr): boolean;
{ See if a macro name is in the macro table }
{ }
{ The returned value is true if the macro exists, else false. }
{ }
{ parameters: }
{ name - name of the macro to search for }
procedure NextCh; extern;
{ Read the next character from the file, skipping comments. }
{ }
{ Globals: }
{ ch - character read }
{ currentChPtr - pointer to ch in source file }
procedure NextToken;
{ Read the next token from the file. }
procedure PutBackToken (var token: tokenType; expandEnabled: boolean;
suppressPrint: boolean);
{ place a token into the token stream }
{ }
{ parameters: }
{ token - token to put back into the token stream }
{ expandEnabled - can macro expansion be performed? }
{ suppressPrint - suppress printing with #pragma expand? }
procedure SetKeywordMask;
{ Set the mask defining which keywords to recognize based on }
{ current language mode. }
procedure TermScanner;
{ Shut down the scanner. }
procedure WriteLine;
{---------------------------------------------------------------}
implementation
const
{special key values}
{------------------}
ALERT = 7; {alert (bell)}
BS = 8; {backspace}
FF = 12; {form feed}
HT = 9; {horizontal tab}
NEWLINE = 10; {newline}
RETURN = 13; {RETURN key code}
VT = 11; {vertical tab}
{keyword categories}
{(also defined in Table.asm)}
c17keyword = 1;
c23keyword = 2;
orcacKeyword = 128;
{misc}
{----}
defaultName = '13:ORCACDefs:Defaults.h'; {default include file name}
maxErr = 10; {max errors on one line}
maxLint = 186; {maximum lint error code}
type
errorType = record {record of a single error}
num: integer; {error number}
line: longint; {line number}
col: integer; {column number}
extraStr: stringPtr; {extra text to include in message}
end;
{file inclusion}
{--------------}
filePtr = ^fileRecord;
fileRecord = record {NOTE: used in scanner.asm}
next: filePtr; {next file in include stack}
name: gsosOutString; {name of the file}
sname: gsosOutString; {name of the file for __FILE__}
lineNumber: longint; {line number at the #include}
disp: longint; {disp of next character to process}
end;
getFileInfoOSDCB = record
pcount: integer;
pathName: gsosInStringPtr;
access: integer;
fileType: integer;
auxType: longint;
storageType: integer;
createDateTime: timeField;
modDateTime: timeField;
optionList: optionListPtr;
dataEOF: longint;
blocksUsed: longint;
resourceEOF: longint;
resourceBlocks: longint;
end;
expandDevicesDCBGS = record
pcount: integer;
inName: gsosInStringPtr;
outName: gsosOutStringPtr;
end;
{conditional compilation parsing}
{-------------------------------}
ifPtr = ^ifRecord;
ifRecord = record
next: ifPtr; {next record in if stack}
status: {what are we doing?}
(processing,skippingToEndif,skippingToElse);
elseFound: boolean; {has an #else been found?}
theFile: filePtr; {file containing the #if}
end;
onOffEnum = (on,off,default); {on-off values in standard pragmas}
var
charStrPrefix: charStrPrefixEnum; {prefix of character/string literal}
currentChPtr: ptr; {pointer to current character in source file}
customDefaultName: stringPtr; {name of custom pre-included default file}
dateStr: longStringPtr; {macro date string}
doingCommandLine: boolean; {are we processing the cc= command line?}
doingDigitSequence: boolean; {do we want a digit sequence (for #line)?}
doingPPExpression: boolean; {are we processing a preprocessor expression?}
doingStringOrCharacter: boolean; {used to suppress comments in strings}
errors: array[1..maxErr] of errorType; {errors in this line}
eofPtr: ptr; {points one byte past the last char in the file}
fileList: filePtr; {include file list}
flagOverflows: boolean; {flag numeric overflows?}
gettingFileName: boolean; {are we in GetFileName?}
lastWasReturn: boolean; {was the last character an eol?}
lineStr: string[10]; {string form of __LINE__}
ifList: ifPtr; {points to the top prep. parse record}
includeChPtr: ptr; {chPtr at start of current token}
includeCount: 0..maxint; {nested include files (for EndInclude)}
macroFound: macroRecordPtr; {last macro found by IsDefined}
mergingStrings: boolean; {is NextToken trying to merge strings?}
needWriteLine: boolean; {is there a line that needs to be written?}
octHexEscape: boolean; {octal/hex escape in char/string?}
onOffValue: onOffEnum; {value of last on-off switch}
wroteLine: boolean; {has the current line already been written?}
numErr: 0..maxErr; {number of errors in this line}
oneStr: string[2]; {string form of __STDC__, etc.}
zeroStr: string[2]; {string form of __STDC_HOSTED__ when not hosted}
ispstring: boolean; {is the current string a p-string?}
saveNumber: boolean; {save the characters in a number?}
skipping: boolean; {skipping tokens?}
stdcVersionStr: string[8]; {string form of __STDC_VERSION__}
timeStr: longStringPtr; {macro time string}
tokenColumn: 0..maxint; {column number at start of this token}
tokenLine: 0..maxint4; {line number at start of this token}
tokenList: tokenListRecordPtr; {token putback buffer}
tokenStart: ptr; {pointer to the first char in the token}
tokenEnd: ptr; {pointer to the first char past the token}
tokenExpandEnabled: boolean; {can token be macro expanded? (only for ident)}
versionStrL: longStringPtr; {macro version string}
workString: pstring; {for building strings and identifiers}
ucnString: string[10]; {string of a UCN}
lintErrors: set of 1..maxLint; {lint error codes}
spaceStr: string[2]; {string ' ' (used in stringization)}
quoteStr: string[2]; {string '"' (used in stringization)}
numericConstants: set of tokenClass; {token classes for numeric constants}
{-- External procedures; see expression evaluator for notes ----}
procedure EndInclude (chPtr: ptr); extern;
{ Saves symbols created by the include file }
{ }
{ Parameters: }
{ chPtr - chPtr when the file returned }
{ }
{ Notes: }
{ 1. Call this subroutine right after processing an }
{ include file. }
{ 2. Fron Header.pas }
procedure ExpandDevicesGS (var parms: expandDevicesDCBGS); prodos ($0154);
procedure Expression (kind: expressionKind; stopSym: tokenSet); extern;
{ handle an expression }
function FindSymbol (var tk: tokenType; class: spaceType; oneLevel: boolean;
staticAllowed: boolean): identPtr; extern;
{ locate a symbol in the symbol table }
{ }
{ parameters: }
{ tk - token record for the identifier to find }
{ class - the kind of variable space to search }
{ oneLevel - search one level only? (used to check for }
{ duplicate symbols) }
{ staticAllowed - can we check for static variables? }
{ }
{ returns: }
{ A pointer to the symbol table entry is returned. If }
{ there is no entry, nil is returned. }
procedure FlagPragmas (pragma: pragmas); extern;
{ record the effects of a pragma }
{ }
{ parameters: }
{ pragma - pragma to record }
{ }
{ Notes: }
{ 1. From Header.pas }
procedure GetFileInfoGS (var parms: getFileInfoOSDCB); prodos ($2006);
procedure StartInclude (name: gsosOutStringPtr); extern;
{ Marks the start of an include file }
{ }
{ Notes: }
{ 1. Call this subroutine right after opening an include }
{ file. }
{ 2. From Header.pas }
function StringType(prefix: charStrPrefixEnum): typePtr; extern;
{ returns the type of a string literal with specified prefix }
{ }
{ parameters: }
{ prefix - the prefix }
procedure TermHeader; extern;
{ Stop processing the header file }
{ }
{ Note: This is called when the first code-generating }
{ subroutine is found, and again when the compile ends. It }
{ closes any open symbol file, and should take no action if }
{ called twice. }
function CnvLLX (val: longlong): extended; extern;
{ convert a long long to a real number }
{ }
{ parameters: }
{ val - the long long value }
function CnvULLX (val: longlong): extended; extern;
{ convert an unsigned long long to a real number }
{ }
{ parameters: }
{ val - the unsigned long long value }
{-- Scanner support --------------------------------------------}
procedure CheckDelimiters (var name: pString);
{ Check for delimiters, making sure they are ':' }
{ }
{ parameters: }
{ name - path name to check }
label 1;
var
dc: char; {delimiter character}
i: 0..255; {loop/index variable}
begin {CheckDelimiters}
dc := ':'; {determine what the delimiter is}
for i := 1 to length(name) do
if name[i] in [':','/'] then begin
dc := name[i];
goto 1;
end; {if}
1: ;
if dc = '/' then {replace '/' delimiters with ':'}
for i := 1 to length(name) do
if name[i] = '/' then
name[i] := ':';
end; {CheckDelimiters}
procedure AddPath (name: pString);
{ Add a path name to the path name table }
{ }
{ parameters: }
{ name - path name to add }
var
pp, ppe: pathRecordPtr; {work pointers}
begin {AddPath}
if length(name) <> 0 then begin
CheckDelimiters(name); {make sure ':' is used}
if name[length(name)] <> ':' then {make sure there is a trailing delimiter}
name := concat(name, ':');
{create the new path record}
pp := pathRecordPtr(GMalloc(sizeof(pathRecord)));
pp^.next := nil;
pp^.path := stringPtr(GMalloc(length(name)+1));
pp^.path^ := name;
if pathList = nil then {add the path to the path list}
pathList := pp
else begin
ppe := pathList;
while ppe^.next <> nil do
ppe := ppe^.next;
ppe^.next := pp;
end; {else}
end; {if}
end; {AddPath}
function Convertsl(var str: pString): longint; extern;
{ Return the integer equivalent of the string. Assumes a valid }
{ 4-byte integer string; supports unsigned values. }
procedure Convertsll(var qval: longlong; var str: pString); extern;
{ Save the integer equivalent of the string to qval. Assumes a }
{ valid 8-byte integer string; supports unsigned values. }
function ConvertHexFloat(var str: pString): extended; extern;
{ Return the extended equivalent of the hexadecimal floating- }
{ point string. }
procedure SetDateTime; extern;
{ set up the macro date/time strings }
function KeyPress: boolean; extern;
{ Has a key been pressed? }
{ }
{ If a key has not been pressed, this function returns }
{ false. If a key has been pressed, it clears the key }
{ strobe. If the key was an open-apple ., a terminal exit }
{ is performed; otherwise, the function returns true. }
function IsDefined {name: stringPtr): boolean};
{ See if a macro name is in the macro table }
{ }
{ The returned value is true if the macro exists, else false. }
{ }
{ parameters: }
{ name - name of the macro to search for }
{ }
{ outputs: }
{ macroFound - pointer to the macro found }
label 1;
var
bPtr: ^macroRecordPtr; {pointer to hash bucket}
mPtr: macroRecordPtr; {for checking list of macros}
begin {IsDefined}
IsDefined := false;
bPtr := pointer(ord4(macros) + Hash(name));
mPtr := bPtr^;
while mPtr <> nil do begin
if mPtr^.name^ = name^ then begin
if mPtr^.algorithm <> 8 then {if not _Pragma pseudo-macro}
IsDefined := true;
goto 1;
end; {if}
mPtr := mPtr^.next;
end; {while}
1:
macroFound := mPtr;
end; {IsDefined}
procedure PutBackToken {var token: tokenType; expandEnabled: boolean;
suppressPrint: boolean};
{ place a token into the token stream }
{ }
{ parameters: }
{ token - token to put back into the token stream }
{ expandEnabled - can macro expansion be performed? }
{ suppressPrint - suppress printing with #pragma expand? }
var
tPtr: tokenListRecordPtr; {work pointer}
begin {PutBackToken}
new(tPtr);
tPtr^.next := tokenList;
tokenList := tPtr;
tPtr^.token := token;
tPtr^.expandEnabled := expandEnabled;
tPtr^.suppressPrint := suppressPrint;
tPtr^.tokenStart := tokenStart;
tPtr^.tokenEnd := tokenEnd;
end; {PutBackToken}
procedure WriteLine;
{ Write the current line and any error messages to the screen. }
{ }
{ Global Variables: }
{ firstPtr - points to the first char in the line }
{ chPtr - points to the end of line character }
label 1;
var
cl: integer; {column number loop index}
cp: ptr; {work pointer}
i: 1..maxErr; {error loop index}
msg: stringPtr; {pointer to the error message}
begin {WriteLine}
if list or (numErr <> 0) then begin
if not wroteLine and not doingCommandLine then begin
if numErr <> 0 then
if filenamesInErrors then
writeln('In ',sourceFileGS.theString.theString,':');
if doingFakeFile then begin
if numErr = 0 then
goto 1
else begin
writeln('In expansion of _Pragma on line ', fileList^.lineNumber:1, ':');
write(' ');
end; {else}
end {if}
else
write(lineNumber:4, ' '); {write the line #}
cp := firstPtr; {write the characters in the line}
while (cp <> eofPtr) and (charKinds[ord(cp^)] <> ch_eol) do begin
write(chr(cp^));
cp := pointer(ord4(cp) + 1);
end; {while}
writeln; {write the end of line character}
wroteLine := true;
end; {if}
for i := 1 to numErr do {write any errors}
with errors[i] do begin
if line = lineNumber then begin
write(' ');
while lineNumber >= 10000 do begin
lineNumber := lineNumber div 10;
write(' ');
end; {while}
lineNumber := line;
cp := firstPtr;
for cl := 1 to col-1 do begin
if cp^ = HT then
write(chr(HT))
else
write(' ');
cp := pointer(ord4(cp) + 1);
end; {for}
write('^ ');
end {if}
else if doingCommandLine then
write(' Error in command line: ')
else
write(' Error in column ', col:1, ' of line ', line:1, ': ');
case num of
1 : msg := @'illegal character';
2 : msg := @'invalid character constant';
3 : msg := @'no end was found to the string';
4 : msg := @'further errors suppressed';
5 : msg := @'cannot redefine a macro';
6 : msg := @'integer overflow';
7 : msg := @'''8'' and ''9'' cannot be used in octal constants';
8 : msg := @'unknown preprocessor command';
9 : msg := @'identifier expected';
10: msg := @'cannot undefine standard macros';
11: msg := @'end of line expected';
12: msg := @''')'' expected';
13: msg := @'''('' expected';
14: msg := @'incorrect number of macro parameters';
15: msg := @'''>'' expected';
16: msg := @'file name is too long';
17: msg := @'keep must appear before any functions';
18: msg := @'integer constant expected';
19: msg := @'only one #else may be used per #if';
20: msg := @'there is no #if for this directive';
21: msg := @'an #if had no closing #endif';
22: msg := @''';'' expected';
23: msg := @'''}'' expected';
24: msg := @''']'' expected';
25: msg := @'the else has no matching if';
26: msg := @'type expected';
27: msg := @'''{'' expected';
28: msg := @'a function cannot be defined here';
29: msg := @''':'' expected';
30: msg := @'''while'' expected';
31: msg := @'undeclared identifier';
32: msg := @'the last if statement was not finished';
33: msg := @'the last do statement was not finished';
34: msg := @'the last compound statement was not finished';
35: msg := @'expression expected';
36: msg := @'expression syntax error';
37: msg := @'operand expected';
38: msg := @'operation expected';
39: msg := @'no matching ''?'' found for this '':'' operator';
40: msg := @'illegal type cast';
41: msg := @'illegal operand in a constant expression';
42: msg := @'duplicate symbol';
{43: msg := @'the function''s type must match the previous declaration';}
44: msg := @'too many initializers';
45: msg := @'the number of array elements must be greater than 0';
46: msg := @'you must initialize the individual elements of a struct, union, or non-char array';
47: msg := @'type conflict';
48: msg := @'pointer initializers must resolve to an integer, address or string';
49: msg := @'the size could not be determined';
50: msg := @'only parameters or types may be declared here';
51: msg := @'lint: undefined function';
52: msg := @'you cannot initialize a type';
53: msg := @'the struct, union, or enum has already been defined';
54: msg := @'bit fields must be less than 32 bits wide';
55: msg := @'a value cannot be zero bits wide';
{56: msg := @'bit fields in unions are not supported by ORCA/C';}
57,otherwise: msg := @'compiler error';
58: msg := @'implementation restriction: too many local labels';
59: msg := @'file name expected';
60: msg := @'implementation restriction: string space exhausted';
61: msg := @'implementation restriction: run-time stack space exhausted';
62: msg := @'auto or register can only be used in a function body';
63: msg := @'token merging produced an illegal token';
64: msg := @'assignment to an array is not allowed';
65: msg := @'assignment to void is not allowed';
66: msg := @'the operation cannot be performed on operands of the type given';
67: msg := @'the last else clause was not finished';
68: msg := @'the last while statement was not finished';
69: msg := @'the last for statement was not finished';
70: msg := @'the last switch statement was not finished';
71: msg := @'switch expressions must evaluate to integers';
72: msg := @'case and default labels must appear in a switch statement';
73: msg := @'duplicate case label';
74: msg := @'only one default label is allowed in a switch statement';
75: msg := @'continue must appear in a while, do or for loop';
76: msg := @'break must appear in a while, do, for or switch statement';
77: msg := @'duplicate label';
78: msg := @'l-value required';
79: msg := @'illegal operand for the indirection operator';
80: msg := @'the selection operator must be used on a structure or union';
81: msg := @'the selected field does not exist in the structure or union';
82: msg := @'''('', ''['' or ''*'' expected';
83: msg := @'string constant expected';
84: msg := @'''dynamic'' expected';
85: msg := @'the number of parameters does not agree with the prototype';
86: msg := @''','' expected';
87: msg := @'invalid storage type for a parameter';
88: msg := @'you cannot initialize a parameter';
89: msg := @'''.'' expected';
90: msg := @'string too long';
91: msg := @'real constants cannot be unsigned';
92: msg := @'statement expected';
93: msg := @'assignment to const is not allowed';
94: msg := @'pascal qualifier is only allowed on functions';
95: msg := @'unidentified operation code';
96: msg := @'incorrect operand size';
97: msg := @'operand syntax error';
98: msg := @'invalid operand';
{99: msg := @'comp data type is not supported by the 68881';}
100: msg := @'integer constants cannot use the f designator';
101: msg := @'digits expected in the exponent';
{102: msg := @'extern variables cannot be initialized';}
103: msg := @'functions cannot return functions or arrays';
104: msg := @'lint: missing function type';
105: msg := @'lint: parameter list not prototyped';
106: msg := @'cannot take the address of a bit field';
107: msg := @'illegal use of forward declaration';
108: msg := @'unknown or invalid cc= option';
109: msg := @'illegal math operation in a constant expression';
110: msg := @'lint: unknown pragma';
{111: msg := @'the & operator cannot be applied to arrays';}
{112: msg := @'segment buffer overflow';}
113: msg := @'all parameters must have a name';
114: msg := @'a function call was made to a non-function';
115: msg := @'illegal bit field declaration';
116: msg := @'missing field name';
117: msg := @'field cannot have incomplete or function type';
118: msg := @'flexible array must be last member of structure';
119: msg := @'inline specifier is only allowed on functions';
{120: msg := @'inline functions without ''static'' or ''extern'' are not supported';}
121: msg := @'invalid digit for binary constant';
122: msg := @'arithmetic is not allowed on a pointer to an incomplete or function type';
123: msg := @'array element type may not be an incomplete or function type';
124: msg := @'lint: invalid format string or arguments';
{125: msg := @'lint: format string is not a string literal';}
126: msg := @'scope rules may not be changed within a function';
127: msg := @'illegal storage class for declaration in for loop';
128: msg := @'lint: integer overflow in expression';
129: msg := @'lint: division by zero';
130: msg := @'lint: invalid shift count';
131: msg := @'numeric constant is too long';
132: msg := @'static assertion failed';
133: msg := @'incomplete or function types may not be used here';
{134: msg := @'''long long'' types are not supported by ORCA/C';}
{135: msg := @'the type _Bool is not supported by ORCA/C';}
136: msg := @'complex or imaginary types are not supported by ORCA/C';
137: msg := @'atomic types are not supported by ORCA/C';
138: msg := @'unsupported alignment';
{139: msg := @'thread-local storage is not supported by ORCA/C';}
{140: msg := @'unexpected token';}
141: msg := @'_Noreturn specifier is only allowed on functions';
142: msg := @'_Alignas may not be used in this declaration or type name';
143: msg := @'only object pointer types may be restrict-qualified';
{144: msg := @'generic selection expressions are not supported by ORCA/C';}
145: msg := @'invalid universal character name';
146: msg := @'Unicode character cannot be represented in execution character set';
147: msg := @'lint: not all parameters were declared with a type';
148: msg := @'all parameters must have a complete type';
149: msg := @'invalid universal character name for use in an identifier';
{150: msg := @'designated initializers are not supported by ORCA/C';}
151: msg := @'lint: type specifier missing';
152: msg := @'lint: return with no value in non-void function';
153: msg := @'lint: return statement in function declared _Noreturn';
154: msg := @'lint: function declared _Noreturn can return or has unreachable code';
155: msg := @'lint: non-void function may not return a value or has unreachable code';
156: msg := @'invalid suffix on numeric constant';
157: msg := @'unknown or malformed standard pragma';
158: msg := @'_Generic expression includes two compatible types';
159: msg := @'_Generic expression includes multiple default cases';
160: msg := @'no matching association in _Generic expression';
161: msg := @'illegal operator in a constant expression';
162: msg := @'invalid escape sequence';
163: msg := @'pointer assignment discards qualifier(s)';
{164: msg := @'compound literals within functions are not supported by ORCA/C';}
165: msg := @'''\p'' may not be used in a prefixed string';
166: msg := @'string literals with these prefixes may not be merged';
167: msg := @'''L''-prefixed character or string constants are not supported by ORCA/C';
168: msg := @'malformed hexadecimal floating constant';
169: msg := @'struct or array may not contain a struct with a flexible array member';
170: msg := @'lint: no whitespace after macro name';
171: msg := @'use of an incomplete enum type is not allowed';
172: msg := @'macro replacement list may not start or end with ''##''';
173: msg := @'''#'' must be followed by a macro parameter';
174: msg := @'''__VA_ARGS__'' may only be used in a variadic macro';
175: msg := @'duplicate macro parameter name';
176: msg := @'declarator expected';
177: msg := @'_Thread_local may not be used with the specified storage class';
178: msg := @'_Thread_local may not appear in a function declaration';
179: msg := @'_Pragma requires one string literal argument';
180: msg := @'decimal digit sequence expected';
181: msg := @'''main'' may not have any function specifiers';
182: msg := @'''='' expected';
183: msg := @'array index out of bounds';
184: msg := @'segment exceeds bank size';
185: msg := @'lint: unused variable: ';
186: msg := @'lint: implicit conversion changes value of constant';
187: msg := @'expression has incomplete struct or union type';
188: msg := @'local variable used in asm statement is out of range for addressing mode';
189: msg := @'malformed numeric constant';
190: msg := @'decimal floating types are not supported by ORCA/C';
end; {case}
if extraStr <> nil then begin
extraStr^ := concat(msg^,extraStr^);
msg := extraStr;
end; {if}
writeln(msg^);
if terminalErrors and (numErrors <> 0)
and (lintIsError or not (num in lintErrors)) then begin
if enterEditor then begin
if doingFakeFile then
ExitToEditor(msg, fileList^.disp-1)
else if line = lineNumber then
ExitToEditor(msg, ord4(firstPtr)+col-ord4(bofPtr)-1)
else
ExitToEditor(msg, ord4(firstPtr)-ord4(bofPtr)-1);
end {if}
else
TermError(0);
end; {if}
if extraStr <> nil then
dispose(extraStr);
end; {with}
{handle pauses}
if ((numErr <> 0) and wait) or KeyPress then begin
DrawHourglass;
while not KeyPress do {nothing};
ClearHourglass;
end; {if}
numErr := 0; {no errors on next line...}
end {if}
else
if KeyPress then begin {handle pauses}
DrawHourglass;
while not KeyPress do {nothing};
ClearHourglass;
end; {if}
Spin; {twirl the spinner}
1:
end; {WriteLine}
procedure PrintToken (token: tokenType);
{ Write a token to standard out }
{ }
{ parameters: }
{ token - token to print }
label 1;
var
ch: char; {work character}
i: integer; {loop counter}
str: string[23]; {temp string}
c16ptr: ^integer; {pointer to char16_t value}
c32ptr: ^longint; {pointer to char32_t value}
procedure PrintHexDigits(i: longint; count: integer);
{ Print a digit as a hex character }
{ }
{ Parameters: }
{ i: value to print in hexadecimal }
{ count: number of digits to print }
var
digit: integer; {hex digit value}
shift: integer; {amount to shift by}
begin {PrintHexDigits}
shift := 4 * (count-1);
while shift >= 0 do begin
digit := ord(i >> shift) & $000F;
if digit < 10 then
write(chr(digit | ord('0')))
else
write(chr(digit + ord('A') - 10));
shift := shift - 4;
end; {while}
end; {PrintHexDigits}
begin {PrintToken}
case token.kind of
typedef,
ident: write(token.name^);
charconst,
scharconst,
ucharconst,
intconst: write(token.ival:1);
ushortconst,
uintconst: write(token.ival:1,'U');
longConst: write(token.lval:1,'L');
ulongConst: write(token.lval:1,'UL');
longlongConst: begin
str := cnvds(CnvLLX(token.qval),1,1);
str[0] := chr(ord(str[0]) - 2);
write(str,'LL');
end;
ulonglongConst: begin
str := cnvds(CnvULLX(token.qval),1,1);
str[0] := chr(ord(str[0]) - 2);
write(str,'ULL');
end;
compConst,
doubleConst: write(token.rval:24);
floatConst: write(token.rval:16,'F');
extendedConst: write(token.rval:29,'L');
stringConst: begin
if token.prefix = prefix_u16 then begin
write('u"');
i := 1;
while i < token.sval^.length-2 do begin
write('\x');
c16Ptr := pointer(@token.sval^.str[i]);
PrintHexDigits(c16Ptr^, 4);
i := i + 2;
end; {while}
end {if}
else if token.prefix = prefix_U32 then begin
write('U"');
i := 1;
while i < token.sval^.length-4 do begin
write('\x');
c32Ptr := pointer(@token.sval^.str[i]);
PrintHexDigits(c32Ptr^, 8);
i := i + 4;
end; {while}
end {else if}
else begin
write('"');
for i := 1 to token.sval^.length-1 do begin
ch := token.sval^.str[i];
if ch in [' '..'~'] then begin
if ch in ['"','\','?'] then
write('\');
write(ch);
end {if}
else begin
write('\');
write((ord(ch)>>6):1);
write(((ord(ch)>>3) & $0007):1);
write((ord(ch) & $0007):1);
end; {else}
end; {for}
end; {else}
write('"');
end;
_Alignassy,_Alignofsy,_Atomicsy,_BitIntsy,_Boolsy,
_Complexsy,_Decimal128sy,_Decimal32sy,_Decimal64sy,_Genericsy,
_Imaginarysy,_Noreturnsy,_Static_assertsy,_Thread_localsy,alignassy,
alignofsy,autosy,asmsy,boolsy,breaksy,
casesy,charsy,continuesy,constsy,constexprsy,
compsy,defaultsy,dosy,doublesy,elsesy,
enumsy,externsy,extendedsy,falsesy,floatsy,
forsy,gotosy,ifsy,intsy,inlinesy,
longsy,nullptrsy,pascalsy,registersy,restrictsy,
returnsy,shortsy,sizeofsy,staticsy,static_assertsy,
structsy,switchsy,segmentsy,signedsy,thread_localsy,
truesy,typedefsy,typeofsy,typeof_unqualsy,unionsy,
unsignedsy,voidsy,volatilesy,whilesy:
write(reservedWords[token.kind]);
tildech,questionch,lparench,rparench,commach,semicolonch,colonch:
begin
for i := minChar to maxChar do
if charSym[i] = token.kind then begin
write(chr(i));
goto 1;
end; {if}
end;
lbrackch: if not token.isDigraph then
write('[')
else
write('<:');
rbrackch: if not token.isDigraph then
write(']')
else
write(':>');
lbracech: if not token.isDigraph then
write('{')
else
write('<%');
rbracech: if not token.isDigraph then
write('}')
else
write('%>');
poundch: if not token.isDigraph then
write('#')
else
write('%:');
minusch: write('-');
plusch: write('+');
ltch: write('<');
gtch: write('>');
eqch: write('=');
excch: write('!');
andch: write('&');
barch: write('|');
percentch: write('%');
carotch: write('^');
asteriskch: write('*');
slashch: write('/');
dotch: write('.');
minusgtop: write('->');
opplusplus,
plusplusop: write('++');
opminusminus,
minusminusop: write('--');
ltltop: write('<<');
gtgtop: write('>>');
lteqop: write('<=');
gteqop: write('>=');
eqeqop: write('==');
exceqop: write('!=');
andandop: write('&&');
barbarop: write('||');
pluseqop: write('+=');
minuseqop: write('-=');
asteriskeqop: write('*=');
slasheqop: write('/=');
percenteqop: write('%=');
ltlteqop: write('<<=');
gtgteqop: write('>>=');
andeqop: write('&=');
caroteqop: write('^=');
bareqop: write('!=');
uminus: write('-');
uplus: write('+');
uand: write('&');
uasterisk: write('*');
poundpoundop: if not token.isDigraph then
write('##')
else
write('%:%:');
dotdotdotsy: write('...');
coloncolonsy: write('::');
otherch: write(token.ch);
ppNumber: write(token.numString^);
macroParm: write('$', token.pnum:1);
parameteroper,
castoper,
eolsy,
eofsy: ;
end; {case}
1:
write(' ');
end; {PrintToken}
{ copy 'Scanner.debug'} {debug}
{-- The Preprocessor -------------------------------------------}
procedure CheckIdentifier; forward;
{ See if an identifier is a reserved word, macro or typedef }
procedure DoNumber; forward;
{ Scan a number from workString }
{ }
{ Globals: }
{ workString - string to take numbers from }
function GetFileType (var name: pString): integer; forward;
{ Checks to see if a file exists }
{ }
{ parameters: }
{ name - file name to check for }
{ }
{ Returns: File type if the file exists, or -1 if the file does }
{ not exist (or if GetFileInfo returns an error) }
function OpenFile (doInclude, default: boolean): boolean; forward;
{ Open a new file and start scanning it }
{ }
{ Parameters: }
{ doInclude - are we doing a #include? }
{ default - use the name <defaults.h>? }
{ }
{ Returns: result from GetFileName }
procedure PreProcess; forward;
{ Handle preprocessor commands }
procedure CheckConditionals;
{ Check that preprocessor conditionals were balanced at the end }
{ of an include file. }
var
ip: ifPtr;
begin {CheckConditionals}
while (ifList <> nil) and (ifList^.theFile = fileList) do begin
Error(21);
ip := ifList;
ifList := ifList^.next;
dispose(ip);
end; {while}
end; {CheckConditionals}
function FindMacro (name: stringPtr): macroRecordPtr;
{ If the current token is a macro, find the macro table entry }
{ }
{ Parameters: }
{ name - name of the suspected macro }
{ }
{ Returns: }
{ Pointer to macro table entry; nil for none }
label 1;
var
bPtr: ^macroRecordPtr; {pointer to hash bucket}
mPtr: macroRecordPtr; {pointer to macro entry}
begin {FindMacro}
FindMacro := nil;
bPtr := pointer(ord4(macros)+Hash(name));
mPtr := bPtr^;
while mPtr <> nil do begin
if mPtr^.name^ = name^ then begin
if mPtr^.parameters = -1 then
FindMacro := mPtr
else if tokenList = nil then begin
while charKinds[ord(ch)] in [ch_white, ch_eol] do begin
if printMacroExpansions and not suppressMacroExpansions then
if charKinds[ord(ch)] = ch_eol then
writeln
else
write(ch);
NextCh;
end; {while}
if ch = '(' then
FindMacro := mPtr;
end {else if}
else if tokenList^.token.kind = lparench then
FindMacro := mPtr;
goto 1;
end; {if}
mPtr := mPtr^.next;
end; {while}
1:
end; {FindMacro}
procedure LongToPString (pstr: stringPtr; lstr: longStringPtr);
{ Convert a long string into a p string }
{ }
{ The long string is assumed to include a terminating null byte,}
{ which is not copied to the p-string. }
{ }
{ Parameters: }
{ pstr - pointer to the p-string }
{ lstr - pointer to the long string }
var
i: integer; {loop variable}
len: integer; {string length}
begin {LongToPString}
len := lstr^.length-1;
if len > 255 then
len := 255;
pstr^[0] := chr(len);
for i := 1 to len do
pstr^[i] := lstr^.str[i];
if len < 255 then
pstr^[len+1] := chr(0);
end; {LongToPString}
procedure ConvertString (var str: tokenType; prefix: charStrPrefixEnum);
{ Convert unprefixed string literal str to a prefixed one }
var
sPtr: longStringPtr; {new string}
i,j,k: integer; {loop counters}
codePoint: ucsCodePoint; {Unicode code point}
c16ptr: ^integer; {pointer to char16_t value}
c32ptr: ^longint; {pointer to char32_t value}
utf8: utf8Rec; {UTF-8 encoding of character}
utf16: utf16Rec; {UTF-16 encoding of character}
begin {ConvertString}
sPtr := pointer(Malloc(str.sval^.length*4));
k := 0;
for i := 1 to str.sval^.length do begin
codePoint := ConvertMacRomanToUCS(str.sval^.str[i]);
if prefix = prefix_u8 then begin
UTF8Encode(codePoint, utf8);
for j := 1 to utf8.length do begin
sPtr^.str[k+1] := chr(utf8.bytes[j]);
k := k+1;
end; {for}
end {if}
else if prefix = prefix_u16 then begin
UTF16Encode(codePoint, utf16);
c16Ptr := pointer(@sPtr^.str[k+1]);
c16Ptr^ := utf16.codeUnits[1];
k := k+2;
if utf16.length = 2 then begin
c16ptr := pointer(@sPtr^.str[k+1]);
c16Ptr^ := utf16.codeUnits[2];
k := k+2;
end; {if}
end {else if}
else if prefix = prefix_U32 then begin
c32Ptr := pointer(@sPtr^.str[k+1]);
c32Ptr^ := codePoint;
k := k+4;
end; {else if}
end; {for}
sPtr^.length := k;
str.sval := sPtr;
str.prefix := prefix;
end; {ConvertString}
procedure Merge (var tk1: tokenType; tk2: tokenType);
{ Merge two tokens (implementing ##) }
{ }
{ Parameters: }
{ tk1 - first token; result is stored here }
{ tk2 - second token }
label 1;
var
class1,class2: tokenClass; {token classes}
i: integer; {loop variable}
kind1,kind2: tokenEnum; {token kinds}
lsaveNumber: boolean; {local copy of saveNumber}
lt: tokenType; {local copy of token}
str1,str2: stringPtr; {identifier strings}
begin {Merge}
kind1 := tk1.kind;
class1 := tk1.class;
kind2 := tk2.kind;
class2 := tk2.class;
if class1 in [identifier,reservedWord] then begin
if class1 = identifier then
str1 := tk1.name
else
str1 := @reservedWords[kind1];
if class2 = identifier then
str2 := tk2.name
else if class2 = reservedWord then
str2 := @reservedWords[kind2]
else if class2 in numericConstants then
str2 := tk2.numString
else if (class2 = stringConstant) and (tk2.prefix = prefix_none) then begin
if str1^ = 'u' then
ConvertString(tk2, prefix_u16)
else if str1^ = 'U' then
ConvertString(tk2, prefix_U32)
else if str1^ = 'u8' then
ConvertString(tk2, prefix_u8)
else
Error(63);
tk1 := tk2;
goto 1;
end {else if}
else begin
Error(63);
goto 1;
end; {else}
workString := concat(str1^, str2^);
for i := 1 to length(workString) do
if not (charKinds[ord(workString[i])] in [letter,digit]) then begin
Error(63);
goto 1;
end; {if}
lt := token;
token.kind := ident;
token.class := identifier;
token.numString := nil;
token.symbolPtr := nil;
token.name := pointer(Malloc(length(workString)+1));
CopyString(pointer(token.name), @workString);
tk1 := token;
token := lt;
goto 1;
end {class1 in [identifier,reservedWord]}
else if class1 in numericConstants then begin
if class2 in numericConstants then
str2 := tk2.numString
else if class2 = identifier then
str2 := tk2.name
else if class2 = reservedWord then
str2 := @reservedWords[kind2]
else if kind2 = dotch then
str2 := @'.'
else if (kind2 = plusch)
and (tk1.numString^[length(tk1.numString^)] in ['e','E','p','P']) then
str2 := @'+'
else if (kind2 = minusch)
and (tk1.numString^[length(tk1.numString^)] in ['e','E','p','P']) then
str2 := @'-'
else begin
Error(63);
goto 1;
end; {else}
workString := concat(tk1.numString^, str2^);
lt := token;
lsaveNumber := saveNumber;
saveNumber := true;
DoNumber;
saveNumber := lsaveNumber;
tk1 := token;
token := lt;
goto 1;
end {else if class1 in numericConstants}
else if kind1 = dotch then begin
if class2 in numericConstants then
if charKinds[ord(tk2.numString^[1])] = digit then begin
workString := concat('.', tk2.numString^);
lt := token;
DoNumber;
tk1 := token;
token := lt;
goto 1;
end; {if}
end {else if class1 in numericConstants}
else if kind1 = poundch then begin
if (kind2 = poundch) and (tk1.isDigraph = tk2.isDigraph) then begin
tk1.kind := poundpoundop;
goto 1;
end; {if}
end {else if}
else if kind1 = minusch then begin
if kind2 = gtch then begin
tk1.kind := minusgtop;
goto 1;
end {if}
else if kind2 = minusch then begin
tk1.kind := minusminusop;
goto 1;
end {else if}
else if kind2 = eqch then begin
tk1.kind := minuseqop;
goto 1;
end; {else if}
end {else if}
else if kind1 = plusch then begin
if kind2 = plusch then begin
tk1.kind := plusplusop;
goto 1;
end {else if}
else if kind2 = eqch then begin
tk1.kind := pluseqop;
goto 1;
end; {else if}
end {else if}
else if kind1 = ltch then begin
if kind2 = ltch then begin
tk1.kind := ltltop;
goto 1;
end {if}
else if kind2 = lteqop then begin
tk1.kind := ltlteqop;
goto 1;
end {else if}
else if kind2 = eqch then begin
tk1.kind := lteqop;
goto 1;
end {else if}
else if kind2 = colonch then begin
tk1.kind := lbrackch;
tk1.isDigraph := true;
goto 1;
end {else if}
else if kind2 = percentch then begin
tk1.kind := lbracech;
tk1.isDigraph := true;
goto 1;
end; {else if}
end {else if}
else if kind1 = ltltop then begin
if kind2 = eqch then begin
tk1.kind := ltlteqop;
goto 1;
end; {if}
end {else if}
else if kind1 = gtch then begin
if kind2 = gtch then begin
tk1.kind := gtgtop;
goto 1;
end {if}
else if kind2 = gteqop then begin
tk1.kind := gtgteqop;
goto 1;
end {else if}
else if kind2 = eqch then begin
tk1.kind := gteqop;
goto 1;
end; {else if}
end {else if}
else if kind1 = gtgtop then begin
if kind2 = eqch then begin
tk1.kind := gtgteqop;
goto 1;
end; {if}
end {else if}
else if kind1 = eqch then begin
if kind2 = eqch then begin
tk1.kind := eqeqop;
goto 1;
end; {if}
end {else if}
else if kind1 = excch then begin
if kind2 = eqch then begin
tk1.kind := exceqop;
goto 1;
end; {if}
end {else if}
else if kind1 = andch then begin
if kind2 = andch then begin
tk1.kind := andandop;
goto 1;
end {if}
else if kind2 = eqch then begin
tk1.kind := andeqop;
goto 1;
end; {else if}
end {else if}
else if kind1 = barch then begin
if kind2 = barch then begin
tk1.kind := barbarop;
goto 1;
end {if}
else if kind2 = eqch then begin
tk1.kind := bareqop;
goto 1;
end; {else if}
end {else if}
else if kind1 = percentch then begin
if kind2 = eqch then begin
tk1.kind := percenteqop;
goto 1;
end {if}
else if kind2 = gtch then begin
tk1.kind := rbracech;
tk1.isDigraph := true;
goto 1;
end {else if}
else if kind2 = colonch then begin
tk1.kind := poundch;
tk1.isDigraph := true;
goto 1;
end; {else if}
end {else if}
else if kind1 = carotch then begin
if kind2 = eqch then begin
tk1.kind := caroteqop;
goto 1;
end; {if}
end {else if}
else if kind1 = asteriskch then begin
if kind2 = eqch then begin
tk1.kind := asteriskeqop;
goto 1;
end; {if}
end {else if}
else if kind1 = slashch then begin
if kind2 = eqch then begin
tk1.kind := slasheqop;
goto 1;
end; {if}
end {else if}
else if kind1 = colonch then begin
if kind2 = gtch then begin
tk1.kind := rbrackch;
tk1.isDigraph := true;
goto 1;
end {if}
else if (kind2 = colonch) and (cStd >= c23) then begin
tk1.kind := coloncolonsy;
goto 1;
end; {else if}
end; {else if}
Error(63);
1:
end; {Merge}
procedure MergeStrings (var tk1: tokenType; tk2: tokenType);
{ Merge two string constant tokens }
{ }
{ Parameters: }
{ tk1 - first token; result is stored here }
{ tk2 - second token }
var
cp: longstringPtr; {pointer to work string}
i: integer; {loop variable}
len,len1: integer; {length of strings}
elementType: typePtr; {string element type}
begin {MergeStrings}
if tk1.prefix = tk2.prefix then
{OK - nothing to do}
else if tk1.prefix = prefix_none then
ConvertString(tk1, tk2.prefix)
else if tk2.prefix = prefix_none then
ConvertString(tk2, tk1.prefix)
else
Error(166);
elementType := StringType(tk1.prefix)^.aType;
len1 := tk1.sval^.length - ord(elementType^.size);
len := len1+tk2.sval^.length;
cp := pointer(Malloc(len+2));
for i := 1 to len1 do
cp^.str[i] := tk1.sval^.str[i];
for i := 1 to len-len1 do
cp^.str[i+len1] := tk2.sval^.str[i];
cp^.length := len;
if tk1.ispstring then
cp^.str[1] := chr(len-2);
tk1.sval := cp;
end; {MergeStrings}
procedure BuildStringToken (cp: ptr; len: integer; rawSourceCode: boolean);
{ Create a string token from a string }
{ }
{ Used to stringize macros. }
{ }
{ Parameters: }
{ cp - pointer to the first character }
{ len - number of characters in the string }
{ rawSourceCode - process trigraphs & line continuations? }
label 1;
var
i: integer; {loop variable}
ch: char; {work character}
begin {BuildStringToken}
token.kind := stringconst;
token.class := stringConstant;
token.ispstring := false;
token.sval := pointer(GMalloc(len+3));
token.prefix := prefix_none;
if rawSourceCode then begin
i := 1;
1: while i <= len do begin
ch := chr(cp^);
{handle trigraphs}
if (ch = '?') and (cStd < c23) then
if i < len-1 then
if chr(ptr(ord4(cp)+1)^) = '?' then
if chr(ptr(ord4(cp)+2)^) in
['=','(','/',')','''','<','!','>','-'] then begin
case chr(ptr(ord4(cp)+2)^) of
'(': ch := '[';
'<': ch := '{';
'/': ch := '\';
'''': ch := '^';
'=': ch := '#';
')': ch := ']';
'>': ch := '}';
'!': ch := '|';
'-': ch := '~';
end; {case}
len := len-2;
cp := pointer(ord4(cp)+2);
end; {if}
if ch = '\' then {handle line continuations}
if i < len then
if charKinds[ptr(ord4(cp)+1)^] = ch_eol then begin
if i < len-1 then
if ptr(ord4(cp)+2)^ in [$06,$07] then begin
len := len-1; {skip debugger characters}
cp := pointer(ord4(cp)+1);
end; {if}
len := len-2;
cp := pointer(ord4(cp)+2);
goto 1;
end;
token.sval^.str[i] := ch;
cp := pointer(ord4(cp)+1);
i := i+1;
end; {while}
end {if}
else
for i := 1 to len do begin
token.sval^.str[i] := chr(cp^);
cp := pointer(ord4(cp)+1);
end; {for}
token.sval^.str[len+1] := chr(0);
token.sval^.length := len+1;
PutBackToken(token, true, false);
end; {BuildStringToken}
procedure DoInclude (default: boolean);
{ #include }
{ }
{ Parameters: }
{ default - open <defaults.h>? }
var
fp: filePtr; {pointer to an include file}
begin {DoInclude}
new(fp); {get a file record for the current file}
fp^.next := fileList;
fileList := fp;
fp^.name := includeFileGS;
fp^.sname := sourceFileGS;
if default then
fp^.lineNumber := lineNumber
else
fp^.lineNumber := lineNumber+1;
if OpenFile(true, default) then begin {open a new file and proceed from there}
lineNumber := 1;
if ifList <> nil then
if fp^.next = nil then
TermHeader;
StartInclude(@includeFileGS);
end {if}
else begin {handle a file name error}
fileList := fp^.next;
dispose(fp);
end; {else}
end; {DoInclude}
procedure FakeInclude(buf: ptr; offset, length: longint; prevCh: char);
{ Set up to process tokens from a buffer in memory, treating it }
{ similarly to an included file. }
{ }
{ Parameters: }
{ buf - the buffer }
{ offset - offset in buffer to start tokenizing from }
{ length - length of buffer }
{ prevCh - character considered to be the previous char }
var
fp: filePtr; {pointer to an include file record}
begin
new(fp); {get a file record for the current file}
fp^.next := fileList;
fileList := fp;
fp^.name := includeFileGS;
fp^.sname := sourceFileGS;
fp^.lineNumber := lineNumber;
fp^.disp := ord4(currentChPtr)-ord4(bofPtr);
needWriteLine := false;
bofPtr := buf;
chPtr := ptr(ord4(buf)+offset); {set the start, end pointers}
eofPtr := pointer(ord4(bofPtr)+length);
firstPtr := bofPtr; {first char in line}
ch := prevCh; {set the initial character}
currentChPtr := buf;
doingFakeFile := true;
end;
procedure Do_Pragma (str: tokenType);
{ Handle a _Pragma(...) preprocessing operator }
{ }
{ Parameters: }
{ str - the argument to _Pragma (a stringconst token) }
var
lfirstPtr: ptr; {local copy of firstPtr}
lSuppressMacroExpansions: boolean; {local copy of suppressMacroExpansions}
buf: longStringPtr; {buffer to hold #pragma directive}
i: 0..maxint; {index variable}
c8ptr: ^byte; {pointer to 8-bit char in input string}
c16ptr: ^integer; {pointer to char16_t in input string}
c32ptr: ^longint; {pointer to char32_t in input string}
endptr: ptr; {pointer to end of input string}
ch: integer;
procedure PutChar (ch: integer);
{ write string constant representation of ch to buf }
begin {PutChar}
if ch < 0 then
Error(146)
else if chr(ch) in [chr(HT),' '..'~',chr($80)..chr($ff)] then begin
if i > longstringlen then
Error(90)
else begin
buf^.str[i] := chr(ch);
i := i+1;
end; {else}
end {else if}
else if ch in [ALERT,BS,FF,NEWLINE,RETURN,VT] then begin
if i > longstringlen-1 then
Error(90)
else begin
buf^.str[i] := '\';
case ch of
ALERT: buf^.str[i+1] := 'a';
BS: buf^.str[i+1] := 'b';
FF: buf^.str[i+1] := 'f';
NEWLINE: buf^.str[i+1] := 'n';
RETURN: buf^.str[i+1] := 'r';
VT: buf^.str[i+1] := 'v';
end; {case}
i := i+2;
end; {else}
end {else if}
else begin
if i > longstringlen-3 then
Error(90)
else begin
buf^.str[i] := '\';
buf^.str[i+1] := chr(((ch >> 6) & $0007) + ord('0'));
buf^.str[i+2] := chr(((ch >> 3) & $0007) + ord('0'));
buf^.str[i+3] := chr(( ch & $0007) + ord('0'));
i := i+4;
end; {else}
end; {else}
end; {PutChar}
begin {Do_Pragma}
new(buf); {build a buffer with #pragma directive}
buf^.str := '#pragma ';
i := 9;
case str.prefix of
prefix_none, prefix_u8: begin
c8ptr := pointer(@str.sval^.str[1]);
endPtr := pointer(ord4(c8ptr) + str.sval^.length - 1);
while ord4(c8ptr) < ord4(endPtr) do begin
ch := c8ptr^;
if (str.prefix = prefix_u8) and (ch > 127) then
ch := -1;
PutChar(ch);
c8ptr := pointer(ord4(c8ptr)+1);
end; {while}
end;
prefix_u16: begin
c16ptr := pointer(@str.sval^.str[1]);
endPtr := pointer(ord4(c16ptr) + str.sval^.length - 2);
while ord4(c16ptr) < ord4(endPtr) do begin
ch := ConvertUCSToMacRoman(ord4(c16ptr^));
PutChar(ch);
c16ptr := pointer(ord4(c16ptr)+2);
end; {while}
end;
prefix_u32: begin
c32ptr := pointer(@str.sval^.str[1]);
endPtr := pointer(ord4(c32ptr) + str.sval^.length - 4);
while ord4(c32ptr) < ord4(endPtr) do begin
ch := ConvertUCSToMacRoman(c32ptr^);
PutChar(ch);
c32ptr := pointer(ord4(c32ptr)+4);
end; {while}
end;
end; {case}
buf^.length := i-1;
lfirstPtr := firstPtr; {include tokens from the buffer}
WriteLine;
wroteLine := false;
FakeInclude(@buf^.str[1], 1, buf^.length, ' ');
lSuppressMacroExpansions := suppressMacroExpansions;
suppressMacroExpansions := true;
PreProcess;
suppressMacroExpansions := lSuppressMacroExpansions;
wroteLine := true;
firstPtr := lfirstPtr;
dispose(buf);
end; {Do_Pragma}
procedure Expand (macro: macroRecordPtr);
{ Expand a preprocessor macro }
{ }
{ Expands a preprocessor macro by putting tokens from the macro }
{ definition into the scanner's putback buffer. }
{ }
{ Parameters: }
{ macro - pointer to the macro to expand }
{ }
{ Globals: }
{ macroList - scanner putback buffer }
label 1;
type
parameterPtr = ^parameterRecord;
parameterRecord = record {parameter list element}
next: parameterPtr; {next parameter}
tokens: tokenListRecordPtr; {token list}
tokenStart,tokenEnd: ptr; {source pointers (for stringization)}
end;
var
bPtr: ^macroRecordPtr; {pointer to hash bucket}
done: boolean; {used to check for loop termination}
expandEnabled: boolean; {can the token be expanded?}
i: integer; {loop counter}
inhibit: boolean; {inhibit parameter expansion?}
lexpandMacros: boolean; {local copy of expandMacros}
lPreprocessing: boolean; {local copy of preprocessing}
lSuppressMacroExpansions: boolean; {local copy of suppressMacroExpansions}
mPtr: macroRecordPtr; {for checking list of macros}
newParm: parameterPtr; {for building a new parameter entry}
tlPtr, tPtr, tcPtr, lastPtr: tokenListRecordPtr; {work pointers}
paramCount: integer; {# of parameters found in the invocation}
parenCount: integer; {paren count; for balancing parenthesis}
parmEnd: parameterPtr; {for building a parameter list}
parms: parameterPtr; {points to the list of parameters}
pptr: parameterPtr; {work pointer for tracing parms list}
sp: longStringPtr; {work pointer}
stringization: boolean; {are we stringizing a parameter?}
endParmTokens: tokenSet; {tokens that end a parameter}
begin {Expand}
lSuppressMacroExpansions := suppressMacroExpansions; {inhibit token printing}
suppressMacroExpansions := true;
lexpandMacros := expandMacros; {prevent expansion of parameters}
expandMacros := false;
saveNumber := true; {save numeric strings}
parms := nil; {no parms so far}
if macro^.parameters >= 0 then begin {find the values of the parameters}
NextToken; {get the '(' (we hope...)}
if token.kind = lparench then begin
lPreprocessing := preprocessing;
preprocessing := true;
NextToken; {skip the '('}
paramCount := 0; {process the parameters}
parmEnd := nil;
endParmTokens := [rparench,commach];
repeat
done := true;
parenCount := 0;
paramCount := paramCount+1;
if macro^.isVarargs then
if paramCount = macro^.parameters then
endParmTokens := endParmTokens - [commach];
new(newParm);
newParm^.next := nil;
if parmEnd = nil then
parms := newParm
else
parmEnd^.next := newParm;
parmEnd := newParm;
newParm^.tokens := nil;
while (token.kind <> eofsy)
and ((parenCount <> 0)
or (not (token.kind in endParmTokens))) do begin
new(tPtr);
tPtr^.next := newParm^.tokens;
newParm^.tokens := tPtr;
tPtr^.token := token;
tPtr^.tokenStart := tokenStart;
tPtr^.tokenEnd := tokenEnd;
tPtr^.expandEnabled := tokenExpandEnabled;
if token.kind = lparench then
parenCount := parenCount+1
else if token.kind = rparench then
parenCount := parenCount-1;
NextToken;
end; {while}
if token.kind = commach then begin
NextToken;
done := false;
end; {if}
until done;
if paramCount = 1 then
if macro^.parameters = 0 then
if parms^.tokens = nil then
paramCount := 0;
if paramCount <> macro^.parameters then
Error(14);
if token.kind <> rparench then begin {insist on a closing ')'}
if not gettingFileName then {put back the source stream token}
PutBackToken(token, true, false);
Error(12);
end; {if}
preprocessing := lPreprocessing;
end {if}
else begin
Error(13);
if not gettingFileName then {put back the source stream token}
PutBackToken(token, true, false);
end; {else}
end; {if}
if macro^.readOnly then begin {handle special macros}
case macro^.algorithm of
1: begin {__LINE__}
if lineNumber <= maxint then begin
token.kind := intconst;
token.class := intconstant;
token.ival := ord(lineNumber);
end {if}
else begin
token.kind := longconst;
token.class := longconstant;
token.lval := lineNumber;
end; {else}
token.numString := @lineStr;
lineStr := cnvis(lineNumber);
tokenStart := @lineStr[1];
tokenEnd := pointer(ord4(tokenStart)+length(lineStr));
end;
2: begin {__FILE__}
token.kind := stringConst;
token.class := stringConstant;
token.ispstring := false;
token.prefix := prefix_none;
sp := pointer(Malloc(3+sourceFileGS.theString.size));
sp^.length := sourceFileGS.theString.size+1;
for i := 1 to sourceFileGS.theString.size do
sp^.str[i] := sourceFileGS.theString.theString[i];
sp^.str[i+1] := chr(0);
token.sval := sp;
tokenStart := @sp^.str;
tokenEnd := pointer(ord4(tokenStart)+sp^.length);
end;
3: begin {__DATE__}
token.kind := stringConst;
token.class := stringConstant;
token.ispstring := false;
token.prefix := prefix_none;
token.sval := dateStr;
tokenStart := @dateStr^.str;
tokenEnd := pointer(ord4(tokenStart)+dateStr^.length);
TermHeader; {Don't save stale value in sym file}
end;
4: begin {__TIME__}
token.kind := stringConst;
token.class := stringConstant;
token.ispstring := false;
token.prefix := prefix_none;
token.sval := timeStr;
tokenStart := @timeStr^.str;
tokenEnd := pointer(ord4(tokenStart)+timeStr^.length);
TermHeader; {Don't save stale value in sym file}
end;
5: begin {__STDC__}
token.kind := intConst; {__ORCAC__}
token.numString := @oneStr; {__STDC_NO_...__}
token.class := intConstant; {__ORCAC_HAS_LONG_LONG__}
token.ival := 1; {__STDC_UTF_16__}
oneStr := '1'; {__STDC_UTF_32__}
tokenStart := @oneStr[1];
tokenEnd := pointer(ord4(tokenStart)+1);
end;
6: begin {__VERSION__}
token.kind := stringConst;
token.class := stringConstant;
token.ispstring := false;
token.prefix := prefix_none;
token.sval := versionStrL;
tokenStart := @versionStrL^.str;
tokenEnd := pointer(ord4(tokenStart)+versionStrL^.length);
end;
7: begin {__STDC_HOSTED__}
if isNewDeskAcc or isClassicDeskAcc or isCDev or isNBA or isXCMD then
begin
token.kind := intConst;
token.numString := @zeroStr;
token.class := intConstant;
token.ival := 0;
zeroStr := '0';
tokenStart := @zeroStr[1];
tokenEnd := pointer(ord4(tokenStart)+1);
end {if}
else begin
token.kind := intConst;
token.numString := @oneStr;
token.class := intConstant;
token.ival := 1;
oneStr := '1';
tokenStart := @oneStr[1];
tokenEnd := pointer(ord4(tokenStart)+1);
end {else}
end;
9: begin {__STDC_VERSION__}
token.kind := longconst;
token.class := longconstant;
token.lval := stdcVersion[cStd];
token.numString := @stdcVersionStr;
stdcVersionStr := concat(cnvis(token.lval),'L');
tokenStart := @stdcVersionStr[1];
tokenEnd := pointer(ord4(tokenStart)+length(stdcVersionStr));
end;
8: begin {_Pragma pseudo-macro}
if (parms <> nil) and (parms^.tokens <> nil)
and (parms^.tokens^.token.kind = stringconst)
and (parms^.tokens^.next = nil) then
Do_Pragma(parms^.tokens^.token)
else
Error(179);
end;
otherwise: Error(57);
end; {case}
if macro^.algorithm <> 8 then {if not _Pragma}
PutBackToken(token, true, false);
end {if}
else begin
{expand the macro}
tlPtr := macro^.tokens; {place the tokens in the buffer...}
lastPtr := nil;
while tlPtr <> nil do begin
if tlPtr^.token.kind = macroParm then begin
pptr := parms; {find the correct parameter}
for i := 1 to tlPtr^.token.pnum do
if pptr <> nil then
pptr := pptr^.next;
if pptr <> nil then begin
{see if the macro is stringized}
stringization := false;
if tlPtr^.next <> nil then
stringization := tlPtr^.next^.token.kind = poundch;
{handle macro stringization}
if stringization then begin
tcPtr := pptr^.tokens;
if tcPtr = nil then
BuildStringToken(nil, 0, false);
while tcPtr <> nil do begin
if tcPtr^.token.kind = stringconst then begin
BuildStringToken(@quoteStr[1], 1, false);
BuildStringToken(@tcPtr^.token.sval^.str,
tcPtr^.token.sval^.length-1, false);
BuildStringToken(@quoteStr[1], 1, false);
end {if}
else begin
if tcPtr <> pptr^.tokens then
if charKinds[tcPtr^.tokenEnd^] = ch_white then
BuildStringToken(@spaceStr[1], 1, false);
BuildStringToken(tcPtr^.tokenStart,
ord(ord4(tcPtr^.tokenEnd)-ord4(tcPtr^.tokenStart)),
true);
{hack because stringconst may not have proper tokenEnd}
if tcPtr^.next <> nil then
if tcPtr^.next^.token.kind = stringconst then
if charKinds[ptr(ord4(tcPtr^.tokenStart)-1)^] = ch_white then
BuildStringToken(@spaceStr[1], 1, false);
end;
tcPtr := tcPtr^.next;
end; {while}
tlPtr := tlPtr^.next;
end {if}
{expand a macro parameter}
else begin
tcPtr := pptr^.tokens;
if tcPtr = nil then begin
if tlPtr^.next <> nil then
if tlPtr^.next^.token.kind = poundpoundop then begin
tlPtr := tlPtr^.next;
goto 1;
end; {if}
if lastPtr <> nil then
if lastPtr^.token.kind = poundpoundop then
if tokenList <> nil then
if tokenList^.token.kind = poundpoundop then
tokenList := tokenList^.next;
end; {if}
while tcPtr <> nil do begin
tokenStart := tcPtr^.tokenStart;
tokenEnd := tcPtr^.tokenEnd;
if tcPtr^.token.kind = ident then begin
mPtr := FindMacro(tcPtr^.token.name);
inhibit := false;
if tlPtr^.next <> nil then
if tlPtr^.next^.token.kind = poundpoundop then
inhibit := true;
if lastPtr <> nil then
if lastPtr^.token.kind = poundpoundop then
inhibit := true;
if not tcPtr^.expandEnabled then
inhibit := true;
if tcPtr = pptr^.tokens then
if (mPtr <> nil) and (mPtr^.parameters >= 0) then
inhibit := true;
if (mPtr <> nil) and (not inhibit) then
Expand(mPtr)
else begin
expandEnabled := tcPtr^.expandEnabled;
if expandEnabled then
if tcPtr^.token.name^ = macro^.name^ then
expandEnabled := false;
PutBackToken(tcPtr^.token, expandEnabled, false);
end; {else}
end {if}
else
PutBackToken(tcPtr^.token, true, false);
tcPtr := tcPtr^.next;
end; {while}
end; {else}
end; {if pptr <> nil}
end {if tlPtr^.token.kind = macroParm}
else begin
{place an explicit parm in the token list}
expandEnabled := true;
if tlPtr^.token.kind = ident then
if tlPtr^.token.name^ = macro^.name^ then
expandEnabled := false;
tokenStart := tlPtr^.tokenStart;
tokenEnd := tlPtr^.tokenEnd;
PutBackToken(tlPtr^.token, expandEnabled, false);
end; {else}
1: lastPtr := tlPtr;
tlPtr := tlPtr^.next;
end; {while}
end; {else}
while parms <> nil do begin {dispose of the parameter list}
tPtr := parms^.tokens;
while tPtr <> nil do begin
tlPtr := tPtr^.next;
dispose(tPtr);
tPtr := tlPtr;
end; {while}
parmEnd := parms^.next;
dispose(parms);
parms := parmEnd;
end; {while}
expandMacros := lexpandMacros; {restore the flags}
suppressMacroExpansions := lSuppressMacroExpansions;
saveNumber := false; {stop saving numeric strings}
end; {Expand}
function GetFileName (mustExist: boolean): boolean;
{ Read a file name from a directive line }
{ }
{ parameters: }
{ mustExist - should we look for an existing file? }
{ }
{ Returns true if successful, false if not. }
{ }
{ Note: The file name is placed in workString. }
const
SRC = $B0; {source file type}
var
i,j: integer; {string index & loop vars}
tempString: stringPtr;
procedure Expand (var name: pString);
{ Expands a name to a full pathname }
{ }
{ parameters: }
{ name - file name to expand }
var
exRec: expandDevicesDCBGS; {expand devices}
begin {Expand}
exRec.pcount := 2;
new(exRec.inName);
exRec.inName^.theString := name;
exRec.inName^.size := length(name);
new(exRec.outName);
exRec.outName^.maxSize := maxPath+4;
ExpandDevicesGS(exRec);
if toolerror = 0 then
with exRec.outName^.theString do begin
if size < maxPath then
theString[size+1] := chr(0);
name := theString;
end; {with}
dispose(exRec.inName);
dispose(exRec.outName);
end; {Expand}
function GetLibraryName (var name: pstring): boolean;
{ See if a library pathname is available }
{ }
{ Parameters: }
{ name - file name; set to pathname if result is true }
{ }
{ Returns: True if a name is available, else false }
var
lname: pString; {local copy of name}
begin {GetLibraryName}
lname := concat('13:ORCACDefs:', name);
Expand(lname);
if GetFileType(lname) = SRC then begin
name := lname;
GetLibraryName := true;
end {if}
else
GetLibraryName := false;
end; {GetLibraryName}
function GetLocalName (var name: pstring): boolean;
{ See if a local pathname is available }
{ }
{ Parameters: }
{ name - file name; set to pathname if result is true }
{ }
{ Returns: True if a name is available, else false }
var
lname: pstring; {work string}
pp: pathRecordPtr; {used to trace the path list}
begin {GetLocalName}
lname := name;
Expand(lname);
if GetFileType(lname) = SRC then begin
GetLocalName := true;
name := lname;
end {if}
else begin
GetLocalName := false;
pp := pathList;
while pp <> nil do begin
lname := concat(pp^.path^, name);
if GetFileType(lname) = SRC then begin
GetLocalName := true;
name := lname;
Expand(name);
pp := nil;
end {if}
else
pp := pp^.next;
end; {while}
end; {else}
end; {GetLocalName}
procedure MakeLibraryName (var name: pstring);
{ Create the library path name for an error message }
{ }
{ Parameters: }
{ name - file name; set to pathname }
begin {MakeLibraryName}
name := concat('13:ORCACDefs:', name);
Expand(name);
end; {MakeLibraryName}
procedure MakeLocalName (var name: pstring);
{ Create the local path name for an error message }
{ }
{ Parameters: }
{ name - file name; set to pathname }
begin {MakeLocalName}
Expand(name);
end; {MakeLocalName}
begin {GetFileName}
GetFileName := true;
gettingFileName := true; {in GetFileName}
while charKinds[ord(ch)] = ch_white do {finish processing the current line}
NextCh;
if ch = '<' then begin {process a library file...}
NextToken; {skip the '<'}
token.kind := stringconst; {convert a <> style name to a string}
token.class := stringConstant;
token.ispstring := false;
token.prefix := prefix_none;
i := 0;
while not (charKinds[ord(ch)] in [ch_eol,ch_gt]) do begin
i := i+1;
if (i = maxLine) then begin
Error(16);
GetFileName := false;
i := 0;
end;
workString[i] := ch;
NextCh;
end; {while}
workString[0] := chr(i);
CheckDelimiters(workString);
if mustExist then begin
if not GetLibraryName(workString) then
if not GetLocalName(workString) then
MakeLibraryName(workString);
end {if}
else
MakeLibraryName(workString);
if ch = '>' then
NextCh
else begin
Error(15);
GetFileName := false;
end; {else}
end {if}
else begin
{handle file names that are strings or macro expansions}
expandMacros := true; {allow macros to be used in the name}
NextToken; {skip the command name}
if (token.kind = stringConst) and (token.prefix = prefix_none) then begin
LongToPString(@workString, token.sval);
CheckDelimiters(workString);
if mustExist then begin
if not GetLocalName(workString) then
if not GetLibraryName(workString) then
MakeLocalName(workString);
end {if}
else
MakeLocalName(workString);
end {if}
else if token.kind = ltch then begin
{expand a macro to create a <filename> form name}
NextToken;
new(tempString);
tempString^[0] := chr(0);
while
(token.class in ([reservedWord] + numericConstants))
or (token.kind in [dotch,ident]) do begin
if token.kind = ident then
tempString^ := concat(tempString^, token.name^)
else if token.kind = dotch then
tempString^ := concat(tempString^, '.')
else if token.class = reservedWord then
tempString^ := concat(tempString^, reservedWords[token.kind])
else {if token.class in numericConstants then}
tempString^ := concat(tempString^, token.numstring^);
NextToken;
end; {while}
workString := tempString^;
dispose(tempString);
CheckDelimiters(workString);
if mustExist then begin
if not GetLibraryName(workString) then
if not GetLocalName(workString) then
MakeLibraryName(workString);
end {if}
else
MakeLibraryName(workString);
if token.kind <> gtch then begin
Error(15);
GetFileName := false;
end; {if}
end {else if}
else begin
Error(59);
GetFileName := false;
end; {else}
end; {else}
while charKinds[ord(ch)] = ch_white {finish processing the current line}
do NextCh;
if charKinds[ord(ch)] <> ch_eol then {check for extra stuff on the line}
begin
Error(11);
GetFileName := false;
end; {if}
gettingFileName := false; {not in GetFileName}
end; {GetFileName}
function GetFileType {var name: pString): integer};
{ Checks to see if a file exists }
{ }
{ parameters: }
{ name - file name to check for }
{ }
{ Returns: File type if the file exists, or -1 if the file does }
{ not exist (or if GetFileInfo returns an error) }
var
pathname: gsosInString; {GS/OS style name}
giRec: getFileInfoOSDCB; {GetFileInfo record}
begin {GetFileType}
giRec.pcount := 3;
giRec.pathName := @pathname;
pathname.theString := name;
pathname.size := length(name);
GetFileInfoGS(giRec);
if ToolError = 0 then
GetFileType := giRec.fileType
else
GetFileType := -1;
end; {GetFileType}
function OpenFile {doInclude, default: boolean): boolean};
{ Open a new file and start scanning it }
{ }
{ Parameters: }
{ doInclude - are we doing a #include? }
{ default - use the name <defaults.h>? }
{ }
{ Returns: result from GetFileName }
var
gotName: boolean; {did we get a file name?}
begin {OpenFile}
if default then begin {get the file name}
if customDefaultName <> nil then
workString := customDefaultName^
else
workString := defaultName;
gotName := true;
end {if}
else
gotName := GetFileName(true);
if gotName then begin {read the file name from the line}
OpenFile := true; {we opened it}
if doInclude and progress then {note our progress}
writeln('Including ', workString);
if not default then {write the source line}
WriteLine;
wroteLine := false;
lineNumber := lineNumber+1;
firstPtr := pointer(ord4(chPtr)+2);
needWriteLine := false;
if doInclude then {set the disp in the file}
fileList^.disp := ord4(chPtr)-ord4(bofPtr);
with ffDCBGS do begin {purge the source file}
pCount := 5;
action := 7;
pathName := @includeFileGS.theString;
end; {with}
FastFileGS(ffDCBGS);
oldincludeFileGS := includeFileGS; {set the file name}
includeFileGS.theString.theString := workString;
includeFileGS.theString.size := length(workString);
sourceFileGS := includeFileGS;
changedSourceFile := true;
ReadFile; {read the file}
chPtr := bofPtr; {set the start, end pointers}
currentChPtr := bofPtr;
eofPtr := pointer(ord4(bofPtr)+ffDCBGS.fileLength);
firstPtr := chPtr; {first char in line}
ch := chr(RETURN); {set the initial character}
if languageNumber <> long(ffDCBGS.auxType).lsw then begin
switchLanguages := true; {switch languages}
chPtr := eofPtr;
if doInclude then
TermError(7);
if fileList <> nil then
TermError(8);
end; {if}
end {if}
else
OpenFile := false; {we failed to opened it}
end; {OpenFile}
procedure PreProcess;
{ Handle preprocessor commands }
label 2;
var
lSuppressMacroExpansions: boolean; {local copy of suppressMacroExpansions}
lReportEOL: boolean; {local copy of reportEOL}
lSaveNumber: boolean; {local copy of saveNumber}
tSkipping: boolean; {temp copy of the skipping variable}
val: integer; {expression value}
nextLineNumber: longint; {number for next line}
function Defined: boolean;
{ See if a macro is defined }
begin {Defined}
expandMacros := false; {block expansions}
NextToken; {skip the command name}
if token.class in [reservedWord,identifier] then begin
Defined := IsDefined(token.name); {see if the macro is defined}
expandMacros := true; {enable expansions}
NextToken; {skip the macro name}
if token.kind <> eolsy then {check for extra stuff on the line}
Error(11);
end {if}
else
Error(9);
end; {Defined}
procedure NumericDirective;
{ Process a constant expression for a directive that has a }
{ single number as the operand. }
{ }
{ Notes: The expression evaluator returns the value in the }
{ global variable expressionValue. }
begin {NumericDirective}
doingPPExpression := true;
NextToken; {skip the directive name}
Expression(preprocessorExpression, [eolsy]); {evaluate the expression}
doingPPExpression := false;
end; {NumericDirective}
procedure OnOffSwitch;
{ Process an of-off-switch, as used in standard pragmas. }
var
flaggedError: boolean; {did we flag an error already?}
begin {OnOffSwitch}
onOffValue := off;
flaggedError := false;
NextToken; {skip the standard pragma name}
if token.kind = typedef then
token.kind := ident;
if token.kind <> ident then begin
Error(157);
flaggedError := true;
end {if}
else if token.name^ = 'ON' then
onOffValue := on
else if token.name^ = 'OFF' then
onOffValue := off
else if token.name^ = 'DEFAULT' then
onOffValue := default
else begin
Error(157);
flaggedError := true;
end; {else}
if not flaggedError then begin
NextToken;
if token.kind <> eolsy then
Error(11);
end; {if}
end; {OnOffSwitch}
procedure ProcessIf (skip: boolean);
{ handle the processing for #if, #ifdef and #ifndef }
{ }
{ parameter: }
{ skip - should we skip to the #else }
var
ip: ifPtr; {used to create a new if record}
begin {ProcessIf}
if token.kind <> eolsy then {check for extra stuff on the line}
if not tSkipping then
Error(11);
new(ip); {create a new if record}
ip^.next := ifList;
ifList := ip;
if tSkipping then {set the status of the record}
ip^.status := skippingToEndif
else if skip then
ip^.status := skippingToElse
else
ip^.status := processing;
ip^.elseFound := false; {no else has been found...}
ip^.theFile := fileList; {record the file containing the #if}
tSkipping := ip^.status <> processing; {decide if we should be skipping}
end; {ProcessIf}
procedure DoAppend;
{ #append }
var
tbool: boolean; {temp boolean}
begin {DoAppend}
TermHeader;
tbool := OpenFile(false, false); {open a new file and proceed from there}
lineNumber := 1;
end; {DoAppend}
procedure DoCDA;
{ #pragma cda NAME START SHUTDOWN }
begin {DoCDA}
FlagPragmas(p_cda);
isClassicDeskAcc := true;
NextToken; {skip the command name}
if token.kind = stringconst then {get the name}
begin
LongToPString(@menuLine, token.sval);
NextToken;
end {if}
else begin
isClassicDeskAcc := false;
Error(83);
end; {else}
if token.kind = ident then begin {get the start name}
openName := token.name;
NextToken;
end {if}
else begin
isClassicDeskAcc := false;
Error(9);
end; {else}
if token.kind = ident then begin {get the shutdown name}
closeName := token.name;
NextToken;
end {if}
else begin
isClassicDeskAcc := false;
Error(9);
end; {else}
if token.kind <> eolsy then {make sure there is nothing else on the line}
Error(11);
end; {DoCDA}
procedure DoCDev;
{ #pragma cdev START }
begin {DoCDev}
FlagPragmas(p_cdev);
isCDev := true;
NextToken; {skip the command name}
if token.kind = ident then begin {get the start name}
openName := token.name;
NextToken;
end {if}
else begin
isCDev := false;
Error(9);
end; {else}
if token.kind <> eolsy then {make sure there is nothing else on the line}
Error(11);
end; {DoCDev}
procedure DoDefine;
{ #define }
{ }
{ The way parameters are handled is a bit obtuse. Parameters }
{ have their own token type, with the token having an }
{ associated parameter number, pnum. Pnum is the number of }
{ parameters to skip to get to the parameter in the parameter }
{ list. }
{ }
{ In the macro record, parameters indicates how many }
{ parameters there are in the definition. -1 indicates that }
{ there is no parameter list, while 0 indicates that a list }
{ must exist, but that there are no parameters in the list. }
label 1,2,3;
type
stringListPtr = ^stringList;
stringList = record {for the parameter list}
next: stringListPtr;
str: pString;
end;
var
bPtr: ^macroRecordPtr; {pointer to head of hash bucket}
done: boolean; {used to test for loop termination}
i: integer; {loop variable}
mf: macroRecordPtr; {pointer to existing macro record}
mPtr: macroRecordPtr; {pointer to new macro record}
np: stringListPtr; {new parameter}
parameterList: stringListPtr; {list of parameter names}
parameters: integer; {local copy of mPtr^.parameters}
ple: stringListPtr; {pointer to the last element in parameterList}
pnum: integer; {for counting parameters}
tPtr,tk1,tk2: tokenListRecordPtr; {pointer to a token}
luseGlobalPool: boolean; {local copy of useGlobalPool}
begin {DoDefine}
lUseGlobalPool := useGlobalPool;
useGlobalPool := true; {use global memory for defines}
expandMacros := false; {block expansions}
saveNumber := true; {save characters in numeric tokens}
parameterList := nil; {no parameters yet}
NextToken; {get the token name}
{convert reserved words to identifiers}
if token.class = reservedWord then begin
token.name := @reservedWords[token.kind];
token.kind := ident;
token.class := identifier;
end {if}
else if token.kind = typedef then
token.kind := ident;
if token.kind = ident then begin {we have a name...}
mPtr := pointer(GMalloc(sizeof(macroRecord))); {create a macro record}
mPtr^.name := token.name; {record the name}
mPtr^.saved := false; {not saved in symbol file}
mPtr^.tokens := nil; {no tokens yet}
mPtr^.isVarargs := false; {not varargs (yet)}
if ch = '(' then begin {scan the parameter list...}
NextToken; {done with the name token...}
NextToken; {skip the opening '('}
parameters := 0; {no parameters yet}
ple := nil;
repeat {get the parameter names}
done := true;
if token.class = reservedWord then begin
token.name := @reservedWords[token.kind];
token.kind := ident;
token.class := identifier;
end {if}
else if token.kind = typedef then
token.kind := ident;
if token.kind = ident then begin
new(np);
np^.next := nil;
np^.str := token.name^;
if ple = nil then
parameterList := np
else
ple^.next := np;
ple := parameterList;
while ple <> np do begin
if ple^.str = token.name^ then begin
np^.str[1] := '?';
Error(175);
end; {if}
ple := ple^.next;
end; {while}
NextToken;
parameters := parameters+1;
if token.kind = commach then begin
NextToken;
done := false;
end; {if}
end {if}
else if token.kind = dotdotdotsy then begin
NextToken;
new(np);
np^.next := nil;
np^.str := '__VA_ARGS__';
if ple = nil then
parameterList := np
else
ple^.next := np;
ple := np;
parameters := parameters + 1;
mPtr^.isVarargs := true;
end; {else}
until done;
if token.kind = rparench then {insist on a matching ')'}
NextToken
else
Error(12);
end {if}
else begin
if (lint & lintC99Syntax) <> 0 then
if not (charKinds[ord(ch)] in [ch_white,ch_eol,ch_eof]) then
Error(170);
parameters := -1; {no parameter list exists}
NextToken; {done with the name token...}
end; {else}
mPtr^.parameters := parameters; {record the # of parameters}
if token.kind = poundpoundop then
Error(172);
tPtr := nil;
while token.kind <> eolsy do begin {place tokens in the replace list...}
if token.class = reservedWord then begin
token.name := @reservedWords[token.kind];
token.kind := ident;
token.class := identifier;
end {if}
else if token.kind = typedef then
token.kind := ident;
if token.kind = ident then begin {special handling for identifiers}
np := parameterList; {change parameters to macroParm}
pnum := 0;
while np <> nil do begin
if np^.str = token.name^ then begin
token.kind := macroParm;
token.class := macroParameter;
token.pnum := pnum;
goto 1;
end; {if}
pnum := pnum+1;
np := np^.next;
end; {while}
if token.name^ = '__VA_ARGS__' then
Error(174);
end; {if}
if parameters >= 0 then
if tPtr <> nil then
if tPtr^.token.kind = poundch then
Error(173);
1: tPtr := pointer(GMalloc(sizeof(tokenListRecord)));
tPtr^.next := mPtr^.tokens;
mPtr^.tokens := tPtr;
tPtr^.token := token;
tPtr^.tokenStart := tokenStart;
tPtr^.tokenEnd := tokenEnd;
tPtr^.expandEnabled := true;
NextToken;
end; {while}
if tPtr <> nil then
if (parameters >= 0) and (tPtr^.token.kind = poundch) then
Error(173)
else if tPtr^.token.kind = poundpoundop then
if tPtr^.next <> nil then
Error(172);
mPtr^.readOnly := false;
mPtr^.algorithm := 0;
if IsDefined(mPtr^.name) then begin
mf := macroFound;
if mf^.readOnly then
goto 3;
if mf^.parameters = mPtr^.parameters then begin
tk1 := mf^.tokens;
tk2 := mPtr^.tokens;
while (tk1 <> nil) and (tk2 <> nil) do begin
if tk1^.token.kind <> tk2^.token.kind then
goto 3;
if tk1^.token.class = tk2^.token.class then
case tk1^.token.class of
reservedWord: ;
reservedSymbol:
if tk1^.token.isDigraph <> tk2^.token.isDigraph then
if tk1^.token.kind in [lbrackch,rbrackch,lbracech,
rbracech,poundch,poundpoundop] then
goto 3;
identifier:
if tk1^.token.name^ <> tk2^.token.name^ then
goto 3;
intConstant:
if tk1^.token.ival <> tk2^.token.ival then
goto 3;
longConstant:
if tk1^.token.lval <> tk2^.token.lval then
goto 3;
longlongConstant:
if (tk1^.token.qval.lo <> tk2^.token.qval.lo) or
(tk1^.token.qval.hi <> tk2^.token.qval.hi) then
goto 3;
realConstant:
if tk1^.token.rval <> tk2^.token.rval then
goto 3;
stringConstant: begin
if tk1^.token.sval^.length <> tk2^.token.sval^.length
then goto 3;
if tk1^.token.ispstring <> tk2^.token.ispstring then
goto 3;
if tk1^.token.prefix <> tk2^.token.prefix then
goto 3;
for i := 1 to tk1^.token.sval^.length do
if tk1^.token.sval^.str[i] <>
tk2^.token.sval^.str[i] then
goto 3;
end;
preprocessingNumber:
if tk1^.token.numString^ <> tk2^.token.numString^ then
goto 3;
macroParameter:
if tk1^.token.pnum <> tk2^.token.pnum then
goto 3;
otherwise:
Error(57);
end; {case}
tk1 := tk1^.next;
tk2 := tk2^.next;
end; {while}
if (tk1 = nil) and (tk2 = nil) then
goto 2;
end; {if}
3: Error(5);
goto 2;
end; {if}
{insert the macro in the macro list}
bPtr := pointer(ord4(macros) + Hash(mPtr^.name));
mPtr^.next := bPtr^;
bPtr^ := mPtr;
end {if}
else
Error(9); {identifier expected}
2:
expandMacros := true; {enable expansions}
while parameterList <> nil do begin {dump the parameter names}
np := parameterList;
parameterList := np^.next;
dispose(np);
end; {while}
saveNumber := false; {stop saving numeric strings}
useGlobalPool := lUseGlobalPool;
end; {DoDefine}
procedure DoElif;
{ #elif expression }
var
ip: ifPtr; {temp; for efficiency}
begin {DoElif}
ip := ifList;
if (ip <> nil) and (ip^.theFile = fileList) then begin
{decide if we should be skipping}
tSkipping := ip^.status <> skippingToElse;
if tSkipping then
ip^.status := skippingToEndif
else begin
{evaluate the condition}
NumericDirective; {evaluate the condition}
if token.kind <> eolsy then {check for extra stuff on the line}
Error(11);
if expressionValue = 0 then
ip^.status := skippingToElse
else
ip^.status := processing;
tSkipping := ip^.status <> processing; {decide if we should be skipping}
end; {else}
end
else
Error(20);
end; {DoElif}
procedure DoElse;
{ #else }
begin {DoElse}
if (ifList <> nil) and (ifList^.theFile = fileList) then begin
if ifList^.elseFound then {check for multiple elses}
Error(19)
else
ifList^.elseFound := true;
{decide if we should be skipping}
tSkipping := ifList^.status <> skippingToElse;
if tSkipping then {set the status}
ifList^.status := skippingToEndif
else
ifList^.status := processing;
end
else
Error(20);
NextToken; {skip the command name}
if token.kind <> eolsy then {check for extra stuff on the line}
Error(11);
end; {DoElse}
procedure DoEndif;
{ #endif }
var
ip: ifPtr; {used to create a new if record}
begin {DoEndif}
if (ifList <> nil) and (ifList^.theFile = fileList) then begin
ip := ifList; {remove the top if record from the list}
ifList := ip^.next;
dispose(ip);
if ifList = nil then {decide if we should be skipping}
tSkipping := false
else
tSkipping := ifList^.status <> processing;
end {if}
else
Error(20);
NextToken; {skip the command name}
if token.kind <> eolsy then {check for extra stuff on the line}
if not allowTokensAfterEndif then
Error(11);
end; {DoEndif}
procedure DoError (isError: boolean);
{ #error pp-tokens(opt) }
var
i: integer; {loop variable}
len: integer; {string length}
msg: stringPtr; {error message ptr}
cp: ptr; {character pointer}
lFirstPtr: ptr; {local copy of firstPtr}
begin {DoError}
lFirstPtr := firstPtr;
if isError then
numErrors := numErrors+1
else
TermHeader;
new(msg);
if isError then
msg^ := '#error:'
else
msg^ := '#warning:';
NextToken; {skip the command name}
while not (token.kind in [eolsy, eofsy]) do begin
msg^ := concat(msg^, ' ');
if token.kind = stringConst then begin
len := token.sval^.length-1;
for i := 1 to len do
msg^ := concat(msg^, token.sval^.str[i]);
end {if}
else begin
len := ord(ord4(tokenEnd) - ord4(tokenStart));
cp := tokenStart;
for i := 1 to len do begin
msg^ := concat(msg^, chr(cp^));
cp := pointer(ord4(cp)+1);
end; {for}
end; {else}
NextToken;
end; {while}
writeln(msg^);
if isError and terminalErrors then begin
if enterEditor then
ExitToEditor(msg, ord4(lFirstPtr)-ord4(bofPtr))
else
TermError(0);
end; {if}
end; {DoError}
procedure DoFloat;
{ #pragma float NUMBER NUMBER }
begin {DoFloat}
FlagPragmas(p_float);
NextToken;
if token.kind in [intconst,uintconst,ushortconst] then begin
floatCard := token.ival;
NextToken;
end {if}
else
Error(18);
if token.kind in [intconst,uintconst,ushortconst] then begin
floatSlot := token.ival;
NextToken;
end {if}
else if token.kind = eolsy then
floatSlot := 0
else
Error(18);
end; {DoFloat}
procedure DoKeep;
{ #pragma keep FILENAME }
begin {DoKeep}
if GetFileName(false) then begin {read the file name}
FlagPragmas(p_keep);
if not ignoreSymbols then
if pragmaKeepFile = nil then begin
new(pragmaKeepFile);
pragmaKeepFile^.maxSize := maxPath + 4;
pragmaKeepFile^.theString.theString := workString;
pragmaKeepFile^.theString.size := length(workString);
end; {if}
if foundFunction then
Error(17);
if liDCBGS.kFlag = 0 then begin {use the old name if there is one...}
liDCBGS.kFlag := 1;
outFileGS.theString.theString := workString;
outFileGS.theString.size := length(workString);
end; {if}
end; {if}
end; {DoKeep}
procedure DoNBA;
{ #pragma nba MAIN }
begin {DoNBA}
FlagPragmas(p_nba);
isNBA := true;
NextToken; {skip the command name}
if token.kind = ident then begin {get the open name}
openName := token.name;
NextToken;
end {if}
else begin
isNBA := false;
Error(9);
end; {else}
if token.kind <> eolsy then {make sure there is nothing else on the line}
Error(11);
end; {DoNBA}
procedure DoNDA;
{ #pragma nda OPEN CLOSE ACTION INIT PERIOD EVENTMASK MENULINE}
function GetInteger: integer;
{ Get a signed integer constant }
var
isNegative: boolean; {is the value negative?}
value: integer; {value to return}
begin {GetInteger}
isNegative := false;
value := 0;
if token.kind = plusch then
NextToken
else if token.kind = minusch then begin
NextToken;
isNegative := true;
end; {else if}
if token.kind in [intconst,uintconst,ushortconst] then begin
value := token.ival;
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(18);
end; {else}
if isNegative then
GetInteger := -value
else
GetInteger := value;
end; {GetInteger}
begin {DoNDA}
FlagPragmas(p_nda);
isNewDeskAcc := true;
NextToken; {skip the command name}
if token.kind = ident then begin {get the open name}
openName := token.name;
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(9);
end; {else}
if token.kind = ident then begin {get the close name}
closeName := token.name;
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(9);
end; {else}
if token.kind = ident then begin {get the action name}
actionName := token.name;
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(9);
end; {else}
if token.kind = ident then begin {get the init name}
initName := token.name;
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(9);
end; {else}
refreshPeriod := GetInteger; {get the period}
eventMask := GetInteger; {get the event Mask}
if token.kind = stringconst then {get the name}
begin
LongToPString(@menuLine, token.sval);
NextToken;
end {if}
else begin
isNewDeskAcc := false;
Error(83);
end; {else}
if token.kind <> eolsy then {make sure there is nothing else on the line}
Error(11);
end; {DoNDA}
procedure DoUndef;
{ #undef }
label 1;
var
bPtr: ^macroRecordPtr; {hash bucket pointer}
mPtr,lastPtr: macroRecordPtr; {work pointers}
begin {DoUndef}
expandMacros := false; {block expansions}
NextToken; {get the token name}
{convert reserved words to identifiers}
if token.class = reservedWord then begin
token.name := @reservedWords[token.kind];
token.kind := ident;
token.class := identifier;
end; {if}
if token.kind = ident then begin
{find the bucket to search}
bPtr := pointer(ord4(macros)+Hash(token.name));
lastPtr := nil; {find and delete the macro entry}
mPtr := bPtr^;
while mPtr <> nil do begin
if mPtr^.name^ = token.name^ then begin
if mPtr^.readOnly then
Error(10)
else begin
if lastPtr = nil then
bPtr^ := mPtr^.next
else
lastPtr^.next := mPtr^.next;
if mPtr^.saved then
TermHeader;
end; {else}
goto 1;
end; {if}
lastPtr := mPtr;
mPtr := mPtr^.next;
end; {while}
end {if}
else
Error(9); {identifier expected}
1:
expandMacros := true; {enable expansions}
NextToken; {skip the macro name}
if token.kind <> eolsy then {make sure there's no junk on the line}
Error(11);
end; {DoUndef}
procedure DoXCMD;
{ #pragma xcmd MAIN }
begin {DoXCMD}
FlagPragmas(p_xcmd);
isXCMD := true;
NextToken; {skip the command name}
if token.kind = ident then begin {get the open name}
openName := token.name;
NextToken;
end {if}
else begin
isXCMD := false;
Error(9);
end; {else}
if token.kind <> eolsy then {make sure there is nothing else on the line}
Error(11);
end; {DoXCMD}
begin {PreProcess}
preprocessing := true;
lSuppressMacroExpansions := suppressMacroExpansions; {inhibit token printing}
suppressMacroExpansions := true;
lReportEOL := reportEOL; {we need to see eol's}
reportEOL := true;
tSkipping := skipping; {don't skip the directive name!}
skipping := false;
nextLineNumber := -1;
while charKinds[ord(ch)] = ch_white do {skip white space}
NextCh;
if ch in ['a','d','e','i','l','p','u','w'] then begin
expandMacros := false;
NextToken;
expandMacros := true;
case token.kind of
ifsy: begin
if not tSkipping then
NumericDirective;
ProcessIf(expressionValue = 0);
goto 2;
end;
elsesy: begin
DoElse;
goto 2;
end;
ident: begin
case token.name^[1] of
'a':
if token.name^ = 'append' then begin
if tskipping then goto 2;
DoAppend;
goto 2;
end; {if}
'd':
if token.name^ = 'define' then begin
if tskipping then goto 2;
DoDefine;
goto 2;
end; {if}
'e':
if token.name^ = 'endif' then begin
DoEndif;
goto 2;
end {if}
else if token.name^ = 'else' then begin
DoElse;
goto 2;
end {else if}
else if token.name^ = 'elif' then begin
DoElif;
goto 2;
end {else if}
else if token.name^ = 'error' then begin
if tskipping then goto 2;
DoError(true);
goto 2;
end; {else if}
'i':
if token.name^ = 'if' then begin
if not tSkipping then
NumericDirective;
ProcessIf(expressionValue = 0);
goto 2;
end {if}
else if token.name^ = 'ifdef' then begin
if tSkipping then
ProcessIf(false)
else
ProcessIf(not Defined);
goto 2;
end {else}
else if token.name^ = 'ifndef' then begin
if tSkipping then
ProcessIf(false)
else
ProcessIf(Defined);
goto 2;
end {else}
else if token.name^ = 'include' then begin
if tskipping then goto 2;
DoInclude(false);
goto 2;
end; {else}
'l':
if token.name^ = 'line' then begin
if tskipping then goto 2;
FlagPragmas(p_line);
lsaveNumber := saveNumber;
saveNumber := true;
doingDigitSequence := true;
NextToken;
doingDigitSequence := false;
saveNumber := lsaveNumber;
if token.kind in [intconst,longconst] then begin
nextLineNumber := 0;
for val := 1 to ord(token.numString^[0]) do begin
if not (token.numString^[val] in ['0'..'9']) then begin
Error(180);
goto 2;
end; {if}
nextLineNumber := nextLineNumber * 10 +
ord(token.numString^[val]) - ord('0');
end; {for}
NextToken;
end {if}
else
Error(180);
if (token.kind = stringconst)
and (token.prefix = prefix_none) then begin
LongToPString(
pointer(ord4(@sourceFileGS.theString)+1),
token.sval);
sourceFileGS.theString.size := token.sval^.length-1;
if sourceFileGS.theString.size > 255 then
sourceFileGS.theString.size := 255;
changedSourceFile := true;
NextToken;
end; {if}
if token.kind <> eolsy then
Error(11);
goto 2;
end; {if}
'p':
if token.name^ = 'pragma' then begin
if tskipping then goto 2;
expandMacros := false;
NextToken;
expandMacros := true;
if token.kind = floatsy then
token.name := @'float'
else if token.class <> identifier then begin
if (lint & lintPragmas) <> 0 then
Error(110);
goto 2;
end; {if}
if token.name^ <> 'STDC' then begin
{Allow macro expansion, other than for STDC }
PutBackToken(token, true, false);
NextToken;
end; {if}
if token.name^ = 'keep' then
DoKeep
else if token.name^ = 'debug' then begin
{ debug bits: }
{ 1 - range checking }
{ 2 - create debug code }
{ 4 - generate profiles }
{ 8 - generate traceback code }
{ 16 - check for stack errors }
{ 32 - check for null pointer dereferences }
{ 32768 - generate inline function names }
FlagPragmas(p_debug);
NumericDirective;
if expressionType^.kind = scalarType then
if expressionType^.baseType in [cgQuad,cgUQuad] then
expressionValue := llExpressionValue.lo;
val := long(expressionValue).lsw;
rangeCheck := odd(val);
debugFlag := odd(val >> 1);
profileFlag := odd(val >> 2);
traceBack := odd(val >> 3);
checkStack := odd(val >> 4);
checkNullPointers := odd(val >> 5);
debugStrFlag := odd(val >> 15);
profileFlag := profileFlag or debugFlag;
if token.kind <> eolsy then
Error(11);
goto 2;
end {else}
else if token.name^ = 'lint' then begin
FlagPragmas(p_lint);
NumericDirective;
if expressionType^.kind = scalarType then
if expressionType^.baseType in [cgQuad,cgUQuad] then
expressionValue := llExpressionValue.lo;
lint := long(expressionValue).lsw;
lintIsError := true;
if token.kind = semicolonch then begin
NumericDirective;
lintIsError := expressionValue <> 0;
end; {if}
if token.kind <> eolsy then
Error(11);
goto 2;
end {else}
else if token.name^ = 'memorymodel' then begin
FlagPragmas(p_memorymodel);
NumericDirective;
smallMemoryModel := expressionValue = 0;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'expand' then begin
FlagPragmas(p_expand);
NumericDirective;
printMacroExpansions := expressionValue <> 0;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'optimize' then begin
{ optimize bits: }
{ 1 - intermediate code peephole }
{ 2 - native peephole }
{ 4 - register value tracking }
{ 8 - remove stack checks }
{ 16 - common subexpression elimination }
{ 32 - loop invariant removal }
{ 64 - remove stack checks for vararg calls}
{ 128 - fp math opts that break IEEE rules }
FlagPragmas(p_optimize);
NumericDirective;
if expressionType^.kind = scalarType then
if expressionType^.baseType in [cgQuad,cgUQuad] then
expressionValue := llExpressionValue.lo;
val := long(expressionValue).lsw;
peepHole := odd(val);
npeepHole := odd(val >> 1);
registers := odd(val >> 2);
saveStack := not odd(val >> 3);
commonSubexpression := odd(val >> 4);
loopOptimizations := odd(val >> 5);
strictVararg := not odd(val >> 6);
fastMath := odd(val >> 7);
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'extensions' then begin
{ extensions bits: }
{ 1 - extended ORCA/C keywords }
{ 2 - change floating params to extended }
FlagPragmas(p_extensions);
NumericDirective;
val := long(expressionValue).lsw;
extendedKeywords := odd(val);
extendedParameters := odd(val >> 1);
SetKeywordMask;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'unix' then begin
{ unix bits: }
{ 1 - int is 32 bits }
FlagPragmas(p_unix);
NumericDirective;
val := long(expressionValue).lsw;
unix_1 := odd(val);
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'stacksize' then begin
FlagPragmas(p_stacksize);
NumericDirective;
stackSize := long(expressionValue).lsw;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'cda' then
DoCDA
else if token.name^ = 'cdev' then
DoCDev
else if token.name^ = 'nda' then
DoNDA
else if token.name^ = 'nba' then
DoNBA
else if token.name^ = 'xcmd' then
DoXCMD
else if token.name^ = 'toolparms' then begin
FlagPragmas(p_toolparms);
NumericDirective;
toolParms := expressionValue <> 0;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'databank' then begin
FlagPragmas(p_databank);
NumericDirective;
dataBank := expressionValue <> 0;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'float' then
DoFloat
else if token.name^ = 'rtl' then begin
FlagPragmas(p_rtl);
rtl := true;
NextToken;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'noroot' then begin
FlagPragmas(p_noroot);
noroot := true;
NextToken;
if token.kind <> eolsy then
Error(11);
end {else if}
{ else if token.name^ = 'printmacros' then begin {debug}
{ PrintMacroTable;
NextToken;
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'path' then begin
FlagPragmas(p_path);
NextToken;
if token.kind = stringConst then begin
LongToPString(workString, token.sval);
AddPath(workString);
NextToken;
end {if}
else
Error(83);
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'ignore' then begin
{ ignore bits: }
{ 1 - don't flag illegal tokens in skipped source }
{ 2 - allow long int character constants }
{ 4 - allow tokens after #endif }
{ 8 - allow // comments }
{ 16 - allow mixed decls & use C99 scope rules }
{ 32 - loosen some standard type checks }
FlagPragmas(p_ignore);
NumericDirective;
if expressionType^.kind = scalarType then
if expressionType^.baseType in [cgQuad,cgUQuad] then
expressionValue := llExpressionValue.lo;
val := long(expressionValue).lsw;
skipIllegalTokens := odd(val);
allowLongIntChar := odd(val >> 1);
allowTokensAfterEndif := odd(val >> 2);
allowSlashSlashComments := odd(val >> 3);
allowMixedDeclarations := odd(val >> 4);
looseTypeChecks := odd(val >> 5);
if allowMixedDeclarations <> c99Scope then begin
if doingFunction then
Error(126)
else
c99Scope := allowMixedDeclarations;
end; {if}
if token.kind <> eolsy then
Error(11);
end {else if}
else if token.name^ = 'STDC' then begin
expandMacros := false;
NextToken;
if token.name^ = 'FP_CONTRACT' then
OnOffSwitch
else if token.name^ = 'CX_LIMITED_RANGE' then
OnOffSwitch
else if token.name^ = 'FENV_ACCESS' then begin
OnOffSwitch;
FlagPragmas(p_fenv_access);
fenvAccess := (onOffValue = on);
if fenvAccess then
if doingFunction then
fenvAccessInFunction := true;
end
else
Error(157);
expandMacros := true;
end {else if}
else if (lint & lintPragmas) <> 0 then
Error(110);
goto 2;
end; {if}
'u':
if token.name^ = 'undef' then begin
if tskipping then goto 2;
DoUndef;
goto 2;
end; {if}
'w':
if token.name^ = 'warning' then
if (cStd >= c23) or not strictMode then begin
if tskipping then goto 2;
DoError(false);
goto 2;
end; {if}
otherwise: Error(57);
end; {case}
end;
otherwise: ;
end; {case}
end {if}
else if charKinds[ord(ch)] = ch_eol {allow null commands}
then begin
NextToken;
goto 2;
end; {else if}
if not tSkipping then
Error(8); {bad preprocessor command}
2:
expandMacros := false; {skip to the end of the line}
flagOverflows := false;
skipping := tSkipping;
while not (token.kind in [eolsy,eofsy]) do
NextToken;
flagOverflows := true;
expandMacros := true;
reportEOL := lReportEOL; {restore flags}
suppressMacroExpansions := lSuppressMacroExpansions;
skipping := tskipping;
preprocessing := false;
if nextLineNumber >= 0 then
lineNumber := nextLineNumber;
end; {PreProcess}
{-- Externally available routines ------------------------------}
procedure DoDefaultsDotH;
{ Handle the defaults.h file }
var
name: pString; {name of the default file}
begin {DoDefaultsDotH}
name := defaultName;
if (customDefaultName <> nil) or (GetFileType(name) <> -1) then
DoInclude(true);
end; {DoDefaultsDotH}
procedure Error {err: integer};
{ flag an error }
{ }
{ err - error number }
begin {Error}
if lintIsError or not (err in lintErrors)
then begin
if (numErr <> maxErr) or (numErrors = 0) then
numErrors := numErrors+1;
liDCBGS.merrf := 16;
end {if}
else
TermHeader;
if numErr = maxErr then {set the error number}
errors[maxErr].num := 4
else begin
numErr := numErr+1;
errors[numErr].num := err;
end; {else}
with errors[numErr] do begin {record the position of the error}
line := tokenLine;
col := tokenColumn;
extraStr := nil;
end; {with}
if numErrors <> 0 then
codeGeneration := false; {inhibit code generation}
end; {Error}
procedure ErrorWithExtraString {err:integer; extraStr: stringPtr};
{ flag an error, with an extra string to be attached to it }
{ }
{ err - error number }
{ extraStr - extra string to include in error message }
{ }
{ Note: }
{ extraStr must point to a pString allocated with new. }
{ This call transfers ownership of it. }
begin {ErrorWithExtraString}
Error(err);
if errors[numErr].num <> 4 then
errors[numErr].extraStr := extraStr;
end; {ErrorWithExtraString}
{procedure Error2 {loc, err: integer} {debug}
{ flag an error }
{ }
{ loc - error location }
{ err - error number }
{begin {Error2}
{writeln('Error ', err:1, ' flagged at location ', loc:1);
Error(err);
end; {Error2}
procedure DoNumber;
{ Scan a number from workString }
{ }
{ Globals: }
{ workString - string to take numbers from }
label 1,2;
var
atEnd: boolean; {at end of workString?}
c2: char; {next character to process}
err: integer; {error code}
i: integer; {loop index}
isBin: boolean; {is the value a binary number?}
isHex: boolean; {is the value a hex number?}
isLong: boolean; {is the value a long number?}
isLongLong: boolean; {is the value a long long number?}
isFloat: boolean; {is the value a number of type float?}
isReal: boolean; {is the value a real number?}
numIndex: 0..maxLine; {index into workString}
sp: stringPtr; {for saving identifier names}
stringIndex: 0..maxLine; {length of the number string}
unsigned: boolean; {is the number unsigned?}
val: integer; {value of a digit}
c1: char; {saved copy of last character}
numString: pString; {characters in the number}
procedure NextChar;
{ Return the next character that is a part of the number }
begin {NextChar}
if ord(workString[0]) <> numIndex then begin
numIndex := numIndex+1;
c2 := workString[numIndex];
end {if}
else begin
atEnd := true;
c2 := ' ';
end; {else}
end; {NextChar}
procedure FlagError (errCode: integer);
{ Handle an error when processing a number. Don't report }
{ errors when skipping code, because pp-numbers in skipped }
{ code never actually get converted to numeric constants. }
begin {FlagError}
if err = 0 then
err := errCode
else if err <> errCode then
err := 189;
end; {FlagError}
procedure GetDigits;
{ Read in a digit stream }
{ }
{ Variables: }
{ c2 - next character to process }
{ numString - digit sequence added to this string }
{ stringIndex - length of the string }
begin {GetDigits}
while (charKinds[ord(c2)] = digit) or
(isHex and (c2 in ['a'..'f','A'..'F'])) do begin
if c2 in ['a'..'f'] then
c2 := chr(ord(c2) & $5F);
stringIndex := stringIndex+1;
if stringIndex > 255 then begin
FlagError(131);
stringIndex := 1;
end; {if}
numString[stringIndex] := c2;
NextChar;
end; {while}
end; {GetDigits}
procedure ShiftAndOrValue (shiftCount, nextDigit: integer);
{ Shift the 64-bit value of token.qval left by shiftCount, }
{ then binary-or it with nextDigit. }
begin {ShiftAndOrValue}
while shiftCount > 0 do begin
token.qval.hi := token.qval.hi << 1;
if (token.qval.lo & $80000000) <> 0 then
token.qval.hi := token.qval.hi | 1;
token.qval.lo := token.qval.lo << 1;
shiftCount := shiftCount - 1;
end; {while}
token.qval.lo := token.qval.lo | nextDigit;
end; {ShiftAndOrValue}
begin {DoNumber}
atEnd := false; {not at end}
isBin := false; {assume it's not binary}
isHex := false; {assume it's not hex}
isReal := false; {assume it's an integer}
isLong := false; {assume a short integer}
isLongLong := false;
isFloat := false;
unsigned := false; {assume signed numbers}
err := 0; {no error so far}
stringIndex := 0; {no digits so far...}
numIndex := 0; {set up the scanner}
NextChar;
if c2 = '.' then begin {handle the case of no leading digits}
stringIndex := 1;
numString[1] := '0';
end {if}
else begin
GetDigits; {read the leading digit stream}
if c2 in ['x','X','b','B'] then {detect hex numbers}
if stringIndex = 1 then
if numString[1] = '0' then begin
c2 := chr(ord(c2) & $5f);
if c2 = 'X' then
isHex := true
else {if c2 = 'B' then}
if (cStd >= c23) or not strictMode then
isBin := true
else
goto 2;
stringIndex := 2;
numString[2] := c2;
NextChar;
GetDigits;
if not isHex or not (c2 in ['.','p','P']) then
goto 1;
end; {if}
end;
2:
if c2 = '.' then begin {handle a decimal}
stringIndex := stringIndex+1;
numString[stringIndex] := '.';
NextChar;
isReal := true;
if (charKinds[ord(c2)] = digit) or
(isHex and (c2 in ['a'..'f','A'..'F'])) then
GetDigits
else if stringIndex = 2 then begin
numString[3] := '0';
stringIndex := 3;
end; {else}
end; {if}
if (not isHex and (c2 in ['e','E'])) {handle an exponent}
or (isHex and (c2 in ['p','P'])) then begin
stringIndex := stringIndex+1;
numString[stringIndex] := c2;
NextChar;
isReal := true;
if c2 in ['+','-'] then begin
stringIndex := stringIndex+1;
numString[stringIndex] := c2;
NextChar;
end; {if}
if c2 in ['0'..'9'] then
GetDigits
else begin
stringIndex := stringIndex+1;
numString[stringIndex] := '0';
FlagError(101);
end; {else}
end; {if}
1:
while c2 in ['l','u','L','U'] do {check for long or unsigned}
if c2 in ['l','L'] then begin
if isLong or isLongLong then
FlagError(156);
c1 := c2;
NextChar;
if c2 = c1 then begin
NextChar;
isLongLong := true;
if isReal then
FlagError(156);
end {if}
else
isLong := true;
end {if}
else {if c2 in ['u','U'] then} begin
NextChar;
if unsigned then
FlagError(156)
else if isReal then
FlagError(91);
unsigned := true;
end; {else}
if c2 in ['f','F'] then begin {allow F designator on reals}
if not isReal then begin
FlagError(100);
isReal := true;
end; {if}
isFloat := true;
NextChar;
end; {if}
numString[0] := chr(stringIndex); {set the length of the string}
if doingPPExpression then
isLongLong := true;
if isReal then begin {convert a real constant}
if isFloat then
token.kind := floatConst
else if isLong then
token.kind := extendedConst
else
token.kind := doubleConst;
token.class := realConstant;
if isHex then begin
token.rval := ConvertHexFloat(numString);
if token.rval <> token.rval then {NAN => invalid format}
FlagError(168);
end {if}
else if stringIndex > 80 then begin
FlagError(131);
token.rval := 0.0;
end {if}
else
token.rval := cnvsd(numString);
end {if}
else if numString[1] <> '0' then begin {convert a decimal integer}
if (stringIndex > 5)
or (not unsigned and (stringIndex = 5) and (numString > '32767'))
or (unsigned and (stringIndex = 5) and (numString > '65535')) then
isLong := true;
if (stringIndex > 10)
or (not unsigned and (stringIndex = 10) and (numString > '2147483647'))
or (unsigned and (stringIndex = 10) and (numString > '4294967295')) then
isLongLong := true;
if (not unsigned and ((stringIndex > 19) or
((stringIndex = 19) and (numString > '9223372036854775807')))) or
(unsigned and ((stringIndex > 20) or
((stringIndex = 20) and (numString > '18446744073709551615')))) then begin
numString := '0';
if flagOverflows then
FlagError(6);
end; {if}
if isLongLong then begin
token.class := longlongConstant;
Convertsll(token.qval, numString);
if unsigned then
token.kind := ulonglongConst
else begin
token.kind := longlongConst;
end; {else}
end {if}
else if isLong then begin
token.class := longConstant;
token.lval := Convertsl(numString);
if unsigned then
token.kind := ulongConst
else
token.kind := longConst;
end {if}
else begin
if unsigned then
token.kind := uintConst
else
token.kind := intConst;
token.class := intConstant;
token.lval := Convertsl(numString);
end; {else}
end {else if}
else begin {hex, octal, & binary}
token.qval.lo := 0;
token.qval.hi := 0;
if isHex then begin
i := 3;
if length(numString) < 3 then
FlagError(189);
while i <= length(numString) do begin
if token.qval.hi & $F0000000 <> 0 then begin
i := maxint;
if flagOverflows then
FlagError(6);
end {if}
else begin
if numString[i] > '9' then
val := (ord(numString[i])-7) & $000F
else
val := ord(numString[i]) & $000F;
ShiftAndOrValue(4, val);
i := i+1;
end; {else}
end; {while}
end {if}
else if isBin then begin
i := 3;
if length(numString) < 3 then
FlagError(189);
while i <= length(numString) do begin
if token.qval.hi & $80000000 <> 0 then begin
i := maxint;
if flagOverflows then
FlagError(6);
end {if}
else begin
if not (numString[i] in ['0','1']) then
FlagError(121);
ShiftAndOrValue(1, ord(numString[i]) & $0001);
i := i+1;
end; {else}
end; {while}
end {if}
else begin
i := 2;
while i <= length(numString) do begin
if token.qval.hi & $E0000000 <> 0 then begin
i := maxint;
if flagOverflows then
FlagError(6);
end {if}
else begin
if numString[i] in ['8','9'] then
if not doingDigitSequence then
FlagError(7);
ShiftAndOrValue(3, ord(numString[i]) & $0007);
i := i+1;
end; {else}
end; {while}
end; {else}
if token.qval.hi <> 0 then
isLongLong := true;
if not isLongLong then
if long(token.qval.lo).msw <> 0 then
isLong := true;
if isLongLong then begin
if unsigned or (token.qval.hi & $80000000 <> 0) then
token.kind := ulonglongConst
else
token.kind := longlongConst;
token.class := longlongConstant;
end {if}
else if isLong then begin
if unsigned or (token.qval.lo & $80000000 <> 0) then
token.kind := ulongConst
else
token.kind := longConst;
token.class := longConstant;
end {if}
else begin
if (long(token.qval.lo).lsw & $8000) <> 0 then
unsigned := true;
if unsigned then
token.kind := uintConst
else
token.kind := intConst;
token.class := intConstant;
end; {else}
end; {else}
if not atEnd then {make sure we read all characters}
FlagError(189);
if err <> 0 then begin {handle unconvertible pp-numbers}
token.class := preprocessingNumber;
token.kind := ppnumber;
token.errCode := err;
end; {if}
if saveNumber or (err <> 0) then begin
sp := pointer(GMalloc(length(workString)+1));
CopyString(pointer(sp), @workString);
token.numString := sp;
end; {if}
end; {DoNumber}
function UniversalCharacterName : ucsCodePoint;
{ Scan a universal character name. }
{ The current character should be the 'u' or 'U'. }
{ }
{ Returns the code point value of the UCN. }
{ }
{ Globals: }
{ ucnString - string representation of this UCN }
var
digits: integer; {number of hex digits (4 or 8)}
codePoint: longint; {the code point specified by this UCN}
dig: 0..15; {value of a hex digit}
i: integer; {index for recording UCN string}
begin {UniversalCharacterName}
i := 1;
ucnString[i] := '\';
i := i + 1;
codePoint := 0;
if ch = 'u' then
digits := 4
else {if ch = 'U' then}
digits := 8;
ucnString[i] := ch;
i := i + 1;
NextCh;
while digits > 0 do begin
if ch in ['0'..'9','a'..'f','A'..'F'] then begin
if ch in ['0'..'9'] then
dig := ord(ch) & $0F
else begin
ch := chr(ord(ch)&$5F);
dig := ord(ch)-ord('A')+10;
end; {else}
codePoint := (codePoint << 4) | dig;
ucnString[i] := ch;
i := i + 1;
NextCh;
digits := digits - 1;
end {while}
else begin
if not skipping then
Error(145);
codePoint := $0000C0;
digits := 0;
end; {else}
end; {while}
ucnString[0] := chr(i - 1);
if (codePoint < 0) or (codePoint > maxUCSCodePoint)
or ((codePoint >= $00D800) and (codePoint <= $00DFFF))
or ((codePoint < $A0) and not (ord(codePoint) in [$24,$40,$60]))
then begin
Error(145);
UniversalCharacterName := $0000C0;
end {if}
else
UniversalCharacterName := codePoint;
{Normalize UCN string to shorter form for codepoints that fit in 16 bits}
if (ord(ucnString[0]) = 10) and (codePoint <= $00FFFF) then begin
ucnString[2] := 'u';
ucnString[3] := ucnString[7];
ucnString[4] := ucnString[8];
ucnString[5] := ucnString[9];
ucnString[6] := ucnString[10];
ucnString[0] := chr(6);
end; {if}
end; {UniversalCharacterName}
procedure InitScanner {start, end: ptr};
{ initialize the scanner }
{ }
{ start - pointer to the first character in the file }
{ end - points one byte past the last character in the file }
var
lch: char; {next command line character}
cp: ptr; {character pointer}
i: 0..hashSize; {loop variable}
stdName: stringPtr; {selected C standard}
tPtr: tokenListRecordPtr; {for building macros from command line}
mp: macroRecordPtr; {for building the predefined macros}
bp: ^macroRecordPtr;
procedure NextCh;
{ Get the next character from the command line }
begin {NextCh}
lch := chr(cp^);
cp := pointer(ord4(cp)+1);
tokenColumn := tokenColumn+1;
if tokenColumn > infoStringGS.theString.size then
lch := chr(0);
end; {NextCh}
function GetWord: stringPtr;
{ Read a word from the command line }
var
i: integer; {string index}
sp: stringPtr; {string pointer}
begin {GetWord}
i := 0;
while not (lch in [' ', chr(0), chr(9), '=']) do begin
i := i+1;
workString[i] := lch;
NextCh;
end; {while}
workString[0] := chr(i);
sp := pointer(malloc(length(workString)+1));
CopyString(pointer(sp), @workString);
GetWord := sp;
end; {GetWord}
function EscapeCh: integer;
{ Find and return the next character in a string or char }
{ constant. Handle escape sequences if they are found. }
{ (The character is returned as an ordinal value.) }
{ }
{ Globals: }
{ lch - first character in sequence; set to first char }
{ after sequence }
label 1;
var
cnt: 0..3; {for counting octal escape sequences}
dig: 0..15; {value of a hex digit}
skipChar: boolean; {get next char when done?}
val: 0..maxint; {hex/octal escape code value}
begin {EscapeCh}
1: skipChar := true;
if lch = '\' then begin
NextCh;
if lch in ['0'..'7','a','b','t','n','v','f','p','r','x',
'''','"','?','\'] then
case lch of
'0','1','2','3','4','5','6','7': begin
val := 0;
cnt := 0;
while (cnt < 3) and (lch in ['0'..'7']) do begin
val := (val << 3) | (ord(lch) & 7);
cnt := cnt+1;
NextCh;
end; {while}
if (val & $FF00) <> 0 then
Error(162);
EscapeCh := val & $FF;
skipChar := false;
end;
'a': EscapeCh := 7;
'b': EscapeCh := 8;
't': EscapeCh := 9;
'n': EscapeCh := 10;
'v': EscapeCh := 11;
'f': EscapeCh := 12;
'p': begin
EscapeCh := ord('p');
ispstring := true;
end;
'r': EscapeCh := 13;
'x': begin
val := 0;
NextCh;
if not (lch in ['0'..'9','a'..'f','A'..'F']) then
Error(162);
while lch in ['0'..'9','a'..'f','A'..'F'] do begin
if lch in ['0'..'9'] then
dig := ord(lch) & $0F
else begin
lch := chr(ord(lch)&$5F);
dig := ord(lch)-ord('A')+10;
end; {else}
val := (val << 4) | dig;
if (val & $FF00) <> 0 then begin
Error(162);
val := 0;
end; {if}
NextCh;
end; {while}
skipChar := false;
EscapeCh := val & $FF;
end;
'''','"','?','\': EscapeCh := ord(ch);
otherwise: Error(57);
end {case}
else begin
Error(162);
EscapeCh := ord(lch);
end; {else}
end {if}
else
EscapeCh := ord(lch);
if skipChar then
NextCh;
end; {EscapeCh}
procedure GetString;
{ read a string token from the command line }
var
i: integer; {string length}
setLength: boolean; {is the current string a p-string?}
sPtr: longstringPtr; {work string pointer}
begin {GetString}
token.kind := stringconst; {set up the token}
token.class := stringConstant;
i := 0; {set up for the string scan}
ispstring := false;
setLength := false;
new(sPtr);
NextCh; {skip the opening "}
{read the characters}
while not (charKinds[ord(lch)] in [ch_string,ch_eol,ch_eof]) do begin
i := i+1;
if i = longstringlen then begin
i := 1001;
Error(90);
end; {if}
sPtr^.str[i] := chr(EscapeCh);
if (i = 1) and ispstring then
setLength := true;
end; {while}
if lch = '"' then {process the end of the string}
NextCh
else
Error(3);
if setLength then {check for a p-string}
sPtr^.str[1] := chr(i-1);
token.ispstring := setLength;
sPtr^.length := i; {set the string length}
token.sval := pointer(Malloc(i+3)); {put the string in the string pool}
CopyLongString(token.sval, pointer(sPtr));
dispose(sPtr);
token.sval^.str[i+1] := chr(0); {add null terminator}
token.sval^.length := i+1;
token.prefix := prefix_none;
end; {GetString}
procedure FlagErrorAndSkip;
{ Flag an error about an invalid cc= option and skip }
{ characters from the command line until whitespace or end. }
begin {FlagErrorAndSkip}
Error(108);
while not (lch in [chr(0),' ',chr(9)]) do
NextCh;
end; {FlagErrorAndSkip}
begin {InitScanner}
printMacroExpansions := false; {don't print the token list}
suppressMacroExpansions := false; {...but do not suppress token printing}
skipIllegalTokens := false; {flag illegal tokens in skipped code}
allowLongIntChar := false; {allow long int char constants}
allowTokensAfterEndif := false; {allow tokens after #endif}
allowSlashSlashComments := true; {allow // comments (C99)}
allowMixedDeclarations := true; {allow mixed declarations & stmts (C99)}
c99Scope := true; {follow C99 rules for block scopes}
looseTypeChecks := true; {loosen some standard type checks}
extendedKeywords := true; {allow extended ORCA/C keywords}
extendedParameters := true; {treat all floating params as extended}
foundFunction := false; {no functions found so far}
fileList := nil; {no included files}
gettingFileName := false; {not in GetFileName}
ifList := nil; {no conditional comp. records}
skipping := false; {not skipping tokens}
flagOverflows := true; {flag overflow errors?}
new(macros); {no preprocessor macros so far}
for i := 0 to hashSize do
macros^[i] := nil;
pathList := nil; {no additional search paths}
tokenList := nil; {nothing in putback buffer}
saveNumber := false; {don't save numbers}
expandMacros := true; {enable macro expansion}
reportEOL := false; {report eolsy as a token?}
lineNumber := 1; {start the line counter}
chPtr := start; {set the start, end pointers}
currentChPtr := start;
eofPtr := endPtr;
firstPtr := start; {first char in line}
numErr := 0; {no errors so far}
numErrors := 0;
includeCount := 0; {no pending calls to EndInclude}
lint := 0; {turn off lint checks}
ch := chr(RETURN); {set the initial character}
needWriteLine := false; {no lines are pending}
wroteLine := false; {current line has not been written}
switchLanguages := false; {not switching languages}
lastWasReturn := false; {last char was not return}
doingStringOrCharacter := false; {not doing a string}
doingPPExpression := false; {not doing a preprocessor expression}
unix_1 := false; {int is 16 bits}
lintIsError := true; {lint messages are considered errors}
fenvAccess := false; {not accessing fp environment}
charStrPrefix := prefix_none; {no char/str prefix seen}
mergingStrings := false; {not currently merging strings}
customDefaultName := nil; {no custom default name}
pragmaKeepFile := nil; {no #pragma keep file so far}
doingFakeFile := false; {not doing a fake file}
doingDigitSequence := false; {not expecting a digit sequence}
preprocessing := false; {not preprocessing}
cStd := c17; {default to C17}
strictMode := false; {...with extensions}
{error codes for lint messages}
{if changed, also change maxLint}
lintErrors :=
[51,104,105,110,124,125,128,129,130,147,151,152,153,154,155,170,185,186];
spaceStr := ' '; {strings used in stringization}
quoteStr := '"';
{set of classes for numeric constants}
numericConstants :=
[intConstant,longConstant,longlongConstant,realConstant,preprocessingNumber];
new(mp); {__LINE__}
mp^.name := @'__LINE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 1;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__FILE__}
mp^.name := @'__FILE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 2;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__DATE__}
mp^.name := @'__DATE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 3;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__TIME__}
mp^.name := @'__TIME__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 4;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC__}
mp^.name := @'__STDC__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__ORCAC__}
mp^.name := @'__ORCAC__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__VERSION__}
mp^.name := @'__VERSION__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 6;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_UTF_16__}
mp^.name := @'__STDC_UTF_16__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_UTF_32__}
mp^.name := @'__STDC_UTF_32__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__ORCAC_HAS_LONG_LONG__}
mp^.name := @'__ORCAC_HAS_LONG_LONG__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_NO_ATOMICS__}
mp^.name := @'__STDC_NO_ATOMICS__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_NO_COMPLEX__}
mp^.name := @'__STDC_NO_COMPLEX__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_NO_THREADS__}
mp^.name := @'__STDC_NO_THREADS__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_NO_VLA__}
mp^.name := @'__STDC_NO_VLA__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {__STDC_HOSTED__}
mp^.name := @'__STDC_HOSTED__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 7;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp); {_Pragma pseudo-macro}
mp^.name := @'_Pragma';
mp^.parameters := 1;
mp^.isVarargs := true;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 8;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
SetDateTime; {set up the macro date/time strings}
{set up the version string}
versionStrL := pointer(GCalloc(3 + length(versionStr)));
versionStrL^.length := length(versionStr)+1;
versionStrL^.str := versionStr;
{Scan the command line options}
cp := @infoStringGS.theString.theString;
tokenLine := 0;
tokenColumn := 0;
doingCommandLine := true;
NextCh;
repeat
while lch in [' ', chr(9)] do {skip leading blanks}
NextCh;
if lch = '-' then begin {see if we have found one}
NextCh;
if lch in ['d','D'] then begin
NextCh; {yes -> get the name}
if charKinds[ord(lch)] <> letter then
Error(9);
new(mp); {form the macro table entry}
mp^.name := GetWord;
mp^.parameters := -1;
mp^.readOnly := false;
mp^.saved := true;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
if lch = '=' then begin
mp^.tokens := nil;
NextCh; {record the value}
if lch in ['+','-','*','&','~','!'] then begin
new(tPtr);
tPtr^.next := mp^.tokens;
mp^.tokens := tPtr;
tPtr^.expandEnabled := true;
tPtr^.tokenStart := ptr(ord4(cp)-1);
tPtr^.tokenEnd := cp;
tPtr^.token.class := reservedSymbol;
tPtr^.token.isDigraph := false;
tPtr^.token.numString := nil;
case lch of
'+': tPtr^.token.kind := plusch;
'-': tPtr^.token.kind := minusch;
'*': tPtr^.token.kind := asteriskch;
'&': tPtr^.token.kind := andch;
'~': tPtr^.token.kind := tildech;
'!': tPtr^.token.kind := excch;
end; {case}
NextCh;
end;
if not (charKinds[ord(lch)] in [ch_white,ch_eol,ch_eof]) then begin
new(tPtr);
tPtr^.next := mp^.tokens;
mp^.tokens := tPtr;
tPtr^.expandEnabled := true;
tPtr^.tokenStart := ptr(ord4(cp)-1);
token.numString := nil;
if charKinds[ord(lch)] = letter then begin
token.kind := ident;
token.class := identifier;
token.name := GetWord;
token.symbolPtr := nil;
end {if}
else if lch in ['.','0'..'9'] then begin
token.name := GetWord;
saveNumber := true;
DoNumber;
saveNumber := false;
end {else if}
else if lch = '"' then
GetString
else begin
FlagErrorAndSkip;
mp^.tokens := tPtr^.next;
end; {else}
tPtr^.token := token;
tPtr^.tokenEnd := ptr(ord4(cp)-1);
end; {if}
end {if}
else begin
new(tPtr);
tPtr^.next := nil;
mp^.tokens := tPtr;
tPtr^.expandEnabled := true;
oneStr := '1';
tPtr^.tokenStart := @oneStr[1];
tPtr^.tokenEnd := pointer(ord4(@oneStr[1])+1);
tPtr^.token.kind := intconst;
tPtr^.token.numString := @oneStr;
tPtr^.token.class := intConstant;
tPtr^.token.ival := 1;
end; {else}
end {if}
else if lch in ['i','I'] then begin
NextCh; {get the pathname}
if lch = '"' then begin
GetString;
LongToPString(workString, token.sval);
AddPath(workString);
end {if}
else
FlagErrorAndSkip;
end {else if}
else if lch in ['p','P'] then begin
NextCh; {get the filename}
if lch = '"' then begin
GetString;
if customDefaultName = nil then
new(customDefaultName)
else
Error(108);
LongToPString(customDefaultName, token.sval);
end {if}
else
FlagErrorAndSkip;
end {else if}
else if lch in ['s','S'] then begin
NextCh;
stdName := GetWord;
if (stdName^ = 'c89compat') or (stdName^ = 'c90compat') then begin
cStd := c89;
strictMode := false;
end {if}
else if (stdName^ = 'c94compat') or (stdName^ = 'c95compat') then begin
cStd := c95;
strictMode := false;
end {else if}
else if (stdName^ = 'c99compat') then begin
cStd := c99;
strictMode := false;
end {else if}
else if (stdName^ = 'c11compat') then begin
cStd := c11;
strictMode := false;
end {else if}
else if (stdName^ = 'c11') then begin
cStd := c11;
strictMode := true;
end {else if}
else if (stdName^ = 'c17compat') or (stdName^ = 'c18compat') then begin
cStd := c17;
strictMode := false;
end {else if}
else if (stdName^ = 'c17') or (stdName^ = 'c18') then begin
cStd := c17;
strictMode := true;
end {else if}
else if (stdName^ = 'c23compat') or (stdName^ = 'c24compat') then begin
cStd := c23;
strictMode := false;
end {else if}
else if (stdName^ = 'c23') or (stdName^ = 'c24') then begin
cStd := c23;
strictMode := true;
end {else if}
else
FlagErrorAndSkip;
end {else if}
else {not -d, -i, -p, -s: flag the error}
FlagErrorAndSkip;
end {if}
else if lch <> chr(0) then begin
Error(108); {unknown option: flag the error}
lch := chr(0);
end; {else}
until lch = chr(0); {if more characters, loop}
if numErr <> 0 then
WriteLine;
doingCommandLine := false;
{Standard-dependent configuration}
if cStd >= c95 then begin
new(mp); {add __STDC_VERSION__ macro}
mp^.name := @'__STDC_VERSION__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.saved := true;
mp^.algorithm := 9;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
end; {if}
if cStd < c99 then begin
allowSlashSlashComments := false;
allowMixedDeclarations := false;
c99Scope := false;
end; {if}
if strictMode then begin
extendedKeywords := false;
extendedParameters := false;
looseTypeChecks := false;
if cStd >= c99 then
lint := lint | lintC99Syntax;
new(mp); {add __KeepNamespacePure__ macro}
mp^.name := @'__KeepNamespacePure__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := false;
mp^.saved := true;
mp^.algorithm := 0;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
end; {if}
SetKeywordMask;
end; {InitScanner}
procedure SetKeywordMask;
{ Set the mask defining which keywords to recognize based on }
{ current language mode. }
begin {SetKeywordMask}
if cStd >= c23 then
keywordMask := c23keyword
else
keywordMask := c17keyword;
if extendedKeywords then
keywordMask := keywordMask | orcacKeyword;
end; {SetKeywordMask}
procedure CheckIdentifier;
{ See if an identifier is a reserved word, macro or typedef }
label 1;
var
mPtr: macroRecordPtr; {for checking list of macros}
rword: tokenEnum; {loop variable}
sp: stringPtr; {for saving identifier names}
lSuppressMacroExpansions: boolean; {local copy of suppressMacroExpansions}
begin {CheckIdentifier}
if expandMacros then {handle macro expansions}
if not skipping then begin
mPtr := FindMacro(@workstring);
if mPtr <> nil then begin
Expand(mPtr);
lSuppressMacroExpansions := suppressMacroExpansions;
suppressMacroExpansions := true;
NextToken;
suppressMacroExpansions := lSuppressMacroExpansions;
goto 1;
end;
end; {if}
{see if it's a reserved word}
if workString[1] in ['_','a'..'g','i','l','n','p','r'..'w'] then
for rword := wordHash[ord(workString[1])-ord('_')] to
pred(wordHash[ord(succ(workString[1]))-ord('_')]) do
if reservedWords[rword] = workString then
if (keywordCategories[rword] & keywordMask) <> 0 then begin
token.kind := rword;
token.class := reservedWord;
goto 1;
end; {if}
token.symbolPtr := nil; {see if it's a typedef name}
if FindSymbol(token,allSpaces,false,false) <> nil then begin
if token.symbolPtr^.class = typedefsy then
token.kind := typedef;
token.name := token.symbolPtr^.name; {use the old name}
end {if}
else begin {record the name}
sp := pointer(Malloc(length(workString)+1));
CopyString(pointer(sp), @workString);
token.name := sp;
end; {else}
1:
end; {CheckIdentifier}
function PeekCh: char;
{ Peek at the next character from the file, without advancing }
{ the character pointer. }
{ }
{ This is typically the character that will be produced by the }
{ next call to NextCh, but this function only deals with }
{ translation phases 1 and 2 (trigraphs and line continuations).}
{ It does not skip comments. }
label 1;
var
cp: ptr; {work pointer}
ch: char; {work character}
begin {PeekCh}
cp := chPtr;
1:
if cp = eofPtr then
PeekCh := chr(0)
else begin
ch := chr(cp^);
if (ch = '?') and (cStd < c23) then {handle trigraphs}
if ord4(eofPtr)-ord4(cp) > 2 then
if chr(ptr(ord4(cp)+1)^) = '?' then
if chr(ptr(ord4(cp)+2)^) in
['=','(','/',')','''','<','!','>','-'] then begin
case chr(ptr(ord4(cp)+2)^) of
'(': ch := '[';
'<': ch := '{';
'/': ch := '\';
'''': ch := '^';
'=': ch := '#';
')': ch := ']';
'>': ch := '}';
'!': ch := '|';
'-': ch := '~';
end; {case}
cp := pointer(ord4(cp)+2);
end; {if}
if ch = '\' then {handle line continuations}
if ord4(eofPtr)-ord4(cp) > 1 then
if charKinds[ptr(ord4(cp)+1)^] = ch_eol then begin
if ord4(eofPtr)-ord4(cp) > 2 then
if ptr(ord4(cp)+2)^ in [$06,$07] then
cp := pointer(ord4(cp)+1); {skip debugger characters}
cp := pointer(ord4(cp)+2);
goto 1;
end; {if}
PeekCh := ch;
end; {else}
end; {PeekCh}
procedure NextToken;
{ Read the next token from the file. }
label 1,2,3,4,5,6,7,8;
type
three = (s100,s1000,sMAX); {these declarations are used for a}
gstringPtr = ^gstringRecord; { variable length string record }
gstringRecord = record
case three of
s100: (len1: integer;
str1: packed array[1..100] of char;
);
s1000: (len2: integer;
str2: packed array[1..1000] of char;
);
sMAX: (len3: integer;
str3: packed array[1..longstringlen] of char;
);
end;
var
done: boolean; {loop termination}
expandEnabled: boolean; {can a token be expanded?}
i,j: 0..maxint; {loop/index counter}
inhibit: boolean; {inhibit macro expansion?}
lExpandMacros: boolean; {local copy of expandMacros}
lSuppressMacroExpansions: boolean; {local copy of suppressMacroExpansions}
mPtr: macroRecordPtr; {for checking list of macros}
setLength: boolean; {is the current string a p-string?}
tPtr: tokenListRecordPtr; {for removing tokens from putback buffer}
tToken: tokenType; {for merging tokens}
sPtr,tsPtr: gstringPtr; {for forming string constants}
suppressPrint: boolean; {suppress printing the token?}
lLastWasReturn: boolean; {local copy of lastWasReturn}
codePoint: longint; {Unicode character value}
chFromUCN: integer; {character given by UCN (converted)}
c16ptr: ^integer; {pointer to char16_t value}
c32ptr: ^longint; {pointer to char32_t value}
utf8: utf8Rec; {UTF-8 encoding of character}
utf16: utf16Rec; {UTF-16 encoding of character}
function EscapeCh: longint;
{ Find and return the next character in a string or char }
{ constant. Handle escape sequences if they are found. }
{ (The character is returned as an ordinal value.) }
{ }
{ Globals: }
{ ch - first character in sequence; set to first char }
{ after sequence }
{ charStrPrefix - prefix of the char constant or string }
{ octHexEscape - true if this was an octal/hex escape seq.}
var
cnt: 0..3; {for counting octal escape sequences}
dig: 0..15; {value of a hex digit}
skipChar: boolean; {get next char when done?}
val: longint; {hex/octal escape code value}
codePoint: ucsCodePoint; {code point given by UCN}
chFromUCN: integer; {character given by UCN (converted)}
begin {EscapeCh}
octHexEscape := false;
skipChar := true;
if ch = '\' then begin
NextCh;
if ch in ['0'..'7','a','b','t','n','v','f','p','r','x','u','U',
'''','"','?','\'] then
case ch of
'0','1','2','3','4','5','6','7': begin
val := 0;
cnt := 0;
while (cnt < 3) and (ch in ['0'..'7']) do begin
val := (val << 3) | (ord(ch) & 7);
cnt := cnt+1;
NextCh;
end; {while}
if (val & $FF00) <> 0 then
if charStrPrefix in [prefix_none,prefix_u8] then begin
if not skipping then
Error(162);
val := 0;
end; {if}
EscapeCh := val;
octHexEscape := true;
skipChar := false;
end;
'a': EscapeCh := 7;
'b': EscapeCh := 8;
't': EscapeCh := 9;
'n': EscapeCh := 10;
'v': EscapeCh := 11;
'f': EscapeCh := 12;
'p': begin
EscapeCh := ord('p');
ispstring := true;
end;
'r': EscapeCh := 13;
'x': begin
val := 0;
NextCh;
if not (ch in ['0'..'9','a'..'f','A'..'F']) then
Error(162);
while ch in ['0'..'9','a'..'f','A'..'F'] do begin
if ch in ['0'..'9'] then
dig := ord(ch) & $0F
else begin
ch := chr(ord(ch)&$5F);
dig := ord(ch)-ord('A')+10;
end; {else}
if ((charStrPrefix = prefix_none) and ((val & $F0) <> 0)) or
((charStrPrefix = prefix_u8) and ((val & $F0) <> 0)) or
((charStrPrefix = prefix_u16) and ((val & $F000) <> 0)) or
((charStrPrefix = prefix_u32) and ((val & $F0000000) <> 0))
then begin
if not skipping then
Error(162);
while ch in ['0'..'9','a'..'f','A'..'F'] do
NextCh;
val := 0;
end {if}
else begin
val := (val << 4) | dig;
NextCh;
end; {else}
end; {while}
skipChar := false;
EscapeCh := val;
octHexEscape := true;
end;
'u','U': begin
codePoint := UniversalCharacterName;
skipChar := false;
if charStrPrefix = prefix_none then begin
chFromUCN := ConvertUCSToMacRoman(codePoint);
if chFromUCN >= 0 then
EscapeCh := chFromUCN
else begin
EscapeCh := 0;
if not skipping then
Error(146);
end; {else}
end {if}
else
EscapeCh := codePoint;
end;
'''','"','?','\': EscapeCh := ord(ch);
otherwise: Error(57);
end {case}
else begin
if not skipping then
Error(162);
EscapeCh := ord(ch);
end; {else}
end {if}
else
if charStrPrefix = prefix_none then
EscapeCh := ord(ch)
else
EscapeCh := ConvertMacRomanToUCS(ord(ch));
if skipChar then
NextCh;
end; {EscapeCh}
procedure CharConstant;
{ Scan a single-quote character constant }
var
cnt: integer; {number of characters scanned}
result: longint; {character value}
begin {CharConstant}
{set up locals}
cnt := 0;
result := 0;
doingStringOrCharacter := true;
{skip the leading quote}
NextCh;
if charStrPrefix = prefix_L then begin
charStrPrefix := prefix_u16;
if not skipping then
Error(167);
end; {if}
{read the characters in the constant}
while (not (charKinds[ord(ch)] in [ch_char,ch_eol,ch_eof])) do begin
if cnt < maxint then
cnt := cnt + 1;
if charStrPrefix = prefix_none then
result := (result << 8) | EscapeCh
else
result := EscapeCh;
end; {while}
doingStringOrCharacter := false;
{skip the closing quote}
if (charKinds[ord(ch)] = ch_char) then begin
if (cnt = 0) and ((not skipping) or (not skipIllegalTokens)) then
Error(2);
NextCh;
end {if}
else if (not skipping) or (not skipIllegalTokens) then
Error(2);
{create the token}
if charStrPrefix = prefix_none then begin
if allowLongIntChar and (cnt >= 3) then begin
token.kind := longconst;
token.class := longConstant;
token.lval := result;
end {if}
else begin
token.kind := intconst;
token.class := intConstant;
token.ival := long(result).lsw;
end; {else}
end {if}
else if charStrPrefix = prefix_u16 then begin
token.kind := ushortconst;
token.class := intConstant;
if octHexEscape then
token.ival := long(result).lsw
else begin
UTF16Encode(result, utf16);
token.ival := utf16.codeUnits[1];
end; {else}
end {else if}
else if charStrPrefix = prefix_U32 then begin
token.kind := ulongconst;
token.class := longConstant;
token.lval := result;
end; {else if}
if saveNumber then {TODO: support token merging}
token.numString := @'?';
charStrPrefix := prefix_none; {no prefix for next char/str (so far)}
end; {CharConstant}
procedure ConcatenateTokenString(tPtr: tokenListRecordPtr);
{ Concatenate the strings for the current token and the one }
{ represented by tPtr, and update tokenStart/tokenEnd to }
{ point to the new string. }
var
len: longint; {length of new token string}
srcPtr, destPtr: ptr; {pointers for data copying}
begin {ConcatenateTokenString}
len := ord4(tokenEnd)-ord4(tokenStart)
+ord4(tPtr^.tokenEnd)-ord4(tPtr^.tokenStart)+1;
if len <= maxint then begin
destPtr := GMalloc(ord(len));
srcPtr := tokenStart;
tokenStart := destPtr;
while srcPtr <> tokenEnd do begin
destPtr^ := srcPtr^;
destPtr := ptr(ord4(destPtr)+1);
srcPtr := ptr(ord4(srcPtr)+1);
end; {while}
srcPtr := tPtr^.tokenStart;
while srcPtr <> tPtr^.tokenEnd do begin
destPtr^ := srcPtr^;
destPtr := ptr(ord4(destPtr)+1);
srcPtr := ptr(ord4(srcPtr)+1);
end; {while}
destPtr^ := tPtr^.tokenEnd^;
tokenEnd := destPtr;
end {if}
else
Error(90);
end; {ConcatenateTokenString}
procedure Number;
{ Scan a preprocessing number token. It is converted to an }
{ integer or floating constant if it matches the syntax for }
{ one of those, or left as a preprocessing number if not. }
var
numLen: 1..maxint;
lastCh: char;
begin {Number}
numLen := 0;
lastCh := chr(0);
while (charKinds[ord(ch)] in [digit,letter,ch_dot])
or ((lastCh in ['e','E','p','P'])
and (charKinds[ord(ch)] in [ch_plus,ch_dash])) do
begin
if numLen < 255 then begin
numLen := numLen + 1;
workString[numLen] := ch;
end {if}
else
numLen := 256;
lastCh := ch;
NextCh;
end; {while}
if numLen = 256 then begin
if not skipping then
Error(131);
numLen := 1;
workString[1] := '0';
end; {if}
workString[0] := chr(numLen);
DoNumber;
end; {Number}
begin {NextToken}
if ifList = nil then {do pending EndInclude calls}
while includeCount <> 0 do begin
EndInclude(includeChPtr);
includeCount := includeCount - 1;
end; {while}
includeChPtr := chPtr;
3:
token.numstring := nil; {wipe out old numstrings}
if tokenList <> nil then begin {get a token put back by a macro}
tPtr := tokenList;
tokenList := tPtr^.next;
expandEnabled := tPtr^.expandEnabled;
tokenExpandEnabled := expandEnabled;
suppressPrint := tPtr^.suppressPrint;
token := tPtr^.token;
tokenStart := tPtr^.tokenStart;
tokenEnd := tPtr^.tokenEnd;
dispose(tPtr);
if token.kind = typedef then {allow for typedefs in a macro}
token.kind := ident;
4:
while (token.kind = stringconst)
and (tokenList <> nil)
and (tokenList^.token.kind = stringconst) do begin
MergeStrings(token, tokenList^.token);
tPtr := tokenList;
tokenList := tPtr^.next;
dispose(tPtr);
end; {while}
if expandMacros and expandEnabled and (not skipping) then
if token.kind = ident then begin {handle macro expansions}
inhibit := false;
if tokenList <> nil then
if tokenList^.token.kind = poundpoundop then
inhibit := true;
if not inhibit then begin
mPtr := FindMacro(token.name);
if mPtr <> nil then begin
Expand(mPtr);
goto 3;
end; {if}
end; {if}
end; {if}
if tokenList <> nil then
if tokenList^.token.kind = poundpoundop then begin
tPtr := tokenList;
tokenList := tPtr^.next;
dispose(tPtr);
if tokenList <> nil then begin
tPtr := tokenList;
tToken := token;
Merge(tToken, tPtr^.token);
ConcatenateTokenString(tPtr);
tokenList := tPtr^.next;
token := tToken;
tokenExpandEnabled := true;
dispose(tPtr);
goto 4;
end; {if}
end; {if}
if token.kind = ident then begin
CopyString(@workString, token.name);
lExpandMacros := expandMacros;
expandMacros := false;
CheckIdentifier;
expandMacros := lExpandMacros;
end; {if}
goto 2;
end {if}
else
suppressPrint := false;
5: {skip white space}
while charKinds[ord(ch)] in [illegal,ch_white,ch_eol,ch_pound] do begin
if charKinds[ord(ch)] = ch_pound then begin
if lastWasReturn or (token.kind = eolsy) then begin
NextCh; {skip the '#' char}
PreProcess {call the preprocessor}
end {if}
else
goto 7;
end {if}
else if (charKinds[ord(ch)] = ch_eol) and reportEOL then begin
token.class := reservedSymbol; {record an eol token}
token.kind := eolsy;
tokenLine := lineNumber;
tokenColumn := ord(ord4(chPtr)-ord4(firstPtr));
tokenStart := pointer(ord4(chPtr)-1);
tokenEnd := chPtr;
NextCh;
goto 2;
end {if}
else if charKinds[ord(ch)] = illegal then begin
tokenLine := lineNumber; {record an illegal token}
tokenColumn := ord(ord4(chPtr)-ord4(firstPtr));
tokenStart := pointer(ord4(chPtr)-1);
tokenEnd := chPtr;
token.kind := questionch; {make sure it is not eolsy}
if (not skipping) or (not skipIllegalTokens) then
Error(1);
NextCh;
end {else if}
else begin {skip white space}
if printMacroExpansions and not suppressMacroExpansions then
if charKinds[ord(ch)] = ch_eol then begin
StopSpin;
writeln;
end {if}
else
write(ch);
NextCh;
end;
end; {while}
7:
tokenLine := lineNumber; {record the position of the token}
tokenColumn := ord(ord4(currentChPtr)-ord4(firstPtr)+1);
tokenStart := currentChPtr;
6:
token.class := reservedSymbol; {default to the most common class}
case charKinds[ord(ch)] of
ch_special : begin
token.kind := charSym[ord(ch)];
token.isDigraph := false;
NextCh;
end;
ch_eof: {end of file}
token.kind := eofsy;
ch_pound : begin {tokens that start with '#'}
NextCh;
token.isDigraph := false;
if ch = '#' then begin
token.kind := poundpoundop;
NextCh;
end
else
token.kind := poundch;
end;
ch_dash : begin {tokens that start with '-'}
NextCh;
if ch = '>' then begin
token.kind := minusgtop;
NextCh;
end
else if ch = '-' then begin
token.kind := minusminusop;
NextCh;
end
else if ch = '=' then begin
token.kind := minuseqop;
NextCh;
end
else
token.kind := minusch;
end;
ch_plus : begin {tokens that start with '+'}
NextCh;
if ch = '+' then begin
token.kind := plusplusop;
NextCh;
end
else if ch = '=' then begin
token.kind := pluseqop;
NextCh;
end
else
token.kind := plusch;
end;
ch_lt : begin {tokens that start with '<'}
NextCh;
if ch = '<' then begin
NextCh;
if ch = '=' then begin
token.kind := ltlteqop;
NextCh;
end
else
token.kind := ltltop;
end
else if ch = '=' then begin
token.kind := lteqop;
NextCh;
end
else if ch = ':' then begin
token.kind := lbrackch; { <: digraph }
token.isDigraph := true;
NextCh;
end
else if ch = '%' then begin
token.kind := lbracech; { <% digraph }
token.isDigraph := true;
NextCh;
end
else
token.kind := ltch;
end;
ch_gt : begin {tokens that start with '>'}
NextCh;
if ch = '>' then begin
NextCh;
if ch = '=' then begin
token.kind := gtgteqop;
NextCh;
end
else
token.kind := gtgtop;
end
else if ch = '=' then begin
token.kind := gteqop;
NextCh;
end
else
token.kind := gtch;
end;
ch_eq : begin {tokens that start with '='}
NextCh;
if ch = '=' then begin
token.kind := eqeqop;
NextCh;
end
else
token.kind := eqch;
end;
ch_exc : begin {tokens that start with '!'}
NextCh;
if ch = '=' then begin
token.kind := exceqop;
NextCh;
end
else
token.kind := excch;
end;
ch_and : begin {tokens that start with '&'}
NextCh;
if ch = '&' then begin
token.kind := andandop;
NextCh;
end
else if ch = '=' then begin
token.kind := andeqop;
NextCh;
end
else
token.kind := andch;
end;
ch_bar : begin {tokens that start with '|'}
NextCh;
if ch = '|' then begin
token.kind := barbarop;
NextCh;
end
else if ch = '=' then begin
token.kind := bareqop;
NextCh;
end
else
token.kind := barch;
end;
ch_percent: begin {tokens that start with '%'}
lLastWasReturn := lastWasReturn or (token.kind = eolsy);
NextCh;
if ch = '=' then begin
token.kind := percenteqop;
NextCh;
end
else if ch = '>' then begin
token.kind := rbracech; {%> digraph}
token.isDigraph := true;
NextCh;
end
else if ch = ':' then begin
NextCh;
token.isDigraph := true;
if (ch = '%') and (PeekCh = ':') then begin
token.kind := poundpoundop; {%:%: digraph}
NextCh;
NextCh;
end
else begin
token.kind := poundch; {%: digraph}
if lLastWasReturn then begin
PreProcess;
goto 5;
end;
end;
end
else
token.kind := percentch;
end;
ch_carot : begin {tokens that start with '^'}
NextCh;
if ch = '=' then begin
token.kind := caroteqop;
NextCh;
end
else
token.kind := carotch;
end;
ch_asterisk: begin {tokens that start with '*'}
NextCh;
if ch = '=' then begin
token.kind := asteriskeqop;
NextCh;
end
else
token.kind := asteriskch;
end;
ch_slash : begin {tokens that start with '/'}
NextCh;
if ch = '=' then begin
token.kind := slasheqop;
NextCh;
end
else
token.kind := slashch;
end;
ch_dot : begin {tokens that start with '.'}
if charKinds[ord(PeekCh)] = digit then
Number
else begin
NextCh;
if (ch = '.') and (PeekCh = '.') then begin
token.kind := dotdotdotsy;
NextCh;
NextCh;
end {if}
else
token.kind := dotch;
end; {else}
end;
ch_colon : begin {tokens that start with ':'}
NextCh;
if ch = '>' then begin
token.kind := rbrackch; {:> digraph}
token.isDigraph := true;
NextCh;
end
else if (ch = ':') and (cStd >= c23) then begin
token.kind := coloncolonsy;
NextCh;
end
else
token.kind := colonch;
end;
ch_char : CharConstant; {character constants}
ch_string: begin {string constants}
doingStringOrCharacter := true; {change character scanning}
token.kind := stringconst; {set up the token}
token.class := stringConstant;
ispstring := false; {set up for the string scan}
setLength := false;
NextCh; {skip the opening "}
{read the characters}
if charStrPrefix = prefix_none then begin
i := 0;
new(sPtr,s100);
while not (charKinds[ord(ch)] in [ch_string,ch_eol,ch_eof]) do begin
i := i+1;
if i = 101 then begin
sPtr^.len1 := 100;
new(tsPtr,s1000);
CopyLongString(pointer(tsPtr), pointer(sPtr));
dispose(sPtr);
sPtr := tsPtr;
end {if}
else if i = 1001 then begin
sPtr^.len2 := 1000;
new(tsPtr,sMAX);
CopyLongString(pointer(tsPtr), pointer(sPtr));
dispose(sPtr);
sPtr := tsPtr;
end {else if}
else if i = longstringlen then begin
i := 1001;
Error(90);
end; {else if}
sPtr^.str1[i] := chr(ord(EscapeCh));
if (i = 1) and ispstring then
setLength := true;
end; {while}
end {if}
else begin
if charStrPrefix = prefix_L then begin
charStrPrefix := prefix_u16;
if not skipping then
Error(167);
end; {if}
i := 1;
new(sPtr,sMAX);
while not (charKinds[ord(ch)] in [ch_string,ch_eol,ch_eof]) do begin
if i > longstringlen-8 then begin {leave space for char and null}
i := 1;
Error(90);
end; {if}
codePoint := EscapeCh;
if charStrPrefix = prefix_u8 then begin
if octHexEscape then begin
sPtr^.str1[i] := chr(ord(codePoint));
i := i+1;
end {if}
else begin
UTF8Encode(codePoint, utf8);
for j := 1 to utf8.length do begin
sPtr^.str1[i] := chr(utf8.bytes[j]);
i := i+1;
end; {for}
end; {else}
end {if}
else if charStrPrefix = prefix_u16 then begin
c16ptr := pointer(@sPtr^.str1[i]);
if octHexEscape then begin
c16ptr^ := ord(codePoint);
i := i+2;
end {if}
else begin
UTF16Encode(codePoint, utf16);
c16Ptr^ := utf16.codeUnits[1];
i := i+2;
if utf16.length = 2 then begin
c16ptr := pointer(@sPtr^.str1[i]);
c16Ptr^ := utf16.codeUnits[2];
i := i+2;
end; {if}
end {else}
end {else}
else if charStrPrefix = prefix_U32 then begin
c32ptr := pointer(@sPtr^.str1[i]);
c32ptr^ := codePoint;
i := i+4;
end {else}
end; {while}
i := i-1;
end; {else}
doingStringOrCharacter := false; {process the end of the string}
if ch = '"' then
NextCh
else
Error(3);
if setLength then {check for a p-string}
if charStrPrefix <> prefix_none then begin
if not skipping then
Error(165);
setLength := false;
end {if}
else
sPtr^.str1[1] := chr(i-1);
token.ispstring := setLength;
sPtr^.len1 := i; {set the string length}
token.sval := pointer(Malloc(i+6)); {put the string in the string pool}
CopyLongString(token.sval, pointer(sPtr));
dispose(sPtr);
token.sval^.str[i+1] := chr(0); {add null terminator}
if charStrPrefix = prefix_u16 then begin
token.sval^.str[i+2] := chr(0);
token.sval^.length := i+2;
end {if}
else if charStrPrefix = prefix_U32 then begin
token.sval^.str[i+2] := chr(0);
token.sval^.str[i+3] := chr(0);
token.sval^.str[i+4] := chr(0);
token.sval^.length := i+4;
end {else if}
else
token.sval^.length := i+1;
token.prefix := charStrPrefix; {record prefix}
charStrPrefix := prefix_none; {no prefix for next char/str (so far)}
end;
letter,ch_backslash: begin {reserved words and identifiers}
token.kind := ident;
token.class := identifier;
token.name := @workString;
tokenExpandEnabled := true;
i := 0;
while charKinds[ord(ch)] in [letter,digit,ch_backslash] do begin
i := i+1;
if ch = '\' then begin
if PeekCh in ['u','U'] then begin
NextCh;
codePoint := UniversalCharacterName;
if not ValidUCNForIdentifier(codePoint, i=1) then
Error(149);
chFromUCN := ConvertUCSToMacRoman(codePoint);
if chFromUCN >= 0 then
workString[i] := chr(chFromUCN)
else begin
for j := 1 to ord(ucnString[0]) do
workString[i+j-1] := ucnString[j];
i := i + ord(ucnString[0]) - 1;
end; {else}
end {if}
else begin
i := i-1;
goto 8;
end; {else}
end {if}
else begin
workString[i] := ch;
NextCh;
end; {if}
end; {while}
8: workString[0] := chr(i);
if i = 1 then begin {detect prefixed char/string literal}
if charKinds[ord(ch)] in [ch_char,ch_string] then begin
if workString[1] in ['L','u','U'] then begin
if workString[1] = 'L' then
charStrPrefix := prefix_L
else if workString[1] = 'u' then
charStrPrefix := prefix_u16
else if workString[1] = 'U' then
charStrPrefix := prefix_U32;
goto 6;
end; {if}
end; {if}
end {if}
else if i = 2 then
if charKinds[ord(ch)] = ch_string then
if workString = 'u8' then begin
charStrPrefix := prefix_u8;
goto 6;
end; {if}
if i = 0 then begin {\ preprocessing token}
token.kind := otherch;
token.class := otherCharacter;
token.ch := ch;
NextCh;
end {if}
else
CheckIdentifier;
end;
digit : {numeric constants}
Number;
ch_other: begin {other non-whitespace char (pp-token)}
token.kind := otherch;
token.class := otherCharacter;
token.ch := ch;
NextCh;
if skipping or preprocessing then
if not skipIllegalTokens then
Error(1);
end;
otherwise: Error(57);
end; {case}
tokenEnd := currentChPtr; {record the end of the token}
2:
if skipping then {conditional compilation branch}
if not (token.kind in [eofsy,eolsy]) then
goto 3;
if (token.kind = stringconst) and not mergingStrings {handle adjacent strings}
then repeat
if reportEOL then begin
while charKinds[ord(ch)] = ch_white do
NextCh;
if charKinds[ord(ch)] = ch_eol then
goto 1;
end; {if}
tToken := token;
lSuppressMacroExpansions := suppressMacroExpansions;
suppressMacroExpansions := true;
mergingStrings := true;
NextToken;
mergingStrings := false;
suppressMacroExpansions := lSuppressMacroExpansions;
if token.kind = stringconst then begin
MergeStrings(tToken, token);
done := false;
end {if}
else begin
PutBackToken(token, tokenExpandEnabled, false);
done := true;
end; {else}
token := tToken;
until done;
1:
if doingPPExpression then begin
if token.class = reservedWord then begin
token.name := @reservedWords[token.kind];
token.kind := ident;
token.class := identifier;
end; {if}
if token.kind = typedef then
token.kind := ident;
end; {if}
if printMacroExpansions then
if not suppressMacroExpansions then
if not suppressPrint then
PrintToken(token); {print the token stream}
if token.kind = otherch then begin
if not (skipping or preprocessing or suppressMacroExpansions)
or doingPPExpression then
Error(1);
end {if}
else if token.kind = ppNumber then
if not (skipping or preprocessing or suppressMacroExpansions)
or doingPPExpression then begin
Error(token.errCode);
token.kind := intconst;
token.class := intConstant;
token.ival := 0;
token.numString := @'0';
end; {if}
end; {NextToken}
procedure TermScanner;
{ Shut down the scanner. }
begin {TermScanner}
if ifList <> nil then
Error(21);
if numErr <> 0 then begin {write any pending errors}
firstPtr := chPtr;
WriteLine;
end; {if}
end; {TermScanner}
end.
{$append 'scanner.asm'}