1 (* Copyright (C) DooM 2D:Forever Developers
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE a_modes.inc}
22 {$IFDEF USE_MEMPOOL}mempool
,{$ENDIF}
23 typinfo
, SysUtils
, Variants
,
27 // ////////////////////////////////////////////////////////////////////////// //
29 TExomaException
= class(Exception
)
31 constructor Create (const amsg
: AnsiString);
32 constructor CreateFmt (const afmt
: AnsiString; const args
: array of const);
35 TExomaParseException
= class(TExomaException
)
37 tokLine
, tokCol
: Integer;
40 constructor Create (pr
: TTextParser
; const amsg
: AnsiString);
41 constructor CreateFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const);
44 // ////////////////////////////////////////////////////////////////////////// //
46 TPropHash
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
54 constructor Create (aklass
: TClass
; const apfx
: AnsiString='');
55 destructor Destroy (); override;
57 function get (obj
: TObject
; const fldname
: AnsiString; out v
: Variant): Boolean;
58 function put (obj
: TObject
; const fldname
: AnsiString; var v
: Variant): Boolean;
62 // ////////////////////////////////////////////////////////////////////////// //
64 TExprConstList
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
66 function valid (const cname
: AnsiString): Boolean; virtual; abstract;
67 function get (const cname
: AnsiString; out v
: Variant): Boolean; virtual; abstract;
71 // ////////////////////////////////////////////////////////////////////////// //
73 TExprScope
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
75 class procedure error (const amsg
: AnsiString);
76 class procedure errorfmt (const afmt
: AnsiString; const args
: array of const);
78 function getObj (const aname
: AnsiString): TObject
; virtual;
79 function getField (obj
: TObject
; const afldname
: AnsiString): Variant; virtual;
80 procedure setField (obj
: TObject
; const afldname
: AnsiString; var aval
: Variant); virtual;
83 TExprBase
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
85 class function coerce2bool (var v0
: Variant): Boolean;
86 class function toInt (var v
: Variant): LongInt;
88 class procedure error (const amsg
: AnsiString);
89 class procedure errorfmt (const afmt
: AnsiString; const args
: array of const);
91 class procedure parseError (pr
: TTextParser
; const amsg
: AnsiString);
92 class procedure parseErrorFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const);
94 class function parse (clist
: TExprConstList
; pr
: TTextParser
; allowAssign
: Boolean=false): TExprBase
;
95 class function parse (clist
: TExprConstList
; const str
: AnsiString; allowAssign
: Boolean=false): TExprBase
;
96 class function parseStatList (clist
: TExprConstList
; const str
: AnsiString): TExprBase
;
98 class function isFloat (var v
: Variant): Boolean; inline;
99 class function isInt (var v
: Variant): Boolean; inline;
100 class function isBool (var v
: Variant): Boolean; inline;
101 class function isStr (var v
: Variant): Boolean; inline;
104 function value (scope
: TExprScope
): Variant; virtual; abstract;
105 procedure assign (scope
: TExprScope
; var v
: Variant); virtual;
106 function clone (): TExprBase
; virtual; abstract;
109 TExprStatList
= class(TExprBase
)
111 mList
: array of TExprBase
;
113 constructor Create ();
114 destructor Destroy (); override;
115 procedure append (e
: TExprBase
);
116 function value (scope
: TExprScope
): Variant; override;
117 function toString (): AnsiString; override;
118 function clone (): TExprBase
; override;
121 TObjExpr
= class(TExprBase
)
125 constructor Create (const aval
: AnsiString);
127 function value (scope
: TExprScope
): Variant; override;
128 function toString (): AnsiString; override;
129 function clone (): TExprBase
; override;
132 TLitExpr
= class(TExprBase
)
136 constructor Create (aval
: Boolean);
137 constructor Create (aval
: LongInt);
138 constructor Create (const aval
: AnsiString);
139 constructor Create (var v
: Variant);
141 function value (scope
: TExprScope
): Variant; override;
142 function toString (): AnsiString; override;
143 function clone (): TExprBase
; override;
146 TUnExpr
= class(TExprBase
)
150 constructor Create (aop0
: TExprBase
);
151 destructor Destroy (); override;
152 function clone (): TExprBase
; override;
155 TUnExprNeg
= class(TUnExpr
)
157 function value (scope
: TExprScope
): Variant; override;
158 function toString (): AnsiString; override;
161 TUnExprNot
= class(TUnExpr
)
163 function value (scope
: TExprScope
): Variant; override;
164 function toString (): AnsiString; override;
167 TDotExpr
= class(TExprBase
)
172 constructor Create (aop0
: TExprBase
; const afield
: AnsiString);
173 function value (scope
: TExprScope
): Variant; override;
174 procedure assign (scope
: TExprScope
; var v
: Variant); override;
175 function toString (): AnsiString; override;
176 function clone (): TExprBase
; override;
179 TBinExpr
= class(TExprBase
)
181 mOp0
, mOp1
: TExprBase
;
183 class procedure coerce (var v0
, v1
: Variant); // modifies both variants
185 constructor Create (aop0
, aop1
: TExprBase
);
186 destructor Destroy (); override;
187 function clone (): TExprBase
; override;
190 TBinExprAdd
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
191 TBinExprSub
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
192 TBinExprMul
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
193 TBinExprDiv
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
194 TBinExprMod
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
196 TBinExprLogAnd
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
197 TBinExprLogOr
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
199 TBinExprCmpLess
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
200 TBinExprCmpGreat
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
201 TBinExprCmpLessEqu
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
202 TBinExprCmpGreatEqu
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
203 TBinExprCmpEqu
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
204 TBinExprCmpNotEqu
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
206 TBinAssign
= class(TBinExpr
) public function value (scope
: TExprScope
): Variant; override; function toString (): AnsiString; override; end;
208 TExprCond
= class(TExprBase
)
210 mCond
, mTrue
, mFalse
: TExprBase
;
212 constructor Create ();
213 destructor Destroy (); override;
214 function value (scope
: TExprScope
): Variant; override;
215 function toString (): AnsiString; override;
216 function clone (): TExprBase
; override;
220 // ////////////////////////////////////////////////////////////////////////// //
221 function typeKind2Str (t
: TTypeKind
): AnsiString;
230 // ////////////////////////////////////////////////////////////////////////// //
231 constructor TExomaException
.Create (const amsg
: AnsiString);
233 inherited Create(amsg
);
236 constructor TExomaException
.CreateFmt (const afmt
: AnsiString; const args
: array of const);
238 inherited Create(formatstrf(afmt
, args
));
242 // ////////////////////////////////////////////////////////////////////////// //
243 constructor TExomaParseException
.Create (pr
: TTextParser
; const amsg
: AnsiString);
245 if (pr
<> nil) then begin tokLine
:= pr
.tokLine
; tokCol
:= pr
.tokCol
; end else begin tokLine
:= 0; tokCol
:= 0; end;
246 inherited Create(amsg
);
249 constructor TExomaParseException
.CreateFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const);
251 if (pr
<> nil) then begin tokLine
:= pr
.tokLine
; tokCol
:= pr
.tokCol
; end else begin tokLine
:= 0; tokCol
:= 0; end;
252 inherited Create(formatstrf(afmt
, args
));
256 // ////////////////////////////////////////////////////////////////////////// //
257 function typeKind2Str (t
: TTypeKind
): AnsiString;
260 tkUnknown
: result
:= 'Unknown';
261 tkInteger
: result
:= 'Integer';
262 tkChar
: result
:= 'AnsiChar';
263 tkEnumeration
: result
:= 'Enumeration';
264 tkFloat
: result
:= 'Float';
265 tkSet
: result
:= 'Set';
266 tkMethod
: result
:= 'Method';
267 tkSString
: result
:= 'ShortString';
268 tkLString
: result
:= 'LString';
269 tkAString
: result
:= 'AnsiString';
270 tkWString
: result
:= 'WideString';
271 tkVariant
: result
:= 'Variant';
272 tkArray
: result
:= 'Array';
273 tkRecord
: result
:= 'Record';
274 tkInterface
: result
:= 'Interface';
275 tkClass
: result
:= 'Class';
276 tkObject
: result
:= 'Object';
277 tkWChar
: result
:= 'WideChar';
278 tkBool
: result
:= 'Boolean';
279 tkInt64
: result
:= 'Int64';
280 tkQWord
: result
:= 'UInt64';
281 tkDynArray
: result
:= 'DynArray';
282 tkInterfaceRaw
: result
:= 'InterfaceRaw';
283 tkProcVar
: result
:= 'ProcVar';
284 tkUString
: result
:= 'UString';
285 tkUChar
: result
:= 'UChar';
286 tkHelper
: result
:= 'Helper';
287 tkFile
: result
:= 'File';
288 tkClassRef
: result
:= 'ClassRef';
289 tkPointer
: result
:= 'Pointer';
290 else result
:= '<unknown>';
295 // ////////////////////////////////////////////////////////////////////////// //
297 procedure dumpPublishedProperties (obj: TObject);
304 if (obj = nil) then exit;
305 //e_LogWritefln('Object of type ''%s'':', [obj.ClassName]);
307 pt := GetTypeData(pi);
308 //e_LogWritefln('property count: %s', [pt.PropCount]);
309 GetMem(pp, pt^.PropCount*sizeof(Pointer));
311 j := GetPropList(pi, [tkInteger, tkBool, tkSString, tkLString, tkAString, tkSet, tkEnumeration], pp);
312 //e_LogWritefln('ordinal property count: %s', [j]);
316 if (typinfo.PropType(obj, pp^[i].name) in [tkSString, tkLString, tkAString]) then
318 e_LogWritefln(' #%s: <%s>; type: %s; value: <%s>', [i+1, pp^[i].name, typeKind2Str(typinfo.PropType(obj, pp^[i].name)), GetStrProp(obj, pp^[i])]);
320 else if (typinfo.PropType(obj, pp^[i].name) = tkSet) then
322 e_LogWritefln(' #%s: <%s>; type: %s; value: %s', [i+1, pp^[i].name, typeKind2Str(typinfo.PropType(obj, pp^[i].name)), GetSetProp(obj, pp^[i], true)]);
324 else if (typinfo.PropType(obj, pp^[i].name) = tkEnumeration) then
326 e_LogWritefln(' #%s: <%s>; type: %s; value: <%s>', [i+1, pp^[i].name, typeKind2Str(typinfo.PropType(obj, pp^[i].name)), GetEnumProp(obj, pp^[i])]);
330 e_LogWritefln(' #%s: <%s>; type: %s; value: %s', [i+1, pp^[i].name, typeKind2Str(typinfo.PropType(obj, pp^[i].name)), GetOrdProp(obj, pp^[i])]);
341 // ////////////////////////////////////////////////////////////////////////// //
342 constructor TPropHash
.Create (aklass
: TClass
; const apfx
: AnsiString='');
350 mNames
:= THashStrInt
.Create();
351 pi
:= aklass
.ClassInfo
;
352 pt
:= GetTypeData(pi
);
353 GetMem(pl
, pt
^.PropCount
*sizeof(Pointer));
354 pc
:= GetPropList(pi
, [tkInteger
, tkBool
, tkSString
, tkLString
, tkAString
, {tkSet,} tkEnumeration
], pl
);
355 for idx
:= 0 to pc
-1 do
357 if (Length(apfx
) > 0) then
359 if (Length(pl
^[idx
].name
) < Length(apfx
)) then continue
;
361 if (Copy(n
, 1, Length(apfx
)) <> apfx
) then continue
;
362 Delete(n
, 1, Length(apfx
));
367 mNames
.put(pl
^[idx
].name
, idx
);
372 destructor TPropHash
.Destroy ();
376 if (pl
<> nil) then FreeMem(pl
);
382 function TPropHash
.get (obj
: TObject
; const fldname
: AnsiString; out v
: Variant): Boolean;
387 if mNames
.get(fldname
, idx
) then
390 case pl
^[idx
].PropType
.Kind
of
391 tkSString
, tkLString
, tkAString
: v
:= GetStrProp(obj
, pl
^[idx
]);
392 tkEnumeration
: v
:= GetEnumProp(obj
, pl
^[idx
]);
393 tkBool
: if (GetOrdProp(obj
, pl
^[idx
]) = 0) then v
:= false else v
:= true;
394 tkInteger
, tkChar
: v
:= LongInt(GetOrdProp(obj
, pl
^[idx
]));
395 //tkFloat: result := 'Float';
396 //tkClass: result := 'Class';
397 //tkInt64: result := 'Int64';
398 //tkClassRef: result := 'ClassRef';
399 else result
:= false;
406 function TPropHash
.put (obj
: TObject
; const fldname
: AnsiString; var v
: Variant): Boolean;
411 if mNames
.get(fldname
, idx
) then
414 case pl
^[idx
].PropType
.Kind
of
415 tkSString
, tkLString
, tkAString
: SetStrProp(obj
, pl
^[idx
], VarToStr(v
));
416 tkEnumeration
: SetEnumProp(obj
, pl
^[idx
], VarToStr(v
));
417 tkBool
: if TExprBase
.coerce2bool(v
) then SetOrdProp(obj
, pl
^[idx
], 1) else SetOrdProp(obj
, pl
^[idx
], 0);
418 tkInteger
, tkChar
: SetOrdProp(obj
, pl
^[idx
], TExprBase
.toInt(v
));
419 //tkFloat: result := 'Float';
420 //tkClass: result := 'Class';
421 //tkInt64: result := 'Int64';
422 //tkClassRef: result := 'ClassRef';
423 else result
:= false;
430 // ////////////////////////////////////////////////////////////////////////// //
431 class procedure TExprScope
.error (const amsg
: AnsiString); begin raise TExomaException
.Create(amsg
); end;
432 class procedure TExprScope
.errorfmt (const afmt
: AnsiString; const args
: array of const); begin raise TExomaException
.CreateFmt(afmt
, args
); end;
434 function TExprScope
.getObj (const aname
: AnsiString): TObject
; begin result
:= nil; errorfmt('unknown object ''%s''', [aname
]); end;
435 function TExprScope
.getField (obj
: TObject
; const afldname
: AnsiString): Variant; begin result
:= Unassigned
; errorfmt('unknown field ''%s''', [afldname
]); end;
436 procedure TExprScope
.setField (obj
: TObject
; const afldname
: AnsiString; var aval
: Variant); begin errorfmt('unknown field ''%s''', [afldname
]); end;
439 // ////////////////////////////////////////////////////////////////////////// //
440 class procedure TExprBase
.error (const amsg
: AnsiString); begin raise TExomaException
.Create(amsg
); end;
441 class procedure TExprBase
.errorfmt (const afmt
: AnsiString; const args
: array of const); begin raise TExomaException
.CreateFmt(afmt
, args
); end;
443 class procedure TExprBase
.parseError (pr
: TTextParser
; const amsg
: AnsiString); begin raise TExomaParseException
.Create(pr
, amsg
); end;
444 class procedure TExprBase
.parseErrorFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const); begin raise TExomaParseException
.CreateFmt(pr
, afmt
, args
); end;
446 class function TExprBase
.coerce2bool (var v0
: Variant): Boolean;
449 varEmpty
: result
:= false;
450 varNull
: result
:= false;
451 varSingle
: result
:= (Single(v0
) <> 0.0);
452 varDouble
: result
:= (Double(v0
) <> 0.0);
453 varString
: result
:= (Length(AnsiString(v0
)) <> 0);
454 varBoolean
: result
:= Boolean(v0
);
455 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= (LongInt(v0
) <> 0);
456 varInt64
: result
:= (Int64(v0
) <> 0);
457 varQWord
: result
:= (UInt64(v0
) <> 0);
458 else begin result
:= false; error('can''t coerce type to boolean'); end;
462 class function TExprBase
.isFloat (var v
: Variant): Boolean; inline;
465 varSingle
, varDouble
: result
:= true;
466 else result
:= false;
470 class function TExprBase
.isInt (var v
: Variant): Boolean; inline;
473 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= true;
474 else result
:= false;
478 class function TExprBase
.isBool (var v
: Variant): Boolean; inline;
480 result
:= (varType(v
) = varBoolean
);
483 class function TExprBase
.isStr (var v
: Variant): Boolean; inline;
485 result
:= (varType(v
) = varString
);
488 class function TExprBase
.toInt (var v
: Variant): LongInt;
491 varSingle
: result
:= trunc(Single(v
));
492 varDouble
: result
:= trunc(Double(v
));
493 varBoolean
: if Boolean(v
) then result
:= 1 else result
:= 0;
494 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(v
);
495 varInt64
: result
:= LongInt(Int64(v
));
496 else begin result
:= 0; TExprBase
.error('can''t coerce type to integer'); end;
500 procedure TExprBase
.assign (scope
: TExprScope
; var v
: Variant); begin error('not an lvalue'); end;
503 // ////////////////////////////////////////////////////////////////////////// //
504 constructor TExprStatList
.Create (); begin mList
:= nil; end;
505 destructor TExprStatList
.Destroy (); var f
: Integer; begin for f
:= 0 to High(mList
) do mList
[f
].Free(); mList
:= nil; end;
507 procedure TExprStatList
.append (e
: TExprBase
);
511 SetLength(mList
, Length(mList
)+1);
512 mList
[High(mList
)] := e
;
516 function TExprStatList
.value (scope
: TExprScope
): Variant;
521 for f
:= 0 to High(mList
) do result
:= mList
[f
].value(scope
);
523 function TExprStatList
.toString (): AnsiString;
528 for f
:= 0 to High(mList
) do result
+= mList
[f
].toString()+';';
530 function TExprStatList
.clone (): TExprBase
;
535 r
:= TExprStatList
.Create();
536 SetLength(r
.mList
, Length(mList
));
537 for f
:= 0 to High(mList
) do r
.mList
[f
] := nil;
539 for f
:= 0 to High(mList
) do r
.mList
[f
] := mList
[f
].clone();
547 // ////////////////////////////////////////////////////////////////////////// //
548 constructor TExprCond
.Create (); begin mCond
:= nil; mTrue
:= nil; mFalse
:= nil; end;
549 destructor TExprCond
.Destroy (); begin mFalse
.Free(); mTrue
.Free(); mCond
.Free(); end;
551 function TExprCond
.value (scope
: TExprScope
): Variant;
553 result
:= mCond
.value(scope
);
554 if coerce2bool(result
) then result
:= mTrue
.value(scope
) else result
:= mFalse
.value(scope
);
557 function TExprCond
.toString (): AnsiString; begin result
:= '('+mCond
.toString()+'?'+mTrue
.toString()+':'+mFalse
.toString()+')'; end;
559 function TExprCond
.clone (): TExprBase
;
561 result
:= TExprCond
.Create();
562 TExprCond(result
).mCond
:= mCond
.clone();
563 TExprCond(result
).mTrue
:= mTrue
.clone();
564 TExprCond(result
).mFalse
:= mFalse
.clone();
568 // ////////////////////////////////////////////////////////////////////////// //
569 constructor TObjExpr
.Create (const aval
: AnsiString); begin mName
:= aval
; end;
570 function TObjExpr
.value (scope
: TExprScope
): Variant; begin result
:= UInt64(PtrUInt(Pointer(scope
.getObj(mName
)))); end;
571 function TObjExpr
.toString (): AnsiString; begin result
:= mName
; end;
572 function TObjExpr
.clone (): TExprBase
; begin result
:= TObjExpr
.Create(mName
); end;
575 // ////////////////////////////////////////////////////////////////////////// //
576 constructor TLitExpr
.Create (aval
: Boolean); begin mValue
:= aval
; end;
577 constructor TLitExpr
.Create (aval
: LongInt); begin mValue
:= aval
; end;
578 constructor TLitExpr
.Create (const aval
: AnsiString); begin mValue
:= aval
; end;
579 constructor TLitExpr
.Create (var v
: Variant); begin mValue
:= v
; end;
580 function TLitExpr
.value (scope
: TExprScope
): Variant; begin result
:= mValue
; end;
581 function TLitExpr
.toString (): AnsiString; begin result
:= VarToStr(mValue
); if isStr(mValue
) then result
:= quoteStr(result
); end;
582 function TLitExpr
.clone (): TExprBase
; begin result
:= TLitExpr
.Create(0); (result
as TLitExpr
).mValue
:= mValue
; end;
585 // ////////////////////////////////////////////////////////////////////////// //
586 constructor TUnExpr
.Create (aop0
: TExprBase
); begin mOp0
:= aop0
; end;
587 destructor TUnExpr
.Destroy (); begin mOp0
.Free(); inherited; end;
588 function TUnExpr
.clone (): TExprBase
; begin result
:= (self
.ClassType
.Create() as TUnExpr
); (result
as TUnExpr
).mOp0
:= mOp0
.clone(); end;
590 function TUnExprNeg
.value (scope
: TExprScope
): Variant;
592 result
:= mOp0
.value(scope
);
593 case varType(result
) of
594 varSingle
: result
:= -Single(result
);
595 varDouble
: result
:= -Double(result
);
596 varShortInt
, varSmallInt
, varInteger
, varByte
, varWord
: result
:= -LongInt(result
);
597 varInt64
: result
:= -Int64(result
);
598 varLongWord
: result
:= -LongInt(result
);
599 else error('can''t negate non-number');
603 function TUnExprNeg
.toString (): AnsiString; begin result
:= '-('+mOp0
.toString()+')'; end;
605 function TUnExprNot
.value (scope
: TExprScope
): Variant;
607 result
:= mOp0
.value(scope
);
608 result
:= not coerce2bool(result
);
611 function TUnExprNot
.toString (): AnsiString; begin result
:= '!('+mOp0
.toString()+')'; end;
614 // ////////////////////////////////////////////////////////////////////////// //
615 constructor TDotExpr
.Create (aop0
: TExprBase
; const afield
: AnsiString);
621 function TDotExpr
.value (scope
: TExprScope
): Variant;
623 result
:= mOp0
.value(scope
);
624 if (varType(result
) <> varQWord
) then errorfmt('can''t take field ''%s'' value of non-object', [mField
]);
625 result
:= scope
.getField(TObject(PtrUInt(UInt64(result
))), mField
);
628 procedure TDotExpr
.assign (scope
: TExprScope
; var v
: Variant);
632 o
:= mOp0
.value(scope
);
633 if (varType(o
) <> varQWord
) then errorfmt('can''t assign value to field ''%s'' of non-object', [mField
]);
634 scope
.setField(TObject(PtrUInt(UInt64(o
))), mField
, v
);
637 function TDotExpr
.clone (): TExprBase
; begin result
:= TDotExpr
.Create(mOp0
, mField
); end;
639 function TDotExpr
.toString (): AnsiString; begin result
:= mOp0
.toString()+'.'+mField
; end;
642 // ////////////////////////////////////////////////////////////////////////// //
643 constructor TBinExpr
.Create (aop0
, aop1
: TExprBase
); begin mOp0
:= aop0
; mOp1
:= aop1
; end;
644 destructor TBinExpr
.Destroy (); begin mOp1
.Free(); mOp0
.Free(); inherited; end;
645 function TBinExpr
.clone (): TExprBase
; begin result
:= (self
.ClassType
.Create() as TBinExpr
); (result
as TBinExpr
).mOp0
:= mOp0
.clone(); (result
as TBinExpr
).mOp1
:= mOp1
.clone(); end;
647 class procedure TBinExpr
.coerce (var v0
, v1
: Variant);
649 if (varType(v0
) <> varType(v1
)) then
651 if isStr(v0
) or isStr(v1
) then
653 if isFloat(v0
) then v0
:= formatstrf('%s', [Double(v0
)])
654 else if isInt(v0
) then v0
:= formatstrf('%s', [LongInt(v0
)])
655 else if isBool(v0
) then v0
:= formatstrf('%s', [Boolean(v0
)])
656 else if isStr(v0
) then begin end
657 else error('can''t coerce value to string');
658 if isFloat(v1
) then v1
:= formatstrf('%s', [Double(v1
)])
659 else if isInt(v1
) then v1
:= formatstrf('%s', [LongInt(v1
)])
660 else if isBool(v1
) then v1
:= formatstrf('%s', [Boolean(v1
)])
661 else if isStr(v0
) then begin end
662 else error('can''t coerce value to string');
664 else if isFloat(v0
) or isFloat(v1
) then
666 if isFloat(v0
) or isInt(v0
) then v0
:= Double(v0
)
667 else if isBool(v0
) then begin if Boolean(v0
) then v0
:= Double(1.0) else v0
:= Double(0.0); end
668 else error('can''t coerce value to float');
669 if isFloat(v1
) or isInt(v1
) then v1
:= Double(v1
)
670 else if isBool(v1
) then begin if Boolean(v1
) then v1
:= Double(1.0) else v1
:= Double(0.0); end
671 else error('can''t coerce value to float');
673 else if isInt(v0
) or isInt(v1
) then
675 if isBool(v0
) then begin if Boolean(v0
) then v0
:= LongInt(1) else v0
:= LongInt(0); end
676 else if isFloat(v0
) then v0
:= LongInt(trunc(Double(v0
)))
677 else if isInt(v0
) then begin end
678 else error('can''t coerce value to integer');
679 if isBool(v1
) then begin if Boolean(v1
) then v1
:= LongInt(1) else v1
:= LongInt(0); end
680 else if isFloat(v1
) then v1
:= LongInt(trunc(Double(v1
)))
681 else if isInt(v1
) then begin end
682 else error('can''t coerce value to integer');
686 error('can''t operate with value of invalid type');
692 // ////////////////////////////////////////////////////////////////////////// //
693 function TBinExprAdd
.value (scope
: TExprScope
): Variant;
697 result
:= mOp0
.value(scope
);
698 r1
:= mOp1
.value(scope
);
700 case varType(result
) of
701 varSingle
, varDouble
: result
:= Double(result
)+Double(r1
);
702 varString
: result
:= AnsiString(result
)+AnsiString(r1
);
703 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(result
)+LongInt(r1
);
704 varInt64
: result
:= Int64(result
)+Int64(r1
);
705 else error('can''t add non-numbers and non-strings');
708 function TBinExprAdd
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'+'+mOp1
.toString
+')'; end;
710 function TBinExprSub
.value (scope
: TExprScope
): Variant;
714 result
:= mOp0
.value(scope
);
715 r1
:= mOp1
.value(scope
);
717 case varType(result
) of
718 varSingle
, varDouble
: result
:= Double(result
)-Double(r1
);
719 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(result
)-LongInt(r1
);
720 varInt64
: result
:= Int64(result
)-Int64(r1
);
721 else error('can''t subtract non-numbers');
724 function TBinExprSub
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'-'+mOp1
.toString
+')'; end;
726 function TBinExprMul
.value (scope
: TExprScope
): Variant;
730 result
:= mOp0
.value(scope
);
731 r1
:= mOp1
.value(scope
);
733 case varType(result
) of
734 varSingle
, varDouble
: result
:= Double(result
)*Double(r1
);
735 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(result
)*LongInt(r1
);
736 varInt64
: result
:= Int64(result
)*Int64(r1
);
737 else error('can''t multiply non-numbers');
740 function TBinExprMul
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'*'+mOp1
.toString
+')'; end;
742 function TBinExprDiv
.value (scope
: TExprScope
): Variant;
746 result
:= mOp0
.value(scope
);
747 r1
:= mOp1
.value(scope
);
749 case varType(result
) of
750 varSingle
, varDouble
: result
:= Double(result
)/Double(r1
);
751 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(result
) div LongInt(r1
);
752 varInt64
: result
:= Int64(result
) div Int64(r1
);
753 else error('can''t divide non-numbers');
756 function TBinExprDiv
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'/'+mOp1
.toString
+')'; end;
758 function TBinExprMod
.value (scope
: TExprScope
): Variant;
762 result
:= mOp0
.value(scope
);
763 r1
:= mOp1
.value(scope
);
765 case varType(result
) of
766 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= LongInt(result
) mod LongInt(r1
);
767 varInt64
: result
:= Int64(result
) mod Int64(r1
);
768 else error('can''t do modulo on non-numbers');
771 function TBinExprMod
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'%'+mOp1
.toString
+')'; end;
773 function TBinExprLogAnd
.value (scope
: TExprScope
): Variant;
775 result
:= mOp0
.value(scope
);
776 if not coerce2bool(result
) then begin result
:= false; exit
; end;
777 result
:= mOp1
.value(scope
);
778 result
:= coerce2bool(result
);
780 function TBinExprLogAnd
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'&&'+mOp1
.toString
+')'; end;
782 function TBinExprLogOr
.value (scope
: TExprScope
): Variant;
784 result
:= mOp0
.value(scope
);
785 if coerce2bool(result
) then begin result
:= true; exit
; end;
786 result
:= mOp1
.value(scope
);
787 result
:= coerce2bool(result
);
789 function TBinExprLogOr
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'||'+mOp1
.toString
+')'; end;
791 function TBinExprCmpLess
.value (scope
: TExprScope
): Variant;
795 result
:= mOp0
.value(scope
);
796 r1
:= mOp1
.value(scope
);
798 case varType(result
) of
799 varSingle
, varDouble
: result
:= Boolean(Double(result
) < Double(r1
));
800 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) < LongInt(r1
));
801 varInt64
: result
:= Boolean(Int64(result
) < Int64(r1
));
802 varString
: result
:= Boolean(AnsiString(result
) < AnsiString(r1
));
803 else error('can''t compare non-numbers and non-strings');
806 function TBinExprCmpLess
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'<'+mOp1
.toString
+')'; end;
808 function TBinExprCmpGreat
.value (scope
: TExprScope
): Variant;
812 result
:= mOp0
.value(scope
);
813 r1
:= mOp1
.value(scope
);
815 case varType(result
) of
816 varSingle
, varDouble
: result
:= Boolean(Double(result
) > Double(r1
));
817 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) > LongInt(r1
));
818 varInt64
: result
:= Boolean(Int64(result
) > Int64(r1
));
819 varString
: result
:= Boolean(AnsiString(result
) > AnsiString(r1
));
820 else error('can''t compare non-numbers and non-strings');
823 function TBinExprCmpGreat
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'>'+mOp1
.toString
+')'; end;
825 function TBinExprCmpLessEqu
.value (scope
: TExprScope
): Variant;
829 result
:= mOp0
.value(scope
);
830 r1
:= mOp1
.value(scope
);
832 case varType(result
) of
833 varSingle
, varDouble
: result
:= Boolean(Double(result
) <= Double(r1
));
834 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) <= LongInt(r1
));
835 varInt64
: result
:= Boolean(Int64(result
) <= Int64(r1
));
836 varString
: result
:= Boolean(AnsiString(result
) <= AnsiString(r1
));
837 else error('can''t compare non-numbers and non-strings');
840 function TBinExprCmpLessEqu
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'<='+mOp1
.toString
+')'; end;
842 function TBinExprCmpGreatEqu
.value (scope
: TExprScope
): Variant;
846 result
:= mOp0
.value(scope
);
847 r1
:= mOp1
.value(scope
);
849 case varType(result
) of
850 varSingle
, varDouble
: result
:= Boolean(Double(result
) >= Double(r1
));
851 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) >= LongInt(r1
));
852 varInt64
: result
:= Boolean(Int64(result
) >= Int64(r1
));
853 varString
: result
:= Boolean(AnsiString(result
) >= AnsiString(r1
));
854 else error('can''t compare non-numbers and non-strings');
857 function TBinExprCmpGreatEqu
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'>='+mOp1
.toString
+')'; end;
859 function TBinExprCmpEqu
.value (scope
: TExprScope
): Variant;
863 result
:= mOp0
.value(scope
);
864 r1
:= mOp1
.value(scope
);
866 case varType(result
) of
867 varSingle
, varDouble
: result
:= Boolean(Double(result
) = Double(r1
));
868 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) = LongInt(r1
));
869 varInt64
: result
:= Boolean(Int64(result
) = Int64(r1
));
870 varString
: result
:= Boolean(AnsiString(result
) = AnsiString(r1
));
871 varBoolean
: result
:= (Boolean(result
) = Boolean(r1
));
872 varQWord
: result
:= (UInt64(result
) = UInt64(r1
));
873 else error('can''t compare non-numbers and non-strings');
876 function TBinExprCmpEqu
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'=='+mOp1
.toString
+')'; end;
878 function TBinExprCmpNotEqu
.value (scope
: TExprScope
): Variant;
882 result
:= mOp0
.value(scope
);
883 r1
:= mOp1
.value(scope
);
885 case varType(result
) of
886 varSingle
, varDouble
: result
:= Boolean(Double(result
) <> Double(r1
));
887 varShortInt
, varSmallint
, varInteger
, varByte
, varWord
, varLongWord
: result
:= Boolean(LongInt(result
) <> LongInt(r1
));
888 varInt64
: result
:= Boolean(Int64(result
) <> Int64(r1
));
889 varString
: result
:= Boolean(AnsiString(result
) <> AnsiString(r1
));
890 varBoolean
: result
:= (Boolean(result
) <> Boolean(r1
));
891 varQWord
: result
:= (UInt64(result
) <> UInt64(r1
));
892 else error('can''t compare non-numbers and non-strings');
895 function TBinExprCmpNotEqu
.toString (): AnsiString; begin result
:= '('+mOp0
.toString()+'<>'+mOp1
.toString
+')'; end;
898 // ////////////////////////////////////////////////////////////////////////// //
899 function TBinAssign
.value (scope
: TExprScope
): Variant;
901 result
:= mOp1
.value(scope
);
902 mOp0
.assign(scope
, result
);
905 function TBinAssign
.toString (): AnsiString; begin result
:= mOp0
.toString()+'='+mOp1
.toString(); end;
908 // ////////////////////////////////////////////////////////////////////////// //
909 class function TExprBase
.parse (clist
: TExprConstList
; const str
: AnsiString; allowAssign
: Boolean=false): TExprBase
;
913 pr
:= TStrTextParser
.Create(str
);
915 result
:= parse(clist
, pr
, allowAssign
);
916 if (pr
.tokType
<> pr
.TTEOF
) then begin result
.Free(); parseError(pr
, 'extra code in expression'); end;
922 class function TExprBase
.parseStatList (clist
: TExprConstList
; const str
: AnsiString): TExprBase
;
924 pr
: TTextParser
= nil;
925 r
: TExprStatList
= nil;
928 pr
:= TStrTextParser
.Create(str
);
929 if (pr
.tokType
= pr
.TTEOF
) then begin pr
.Free(); result
:= nil; exit
; end;
930 r
:= TExprStatList
.Create();
936 while pr
.eatDelim(';') do begin end;
937 if (pr
.tokType
= pr
.TTEOF
) then break
;
938 e
:= parse(clist
, pr
, true);
939 if (e
= nil) then break
;
940 //writeln(': ', e.toString());
942 if (pr
.tokType
= pr
.TTEOF
) then break
;
943 //writeln('tt=', pr.tokType, ' <', pr.tokStr, '>');
944 //writeln(r.toString());
950 on e
: TExomaException
do
951 raise TExomaParseException
.Create(pr
, e
.message);
953 raise TExomaParseException
.Create(pr
, e
.message);
964 class function TExprBase
.parse (clist
: TExprConstList
; pr
: TTextParser
; allowAssign
: Boolean=false): TExprBase
;
966 function expr (): TExprBase
; forward;
968 function doTerm (): TExprBase
;
975 if pr
.eatDelim('(') then begin result
:= expr(); pr
.expectDelim(')'); exit
; end;
976 if pr
.eatDelim('!') then begin result
:= doTerm(); result
:= TUnExprNot
.Create(result
); exit
; end;
977 if pr
.eatDelim('+') then begin result
:= doTerm(); exit
; end;
978 if pr
.eatDelim('-') then begin result
:= doTerm(); result
:= TUnExprNeg
.Create(result
); exit
; end;
979 if (pr
.tokType
= pr
.TTInt
) then begin result
:= TLitExpr
.Create(pr
.expectInt()); exit
; end;
980 if (pr
.tokType
= pr
.TTStr
) then begin result
:= TLitExpr
.Create(pr
.expectStr(true)); exit
; end;
981 if (pr
.tokType
= pr
.TTId
) then
983 if (pr
.tokStr
= 'true') then begin result
:= TLitExpr
.Create(true); pr
.skipToken(); exit
; end;
984 if (pr
.tokStr
= 'false') then begin result
:= TLitExpr
.Create(false); pr
.skipToken(); exit
; end;
985 if (CompareText(pr
.tokStr
, 'true') = 0) or (CompareText(pr
.tokStr
, 'false') = 0) then parseError(pr
, '`true` and `false` are case-sensitive');
987 if (clist
<> nil) then
989 if clist
.get(id
, v
) then
991 result
:= TLitExpr
.Create(v
);
994 if not clist
.valid(id
) then parseErrorFmt(pr
, 'unknown identifier ''%s''', [id
]);
996 result
:= TObjExpr
.Create(id
);
997 while (pr
.tokType
= pr
.TTDelim
) and (pr
.tokChar
= '.') do
1000 result
:= TDotExpr
.Create(result
, pr
.expectId());
1008 parseError(pr
, 'invalid term');
1011 function doMulDiv (): TExprBase
;
1017 if pr
.eatDelim('*') then result
:= TBinExprMul
.Create(result
, doTerm())
1018 else if pr
.eatDelim('/') then result
:= TBinExprDiv
.Create(result
, doTerm())
1019 else if pr
.eatDelim('%') then result
:= TBinExprMod
.Create(result
, doTerm())
1028 function doPlusMinus (): TExprBase
;
1030 result
:= doMulDiv();
1034 if pr
.eatDelim('+') then result
:= TBinExprAdd
.Create(result
, doMulDiv())
1035 else if pr
.eatDelim('-') then result
:= TBinExprSub
.Create(result
, doMulDiv())
1044 function doCmp (): TExprBase
;
1046 result
:= doPlusMinus();
1050 if pr
.eatDelim('<') then result
:= TBinExprCmpLess
.Create(result
, doPlusMinus())
1051 else if pr
.eatDelim('>') then result
:= TBinExprCmpGreat
.Create(result
, doPlusMinus())
1052 else if pr
.eatTT(pr
.TTLessEqu
) then result
:= TBinExprCmpLessEqu
.Create(result
, doPlusMinus())
1053 else if pr
.eatTT(pr
.TTGreatEqu
) then result
:= TBinExprCmpGreatEqu
.Create(result
, doPlusMinus())
1062 function doCmpEqu (): TExprBase
;
1068 if pr
.eatTT(pr
.TTEqu
) then result
:= TBinExprCmpEqu
.Create(result
, doCmp())
1069 else if pr
.eatTT(pr
.TTNotEqu
) then result
:= TBinExprCmpNotEqu
.Create(result
, doCmp())
1078 function doLogAnd (): TExprBase
;
1080 result
:= doCmpEqu();
1084 if pr
.eatTT(pr
.TTLogAnd
) then result
:= TBinExprLogAnd
.Create(result
, doCmpEqu()) else break
;
1092 function doLogOr (): TExprBase
;
1094 result
:= doLogAnd();
1098 if pr
.eatTT(pr
.TTLogOr
) then result
:= TBinExprLogOr
.Create(result
, doLogAnd()) else break
;
1119 function expr0 (): TExprBase
;
1123 list
: TExprStatList
= nil;
1129 if pr
.eatDelim('-') then neg
:= true
1130 else if pr
.eatDelim('+') then neg
:= false
1133 if neg
then e
:= TUnExprNeg
.Create(e
);
1134 if allowAssign
and pr
.eatDelim('=') then e
:= TBinAssign
.Create(e
, expr());
1135 if not pr
.eatDelim(',') then
1137 if (result
= nil) then result
:= e
else list
.append(e
);
1141 if (list
= nil) then
1143 list
:= TExprStatList
.Create();
1155 function expr (): TExprBase
;
1161 if pr
.eatDelim('?') then
1163 c
:= TExprCond
.Create();
1167 pr
.expectDelim(':');
1177 oas
: TTextParser
.TOptions
;
1179 if (pr
= nil) or (pr
.tokType
= pr
.TTEOF
) then begin result
:= nil; exit
; end;
1182 pr
.options
:= pr
.options
-[pr
.TOption
.SignedNumbers
];
1189 on e
: TExomaException
do
1190 raise TExomaParseException
.Create(pr
, e
.message);
1192 raise TExomaParseException
.Create(pr
, e
.message);