ISPP.Preprocessor.pas 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878
  1. {
  2. Inno Setup Preprocessor
  3. Copyright (C) 2001-2002 Alex Yackimoff
  4. Inno Setup
  5. Copyright (C) 1997-2024 Jordan Russell
  6. Portions by Martijn Laan
  7. For conditions of distribution and use, see LICENSE.TXT.
  8. }
  9. unit ISPP.Preprocessor;
  10. interface
  11. uses
  12. Windows, SysUtils, Classes, Shared.PreprocInt, IniFiles, Registry, ISPP.Intf,
  13. ISPP.Base, ISPP.Stack, ISPP.IdentMan, ISPP.Parser;
  14. type
  15. TPreprocessor = class;
  16. EPreprocError = class(Exception)
  17. FileName: string;
  18. LineNumber: Integer;
  19. ColumnNumber: Integer;
  20. constructor Create(Preproc: TPreprocessor; const Msg: string);
  21. end;
  22. TConditionalBlockInfo = packed record
  23. BlockState, Fired, HadElse, Reserved: Boolean;
  24. end;
  25. TConditionalVerboseMsg = (cvmIf, cvmElif, cvmElse, cvmEndif);
  26. TConditionalTranslationStack = class(TStack)
  27. private
  28. FPreproc: TPreprocessor;
  29. FCache: Boolean;
  30. FCacheValid: Boolean;
  31. procedure VerboseMsg(Msg: TConditionalVerboseMsg; Eval: Boolean);
  32. protected
  33. function Last: TConditionalBlockInfo;
  34. procedure UpdateLast(const Value: TConditionalBlockInfo);
  35. public
  36. constructor Create(Preproc: TPreprocessor);
  37. procedure IfInstruction(Eval: Boolean);
  38. procedure ElseIfInstruction(Eval: Boolean);
  39. procedure ElseInstruction;
  40. procedure EndIfInstruction;
  41. function Include: Boolean;
  42. procedure Resolved;
  43. end;
  44. TPreprocessorCommand = (pcError, pcIf, pcIfDef, pcIfNDef, pcIfExist,
  45. pcIfNExist, pcElseIf, pcElse, pcEndIf, pcDefine, pcUndef, pcInclude,
  46. pcErrorDir, pcPragma, pcLine, pcImport, pcPrint, pcPrintEnv, pcFile,
  47. pcExecute, pcGlue, pcEndGlue, pcDim, pcProcedure, pcEndProc, pcEndLoop,
  48. pcFor, pcReDim);
  49. TDropGarbageProc = procedure(Item: Pointer);
  50. TIsppMessageType = (imtStatus, imtWarning);
  51. TPreprocessor = class(TObject, IIdentManager)
  52. private
  53. FCompilerParams: TPreprocessScriptParams;
  54. FCompilerPath: string;
  55. FCounter: Integer;
  56. FCurrentFile: Word;
  57. FCurrentLine: Word;
  58. FDefaultScope: TDefineScope;
  59. FFileStack: TStringList; { strs: files being included }
  60. FIncludes: TStringList; { strs: files been included, for error msgs }
  61. FIncludePath: string;
  62. FInsertionPoint: Integer;
  63. FLinePointer: Integer;
  64. FMainCounter: Word;
  65. FOutput: TStringList; { strs: translation }
  66. FQueuedLine: string;
  67. FQueuedLineCount: Integer;
  68. FSourcePath: string;
  69. FStack: TConditionalTranslationStack;
  70. FIdentManager: TIdentManager;
  71. FInProcBody: Boolean;
  72. FInForBody: Boolean;
  73. FProcs: TStringList;
  74. FGarbageCollection: TList;
  75. procedure DropGarbage;
  76. function ProcessInlineDirectives(P: PChar): string;
  77. function ProcessPreprocCommand(Command: TPreprocessorCommand;
  78. var Params: string; ParamsOffset: Integer): Boolean;
  79. procedure PushFile(const FileName: string);
  80. procedure PopFile;
  81. function CheckFile(const FileName: string): Boolean;
  82. function EmitDestination: TStringList;
  83. procedure SendMsg(Msg: string; Typ: TIsppMessageType);
  84. function GetFileName(Code: Integer): string;
  85. function GetLineNumber(Code: Integer): Word;
  86. procedure RaiseErrorEx(const Message: string; Column: Integer);
  87. procedure ExecProc(Body: TStrings);
  88. protected
  89. function GetDefaultScope: TDefineScope;
  90. procedure SetDefaultScope(Scope: TDefineScope);
  91. procedure InternalAddLine(const LineRead: string; FileIndex, LineNo: Word;
  92. NonISS: Boolean);
  93. function InternalQueueLine(const LineRead: string; FileIndex, LineNo: Word;
  94. NonISS: Boolean): Integer;
  95. function ParseFormalParams(Parser: TParser; var ParamList: PParamList): Integer;
  96. { IUnknown }
  97. function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
  98. function _AddRef: Integer; stdcall;
  99. function _Release: Integer; stdcall;
  100. { IIdentManager }
  101. function LookupPredefined(Name: string; Value: PIsppVariant): Boolean;
  102. function Defined(const Name: String): Boolean;
  103. function GetIdent(const Name: String;
  104. out CallContext: ICallContext): TIdentType;
  105. function TypeOf(const Name: String): Byte;
  106. function DimOf(const Name: String): Integer;
  107. public
  108. FOptions: TISPPOptions;
  109. constructor Create(const CompilerParams: TPreprocessScriptParams;
  110. VarManager: TIdentManager; const Options: TIsppOptions;
  111. const SourcePath: string; const CompilerPath: string; const FileName: string = '');
  112. destructor Destroy; override;
  113. procedure CallIdleProc;
  114. procedure VerboseMsg(Level: Byte; const Msg: string); overload;
  115. procedure VerboseMsg(Level: Byte; const Msg: string; const Args: array of const); overload;
  116. procedure StatusMsg(const Msg: string); overload;
  117. procedure StatusMsg(const Msg: string; const Args: array of const); overload;
  118. procedure WarningMsg(const Msg: string); overload;
  119. procedure WarningMsg(const Msg: string; const Args: array of const); overload;
  120. function GetNextOutputLine(var LineFilename: string; var LineNumber: Integer;
  121. var LineText: string): Boolean;
  122. procedure GetNextOutputLineReset;
  123. procedure IncludeFile(FileName: string; Builtins, UseIncludePathOnly, ResetCurrentFile: Boolean);
  124. procedure QueueLine(const LineRead: string);
  125. function PrependDirName(const FileName, Dir: string): string;
  126. procedure RegisterFunction(const Name: string; Handler: TIsppFunction; Ext: Longint);
  127. procedure RaiseError(const Message: string);
  128. procedure SaveToFile(const FileName: string);
  129. procedure CollectGarbage(Item: Pointer; Proc: TDropGarbageProc);
  130. procedure UncollectGarbage(Item: Pointer);
  131. property IncludedFiles: TStringList read FIncludes;
  132. property IncludePath: string read FIncludePath write FIncludePath;
  133. property SourcePath: string read FSourcePath;
  134. property StringList: TStringList read FOutput;
  135. property Stack: TConditionalTranslationStack read FStack;
  136. property VarMan: TIdentManager read FIdentManager;
  137. end;
  138. implementation
  139. uses
  140. ISPP.Consts, ISPP.Funcs, ISPP.VarUtils, ISPP.Sessions, ISPP.CTokenizer, PathFunc,
  141. Shared.CommonFunc, Shared.FileClass, Shared.Struct;
  142. const
  143. PreprocCommands: array[TPreprocessorCommand] of String =
  144. ('', 'if', 'ifdef', 'ifndef', 'ifexist', 'ifnexist', 'elif', 'else',
  145. 'endif', 'define', 'undef', 'include', 'error', 'pragma', 'line', 'import',
  146. 'emit', 'env', 'file', 'expr', 'insert', 'append', 'dim', 'sub', 'endsub',
  147. 'endloop', 'for', 'redim');
  148. PpCmdSynonyms: array[TPreprocessorCommand] of Char =
  149. (#0, '?', #0, #0, #0, #0, #0, '^', '.', ':', #0, '+', #0, #0, #0, #0,
  150. '=', '%', #0, '!', #0, #0, #0, #0, #0, #0, #0, #0);
  151. function GetEnv(const EnvVar: String): String;
  152. function AdjustLength(var S: String; const Res: Cardinal): Boolean;
  153. begin
  154. Result := Integer(Res) < Length(S);
  155. SetLength (S, Res);
  156. end;
  157. var
  158. Res: DWORD;
  159. begin
  160. SetLength(Result, 255);
  161. repeat
  162. Res := GetEnvironmentVariable(PChar(EnvVar), PChar(Result), Length(Result));
  163. if Res = 0 then begin
  164. Result := '';
  165. Break;
  166. end;
  167. until AdjustLength(Result, Res);
  168. end;
  169. function ParsePreprocCommand(var P: PChar; ExtraTerminator: Char): TPreprocessorCommand;
  170. begin
  171. for Result := TPreprocessorCommand(1) to High(TPreprocessorCommand) do
  172. begin
  173. if (P^ = PpCmdSynonyms[Result]) then
  174. Inc(P)
  175. else if (StrLIComp(P, @PreprocCommands[Result][1], Length(PreprocCommands[Result])) = 0) and
  176. CharInSet(P[Length(PreprocCommands[Result])], [#0..#32, ExtraTerminator]) then
  177. Inc(P, Length(PreprocCommands[Result]))
  178. else
  179. Continue;
  180. Exit;
  181. end;
  182. if StrLIComp('echo', P, 4) = 0 then
  183. begin
  184. Result := pcPrint;
  185. Inc(P, 4)
  186. end
  187. else if StrLIComp('call', P, 4) = 0 then
  188. begin
  189. Result := pcExecute;
  190. Inc(P, 4);
  191. end
  192. else
  193. Result := pcError;
  194. end;
  195. { EPreprocError }
  196. constructor EPreprocError.Create(Preproc: TPreprocessor; const Msg: string);
  197. begin
  198. inherited Create(Msg + '.');
  199. FileName := Preproc.GetFileName(-1);
  200. LineNumber := Preproc.GetLineNumber(-1);
  201. end;
  202. { TPreprocessor }
  203. function CheckReservedIdent(const Ident: string): string;
  204. begin
  205. Result := UpperCase(Ident);
  206. if (Result = SLocal) or
  207. (Result = SGlobal) or
  208. (Result = SInt) or
  209. (Result = SStr) or
  210. (Result = SAny) then
  211. raise EParsingError.CreateFmt(SExpectedButFound, [SIdent, '''' + Result + '''']);
  212. Result := Ident;
  213. end;
  214. constructor TPreprocessor.Create(const CompilerParams: TPreprocessScriptParams;
  215. VarManager: TIdentManager; const Options: TIsppOptions;
  216. const SourcePath, CompilerPath, FileName: string);
  217. begin
  218. PushPreproc(Self);
  219. if VarManager = nil then
  220. FIdentManager := TIdentManager.Create(Self, Longint(Self))
  221. else
  222. FIdentManager := VarManager;
  223. FOptions := Options;
  224. FIdentManager._AddRef;
  225. FIdentManager.BeginLocal;
  226. FCompilerParams := CompilerParams;
  227. FCompilerPath := CompilerPath;
  228. FSourcePath := SourcePath;
  229. FFileStack := TStringList.Create;
  230. FIncludes := TStringList.Create;
  231. FIncludes.Add(FileName); //main file - no name
  232. FInsertionPoint := -1;
  233. FOutput := TStringList.Create;
  234. FProcs := TStringList.Create;
  235. FStack := TConditionalTranslationStack.Create(Self);
  236. if VarManager = nil then ISPP.Funcs.RegisterFunctions(Self);
  237. end;
  238. destructor TPreprocessor.Destroy;
  239. begin
  240. DropGarbage;
  241. if PopPreproc <> Self then
  242. RaiseError('Internal error: FSP');
  243. FStack.Free;
  244. FProcs.Free;
  245. FOutput.Free;
  246. FIncludes.Free;
  247. if FFileStack.Count <> 0 then
  248. RaiseError('Internal error: FNE');
  249. FFileStack.Free;
  250. FIdentManager.EndLocal;
  251. FIdentManager._Release;
  252. end;
  253. function TPreprocessor.GetFileName(Code: Integer): string;
  254. begin
  255. if Code = -1 then
  256. Result := FIncludes[FCurrentFile]
  257. else
  258. Result := FIncludes[Longint(FOutput.Objects[Code]) shr 16];
  259. end;
  260. function TPreprocessor.GetLineNumber(Code: Integer): Word;
  261. begin
  262. if Code = -1 then
  263. Result := FCurrentLine
  264. else
  265. Result := Word(FOutput.Objects[Code]) and $FFFF
  266. end;
  267. function TPreprocessor.GetNextOutputLine(var LineFilename: string; var LineNumber: Integer;
  268. var LineText: string): Boolean;
  269. begin
  270. Result := False;
  271. if FLinePointer < FOutput.Count then
  272. begin
  273. LineFilename := GetFileName(FLinePointer);
  274. LineNumber := GetLineNumber(FLinePointer);
  275. LineText := FOutput[FLinePointer];
  276. Inc(FLinePointer);
  277. Result := True;
  278. end;
  279. end;
  280. procedure TPreprocessor.GetNextOutputLineReset;
  281. begin
  282. FLinePointer := 0;
  283. end;
  284. procedure TPreprocessor.InternalAddLine(const LineRead: string; FileIndex, LineNo: Word;
  285. NonISS: Boolean);
  286. var
  287. IncludeLine: Boolean;
  288. P, P1: PChar;
  289. Command: TPreprocessorCommand;
  290. DirectiveOffset: Integer;
  291. State: Boolean;
  292. S, S1: string;
  293. begin
  294. try
  295. Inc(LineNo);
  296. FCurrentFile := FileIndex;
  297. FCurrentLine := LineNo;
  298. P := PChar(LineRead);
  299. IncludeLine := True;
  300. if P^ <> #0 then
  301. begin
  302. P1 := P;
  303. while CharInSet(P^, [#1..#32]) do Inc(P);
  304. if P^ = '#' then
  305. begin
  306. Inc(P);
  307. while CharInSet(P^, [#1..#32]) do Inc(P);
  308. IncludeLine := FInProcBody;
  309. Command := ParsePreprocCommand(P, #0);
  310. if FInProcBody then
  311. begin
  312. case Command of
  313. pcError: RaiseError(SUnknownPreprocessorDirective);
  314. pcProcedure: RaiseError('Nested procedure declaration not allowed');
  315. pcEndProc:
  316. begin
  317. S := P;
  318. ProcessPreprocCommand(Command, S, P - P1);
  319. IncludeLine := False;
  320. end
  321. else
  322. S := LineRead;
  323. end;
  324. end
  325. else
  326. begin
  327. State := FStack.Include;
  328. DirectiveOffset := P - P1;
  329. //S := Copy(LineRead, DirectiveOffset + 1, MaxInt);
  330. S := P;
  331. case Command of
  332. pcIf..pcIfNExist:
  333. FStack.IfInstruction(FStack.Include and
  334. ProcessPreprocCommand(Command, S, DirectiveOffset));
  335. pcElseIf:
  336. FStack.ElseIfInstruction(FStack.Last.Fired or
  337. (FStack.Include or not FStack.Last.BlockState) and
  338. ProcessPreprocCommand(Command, S, DirectiveOffset));
  339. pcElse: FStack.ElseInstruction;
  340. pcEndIf: FStack.EndIfInstruction
  341. else
  342. if State then
  343. case Command of
  344. pcPrint, pcPrintEnv:
  345. begin
  346. ProcessPreprocCommand(Command, S, DirectiveOffset);
  347. VerboseMsg(8, SLineEmitted, [S]);
  348. IncludeLine := True
  349. end;
  350. pcFile: RaiseError(SFileDirectiveCanBeOnlyInline);
  351. else
  352. ProcessPreprocCommand(Command, S, DirectiveOffset);
  353. end;
  354. end
  355. end;
  356. end
  357. else
  358. if not FInProcBody and not FStack.Include then
  359. IncludeLine := False
  360. else
  361. if ((P^ = '/') and (P[1] = '/')) or
  362. ((P^ = #0) and not (optEmitEmptyLines in FOptions.Options)) then //P^ is #0 if the line was all whitespace
  363. IncludeLine := False
  364. else
  365. if (P^ <> #0) and (P^ <> ';') and not FInProcBody then
  366. S := PChar(ProcessInlineDirectives(P1))
  367. else
  368. S := P1;
  369. end
  370. else
  371. begin
  372. S := '';
  373. IncludeLine := optEmitEmptyLines in FOptions.Options
  374. end;
  375. if IncludeLine then
  376. begin
  377. P := PChar(S);
  378. repeat
  379. P1 := P;
  380. while not CharInSet(P^, [#0, #10, #13]) do Inc(P);
  381. SetString(S1, P1, P - P1);
  382. if FInsertionPoint >= 0 then
  383. begin
  384. EmitDestination.InsertObject(FInsertionPoint, S1,
  385. TObject(FileIndex shl 16 or LineNo));
  386. Inc(FInsertionPoint);
  387. end
  388. else
  389. EmitDestination.AddObject(S1, TObject(FileIndex shl 16 or LineNo));
  390. while CharInSet(P^, [#10, #13]) do Inc(P);
  391. until P^ = #0;
  392. end;
  393. except
  394. on E: EParsingError do
  395. RaiseErrorEx(E.Message, E.Position);
  396. on E: EPreprocError do
  397. raise;
  398. on E: Exception do
  399. RaiseError(E.Message);
  400. end;
  401. end;
  402. function TPreprocessor.ProcessInlineDirectives(P: PChar): string;
  403. var
  404. S: string;
  405. Command: TPreprocessorCommand;
  406. LineStack: TConditionalTranslationStack;
  407. LineStart, P1, DStart, DEnd: PChar;
  408. function ScanForInlineStart(var P, D: PChar): Boolean;
  409. var
  410. I: Integer;
  411. begin
  412. Result := False;
  413. while P^ <> #0 do
  414. begin
  415. if P^ = FOptions.InlineStart[1] then
  416. begin
  417. D := P;
  418. Result := True;
  419. for I := 2 to Length(FOptions.InlineStart) do
  420. begin
  421. Inc(D);
  422. if D^ <> FOptions.InlineStart[I] then
  423. begin
  424. Result := False;
  425. Break;
  426. end;
  427. end;
  428. Inc(D);
  429. end;
  430. if Result then Break;
  431. Inc(P);
  432. end;
  433. end;
  434. function ScanForInlineEnd(var P: PChar): PChar;
  435. var
  436. I: Integer;
  437. begin
  438. Result := nil;
  439. while P^ <> #0 do
  440. begin
  441. if P^ = FOptions.InlineEnd[1] then
  442. begin
  443. Result := P;
  444. for I := 2 to Length(FOptions.InlineEnd) do
  445. begin
  446. Inc(P);
  447. if P^ <> FOptions.InlineEnd[I] then
  448. begin
  449. Result := nil;
  450. Break;
  451. end;
  452. end;
  453. Inc(P);
  454. end;
  455. if Result <> nil then Exit;
  456. Inc(P);
  457. end;
  458. RaiseError(SUnterminatedPreprocessorDirectiv);
  459. end;
  460. begin
  461. LineStack := TConditionalTranslationStack.Create(Self);
  462. try
  463. Result := '';
  464. LineStart := P;
  465. P1 := P;
  466. while ScanForInlineStart(P, DStart) do
  467. begin
  468. SetString(S, P1, P - P1);
  469. if LineStack.Include then Result := Result + S;
  470. Command := ParsePreprocCommand(DStart, Char(FOptions.InlineEnd[1]));
  471. if Command = pcError then
  472. Command := pcPrint;
  473. DEnd := DStart;
  474. SetString(S, DStart, ScanForInlineEnd(DEnd) - DStart);
  475. case Command of
  476. pcError: RaiseError(SUnknownPreprocessorDirective);
  477. pcIf..pcIfNExist:
  478. LineStack.IfInstruction(LineStack.Include and
  479. ProcessPreprocCommand(Command, S, DStart - LineStart));
  480. pcElseIf:
  481. LineStack.ElseIfInstruction(LineStack.Last.Fired or
  482. (LineStack.Include or not LineStack.Last.BlockState) and
  483. ProcessPreprocCommand(Command, S, DStart - LineStart));
  484. pcElse: LineStack.ElseInstruction;
  485. pcEndIf: LineStack.EndIfInstruction;
  486. else
  487. if LineStack.Include then
  488. case Command of
  489. pcInclude, pcGlue..pcEndLoop:
  490. RaiseError(Format(SDirectiveCannotBeInline,
  491. [PreprocCommands[Command]]));
  492. pcPrint, pcPrintEnv, pcFile:
  493. begin
  494. ProcessPreprocCommand(Command, S, DStart - LineStart);
  495. Result := Result + S;
  496. end;
  497. else
  498. ProcessPreprocCommand(Command, S, DStart - LineStart)
  499. end;
  500. end;
  501. P1 := DEnd;
  502. P := DEnd;
  503. //Inc(P);
  504. end;
  505. Result := Result + P1;
  506. LineStack.Resolved;
  507. finally
  508. LineStack.Free
  509. end;
  510. end;
  511. function TPreprocessor.GetDefaultScope: TDefineScope;
  512. begin
  513. if FFileStack.Count > 0 then
  514. Result := TDefineScope(FFileStack.Objects[FFileStack.Count - 1])
  515. else
  516. Result := FDefaultScope;
  517. end;
  518. procedure TPreprocessor.SetDefaultScope(Scope: TDefineScope);
  519. begin
  520. if Scope = dsAny then Scope := dsPublic;
  521. if FFileStack.Count > 0 then
  522. FFileStack.Objects[FFileStack.Count - 1] := TObject(Scope)
  523. else
  524. FDefaultScope := Scope;
  525. end;
  526. type
  527. TParserAccess = class(TParser);
  528. function TPreprocessor.ProcessPreprocCommand(Command: TPreprocessorCommand;
  529. var Params: string; ParamsOffset: Integer): Boolean;
  530. function ParseScope(Parser: TParser; ExpectedTokens: TTokenKinds = [tkIdent]): TDefineScope;
  531. const
  532. ScopeClauses: array[dsPublic..dsPrivate] of string =
  533. ('public', 'protected', 'private');
  534. begin
  535. Parser.NextTokenExpect([tkIdent]);
  536. for Result := Low(ScopeClauses) to High(ScopeClauses) do
  537. if CompareText(Parser.TokenString, ScopeClauses[Result]) = 0 then
  538. begin
  539. Parser.NextTokenExpect(ExpectedTokens);
  540. Exit;
  541. end;
  542. Result := dsAny;
  543. end;
  544. function GetScope(Parser: TParser): TDefineScope;
  545. begin
  546. Result := ParseScope(Parser);
  547. if Result = dsAny then Result := GetDefaultScope;
  548. end;
  549. procedure ParseDim(Parser: TParserAccess; ReDim: Boolean);
  550. var
  551. Name: string;
  552. N, NValues, I: Integer;
  553. Scope: TDefineScope;
  554. Values: array of TIsppVariant;
  555. begin
  556. with Parser do
  557. try
  558. Scope := GetScope(Parser);
  559. Name := CheckReservedIdent(TokenString);
  560. NextTokenExpect([tkOpenBracket]);
  561. N := IntExpr(True);
  562. NValues := 0;
  563. NextTokenExpect([tkCloseBracket]);
  564. if PeekAtNextToken = tkOpenBrace then
  565. begin
  566. NextToken;
  567. SetLength(Values, N);
  568. NValues := 0;
  569. while True do begin
  570. if NValues >= N then
  571. raise EIdentError.CreateFmt(SIndexIsOutOfArraySize, [NValues, Name]);
  572. Values[NValues] := Expr(True);
  573. MakeRValue(Values[NValues]);
  574. Inc(NValues);
  575. if PeekAtNextToken <> tkComma then
  576. Break;
  577. NextToken;
  578. end;
  579. NextTokenExpect([tkCloseBrace]);
  580. end;
  581. FIdentManager.DimVariable(Name, N, Scope, ReDim);
  582. if ReDim and (NValues <> 0) then
  583. Error('Initializers not allowed on #redim of existing array');
  584. for I := 0 to NValues-1 do
  585. FIdentManager.DefineVariable(Name, I, Values[I], Scope);
  586. finally
  587. //Free
  588. end;
  589. end;
  590. procedure ParseDefine(Parser: TParserAccess);
  591. var
  592. Name: string;
  593. Start, P: PChar;
  594. IsMacroDefine: Boolean;
  595. //Ident: string;
  596. //Param: TIsppMacroParam;
  597. ParamList: PParamList;
  598. AParamCount: Byte;
  599. AExpr: string;
  600. VarIndex: Integer;
  601. Scope: TDefineScope;
  602. MacroExprPos: TExprPosition;
  603. begin
  604. with Parser do
  605. begin
  606. Start := FExpr;
  607. Scope := ParseScope(Parser, [tkEOF, tkIdent, tkSemicolon]);
  608. if Scope = dsAny then
  609. Scope := GetDefaultScope
  610. else
  611. if Token <> tkIdent then
  612. begin
  613. SetDefaultScope(Scope);
  614. Exit;
  615. end;
  616. Name := CheckReservedIdent(TokenString);
  617. IsMacroDefine := FExpr^ = '(';
  618. if IsMacroDefine then
  619. begin
  620. NextToken;
  621. AParamCount := ParseFormalParams(Parser, ParamList);
  622. try
  623. Inc(FExpr);
  624. P := FExpr;
  625. MacroExprPos.FileIndex := FCurrentFile;
  626. MacroExprPos.Line := FCurrentLine;
  627. MacroExprPos.Column := (FExpr - Start) + ParamsOffset;
  628. while P^ <> #0 do Inc(P);
  629. SetString(AExpr, FExpr, P - FExpr);
  630. AExpr := Trim(AExpr);
  631. if AExpr = '' then RaiseError(SMacroExpressionExpected);
  632. FIdentManager.DefineMacro(Name, AExpr, MacroExprPos, FOptions.ParserOptions,
  633. Slice(ParamList^, AParamCount), Scope);
  634. finally
  635. Finalize(ParamList^[0], AParamCount);
  636. FreeMem(ParamList)
  637. end;
  638. end
  639. else
  640. begin
  641. VarIndex := -1;
  642. if PeekAtNextToken = tkOpenBracket then
  643. begin
  644. NextToken;
  645. VarIndex := IntExpr(True);
  646. NextTokenExpect([tkCloseBracket]);
  647. end;
  648. case PeekAtNextToken of
  649. opAssign: NextToken;
  650. tkEOF:
  651. begin
  652. FIdentManager.DefineVariable(Name, VarIndex, NULL, Scope);
  653. Exit;
  654. end
  655. end;
  656. FIdentManager.DefineVariable(Name, VarIndex, Evaluate, Scope);
  657. end;
  658. end;
  659. end;
  660. procedure ParseUndef(Parser: TParserAccess);
  661. var
  662. Scope: TDefineScope;
  663. begin
  664. with Parser do
  665. begin
  666. Scope := GetScope(Parser);
  667. FIdentManager.Delete(CheckReservedIdent(TokenString), Scope);
  668. EndOfExpr;
  669. end
  670. end;
  671. procedure IncludeFile(const Params: string);
  672. var
  673. FileName: string;
  674. function TryPascal: Boolean;
  675. begin
  676. Result := not (optPascalStrings in FOptions.ParserOptions.Options);
  677. if Result then
  678. begin
  679. Include(FOptions.ParserOptions.Options, optPascalStrings);
  680. try
  681. try
  682. FileName := ParseStr(Self, Params, ParamsOffset,
  683. @FOptions.ParserOptions);
  684. except
  685. Result := False
  686. end;
  687. finally
  688. Exclude(FOptions.ParserOptions.Options, optPascalStrings);
  689. end;
  690. end
  691. end;
  692. var
  693. IncludePathOnly: Boolean;
  694. begin
  695. FileName := Params;
  696. if Pos(';', FileName) > 0 then
  697. Delete(FileName, Pos(';', FileName), MaxInt);
  698. FileName := Trim(FileName);
  699. if (FileName <> '') and (FileName[1] = '<') and
  700. (FileName[Length(FileName)] = '>') then
  701. begin
  702. FileName := Copy(FileName, 2, Length(FileName) - 2);
  703. IncludePathOnly := True;
  704. end
  705. else
  706. begin
  707. try
  708. FileName := ParseStr(Self, Params, ParamsOffset, @FOptions.ParserOptions);
  709. except
  710. if not TryPascal then
  711. raise
  712. end;
  713. IncludePathOnly := False;
  714. end;
  715. Self.IncludeFile(FileName, False, IncludePathOnly, False);
  716. end;
  717. procedure Pragma(Parser: TParserAccess);
  718. var
  719. P: string;
  720. function StrPragma(AllowEmpty: Boolean): string;
  721. begin
  722. Result := Parser.StrExpr(True);
  723. if (Result = '') and not AllowEmpty then
  724. RaiseError(SNonEmptyStringExpected);
  725. Parser.EndOfExpr;
  726. end;
  727. procedure OptionPragma(var Options: TOptions);
  728. var
  729. C: Char;
  730. V: Boolean;
  731. begin
  732. with Parser do
  733. begin
  734. NextTokenExpect([opSubtract]);
  735. repeat
  736. NextTokenExpect([tkIdent]);
  737. if Length(TokenString) > 1 then
  738. RaiseError(SInvalidOptionName);
  739. C := TokenString[1];
  740. V := NextTokenExpect([opAdd, opSubtract]) = opAdd;
  741. SetOption(Options, C, V);
  742. until NextTokenExpect([tkEOF, opSubtract, tkSemicolon]) <> opSubtract;
  743. end;
  744. end;
  745. var
  746. CatchException: Boolean;
  747. ErrorMsg: string;
  748. begin
  749. CatchException := True;
  750. try
  751. with Parser do
  752. begin
  753. NextTokenExpect([tkIdent]);
  754. P := LowerCase(TokenString);
  755. if P = 'include' then
  756. FIncludePath := StrPragma(True)
  757. else if P = 'inlinestart' then
  758. FOptions.InlineStart := StrPragma(False)
  759. else if P = 'inlineend' then
  760. FOptions.InlineEnd := StrPragma(False)
  761. else if P = 'spansymbol' then
  762. FOptions.SpanSymbol := StrPragma(False)[1]
  763. else if P = 'parseroption' then
  764. OptionPragma(FOptions.ParserOptions.Options)
  765. else if P = 'option' then
  766. OptionPragma(FOptions.Options)
  767. else if P = 'verboselevel' then
  768. begin
  769. Include(FOptions.Options, optVerbose);
  770. FOptions.VerboseLevel := IntExpr(True);
  771. VerboseMsg(0, SChangedVerboseLevel, [FOptions.VerboseLevel]);
  772. EndOfExpr;
  773. end
  774. else if P = 'warning' then begin
  775. { Also see WarningFunc in IsppFuncs }
  776. WarningMsg(StrPragma(True))
  777. end else if P = 'message' then begin
  778. { Also see MessageFunc in IsppFuncs }
  779. StatusMsg(StrPragma(True))
  780. end else if P = 'error' then begin
  781. { Also see ErrorFunc in IsppFuncs }
  782. ErrorMsg := StrPragma(True);
  783. if ErrorMsg = '' then ErrorMsg := 'Error';
  784. CatchException := False;
  785. RaiseError(ErrorMsg)
  786. end
  787. else
  788. WarningMsg(SFailedToParsePragmaDirective);
  789. end;
  790. except
  791. if CatchException then
  792. WarningMsg(SFailedToParsePragmaDirective)
  793. else
  794. raise
  795. end;
  796. end;
  797. function DoFile(FileName: string): string;
  798. function GetTempFileName(const Original: string): string;
  799. var
  800. Path: string;
  801. begin
  802. SetLength(Path, MAX_PATH);
  803. SetLength(Path, GetTempPath(MAX_PATH, PChar(Path)));
  804. SetLength(Result, MAX_PATH);
  805. if Windows.GetTempFileName(PChar(Path), PChar(UpperCase(Original)), 0, PChar(Result)) <> 0 then
  806. SetLength(Result, StrLen(PChar(Result)))
  807. else
  808. RaiseLastOSError;
  809. end;
  810. var
  811. F: TTextFileReader;
  812. ALine: string;
  813. Preprocessor: TPreprocessor;
  814. NewOptions: TIsppOptions;
  815. begin
  816. FileName := PrependDirName(FileName, FSourcePath);
  817. if FileExists(FileName) then
  818. begin
  819. Result := GetTempFileName(ExtractFileName(FileName));
  820. StatusMsg(SProcessingExternalFile, [FileName]);
  821. NewOptions := FOptions;
  822. Preprocessor := TPreprocessor.Create(FCompilerParams, FIdentManager,
  823. NewOptions, FSourcePath, FCompilerPath, FileName);
  824. try
  825. F := TTextFileReader.Create(Filename, fdOpenExisting, faRead, fsRead);
  826. try
  827. while not F.Eof do begin
  828. ALine := F.ReadLine;
  829. Preprocessor.QueueLine(ALine);
  830. end;
  831. finally
  832. F.Free;
  833. end;
  834. Preprocessor.SaveToFile(Result);
  835. QueueFileForDeletion(Result);
  836. VerboseMsg(1, STemporaryFileCreated, [Result]);
  837. finally
  838. Preprocessor.Free;
  839. end;
  840. end
  841. else
  842. RaiseError(Format(SFileNotFound, [FileName]));
  843. end;
  844. procedure ParseFor(Parser: TParserAccess);
  845. var
  846. Condition, Action, Body: PChar;
  847. begin
  848. Parser.NextTokenExpect([tkOpenBrace]);
  849. Parser.Expr(False);
  850. Parser.NextTokenExpect([tkSemicolon]);
  851. { Skip condition and remember it }
  852. Condition := Parser.FExpr;
  853. Parser.Sequentional(False);
  854. Parser.NextTokenExpect([tkSemicolon]);
  855. Action := Parser.FExpr;
  856. Parser.Sequentional(False);
  857. Parser.NextTokenExpect([tkCloseBrace]);
  858. Body := Parser.FExpr;
  859. Parser.Sequentional(False);
  860. Parser.EndOfExpr;
  861. Parser.SetPos(Condition);
  862. while Parser.IntExpr(False) <> 0 do
  863. begin
  864. Parser.SetPos(Body);
  865. Parser.Sequentional(True);
  866. Parser.SetPos(Action);
  867. Parser.Sequentional(True);
  868. Parser.SetPos(Condition);
  869. end;
  870. end;
  871. procedure Glue(LineNo: Integer);
  872. begin
  873. if LineNo > FOutput.Count then
  874. RaiseError(Format(SInsertLineNoTooBig, [LineNo]));
  875. FInsertionPoint := LineNo;
  876. VerboseMsg(2, SChangingInsertionPointToLine, [FInsertionPoint]);
  877. end;
  878. procedure EndGlue;
  879. begin
  880. VerboseMsg(2, SResettingInsertionPoint);
  881. FInsertionPoint := -1;
  882. end;
  883. procedure BeginProcDecl(Parser: TParserAccess);
  884. var
  885. ProcName: string;
  886. begin
  887. if FInForBody or FInProcBody then
  888. RaiseError('Nested procedure declaration and compound loops not allowed');
  889. FInProcBody := True;
  890. Parser.NextTokenExpect([tkIdent]);
  891. ProcName := Parser.TokenString;
  892. Parser.EndOfExpr;
  893. FProcs.AddObject(ProcName, TStringList.Create);
  894. EmitDestination.Add('#define private');
  895. end;
  896. procedure EndProcDecl;
  897. begin
  898. if not FInProcBody then
  899. RaiseError('''endproc'' without ''procedure''');
  900. FInProcBody := False;
  901. end;
  902. var
  903. IfCondition: TIsppVariant;
  904. DummyContext: ICallContext;
  905. Parser: TParserAccess;
  906. begin
  907. Result := False;
  908. Parser := TParserAccess.Create(Self, Params, ParamsOffset, @FOptions.ParserOptions);
  909. with Parser do
  910. try
  911. case Command of
  912. pcError: RaiseError(SUnknownPreprocessorDirective);
  913. pcIf, pcElseIf:
  914. begin
  915. IfCondition := Evaluate;
  916. case IfCondition.Typ of
  917. evInt: Result := IfCondition.AsInt <> 0;
  918. evStr: Result := IfCondition.AsStr <> ''
  919. else
  920. WarningMsg(SSpecifiedConditionEvalatedToVoid);
  921. Result := False
  922. end;
  923. end;
  924. pcIfdef, pcIfndef:
  925. begin
  926. NextTokenExpect([tkIdent]);
  927. case GetIdent(TokenString, DummyContext) of
  928. itUnknown: Result := Command = pcIfNDef;
  929. itVariable, itMacro: Result := Command = pcIfDef;
  930. itFunc:
  931. begin
  932. Result := Command = pcIfDef;
  933. WarningMsg(SFuncIdentForIfdef);
  934. end;
  935. else
  936. begin
  937. Result := Command = pcIfNDef;
  938. WarningMsg(SSpecFuncIdentForIfdef);
  939. end;
  940. end;
  941. EndOfExpr;
  942. end;
  943. pcIfExist, pcIfNExist:
  944. Result := FileExists(PrependDirName(StrExpr(False), FSourcePath)) xor (Command = pcIfNExist);
  945. pcDefine: ParseDefine(Parser);
  946. pcDim: ParseDim(Parser, False);
  947. pcReDim: ParseDim(Parser, True);
  948. pcUndef: ParseUndef(Parser);
  949. pcInclude: IncludeFile(Params);
  950. pcErrorDir:
  951. begin
  952. { Also see ErrorFunc in IsppFuncs }
  953. if Params = '' then Params := 'Error';
  954. RaiseError(Params.Trim);
  955. end;
  956. pcPragma: Pragma(Parser);
  957. pcPrint: Params := ToStr(Evaluate).AsStr;
  958. pcPrintEnv:
  959. begin
  960. NextTokenExpect([tkIdent]);
  961. Params := GetEnv(TokenString);
  962. EndOfExpr;
  963. end;
  964. pcFile: Params := DoFile(StrExpr(False));
  965. pcExecute: Evaluate;
  966. pcGlue: Glue(IntExpr(False));
  967. pcEndGlue: EndGlue;
  968. pcFor: ParseFor(Parser);
  969. pcProcedure: BeginProcDecl(Parser);
  970. pcEndProc: EndProcDecl;
  971. else
  972. WarningMsg(SDirectiveNotYetSupported, [PreprocCommands[Command]])
  973. end;
  974. finally
  975. Free
  976. end;
  977. end;
  978. function TPreprocessor.InternalQueueLine(const LineRead: string;
  979. FileIndex, LineNo: Word; NonISS: Boolean): Integer; //how many just been added
  980. var
  981. L: Integer;
  982. begin
  983. L := Length(LineRead);
  984. if (L > 2) and (LineRead[L] = FOptions.SpanSymbol) and (LineRead[L - 1] <= #32) then
  985. begin
  986. FQueuedLine := FQueuedLine + TrimLeft(Copy(LineRead, 1, L - 1));
  987. Inc(FQueuedLineCount);
  988. Result := 0;
  989. end
  990. else
  991. if FQueuedLineCount > 0 then
  992. begin
  993. InternalAddLine(FQueuedLine + TrimLeft(LineRead), FileIndex, LineNo, NonISS);
  994. FQueuedLine := '';
  995. Result := FQueuedLineCount + 1;
  996. FQueuedLineCount := 0;
  997. end
  998. else
  999. begin
  1000. InternalAddLine(LineRead, FileIndex, LineNo, NonISS);
  1001. Result := 1;
  1002. end;
  1003. end;
  1004. procedure TPreprocessor.QueueLine(const LineRead: string);
  1005. begin
  1006. Inc(FMainCounter, InternalQueueLine(LineRead, 0, FMainCounter, False));
  1007. end;
  1008. procedure TPreprocessor.RegisterFunction(const Name: string; Handler: TIsppFunction; Ext: Longint);
  1009. begin
  1010. FIdentManager.DefineFunction(Name, Handler, Ext);
  1011. end;
  1012. procedure TPreprocessor.SaveToFile(const FileName: string);
  1013. begin
  1014. var OldWriteBOM := FOutput.WriteBOM;
  1015. try
  1016. FOutput.WriteBOM := False;
  1017. FOutput.SaveToFile(FileName, TEncoding.UTF8);
  1018. finally
  1019. FOutput.WriteBOM := OldWriteBOM;
  1020. end;
  1021. end;
  1022. function TPreprocessor.CheckFile(const FileName: string): Boolean;
  1023. begin
  1024. Result := FFileStack.IndexOf(ExpandFileName(FileName)) < 0;
  1025. end;
  1026. procedure TPreprocessor.PopFile;
  1027. begin
  1028. FFileStack.Delete(FFileStack.Count - 1);
  1029. end;
  1030. procedure TPreprocessor.PushFile(const FileName: string);
  1031. begin
  1032. FFileStack.AddObject(ExpandFileName(FileName), TObject(dsPublic));
  1033. end;
  1034. procedure TPreprocessor.CallIdleProc;
  1035. begin
  1036. FCompilerParams.IdleProc(FCompilerParams.CompilerData);
  1037. end;
  1038. procedure TPreprocessor.VerboseMsg(Level: Byte; const Msg: string);
  1039. begin
  1040. if (optVerbose in FOptions.Options) and (FOptions.VerboseLevel >= Level) then
  1041. StatusMsg(Msg);
  1042. end;
  1043. procedure TPreprocessor.VerboseMsg(Level: Byte; const Msg: string;
  1044. const Args: array of const);
  1045. begin
  1046. VerboseMsg(Level, Format(Msg, Args));
  1047. end;
  1048. procedure TPreprocessor.StatusMsg(const Msg: string);
  1049. begin
  1050. SendMsg(Msg, imtStatus);
  1051. end;
  1052. procedure TPreprocessor.StatusMsg(const Msg: string; const Args: array of const);
  1053. begin
  1054. StatusMsg(Format(Msg, Args));
  1055. end;
  1056. procedure TPreprocessor.WarningMsg(const Msg: string);
  1057. begin
  1058. SendMsg(Msg, imtWarning);
  1059. end;
  1060. procedure TPreprocessor.WarningMsg(const Msg: string; const Args: array of const);
  1061. begin
  1062. WarningMsg(Format(Msg, Args));
  1063. end;
  1064. procedure TPreprocessor.SendMsg(Msg: string; Typ: TIsppMessageType);
  1065. const
  1066. MsgPrefixes: array[TIsppMessageType] of string = ('', 'Warning: ');
  1067. var
  1068. LineNumber: Word;
  1069. FileName: String;
  1070. begin
  1071. Msg := MsgPrefixes[Typ] + Msg;
  1072. LineNumber := GetLineNumber(-1);
  1073. if LineNumber <> 0 then begin
  1074. FileName := GetFileName(-1);
  1075. if FileName <> '' then
  1076. Msg := Format('Line %d of %s: %s', [LineNumber, PathExtractName(FileName), Msg])
  1077. else
  1078. Msg := Format('Line %d: %s', [LineNumber, Msg]);
  1079. end;
  1080. FCompilerParams.StatusProc(FCompilerParams.CompilerData, PChar(Msg), Typ = imtWarning);
  1081. end;
  1082. function TPreprocessor.DimOf(const Name: String): Integer;
  1083. begin
  1084. Result := FIdentManager.DimOf(Name)
  1085. end;
  1086. function TPreprocessor.EmitDestination: TStringList;
  1087. begin
  1088. if FInProcBody then
  1089. Result := TStringList(FProcs.Objects[FProcs.Count - 1])
  1090. else
  1091. Result := FOutput;
  1092. end;
  1093. procedure TPreprocessor.ExecProc(Body: TStrings);
  1094. var
  1095. I: Integer;
  1096. begin
  1097. for I := 0 to Body.Count - 1 do
  1098. InternalAddLine(Body[I], Integer(Body.Objects[I]) shr 16,
  1099. Integer(Body.Objects[I]) and $FFFF - 1, False);
  1100. end;
  1101. { TConditionalTranslationStack }
  1102. constructor TConditionalTranslationStack.Create(Preproc: TPreprocessor);
  1103. begin
  1104. inherited Create;
  1105. FPreproc := Preproc;
  1106. FCache := True;
  1107. end;
  1108. procedure TConditionalTranslationStack.IfInstruction(Eval: Boolean);
  1109. var
  1110. A: TConditionalBlockInfo;
  1111. begin
  1112. A.BlockState := Eval;
  1113. A.Fired := Eval;
  1114. A.HadElse := False;
  1115. PushItem(Pointer(A));
  1116. FCacheValid := False;
  1117. VerboseMsg(cvmIf, Eval);
  1118. end;
  1119. procedure TConditionalTranslationStack.ElseIfInstruction(Eval: Boolean);
  1120. var
  1121. A: TConditionalBlockInfo;
  1122. begin
  1123. if AtLeast(1) then
  1124. begin
  1125. A := Last;
  1126. with A do
  1127. begin
  1128. if HadElse then FPreproc.RaiseError(SElifAfterElse);
  1129. BlockState := not Fired and Eval;
  1130. Fired := Fired or Eval;
  1131. FCacheValid := False;
  1132. end;
  1133. UpdateLast(A);
  1134. VerboseMsg(cvmElif, Eval);
  1135. end
  1136. else
  1137. FPreproc.RaiseError(SElseWithoutIf);
  1138. end;
  1139. procedure TConditionalTranslationStack.ElseInstruction;
  1140. var
  1141. A: TConditionalBlockInfo;
  1142. begin
  1143. if AtLeast(1) then
  1144. begin
  1145. A := Last;
  1146. with A do
  1147. begin
  1148. if HadElse then FPreproc.RaiseError(SDoubleElse);
  1149. BlockState := not Fired;
  1150. Fired := True;
  1151. HadElse := True;
  1152. FCacheValid := False;
  1153. end;
  1154. UpdateLast(A);
  1155. VerboseMsg(cvmElse, False);
  1156. end
  1157. else
  1158. FPreproc.RaiseError(SElseWithoutIf);
  1159. end;
  1160. procedure TConditionalTranslationStack.EndIfInstruction;
  1161. begin
  1162. if AtLeast(1) then
  1163. begin
  1164. PopItem;
  1165. FCacheValid := False;
  1166. VerboseMsg(cvmEndif, False);
  1167. end
  1168. else
  1169. FPreproc.RaiseError(SEndifWithoutIf);
  1170. end;
  1171. function TConditionalTranslationStack.Include: Boolean;
  1172. var
  1173. I: Integer;
  1174. begin
  1175. if FCacheValid then
  1176. Result := FCache
  1177. else
  1178. begin
  1179. FCacheValid := True;
  1180. if Count > 0 then
  1181. begin
  1182. Result := False;
  1183. FCache := False;
  1184. for I := Count - 1 downto 0 do
  1185. if not TConditionalBlockInfo(List[I]).BlockState then Exit;
  1186. end;
  1187. Result := True;
  1188. FCache := True;
  1189. end;
  1190. end;
  1191. procedure TConditionalTranslationStack.Resolved;
  1192. begin
  1193. if Count > 0 then FPreproc.RaiseError(SEndifExpected);
  1194. end;
  1195. function TConditionalTranslationStack.Last: TConditionalBlockInfo;
  1196. begin
  1197. Result := TConditionalBlockInfo(Longint(List.Last))
  1198. end;
  1199. procedure TConditionalTranslationStack.UpdateLast(
  1200. const Value: TConditionalBlockInfo);
  1201. begin
  1202. List.Items[List.Count - 1] := Pointer(Value)
  1203. end;
  1204. procedure TConditionalTranslationStack.VerboseMsg(
  1205. Msg: TConditionalVerboseMsg; Eval: Boolean);
  1206. const
  1207. B: array[Boolean] of string = ('false', 'true');
  1208. var
  1209. M: string;
  1210. begin
  1211. case Msg of
  1212. cvmIf: M := SStartingConditionalInclusionIf;
  1213. cvmElif: M := SUpdatingConditionalInclusionElif;
  1214. cvmElse: M := SUpdatingConditionalInclusionElse;
  1215. else
  1216. begin
  1217. FPreproc.VerboseMsg(6, SFinishedConditionalInclusion);
  1218. Exit;
  1219. end;
  1220. end;
  1221. FPreproc.VerboseMsg(6, M);
  1222. end;
  1223. { TPreprocessor }
  1224. function TPreprocessor._AddRef: Integer;
  1225. begin
  1226. Result := -1
  1227. end;
  1228. function TPreprocessor._Release: Integer;
  1229. begin
  1230. Result := -1;
  1231. end;
  1232. function TPreprocessor.QueryInterface(const IID: TGUID; out Obj): HRESULT;
  1233. begin
  1234. if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE
  1235. end;
  1236. procedure TPreprocessor.RaiseError(const Message: string);
  1237. begin
  1238. RaiseErrorEx(Message, 0);
  1239. end;
  1240. procedure TPreprocessor.RaiseErrorEx(const Message: string; Column: Integer);
  1241. var
  1242. E: EPreprocError;
  1243. begin
  1244. E := EPreprocError.Create(Self, Message);
  1245. E.ColumnNumber := Column;
  1246. raise E;
  1247. end;
  1248. { TPredefinedVarCallContext }
  1249. type
  1250. TPredefinedVarCallContext = class(TInterfacedObject, ICallContext)
  1251. private
  1252. FValue: TIsppVariant;
  1253. public
  1254. constructor Create(const Value: TIsppVariant);
  1255. procedure Add(const Name: String; const Value: TIsppVariant);
  1256. function Call: TIsppVariant; dynamic;
  1257. function GroupingStyle: TArgGroupingStyle;
  1258. procedure Clone(out NewCallContext: ICallContext);
  1259. end;
  1260. TCounterCallContext = class(TPredefinedVarCallContext)
  1261. private
  1262. FCounter: PInteger;
  1263. public
  1264. constructor Create(Counter: PInteger);
  1265. function Call: TIsppVariant; override;
  1266. end;
  1267. TProcCallContext = class(TInterfacedObject, ICallContext)
  1268. private
  1269. FPreproc: TPreprocessor;
  1270. FBody: TStrings;
  1271. FScopeUpdated: Boolean;
  1272. FIndex: Integer;
  1273. procedure UpdateScope;
  1274. public
  1275. constructor Create(Proprocessor: TPreprocessor; ProcBody: TStrings);
  1276. procedure Add(const Name: String; const Value: TIsppVariant);
  1277. function Call: TIsppVariant;
  1278. procedure Clone(out NewContext: ICallContext);
  1279. function GroupingStyle: TArgGroupingStyle;
  1280. end;
  1281. constructor TCounterCallContext.Create(Counter: PInteger);
  1282. begin
  1283. FCounter := Counter;
  1284. end;
  1285. function TCounterCallContext.Call: TIsppVariant;
  1286. begin
  1287. MakeInt(Result, FCounter^);
  1288. Inc(FCounter^);
  1289. end;
  1290. constructor TPredefinedVarCallContext.Create(const Value: TIsppVariant);
  1291. begin
  1292. FValue := Value;
  1293. end;
  1294. procedure TPredefinedVarCallContext.Add(const Name: String;
  1295. const Value: TIsppVariant);
  1296. begin
  1297. raise EIdentError.Create(SParameterlessVariable);
  1298. end;
  1299. function TPredefinedVarCallContext.Call: TIsppVariant;
  1300. begin
  1301. Result := FValue;
  1302. end;
  1303. function TPredefinedVarCallContext.GroupingStyle: TArgGroupingStyle;
  1304. begin
  1305. Result := agsNone;
  1306. end;
  1307. { IIdentManager }
  1308. function LookupAlwaysDefined(const Name: string): Boolean;
  1309. const
  1310. AlwaysDefined: array[0..3] of string =
  1311. ('ISPP_INVOKED', 'WINDOWS', '__WIN32__', 'UNICODE');
  1312. var
  1313. I: Integer;
  1314. begin
  1315. Result := True;
  1316. for I := Low(AlwaysDefined) to High(AlwaysDefined) do
  1317. if CompareText(AlwaysDefined[I], Name) = 0 then Exit;
  1318. Result := False;
  1319. end;
  1320. const
  1321. SCounter = '__COUNTER__';
  1322. function TPreprocessor.Defined(const Name: String): Boolean;
  1323. begin
  1324. Result := LookupAlwaysDefined(Name) or LookupPredefined(Name, nil) or
  1325. (CompareText(Name, SCounter) = 0) or FIdentManager.Defined(Name);
  1326. end;
  1327. function TPreprocessor.GetIdent(const Name: String;
  1328. out CallContext: ICallContext): TIdentType;
  1329. var
  1330. V: TIsppVariant;
  1331. I: Integer;
  1332. begin
  1333. Result := itVariable;
  1334. I := FProcs.IndexOf(Name);
  1335. if I >= 0 then
  1336. begin
  1337. Result := itFunc;
  1338. CallContext := TProcCallContext.Create(Self, TStrings(FProcs.Objects[I]));
  1339. end
  1340. else
  1341. if LookupAlwaysDefined(Name) then
  1342. CallContext := TPredefinedVarCallContext.Create(NULL)
  1343. else
  1344. if LookupPredefined(Name, @V) then
  1345. CallContext := TPredefinedVarCallContext.Create(V)
  1346. else
  1347. if CompareText(Name, SCounter) = 0 then
  1348. CallContext := TCounterCallContext.Create(@FCounter)
  1349. else
  1350. Result := FIdentManager.GetIdent(Name, CallContext)
  1351. end;
  1352. function TPreprocessor.TypeOf(const Name: String): Byte;
  1353. var
  1354. V: TIsppVariant;
  1355. begin
  1356. if LookupAlwaysDefined(Name) then
  1357. Result := TYPE_NULL
  1358. else
  1359. if LookupPredefined(Name, @V) then
  1360. case V.Typ of
  1361. evInt: Result := TYPE_INTEGER;
  1362. evStr: Result := TYPE_STRING
  1363. else
  1364. Result := TYPE_NULL
  1365. end
  1366. else
  1367. if CompareText(Name, SCounter) = 0 then
  1368. Result := TYPE_INTEGER
  1369. else
  1370. Result := FIdentManager.TypeOf(Name)
  1371. end;
  1372. function TPreprocessor.LookupPredefined(Name: string;
  1373. Value: PIsppVariant): Boolean;
  1374. begin
  1375. Result := True;
  1376. Name := UpperCase(Name);
  1377. if (Name = '__FILENAME__') or (Name = '__FILE__') then
  1378. begin
  1379. if Value <> nil then MakeStr(Value^, ExtractFileName(FIncludes[FCurrentFile]))
  1380. end
  1381. else if Name = '__PATHFILENAME__' then
  1382. begin
  1383. if Value <> nil then MakeStr(Value^, FIncludes[FCurrentFile])
  1384. end
  1385. else if Name = '__DIR__' then
  1386. begin
  1387. if Value <> nil then MakeStr(Value^, ExtractFileDir(FIncludes[FCurrentFile]))
  1388. end
  1389. else if Name = '__LINE__' then
  1390. begin
  1391. if Value <> nil then MakeInt(Value^, FCurrentLine)
  1392. end
  1393. else if Name = 'PREPROCVER' then
  1394. begin
  1395. if Value <> nil then MakeInt(Value^, SetupBinVersion)
  1396. end
  1397. else if Name = '__INCLUDE__' then
  1398. begin
  1399. if Value <> nil then MakeStr(Value^, FIncludePath);
  1400. end
  1401. else if (Length(Name) = 9) and (Copy(Name, 1, 6) = '__OPT_') and
  1402. (Copy(Name, 8, 2) = '__') then
  1403. begin
  1404. if Value <> nil then Value^ := NULL;
  1405. Result := GetOption(FOptions.Options, Name[7]);
  1406. end
  1407. else if (Length(Name) = 10) and (Copy(Name, 1, 7) = '__POPT_') and
  1408. (Copy(Name, 9, 2) = '__') then
  1409. begin
  1410. if Value <> nil then Value^ := NULL;
  1411. Result := GetOption(FOptions.ParserOptions.Options, Name[8]);
  1412. end
  1413. else
  1414. Result := False;
  1415. end;
  1416. procedure TPredefinedVarCallContext.Clone(
  1417. out NewCallContext: ICallContext);
  1418. begin
  1419. NewCallContext := Self
  1420. end;
  1421. procedure TPreprocessor.CollectGarbage(Item: Pointer;
  1422. Proc: TDropGarbageProc);
  1423. begin
  1424. if (Item = nil) or (@Proc = nil) then Exit;
  1425. if FGarbageCollection = nil then
  1426. FGarbageCollection := TList.Create;
  1427. FGarbageCollection.Add(Item);
  1428. FGarbageCollection.Add(@Proc);
  1429. end;
  1430. procedure TPreprocessor.UncollectGarbage(Item: Pointer);
  1431. var
  1432. I: Integer;
  1433. begin
  1434. if FGarbageCollection = nil then Exit;
  1435. for I := 0 to FGarbageCollection.Count div 2 - 1 do
  1436. if FGarbageCollection.Items[I * 2] = Item then
  1437. begin
  1438. FGarbageCollection.Items[I * 2] := nil;
  1439. FGarbageCollection.Items[I * 2 + 1] := nil;
  1440. end;
  1441. FGarbageCollection.Pack;
  1442. if FGarbageCollection.Count = 0 then FreeAndNil(FGarbageCollection);
  1443. end;
  1444. procedure TPreprocessor.DropGarbage;
  1445. var
  1446. I: Integer;
  1447. Proc: TDropGarbageProc;
  1448. Item: Pointer;
  1449. begin
  1450. if FGarbageCollection <> nil then
  1451. try
  1452. for I := 0 to FGarbageCollection.Count div 2 - 1 do
  1453. begin
  1454. Item := FGarbageCollection.Items[I * 2];
  1455. Proc := FGarbageCollection.Items[I * 2 + 1];
  1456. try
  1457. if @Proc <> nil then
  1458. try
  1459. Proc(Item);
  1460. except
  1461. end
  1462. else
  1463. if Item <> nil then
  1464. begin
  1465. try
  1466. TObject(Item).Free
  1467. except
  1468. try Dispose(Item) except end;
  1469. end;
  1470. end;
  1471. finally
  1472. FGarbageCollection.Items[I * 2] := nil;
  1473. FGarbageCollection.Items[I * 2 + 1] := nil;
  1474. end;
  1475. end;
  1476. finally
  1477. FreeAndNil(FGarbageCollection);
  1478. end;
  1479. end;
  1480. function TPreprocessor.PrependDirName(const FileName, Dir: string): string;
  1481. var
  1482. P: PChar;
  1483. begin
  1484. P := FCompilerParams.PrependDirNameProc(FCompilerParams.CompilerData,
  1485. PChar(FileName), PChar(Dir), PChar(GetFileName(-1)), GetLineNumber(-1), 0);
  1486. if P = nil then
  1487. RaiseError('PrependDirNameProc failed');
  1488. Result := P;
  1489. end;
  1490. procedure TPreprocessor.IncludeFile(FileName: string;
  1491. Builtins, UseIncludePathOnly, ResetCurrentFile: Boolean);
  1492. function IsDotRelativePath(const Filename: String): Boolean;
  1493. begin
  1494. { Check for '.\' and '..\' }
  1495. if (Length(Filename) >= 2) and (Filename[1] = '.') and PathCharIsSlash(Filename[2]) then
  1496. Result := True
  1497. else if (Length(Filename) >= 3) and (Filename[1] = '.') and (Filename[2] = '.') and
  1498. PathCharIsSlash(Filename[3]) then
  1499. Result := True
  1500. else
  1501. Result := False;
  1502. end;
  1503. procedure AddToPath(var Path: string; const Dir: string);
  1504. begin
  1505. if (Dir <> '') and (Pos(';' + Dir + ';', ';' + Path + ';') = 0) then
  1506. begin
  1507. if Path <> '' then Path := Path + ';';
  1508. Path := Path + Dir;
  1509. end;
  1510. end;
  1511. function RemoveSlash(const S: string): string;
  1512. begin
  1513. Result := S;
  1514. if (Length(Result) > 3) and (Result[Length(Result)] = '\') then
  1515. Delete(Result, Length(Result), 1);
  1516. end;
  1517. function DoSearch(const SearchDirs: String): String;
  1518. var
  1519. FilePart: PChar;
  1520. begin
  1521. SetLength(Result, MAX_PATH);
  1522. SetLength(Result, SearchPath(PChar(SearchDirs), PChar(FileName), nil, MAX_PATH,
  1523. PChar(Result), FilePart));
  1524. end;
  1525. var
  1526. CurPath, SearchDirs, FullFileName: String;
  1527. FileHandle: TPreprocFileHandle;
  1528. I, FileIndex: Integer;
  1529. J: Word;
  1530. LineText: PChar;
  1531. LineTextStr: string;
  1532. begin
  1533. if ResetCurrentFile then begin
  1534. FCurrentFile := 0;
  1535. FCurrentLine := 0;
  1536. end;
  1537. { Expand any prefix on the filename (e.g. 'compiler:') }
  1538. FileName := PrependDirName(FileName, '');
  1539. if IsDotRelativePath(FileName) then
  1540. begin
  1541. { Make filenames beginning with '.\' and '..\' relative to the directory
  1542. containing the current file }
  1543. CurPath := PathExtractPath(FIncludes[FCurrentFile]);
  1544. if CurPath = '' then
  1545. CurPath := FSourcePath;
  1546. FileName := PathCombine(CurPath, FileName);
  1547. end
  1548. else if not PathIsRooted(FileName) then
  1549. begin
  1550. if not UseIncludePathOnly then
  1551. begin
  1552. for I := FFileStack.Count - 1 downto 0 do
  1553. AddToPath(SearchDirs, ExtractFileDir(FFileStack[I]));
  1554. if FIncludes[0] <> '' then
  1555. AddToPath(SearchDirs, ExtractFileDir(FIncludes[0]));
  1556. AddToPath(SearchDirs, RemoveSlash(FSourcePath));
  1557. end;
  1558. AddToPath(SearchDirs, FIncludePath);
  1559. AddToPath(SearchDirs, GetEnv('INCLUDE'));
  1560. if not UseIncludePathOnly then
  1561. AddToPath(SearchDirs, RemoveSlash(FCompilerPath));
  1562. end;
  1563. FullFileName := DoSearch(SearchDirs);
  1564. if FullFileName <> '' then
  1565. begin
  1566. if not CheckFile(FullFileName) then
  1567. RaiseError(Format(SFileIsAlreadyBeingIncluded, [FullFileName]));
  1568. if not Builtins then
  1569. StatusMsg(SIncludingFile, [FullFileName]);
  1570. PushFile(FullFileName);
  1571. try
  1572. FileHandle := FCompilerParams.LoadFileProc(FCompilerParams.CompilerData,
  1573. PChar(FullFileName), PChar(GetFileName(-1)), GetLineNumber(-1), 0);
  1574. if FileHandle < 0 then
  1575. RaiseError('LoadFileProc failed');
  1576. FileIndex := FIncludes.Add(FullFileName);
  1577. FIdentManager.BeginLocal;
  1578. try
  1579. I := 0;
  1580. J := 0;
  1581. while True do
  1582. begin
  1583. LineText := FCompilerParams.LineInProc(FCompilerParams.CompilerData,
  1584. FileHandle, I);
  1585. if LineText = nil then
  1586. Break;
  1587. LineTextStr := LineText;
  1588. Inc(J, InternalQueueLine(LineTextStr, FileIndex, J, False));
  1589. Inc(I);
  1590. end;
  1591. finally
  1592. FIdentManager.EndLocal
  1593. end;
  1594. finally
  1595. PopFile;
  1596. end;
  1597. end
  1598. else
  1599. RaiseError(Format(SFileNotFound, [FileName]));
  1600. end;
  1601. // ParseFormalParams
  1602. // Parser must be behind the opening parenthesis
  1603. function TPreprocessor.ParseFormalParams(Parser: TParser;
  1604. var ParamList: PParamList): Integer;
  1605. var
  1606. Param: TIsppMacroParam;
  1607. Ident: string;
  1608. procedure Grow;
  1609. var
  1610. OldCapacity, NewCapacity: Integer;
  1611. begin
  1612. OldCapacity := ((Result div 4) * 4) * SizeOf(TIsppMacroParam);
  1613. NewCapacity := ((Result div 4 + 1) * 4);
  1614. if NewCapacity > High(Byte) then RaiseError(STooManyFormalParams);
  1615. NewCapacity := NewCapacity * SizeOf(TIsppMacroParam);
  1616. ReallocMem(ParamList, NewCapacity);
  1617. { Initilizing to zeroes is required to prevent compiler's attempts to
  1618. finilize not existing strings }
  1619. FillChar(ParamList^[Result], NewCapacity - OldCapacity, 0)
  1620. end;
  1621. begin
  1622. with Parser do
  1623. begin
  1624. Result := 0;
  1625. ParamList := AllocMem(SizeOf(TIsppMacroParam) * 4);
  1626. while not (PeekAtNextToken in [tkEOF, tkCloseParen]) do
  1627. begin
  1628. Param.Name := '';
  1629. Param.DefValue.AsStr := '';
  1630. FillChar(Param, SizeOf(Param), 0);
  1631. Param.ParamFlags := [];
  1632. if NextTokenExpect([tkIdent, opMul]) = tkIdent then
  1633. begin
  1634. Ident := TokenString;
  1635. if not (PeekAtNextToken in [tkEOF, tkComma, tkCloseParen, opAssign]) then
  1636. begin
  1637. Ident := UpperCase(Ident);
  1638. if Ident = sAny then {do nothing }
  1639. else if Ident = sInt then Param.DefValue.Typ := evInt
  1640. else if Ident = sStr then Param.DefValue.Typ := evStr
  1641. else if Ident = 'FUNC' then
  1642. begin
  1643. Param.DefValue.Typ := evCallContext;
  1644. Include(Param.ParamFlags, pfFunc)
  1645. end
  1646. else if Ident = 'ARRAY' then Param.DefValue.Typ := evCallContext
  1647. else RaiseError(Format(SInvalidTypeId, [Ident]));
  1648. if Param.DefValue.Typ <> evSpecial then
  1649. Include(Param.ParamFlags, pfTypeDefined);
  1650. if NextTokenExpect([tkIdent, opMul]) = opMul then
  1651. begin
  1652. Include(Param.ParamFlags, pfByRef);
  1653. NextTokenExpect([tkIdent]);
  1654. end;
  1655. end;
  1656. end
  1657. else
  1658. begin
  1659. Include(Param.ParamFlags, pfByRef);
  1660. NextTokenExpect([tkIdent]);
  1661. end;
  1662. Ident := TokenString;
  1663. Param.Name := CheckReservedIdent(Ident);
  1664. if PeekAtNextToken = opAssign then
  1665. begin
  1666. if pfByRef in Param.ParamFlags then
  1667. RaiseError(SByRefNoDefault);
  1668. NextToken;
  1669. case Param.DefValue.Typ of
  1670. evSpecial: Param.DefValue := GetRValue(Expr(True));
  1671. evInt: Param.DefValue.AsInt := IntExpr(True);
  1672. evStr: Param.DefValue.AsStr := StrExpr(True);
  1673. end;
  1674. Include(Param.ParamFlags, pfHasDefault);
  1675. end;
  1676. ParamList^[Result] := Param;
  1677. Inc(Result);
  1678. if Result mod 4 = 0 then
  1679. Grow;
  1680. if NextTokenExpect([tkComma, tkCloseParen]) = tkCloseParen then Break;
  1681. end;
  1682. end;
  1683. end;
  1684. { TProcCallContext }
  1685. procedure TProcCallContext.Add(const Name: String;
  1686. const Value: TIsppVariant);
  1687. begin
  1688. UpdateScope;
  1689. if Name <> '' then
  1690. FPreproc.FIdentManager.DefineVariable(Name, -1, Value, dsPrivate);
  1691. FPreproc.FIdentManager.DefineVariable(SLocal, FIndex, Value, dsPrivate);
  1692. Inc(FIndex);
  1693. end;
  1694. function TProcCallContext.Call: TIsppVariant;
  1695. begin
  1696. UpdateScope;
  1697. try
  1698. FPreproc.ExecProc(FBody);
  1699. finally
  1700. FPreproc.FIdentManager.EndLocal
  1701. end;
  1702. end;
  1703. procedure TProcCallContext.Clone(out NewContext: ICallContext);
  1704. begin
  1705. NewContext := TProcCallContext.Create(FPreproc, FBody);
  1706. end;
  1707. constructor TProcCallContext.Create(Proprocessor: TPreprocessor;
  1708. ProcBody: TStrings);
  1709. begin
  1710. FPreproc := Proprocessor;
  1711. FBody := ProcBody
  1712. end;
  1713. function TProcCallContext.GroupingStyle: TArgGroupingStyle;
  1714. begin
  1715. Result := agsParenteses;
  1716. end;
  1717. procedure TProcCallContext.UpdateScope;
  1718. var
  1719. ReDim: Boolean;
  1720. begin
  1721. if not FScopeUpdated then
  1722. begin
  1723. FPreproc.FIdentManager.BeginLocal;
  1724. ReDim := False;
  1725. FPreproc.FIdentManager.DimVariable(SLocal, 16, dsPrivate, ReDim);
  1726. FScopeUpdated := True;
  1727. end;
  1728. end;
  1729. end.