ISPP.Preprocessor.pas 54 KB

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