SimpleExpression.pas 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. unit SimpleExpression;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2013 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Evaluator for simple boolean expressions
  8. Grammar:
  9. -expression = term ('or' term)*
  10. -term = factor ('and' factor)*
  11. -factor = '(' expression ')' | 'not' factor | identifier ( '(' parameters ')' )
  12. -identifier = letter | '_' (letter | number | '_' | '\')*
  13. -parameters = string | number | boolean (',' string | number | boolean )*
  14. As a special optional rule it can insert an 'or' if an identifier is encountered
  15. at the place where an 'or' could be.
  16. Function calls withing parameter lists are currently not supported, except for calls
  17. to the special ExpandConstant function.
  18. }
  19. interface
  20. type
  21. TSimpleExpression = class;
  22. TSimpleExpressionOnEvalIdentifier = function(Sender: TSimpleExpression;
  23. const Name: String; const Parameters: array of const): Boolean of object;
  24. TSimpleExpressionOnExpandConstant = function(Sender: TSimpleExpression;
  25. const Constant: String): String of object;
  26. TSimpleExpression = class
  27. private
  28. FExpression: String;
  29. FLazy: Boolean;
  30. FOnEvalIdentifier: TSimpleExpressionOnEvalIdentifier;
  31. FOnExpandConstant: TSimpleExpressionOnExpandConstant;
  32. FParametersAllowed: Boolean;
  33. FSingleIdentifierMode: Boolean;
  34. FSilentOrAllowed: Boolean;
  35. FTag: LongInt;
  36. FText: PChar;
  37. FTokenId: (tiEOF, tiOpenRound, tiCloseRound, tiComma, tiNot, tiAnd, tiOr, tiIdentifier, tiString, tiInteger, tiBoolean);
  38. FToken: String;
  39. function FReadParameters(var Parameters: array of const): Integer;
  40. function FEvalIdentifier(const InLazyBranch: Boolean): Boolean;
  41. function FEvalFactor(const InLazyBranch: Boolean): Boolean;
  42. function FEvalTerm(const InLazyBranch: Boolean): Boolean;
  43. function FEvalExpression(const InLazyBranch: Boolean): Boolean;
  44. procedure Next;
  45. public
  46. function Eval: Boolean;
  47. property Expression: String read FExpression write FExpression;
  48. property Lazy: Boolean read FLazy write FLazy;
  49. property OnEvalIdentifier: TSimpleExpressionOnEvalIdentifier read FOnEvalIdentifier write FOnEvalIdentifier;
  50. property OnExpandConstant: TSimpleExpressionOnExpandConstant read FOnExpandConstant write FOnExpandConstant;
  51. property ParametersAllowed: Boolean read FParametersAllowed write FParametersAllowed;
  52. property SilentOrAllowed: Boolean read FSilentOrAllowed write FSilentOrAllowed;
  53. property SingleIdentifierMode: Boolean read FSingleIdentifierMode write FSingleIdentifierMode;
  54. property Tag: LongInt read FTag write FTag;
  55. end;
  56. implementation
  57. uses
  58. SysUtils;
  59. {$IFNDEF UNICODE}
  60. type
  61. TSysCharSet = set of AnsiChar;
  62. function CharInSet(C: AnsiChar; const CharSet: TSysCharSet): Boolean;
  63. begin
  64. Result := C in CharSet;
  65. end;
  66. {$ENDIF}
  67. procedure AssignStringToVarRec(var VarRec: TVarRec; const S: String);
  68. begin
  69. {$IFDEF UNICODE}
  70. VarRec.VType := vtUnicodeString;
  71. UnicodeString(VarRec.VUnicodeString) := S;
  72. {$ELSE}
  73. VarRec.VType := vtAnsiString;
  74. AnsiString(VarRec.VAnsiString) := S;
  75. {$ENDIF}
  76. end;
  77. {---}
  78. procedure TSimpleExpression.Next;
  79. var
  80. P: PChar;
  81. begin
  82. { Ignore whitespace }
  83. while CharInSet(FText^ , [#1..#32]) do
  84. Inc(FText);
  85. case FText^ of
  86. #0:
  87. begin
  88. FToken := '';
  89. FTokenId := tiEOF;
  90. end;
  91. '(':
  92. begin
  93. FToken := FText^;
  94. FTokenId := tiOpenRound;
  95. Inc(FText);
  96. end;
  97. ')':
  98. begin
  99. FToken := FText^;
  100. FTokenId := tiCloseRound;
  101. Inc(FText);
  102. end;
  103. ',':
  104. begin
  105. FToken := FText^;
  106. FTokenId := tiComma;
  107. Inc(FText);
  108. end;
  109. 'A'..'Z', 'a'..'z', '_':
  110. begin
  111. P := FText;
  112. Inc(FText);
  113. while CharInSet(FText^ , ['0'..'9', 'A'..'Z', 'a'..'z', '_', '\']) do
  114. Inc(FText);
  115. SetString(FToken, P, FText - P);
  116. if CompareText(FToken, 'not') = 0 then
  117. FTokenId := tiNot
  118. else if CompareText(FToken, 'and') = 0 then
  119. FTokenId := tiAnd
  120. else if CompareText(FToken, 'or') = 0 then
  121. FTokenId := tiOr
  122. else if CompareText(FToken, 'true') = 0 then
  123. FTokenId := tiBoolean
  124. else if CompareText(FToken, 'false') = 0 then
  125. FTokenId := tiBoolean
  126. else
  127. FTokenId := tiIdentifier;
  128. end;
  129. '0'..'9':
  130. begin
  131. P := FText;
  132. Inc(FText);
  133. while CharInSet(FText^ , ['0'..'9']) do
  134. Inc(FText);
  135. SetString(FToken, P, FText - P);
  136. FTokenId := tiInteger;
  137. end;
  138. '''':
  139. begin
  140. FToken := '';
  141. while True do begin
  142. Inc(FText);
  143. case FText^ of
  144. #0: raise Exception.Create('Unexpected end of expression while reading string constant');
  145. #10, #13: raise Exception.Create('Unterminated string');
  146. else
  147. if FText^ = '''' then begin
  148. Inc(FText);
  149. if FText^ <> '''' then
  150. Break;
  151. end;
  152. FToken := FToken + FText^;
  153. end;
  154. end;
  155. FTokenId := tiString;
  156. end;
  157. else
  158. raise Exception.CreateFmt('Invalid symbol ''%s'' found', [FText^]);
  159. end;
  160. end;
  161. function TSimpleExpression.FReadParameters(var Parameters: array of const): Integer;
  162. var
  163. I: Integer;
  164. begin
  165. I := 0;
  166. while FTokenId in [tiIdentifier, tiString, tiInteger, tiBoolean] do begin
  167. if I <= High(Parameters) then begin
  168. if FTokenId = tiIdentifier then begin
  169. { Currently only calls to 'ExpandConstant' are supported in parameter lists }
  170. if CompareText(FToken, 'ExpandConstant') <> 0 then
  171. raise Exception.Create('Can only call function "ExpandConstant" within parameter lists');
  172. Next;
  173. if FTokenId <> tiOpenRound then
  174. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  175. Next;
  176. if FTokenId <> tiString then
  177. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  178. if Assigned(FOnExpandConstant) then
  179. AssignStringToVarRec(Parameters[I], FOnExpandConstant(Self, FToken))
  180. else
  181. AssignStringToVarRec(Parameters[I], FToken);
  182. Next;
  183. if FTokenId <> tiCloseRound then
  184. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  185. end else if FTokenId = tiString then begin
  186. AssignStringToVarRec(Parameters[I], FToken);
  187. end else if FTokenId = tiInteger then begin
  188. Parameters[I].VType := vtInteger;
  189. Parameters[I].vInteger := StrToInt(FToken);
  190. end else begin
  191. Parameters[I].VType := vtBoolean;
  192. Parameters[I].vBoolean := CompareText(FToken, 'true') = 0;
  193. end;
  194. Inc(I);
  195. end else
  196. raise Exception.Create('Maximum number of parameters exceeded');
  197. Next;
  198. if FTokenId <> tiComma then
  199. Break
  200. else
  201. Next;
  202. end;
  203. Result := I;
  204. end;
  205. function TSimpleExpression.FEvalIdentifier(const InLazyBranch: Boolean): Boolean;
  206. var
  207. Name: String;
  208. Parameters: array[0..9] of TVarRec;
  209. ParameterCount: Integer;
  210. I: Integer;
  211. begin
  212. Name := FToken;
  213. Next;
  214. FillChar(Parameters, SizeOf(Parameters), 0);
  215. try
  216. if FParametersAllowed and (FTokenId = tiOpenRound) then begin
  217. Next;
  218. ParameterCount := FReadParameters(Parameters);
  219. if FTokenId <> tiCloseRound then
  220. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  221. Next;
  222. end else
  223. ParameterCount := 0;
  224. if not Lazy or not InLazyBranch then begin
  225. if Assigned(FOnEvalIdentifier) then
  226. Result := FOnEvalIdentifier(Self, Name, Slice(Parameters, ParameterCount))
  227. else
  228. Result := True;
  229. end else
  230. Result := True; { Lazy and in lazy branch, just return something }
  231. finally
  232. for I := High(Parameters) downto Low(Parameters) do
  233. if Parameters[I].VType = {$IFDEF UNICODE} vtUnicodeString {$ELSE} vtAnsiString {$ENDIF} then
  234. AssignStringToVarRec(Parameters[I], '');
  235. end
  236. end;
  237. function TSimpleExpression.FEvalFactor(const InLazyBranch: Boolean): Boolean;
  238. begin
  239. case FTokenId of
  240. tiOpenRound:
  241. begin
  242. Next;
  243. Result := FEvalExpression(InLazyBranch);
  244. if FTokenId <> tiCloseRound then
  245. raise Exception.Create('Invalid token');
  246. Next;
  247. end;
  248. tiNot:
  249. begin
  250. Next;
  251. Result := not FEvalFactor(InLazyBranch);
  252. end;
  253. tiIdentifier:
  254. begin
  255. Result := FEvalIdentifier(InLazyBranch);
  256. end;
  257. else
  258. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  259. end;
  260. end;
  261. function TSimpleExpression.FEvalTerm(const InLazyBranch: Boolean): Boolean;
  262. begin
  263. Result := FEvalFactor(InLazyBranch);
  264. while FTokenId = tiAnd do begin
  265. Next;
  266. if not Result then begin
  267. { End term result known, but continue parsing }
  268. FEvalFactor(True)
  269. end else
  270. Result := FEvalFactor(InLazyBranch);
  271. end;
  272. end;
  273. function TSimpleExpression.FEvalExpression(const InLazyBranch: Boolean): Boolean;
  274. begin
  275. Result := FEvalTerm(InLazyBranch);
  276. while (FTokenId = tiOr) or
  277. (FSilentOrAllowed and (FTokenId = tiIdentifier)) do begin
  278. if FTokenId = tiOr then
  279. Next;
  280. if Result then begin
  281. { End expression result known, but continue parsing }
  282. FEvalTerm(True)
  283. end else
  284. Result := FEvalTerm(InLazyBranch);
  285. end;
  286. end;
  287. {---}
  288. function TSimpleExpression.Eval: Boolean;
  289. begin
  290. FText := PChar(FExpression);
  291. Next;
  292. if not FSingleIdentifierMode then
  293. Result := FEvalExpression(False)
  294. else begin
  295. if FTokenId <> tiIdentifier then
  296. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  297. Result := FEvalIdentifier(False);
  298. end;
  299. if FTokenID <> tiEOF then
  300. raise Exception.CreateFmt('Invalid token ''%s'' found', [FToken]);
  301. end;
  302. end.