FastReport_FMX_2.8.12/FastScript/fs_iilparser.pas
2024-07-06 22:41:12 +02:00

2060 lines
54 KiB
ObjectPascal

{******************************************}
{ }
{ FastScript v1.9 }
{ Intermediate Language parser }
{ }
{ (c) 2003-2007 by Alexander Tzyganenko, }
{ Fast Reports Inc }
{ }
{******************************************}
//VCL uses section
{$IFNDEF FMX}
unit fs_iilparser;
interface
{$i fs.inc}
uses
SysUtils, Classes, fs_iinterpreter, fs_iparser, fs_iexpression, fs_xml
{$IFDEF Delphi6}
, Variants
{$ENDIF};
{$ELSE}
interface
{$i fmx.inc}
{$i fs.inc}
uses
System.SysUtils, System.Classes, FMX.fs_iinterpreter, FMX.fs_iparser, FMX.fs_iexpression, FMX.fs_xml, System.Variants;
{$ENDIF}
type
TfsEmitOp = (emNone, emCreate, emFree);
{ TfsILParser performs the syntax analyze of source code. Source code
can be on ANY language. Grammars are stored in the XML file and
can be easily changed to support any structured language. Currently
supported languages are Pascal, C++, Basic and Java subsets.
The result of the analyze (function MakeScript) is the output XML script
(called Intermediate Language). This output processed by the ParseILScript
method. This method creates the program structure (defined in the
fs_Interpreter unit) and fills it by the data }
TfsILParser = class(TObject)
private
FErrorPos: String;
FGrammar: TfsXMLDocument;
FILScript: TfsXMLDocument;
FLangName: String;
FNeedDeclareVars: Boolean;
FParser: TfsParser;
FProgram: TfsScript;
FProgRoot: TfsXMLItem;
FRoot: TfsXMLItem;
FUnitName: String;
FUsesList: TStrings;
FWithList: TStringList;
function PropPos(xi: TfsXMLItem): String;
procedure ErrorPos(xi: TfsXMLItem);
procedure CheckIdent(Prog: TfsScript; const Name: String);
function FindClass(const TypeName: String): TfsClassVariable;
procedure CheckTypeCompatibility(Var1, Var2: TfsCustomVariable);
function FindVar(Prog: TfsScript; const Name: String): TfsCustomVariable;
function FindType(s: String): TfsVarType;
function CreateVar(xi: TfsXMLItem; Prog: TfsScript; const Name: String;
Statement: TfsStatement = nil; CreateParam: Boolean = False;
IsVarParam: Boolean = False): TfsCustomVariable;
function DoSet(xi: TfsXMLItem; Prog: TfsScript): TfsSetExpression;
function DoExpression(xi: TfsXMLItem; Prog: TfsScript): TfsExpression;
procedure DoUses(xi: TfsXMLItem; Prog: TfsScript);
procedure DoVar(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoConst(xi: TfsXMLItem; Prog: TfsScript);
procedure DoParameters(xi: TfsXMLItem; v: TfsProcVariable);
procedure DoProc1(xi: TfsXMLItem; Prog: TfsScript);
procedure DoProc2(xi: TfsXMLItem; Prog: TfsScript);
procedure DoFunc1(xi: TfsXMLItem; Prog: TfsScript);
procedure DoFunc2(xi: TfsXMLItem; Prog: TfsScript);
procedure DoAssign(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoCall(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoIf(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoVbFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoCppFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoWhile(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoRepeat(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoCase(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoTry(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoBreak(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoContinue(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoExit(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoReturn(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoWith(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoDelete(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoCompoundStmt(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoStmt(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
procedure DoProgram(xi: TfsXMLItem; Prog: TfsScript);
public
constructor Create(AProgram: TfsScript);
destructor Destroy; override;
procedure SelectLanguage(const LangName: String);
{ convert the input script to the Intermediate Language }
function MakeILScript(const Text: String): Boolean;
{ parse IL }
procedure ParseILScript;
{ this method is needed here to implement late-binding }
function DoDesignator(xi: TfsXMLItem; Prog: TfsScript;
EmitOp: TfsEmitOp = emNone): TfsDesignator;
property ILScript: TfsXMLDocument read FILScript;
end;
implementation
//VCL uses section
{$IFNDEF FMX}
uses fs_itools, fs_iconst
{$IFDEF CROSS_COMPILE}
, Types
{$ELSE}
, Windows
{$ENDIF}
{$IFDEF OLE}
, fs_idisp
{$ENDIF};
//FMX uses section
{$ELSE}
uses FMX.fs_itools, FMX.fs_iconst, System.Types, FMX.Types
{$IFDEF OLE}
, fs_idisp
{$ENDIF};
{$ENDIF}
{ TfsILParser }
constructor TfsILParser.Create(AProgram: TfsScript);
begin
FNeedDeclareVars := True;
FProgram := AProgram;
FGrammar := TfsXMLDocument.Create;
FILScript := TfsXMLDocument.Create;
FParser := TfsParser.Create;
FUsesList := TStringList.Create;
FWithList := TStringList.Create;
end;
destructor TfsILParser.Destroy;
begin
FGrammar.Free;
FILScript.Free;
FParser.Free;
FUsesList.Free;
FWithList.Free;
inherited;
end;
procedure TfsILParser.SelectLanguage(const LangName: String);
var
i: Integer;
Name, PropText: String;
xi: TfsXMLItem;
ParserRoot: TfsXMLItem;
ss: TStringStream;
begin
FParser.Clear;
FLangName := LangName;
ss := TStringStream.Create(fsGetLanguage(LangName));
try
FGrammar.LoadFromStream(ss);
finally
ss.Free;
end;
FRoot := FGrammar.Root;
ParserRoot := FRoot.FindItem('parser');
xi := ParserRoot.FindItem('keywords');
for i := 0 to xi.Count - 1 do
FParser.Keywords.Add(xi[i].Name);
for i := 0 to ParserRoot.Count - 1 do
begin
Name := LowerCase(ParserRoot[i].Name);
PropText := ParserRoot[i].Prop['text'];
if Name = 'identchars' then
FParser.ConstructCharset(PropText)
else if Name = 'commentline1' then
FParser.CommentLine1 := PropText
else if Name = 'commentline2' then
FParser.CommentLine2 := PropText
else if Name = 'commentblock1' then
FParser.CommentBlock1 := PropText
else if Name = 'commentblock2' then
FParser.CommentBlock2 := PropText
else if Name = 'stringquotes' then
FParser.StringQuotes := PropText
else if Name = 'hexsequence' then
FParser.HexSequence := PropText
else if Name = 'specstrchar' then
begin
if PropText = '1' then
FParser.SpecStrChar := true;
end
else if Name = 'declarevars' then
begin
if PropText = '0' then
FNeedDeclareVars := False;
end
else if Name = 'skipeol' then
begin
if PropText = '0' then
FParser.SkipEOL := False;
end
else if Name = 'skipchar' then
FParser.SkipChar := PropText
else if Name = 'casesensitive' then
begin
if PropText = '1' then
FParser.CaseSensitive := True;
end
end;
if FProgram.ExtendedCharset then
for i := 128 to 255 do
FParser.IdentifierCharset := FParser.IdentifierCharset + [Chr(i)];
end;
function TfsILParser.MakeILScript(const Text: String): Boolean;
var
FList: TStrings;
FStream: TStream;
FErrorMsg: String;
FErrorPos: String;
FTermError: Boolean;
i: Integer;
function Run(xi: TfsXMLItem): Boolean;
var
i, j, ParsPos, ParsPos1, LoopPos, ListPos: Integer;
s, NodeName, Token, PropText, PropAdd, PropAddText, PropNode: String;
Completed, TopLevelNode, Flag: Boolean;
const
PathD = {$IFDEF MSWINDOWS} '\'; {$ELSE} '/'; {$ENDIF}
procedure DoInclude(const Name: String);
var
sl: TStringList;
p: TfsILParser;
ss: TStringStream;
s, UnitPath: String;
idx: Integer;
begin
if FUsesList.IndexOf(Name) <> -1 then
Exit;
FUsesList.Add(Name);
sl := TStringList.Create;
try
if Assigned(FProgram.OnGetUnit) then
begin
s := '';
FProgram.OnGetUnit(FProgram, Name, s);
sl.Text := s;
end
else
begin
UnitPath := '';
for idx := 0 to FProgram.IncludePath.Count - 1 do
begin
UnitPath := FProgram.IncludePath[idx];
if (UnitPath <> '') and (PathD <> UnitPath[Length(UnitPath)]) then
UnitPath := UnitPath + PathD;
if FileExists(UnitPath + Name) then
break;
end;
sl.LoadFromFile(UnitPath + Name);
end;
p := TfsILParser.Create(FProgram);
p.FUnitName := Name;
ss := TStringStream.Create(''{$IFDEF Delphi12},TEncoding.UTF8{$ENDIF});
try
s := '';
if sl.Count > 0 then
begin
p.SelectLanguage(FLangName);
p.FUsesList.Assign(FUsesList);
if p.MakeILScript(sl.Text) then
begin
FUsesList.Assign(p.FUsesList);
p.ILScript.SaveToStream(ss);
s := ss.DataString;
Delete(s, 1, Pos('?>', s) + 1);
end
else
begin
FErrorMsg := FProgram.ErrorMsg;
FErrorPos := FProgram.ErrorPos;
if FProgram.ErrorUnit = '' then
FProgram.ErrorUnit := Name;
end;
end;
FList.Insert(ListPos, '</uses>');
FList.Insert(ListPos, s);
FList.Insert(ListPos, '<uses' + ' unit="' + Name + '">');
Inc(ListPos, 3);
finally
p.Free;
ss.Free;
end;
finally
sl.Free;
end;
end;
procedure CheckPropNode(Flag: Boolean);
var
i, ParsPos1: Integer;
s: String;
begin
if CompareText(PropNode, 'uses') = 0 then
begin
while FList.Count > ListPos do
begin
s := FList[FList.Count - 1];
i := Pos('text="', s);
Delete(s, 1, i + 5);
i := Pos('" ', s);
Delete(s, i, 255);
DoInclude(Copy(s, 2, Length(s) - 2));
FList.Delete(FList.Count - 1);
end;
end
else if PropNode <> '' then
if Flag then
begin
ParsPos1 := FParser.Position;
FParser.Position := ParsPos;
FParser.SkipSpaces;
s := '<' + PropNode + ' pos="' + FParser.GetXYPosition + '"';
FParser.Position := ParsPos1;
if PropNode = 'expr' then
s := s + ' pos1="' + FParser.GetXYPosition + '"';
s := s + '>';
FList.Insert(ListPos, s);
FList.Add('</' + PropNode + '>');
end
else
begin
while FList.Count > ListPos do
FList.Delete(FList.Count - 1);
end;
end;
procedure AddError(xi: TfsXMLItem);
var
PropErr: String;
xi1: TfsXMLItem;
begin
PropErr := xi.Prop['err'];
if (PropErr <> '') and (FErrorMsg = '') then
begin
xi1 := FRoot.FindItem('parser');
xi1 := xi1.FindItem('errors');
FErrorMsg := xi1.FindItem(PropErr).Prop['text'];
FParser.Position := ParsPos;
FParser.SkipSpaces;
FErrorPos := FParser.GetXYPosition;
FTermError := xi.Prop['term'] = '1';
end;
end;
begin
Result := True;
ParsPos := FParser.Position;
ListPos := FList.Count;
NodeName := AnsiLowerCase(xi.Name);
PropText := AnsiLowerCase(xi.Prop['text']);
PropNode := LowerCase(xi.Prop['node']);
TopLevelNode := xi.Parent = FRoot;
Completed := False;
Flag := False;
Token := '';
if TopLevelNode then
Completed := True
else if NodeName = 'char' then
begin
if xi.Prop['skip'] <> '0' then
FParser.SkipSpaces;
Token := FParser.GetChar;
Flag := True;
end
else if NodeName = 'keyword' then
begin
Token := FParser.GetWord;
Flag := True;
end
else if NodeName = 'ident' then
begin
Token := FParser.GetIdent;
Flag := True;
end
else if NodeName = 'number' then
begin
Token := FParser.GetNumber;
Flag := True;
end
else if NodeName = 'string' then
begin
Token := FParser.GetString;
Flag := True;
end
else if NodeName = 'frstring' then
begin
Token := FParser.GetFRString;
s := FParser.GetXYPosition;
FList.Add('<dsgn pos="' + s + '">');
FList.Add('<node text="Get" pos="' + s + '"/>');
FList.Add('<expr pos="' + s + '">');
FList.Add('<string text="''' + StrToXML(Token) + '''" pos="' + s + '"/>');
FList.Add('</expr>');
FList.Add('</dsgn>');
Flag := True;
end
else if NodeName = 'eol' then
Completed := FParser.GetEOL
else if NodeName = 'sequence' then
Completed := True
else if (NodeName = 'switch') or (NodeName = 'optionalswitch') then
begin
Completed := True;
for i := 0 to xi.Count - 1 do
begin
Completed := Run(xi[i]);
if Completed then
break;
end;
if not Completed then
if NodeName <> 'optionalswitch' then
begin
Result := False;
AddError(xi);
end;
Exit;
end
else if (NodeName = 'loop') or (NodeName = 'optionalloop') then
begin
j := 0;
repeat
Inc(j);
Flag := False;
LoopPos := FParser.Position;
for i := 0 to xi.Count - 1 do
begin
Result := Run(xi[i]);
if not Result then
begin
Flag := True;
break;
end;
end;
{ try loop delimiter }
ParsPos1 := FParser.Position;
if Result and (PropText <> '') then
begin
FParser.SkipSpaces;
if FParser.GetChar <> PropText then
begin
FParser.Position := ParsPos1;
Flag := True;
end;
end;
{ avoid infinity loop }
if FParser.Position = LoopPos then
Flag := True;
until Flag;
{ at least one loop was succesful }
if j > 1 then
begin
{ special case - now implemented only in "case" statement }
if (xi.Prop['skip'] = '1') or FTermError then
FErrorMsg := '';
FParser.Position := ParsPos1;
Result := True;
end;
if NodeName = 'optionalloop' then
begin
if not Result then
FParser.Position := ParsPos;
Result := True;
end;
Exit;
end
else if NodeName = 'optional' then
begin
for i := 0 to xi.Count - 1 do
if not Run(xi[i]) then
begin
FParser.Position := ParsPos;
break;
end;
Exit;
end
else
begin
j := FRoot.Find(NodeName);
if j = -1 then
raise Exception.Create(SInvalidLanguage);
Completed := Run(FRoot[j]);
end;
if Flag then
begin
if FParser.CaseSensitive then
Completed := (Token <> '') and
((PropText = '') or (Token = PropText))
else
Completed := (Token <> '') and
((PropText = '') or (AnsiCompareText(Token, PropText) = 0));
end;
if not Completed then
begin
Result := False;
AddError(xi);
end
else
begin
if not TopLevelNode then
CheckPropNode(True);
PropAdd := xi.Prop['add'];
PropAddText := xi.Prop['addtext'];
if PropAdd <> '' then
begin
if PropAddText = '' then
s := Token else
s := PropAddText;
FList.Add('<' + PropAdd + ' text="' + StrToXML(s) + '" pos="' +
FParser.GetXYPosition + '"/>');
end;
for i := 0 to xi.Count - 1 do
begin
Result := Run(xi[i]);
if not Result then
break;
end;
end;
if not Result then
FParser.Position := ParsPos;
if TopLevelNode then
CheckPropNode(Result);
end;
begin
FList := TStringList.Create;
FErrorMsg := '';
FErrorPos := '';
Result := False;
try
FParser.Text := Text;
i := 1;
if FParser.GetChar = '#' then
begin
if CompareText(FParser.GetIdent, 'language') = 0 then
begin
i := FParser.Position;
{$IFDEF Windows}
while (i <= Length(Text)) and (Text[i] <> #13) do
{$ELSE}
while (i <= Length(Text)) and (Text[i] <> #10) do
{$ENDIF}
Inc(i);
SelectLanguage(Trim(Copy(Text, FParser.Position, i - FParser.Position)));
Inc(i, 2);
end;
end;
FParser.Position := i;
if Run(FRoot.FindItem('program')) and (FErrorMsg = '') then
begin
FErrorMsg := '';
FErrorPos := '';
FStream := TMemoryStream.Create;
try
FList.Insert(0, '<?xml version="1.0"?>');
FList.Insert(1, '<program>');
FList.Add('</program>');
FList.SaveToStream(FStream{$IFDEF Delphi12}, TEncoding.UTF8{$ENDIF});
{$IFDEF Delphi12}
FStream.Position := 3;
{$ELSE}
FStream.Position := 0;
{$ENDIF}
FILScript.LoadFromStream(FStream);
FILScript.Root.Add.Assign(FRoot.FindItem('types'));
// uncomment the following lines to see what is IL script
// FILScript.AutoIndent := True;
// FILScript.SaveToFile(ExtractFilePath(ParamStr(0)) + 'out.xml');
Result := True;
finally
FStream.Free;
end;
end;
FProgram.ErrorPos := FErrorPos;
FProgram.ErrorMsg := FErrorMsg;
finally
FList.Free;
end;
end;
procedure TfsILParser.ParseILScript;
begin
FWithList.Clear;
FProgram.ErrorUnit := '';
FUnitName := '';
FUsesList.Clear;
try
DoProgram(FILScript.Root, FProgram);
FProgram.ErrorPos := '';
except
on e: Exception do
begin
FProgram.ErrorMsg := e.Message;
FProgram.ErrorPos := FErrorPos;
FProgram.ErrorUnit := FUnitName;
end;
end;
end;
function TfsILParser.PropPos(xi: TfsXMLItem): String;
begin
Result := xi.Prop['pos'];
end;
procedure TfsILParser.ErrorPos(xi: TfsXMLItem);
begin
FErrorPos := PropPos(xi);
end;
procedure TfsILParser.CheckIdent(Prog: TfsScript; const Name: String);
begin
if Prog.FindLocal(Name) <> nil then
raise Exception.Create(SIdRedeclared + '''' + Name + '''');
end;
function TfsILParser.FindClass(const TypeName: String): TfsClassVariable;
begin
Result := FProgram.FindClass(TypeName);
if Result = nil then
raise Exception.Create(SUnknownType + '''' + TypeName + '''');
end;
procedure TfsILParser.CheckTypeCompatibility(Var1, Var2: TfsCustomVariable);
begin
if not AssignCompatible(Var1, Var2, FProgram) then
raise Exception.Create(SIncompatibleTypes + ': ''' + Var1.GetFullTypeName +
''', ''' + Var2.GetFullTypeName + '''');
end;
function TfsILParser.FindVar(Prog: TfsScript; const Name: String): TfsCustomVariable;
begin
Result := Prog.Find(Name);
if Result = nil then
if not FNeedDeclareVars then
begin
Result := TfsVariable.Create(Name, fvtVariant, '');
FProgram.Add(Name, Result);
end
else
raise Exception.Create(SIdUndeclared + '''' + Name + '''');
end;
function TfsILParser.FindType(s: String): TfsVarType;
var
xi: TfsXMLItem;
begin
xi := FProgRoot.FindItem('types');
if xi.Find(s) <> -1 then
s := xi[xi.Find(s)].Prop['type']
else
begin
xi := FGrammar.Root.FindItem('types');
if xi.Find(s) <> -1 then
s := xi[xi.Find(s)].Prop['type']
end;
Result := StrToVarType(s, FProgram);
if Result = fvtClass then
FindClass(s);
end;
function TfsILParser.CreateVar(xi: TfsXMLItem; Prog: TfsScript; const Name: String;
Statement: TfsStatement = nil; CreateParam: Boolean = False;
IsVarParam: Boolean = False): TfsCustomVariable;
var
i, j: Integer;
Typ: TfsVarType;
TypeName: String;
RefItem: TfsCustomVariable;
InitValue: Variant;
InitItem: TfsXMLItem;
AssignStmt: TfsAssignmentStmt;
IsPascal: Boolean;
SourcePos: String;
procedure DoArray(xi: TfsXMLItem);
var
i, n: Integer;
v: array of {$IFDEF FPC}SizeInt{$ELSE}Integer{$ENDIF};
Expr: TfsExpression;
begin
n := xi.Count;
SetLength(v, n * 2);
for i := 0 to n - 1 do
begin
Expr := DoExpression(xi[i][0], Prog);
v[i * 2] := Expr.Value;
Expr.Free;
if xi[i].Count = 2 then
begin
Expr := DoExpression(xi[i][1], Prog);
v[i * 2 + 1] := Expr.Value;
Expr.Free;
end
else
begin
v[i * 2 + 1] := v[i * 2] - 1;
v[i * 2] := 0;
end;
end;
if n = 0 then
begin
SetLength(v, 2);
v[0] := 0;
v[1] := 0;
n := 1;
end;
InitValue := VarArrayCreate(v, varVariant);
RefItem := TfsArrayHelper.Create('', n, Typ, TypeName);
Prog.Add('', RefItem);
v := nil;
Typ := fvtArray;
end;
procedure DoInit(xi: TfsXMLItem);
var
Expr: TfsExpression;
Temp: TfsVariable;
begin
Temp := TfsVariable.Create('', Typ, TypeName);
try
Expr := DoExpression(xi[0], Prog);
InitValue := Expr.Value;
try
CheckTypeCompatibility(Temp, Expr);
finally
Expr.Free;
end;
finally
Temp.Free;
end;
end;
begin
RefItem := nil;
InitItem := nil;
TypeName := 'Variant';
IsPascal := False;
SourcePos := FErrorPos;
(*
<var>
<ident text="ar"/>
<array>
<dim>
<expr/>
<expr/>
</dim>
...
</array>
<type text="String"/>
<init>
<expr/>
</init>
</var>
- type may be first (in C-like languages) or last (in Pascal-like ones)
- type may be skipped (treated as variant)
- array and init may be either skipped, or after each <ident>
- array and init may be after each <ident>
- do not handle <ident> tags - they are handled in calling part
*)
{ find the type }
for i := 0 to xi.Count - 1 do
if CompareText(xi[i].name, 'type') = 0 then
begin
IsPascal := i <> 0;
TypeName := xi[i].Prop['text'];
ErrorPos(xi[i]);
break;
end;
Typ := FindType(TypeName);
case Typ of
fvtInt, fvtInt64, fvtFloat, fvtClass:
InitValue := 0;
fvtBool:
InitValue := False;
fvtChar, fvtString:
InitValue := '';
else
InitValue := Null;
end;
{ fing the <ident> tag corresponding to our variable }
for i := 0 to xi.Count - 1 do
if CompareText(xi[i].Prop['text'], Name) = 0 then
begin
{ process <array> and <init> tags if any }
j := i + 1;
while (j < xi.Count) and (IsPascal or (CompareText(xi[j].Name, 'ident') <> 0)) do
begin
if CompareText(xi[j].Name, 'array') = 0 then
DoArray(xi[j])
else if CompareText(xi[j].Name, 'init') = 0 then
begin
if Statement = nil then
DoInit(xi[j]);
InitItem := xi[j];
end;
Inc(j);
end;
break;
end;
if CreateParam then
Result := TfsParamItem.Create(Name, Typ, TypeName, InitItem <> nil, IsVarParam)
else if Typ in [fvtChar, fvtString] then
Result := TfsStringVariable.Create(Name, Typ, TypeName) else
Result := TfsVariable.Create(Name, Typ, TypeName);
try
Result.Value := InitValue;
Result.RefItem := RefItem;
Result.SourcePos := SourcePos;
Result.SourceUnit := FUnitName;
Result.OnGetVarValue := FProgram.OnGetVarValue;
{ create init statement }
if (InitItem <> nil) and (Statement <> nil) then
begin
AssignStmt := TfsAssignmentStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(AssignStmt);
AssignStmt.Designator := TfsVariableDesignator.Create(Prog);
AssignStmt.Designator.RefItem := Result;
AssignStmt.Expression := DoExpression(InitItem[0], Prog);
CheckTypeCompatibility(Result, AssignStmt.Expression);
AssignStmt.Optimize;
end;
except
on e: Exception do
begin
Result.Free;
raise;
end;
end;
end;
{$HINTS OFF}
function TfsILParser.DoDesignator(xi: TfsXMLItem; Prog: TfsScript;
EmitOp: TfsEmitOp = emNone): TfsDesignator;
var
i, j: Integer;
NodeName, NodeText, TypeName: String;
Expr: TfsExpression;
Item, PriorItem: TfsDesignatorItem;
ClassVar: TfsClassVariable;
StringVar: TfsStringVariable;
Typ: TfsVarType;
LateBinding, PriorIsIndex: Boolean;
NewDesignator: TfsDesignator;
PriorValue: Variant;
Component: TComponent;
function FindInWithList(const Name: String; ResultDS: TfsDesignator;
Item: TfsDesignatorItem): Boolean;
var
i: Integer;
WithStmt: TfsWithStmt;
WithItem: TfsDesignatorItem;
ClassVar: TfsClassVariable;
xi1: TfsXMLItem;
begin
Result := False;
LateBinding := False;
for i := FWithList.Count - 1 downto 0 do
begin
{ prevent checking non-local 'with' }
if Prog.FindLocal(FWithList[i]) = nil then
continue;
WithStmt := TfsWithStmt(FWithList.Objects[i]);
if WithStmt.Variable.Typ = fvtVariant then
begin
{ first check all known variables }
if Prog.Find(Name) <> nil then
Exit;
{ if nothing found, create late binding information }
Item.Ref := WithStmt.Variable;
ResultDS.Finalize;
ResultDS.LateBindingXMLSource := TfsXMLItem.Create;
ResultDS.LateBindingXMLSource.Assign(xi);
xi1 := TfsXMLItem.Create;
xi1.Name := 'node';
xi1.Text := 'text="' + FWithList[i] + '"';
ResultDS.LateBindingXMLSource.InsertItem(0, xi1);
LateBinding := True;
Result := True;
break;
end
else
begin
ClassVar := FindClass(WithStmt.Variable.TypeName);
Item.Ref := ClassVar.Find(NodeText);
end;
if Item.Ref <> nil then
begin
WithItem := TfsDesignatorItem.Create;
WithItem.Ref := WithStmt.Variable;
WithItem.SourcePos := Item.SourcePos;
ResultDS.Remove(Item);
ResultDS.Add(WithItem);
ResultDS.Add(Item);
Result := True;
break;
end;
end;
end;
{$IFDEF OLE}
procedure CreateOLEHelpers(Index: Integer);
var
i: Integer;
OLEHelper: TfsOLEHelper;
begin
for i := Index to xi.Count - 1 do
begin
ErrorPos(xi[i]);
NodeName := LowerCase(xi[i].Name);
NodeText := xi[i].Prop['text'];
if (NodeName = 'node') and (NodeText <> '[') then
begin
Item := TfsDesignatorItem.Create;
Result.Add(Item);
Item.SourcePos := FErrorPos;
OLEHelper := TfsOLEHelper.Create(NodeText);
Prog.Add('', OLEHelper);
Item.Ref := OLEHelper;
end
else if NodeName = 'expr' then
begin
Expr := DoExpression(xi[i], Prog);
PriorItem := Result.Items[Result.Count - 1];
PriorItem.Add(Expr);
PriorItem.Ref.Add(TfsParamItem.Create('', fvtVariant, '', False, False));
end
end;
end;
{$ENDIF}
begin
Result := TfsDesignator.Create(Prog);
try
for i := 0 to xi.Count - 1 do
begin
ErrorPos(xi[i]);
NodeName := LowerCase(xi[i].Name);
NodeText := xi[i].Prop['text'];
if NodeName = 'node' then
begin
Item := TfsDesignatorItem.Create;
Result.Add(Item);
Item.SourcePos := FErrorPos;
if Result.Count = 1 then
begin
if not FindInWithList(NodeText, Result, Item) then
Item.Ref := FindVar(Prog, NodeText);
{ LateBinding flag turned on in the FindInWithList }
if LateBinding then
Exit;
{ add .Create for cpp NEW statement, i.e convert o = new TObject
to o = TObject.Create }
if EmitOp = emCreate then
begin
if not (Item.Ref is TfsClassVariable) then
raise Exception.Create(SClassRequired);
ClassVar := TfsClassVariable(Item.Ref);
Item := TfsDesignatorItem.Create;
Result.Add(Item);
Item.Ref := ClassVar.Find('Create');
end;
end
else
begin
PriorItem := Result.Items[Result.Count - 2];
PriorIsIndex := (PriorItem.Ref is TfsMethodHelper) and
TfsMethodHelper(PriorItem.Ref).IndexMethod and not PriorItem.Flag;
Typ := PriorItem.Ref.Typ;
{ late binding }
if (Typ = fvtVariant) and not PriorIsIndex then
begin
PriorValue := PriorItem.Ref.Value;
if VarIsNull(PriorValue) then
begin
Result.Remove(Item);
Item.Free;
Result.Finalize;
Result.LateBindingXMLSource := TfsXMLItem.Create;
Result.LateBindingXMLSource.Assign(xi);
Exit;
end
else
begin
if (TVarData(PriorValue).VType = varString) {$IFDEF Delphi12}or (TVarData(PriorValue).VType = varUString){$ENDIF} then
{ accessing string elements }
Typ := fvtString
{$IFDEF OLE}
else if TVarData(PriorValue).VType = varDispatch then
begin
{ calling ole }
Result.Remove(Item);
Item.Free;
CreateOLEHelpers(i);
Result.Finalize;
Exit;
end
{$ENDIF}
else if (TVarData(PriorValue).VType and varArray) = varArray then
begin
{ accessing array elements }
if NodeText = '[' then { set ref to arrayhelper }
Item.Ref := FindVar(Prog, '__ArrayHelper')
else
raise Exception.Create(SIndexRequired);
continue;
end
else
begin
{ accessing class items }
Typ := fvtClass;
PriorItem.Ref.TypeName := TObject(frxInteger(PriorItem.Ref.Value)).ClassName;
end;
end;
end;
if PriorIsIndex then
begin
PriorItem.Flag := True;
Result.Remove(Item); { previous item is set up already }
Item.Free;
FErrorPos := PriorItem.SourcePos;
if NodeText <> '[' then
raise Exception.Create(SIndexRequired);
end
else if Typ = fvtString then
begin
if NodeText = '[' then { set ref to stringhelper }
Item.Ref := FindVar(Prog, '__StringHelper')
else
raise Exception.Create(SStringError);
end
else if Typ = fvtClass then
begin
TypeName := PriorItem.Ref.TypeName;
ClassVar := FindClass(TypeName);
if NodeText = '[' then { default property }
begin
Item.Flag := True;
Item.Ref := ClassVar.DefProperty;
if Item.Ref = nil then
raise Exception.CreateFmt(SClassError, [TypeName]);
end
else { property or method }
begin
Item.Ref := ClassVar.Find(NodeText, False);
{ property not found. Probably it's a form element such as button? }
if Item.Ref = nil then
begin
PriorValue := PriorItem.Ref.Value;
if ((VarIsNull(PriorValue) or (PriorValue = 0)) and not Prog.IsRunning) and Prog.UseClassLateBinding then
begin
{ at compile time, we don't know anything about form elements.
So clear the designator items and use the late binding. }
Result.Remove(Item);
Item.Free;
while Result.Count > 1 do
begin
Item := Result.Items[Result.Count - 1];
Result.Remove(Item);
Item.Free;
end;
Item := Result.Items[0];
Result.Finalize;
Result.Typ := fvtVariant;
Result.LateBindingXMLSource := TfsXMLItem.Create;
Result.LateBindingXMLSource.Assign(xi);
Exit;
end
else
begin
{ we at run time now. Try to search in the form's elements. }
if TObject(frxInteger(PriorValue)) is TComponent then
begin
Component := TComponent(frxInteger(PriorValue)).FindComponent(NodeText);
if Component <> nil then
{ creates TfsComponentHelper }
Item.Ref := ClassVar.AddComponent(Component);
end;
if Item.Ref = nil then
raise Exception.Create(SIdUndeclared + '''' + NodeText + '''');
end
end;
end;
end
else if Typ = fvtArray then { set ref to array helper }
Item.Ref := PriorItem.Ref.RefItem
else
raise Exception.Create(SArrayRequired);
end;
end
else if NodeName = 'expr' then
begin
Expr := DoExpression(xi[i], Prog);
Result.Items[Result.Count - 1].Add(Expr);
end
else if NodeName = 'addr' then { @ operator }
begin
if xi.Count <> 2 then
raise Exception.Create(SVarRequired);
Item := TfsDesignatorItem.Create;
Result.Add(Item);
ErrorPos(xi[1]);
Item.SourcePos := FErrorPos;
{ we just return the string containing a referenced item name. For
example, var s: String; procedure B1; begin end; s := @B1
will assign 'B1' to the s }
StringVar := TfsStringVariable.Create('', fvtString, '');
StringVar.Value := xi[1].Prop['text'];
Prog.Add('', StringVar);
Item.Ref := StringVar;
break;
end;
end;
if EmitOp = emFree then
begin
PriorItem := Result.Items[Result.Count - 1];
if (PriorItem.Ref.Typ <> fvtClass) and (PriorItem.Ref.Typ <> fvtVariant) then
raise Exception.Create(SClassRequired);
Item := TfsDesignatorItem.Create;
Result.Add(Item);
ClassVar := FindClass('TObject');
Item.Ref := ClassVar.Find('Free');
end;
Result.Finalize;
if Result.Kind <> dkOther then
begin
NewDesignator := nil;
if Result.Kind = dkVariable then
NewDesignator := TfsVariableDesignator.Create(Prog)
else if Result.Kind = dkStringArray then
NewDesignator := TfsStringDesignator.Create(Prog)
else if Result.Kind = dkArray then
NewDesignator := TfsArrayDesignator.Create(Prog);
NewDesignator.Borrow(Result);
Result.Free;
Result := NewDesignator;
end;
for i := 0 to Result.Count - 1 do
begin
Item := Result[i];
FErrorPos := Item.SourcePos;
if Item.Ref is TfsDesignator then continue;
if Item.Count < Item.Ref.GetNumberOfRequiredParams then
raise Exception.Create(SNotEnoughParams)
else if Item.Count > Item.Ref.Count then
raise Exception.Create(STooManyParams)
else if Item.Count <> Item.Ref.Count then { construct the default params }
for j := Item.Count to Item.Ref.Count - 1 do
begin
Expr := TfsExpression.Create(FProgram);
Item.Add(Expr);
Expr.AddConstWithType(Item.Ref[j].DefValue, Item.Ref[j].Typ);
Expr.Finalize;
end;
for j := 0 to Item.Count - 1 do
begin
FErrorPos := Item[j].SourcePos;
CheckTypeCompatibility(Item.Ref[j], Item[j]);
end;
end;
except
on e: Exception do
begin
Result.Free;
raise;
end;
end;
end;
{$HINTS ON}
function TfsILParser.DoSet(xi: TfsXMLItem; Prog: TfsScript): TfsSetExpression;
var
i: Integer;
Name: String;
begin
Result := TfsSetExpression.Create('', fvtVariant, '');
try
for i := 0 to xi.Count - 1 do
begin
Name := LowerCase(xi[i].Name);
if Name = 'expr' then
Result.Add(DoExpression(xi[i], Prog))
else if Name = 'range' then
Result.Add(nil);
end;
except
on e: Exception do
begin
Result.Free;
raise;
end;
end;
end;
function TfsILParser.DoExpression(xi: TfsXMLItem; Prog: TfsScript): TfsExpression;
var
ErPos: String;
SourcePos1, SourcePos2: TPoint;
procedure DoExpressionItems(xi: TfsXMLItem; Expression: TfsExpression);
var
i: Integer;
NodeName: String;
OpName: String;
begin
i := 0;
while i < xi.Count do
begin
ErrorPos(xi[i]);
Expression.SourcePos := FErrorPos;
NodeName := Lowercase(xi[i].Name);
OpName := xi[i].Prop['text'];
if (NodeName = 'op') then
begin
OpName := LowerCase(OpName);
if (OpName = ')') or (i < xi.Count - 1) then
Expression.AddOperator(OpName);
end
else if (NodeName = 'number') or (NodeName = 'string') then
Expression.AddConst(ParserStringToVariant(OpName))
else if NodeName = 'dsgn' then
Expression.AddDesignator(DoDesignator(xi[i], Prog))
else if NodeName = 'set' then
Expression.AddSet(DoSet(xi[i], Prog))
else if NodeName = 'new' then
Expression.AddDesignator(DoDesignator(xi[i][0], Prog, emCreate))
else if NodeName = 'expr' then
DoExpressionItems(xi[i], Expression);
Inc(i);
end;
end;
function GetSource(pt1, pt2: TPoint): String;
var
i1, i2: Integer;
begin
i1 := FParser.GetPlainPosition(pt1);
i2 := FParser.GetPlainPosition(pt2);
if (i1 = -1) or (i2 = -1) then
Result := ''
else
Result := Copy(FParser.Text, i1, i2 - i1);
end;
begin
Result := TfsExpression.Create(FProgram);
try
DoExpressionItems(xi, Result);
SourcePos1 := fsPosToPoint(PropPos(xi));
SourcePos2 := fsPosToPoint(xi.Prop['pos1']);
Result.Source := GetSource(SourcePos1, SourcePos2);
ErPos := Result.Finalize;
if ErPos <> '' then
begin
FErrorPos := ErPos;
raise Exception.Create(SIncompatibleTypes);
end;
except
on e: Exception do
begin
Result.Free;
raise;
end;
end;
end;
procedure TfsILParser.DoUses(xi: TfsXMLItem; Prog: TfsScript);
var
i: Integer;
SaveUnitName: String;
s: String;
sl: TStringList;
ms: TMemoryStream;
xd: TfsXMLDocument;
begin
SaveUnitName := FUnitName;
FUnitName := xi.Prop['unit'];
xd := nil;
if FUsesList.IndexOf(FUnitName) <> -1 then
begin
FUnitName := SaveUnitName;
Exit;
end;
FUsesList.Add(FUnitName);
if Assigned(FProgram.OnGetILUnit) then
begin
s := '';
FProgram.OnGetILUnit(FProgram, FUnitName, s);
if s <> '' then
begin
sl := TStringList.Create;
sl.Text := s;
ms := TMemoryStream.Create;
sl.SaveToStream(ms);
sl.Free;
ms.Position := 0;
xd := TfsXMLDocument.Create;
xd.LoadFromStream(ms);
ms.Free;
end;
end;
if xd <> nil then
begin
try
DoProgram(xd.Root, Prog);
finally
xd.Free;
end;
end
else
begin
for i := 0 to xi.Count - 1 do
DoProgram(xi[i], Prog);
end;
FUnitName := SaveUnitName;
end;
procedure TfsILParser.DoVar(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Name: String;
begin
for i := 0 to xi.Count - 1 do
begin
ErrorPos(xi[i]);
if CompareText(xi[i].Name, 'ident') = 0 then
begin
Name := xi[i].Prop['text'];
CheckIdent(Prog, Name);
Prog.Add(Name, CreateVar(xi, Prog, Name, Statement));
end;
end;
end;
procedure TfsILParser.DoConst(xi: TfsXMLItem; Prog: TfsScript);
var
Name: String;
Expr: TfsExpression;
v: TfsVariable;
begin
Name := xi[0].Prop['text'];
ErrorPos(xi[0]);
CheckIdent(Prog, Name);
Expr := DoExpression(xi[1], Prog);
v := TfsVariable.Create(Name, Expr.Typ, Expr.TypeName);
v.Value := Expr.Value;
v.IsReadOnly := True;
Expr.Free;
Prog.Add(Name, v);
end;
procedure TfsILParser.DoParameters(xi: TfsXMLItem; v: TfsProcVariable);
var
i: Integer;
s: String;
varParams: Boolean;
procedure DoParam(xi: TfsXMLItem);
var
i: Integer;
Name: String;
Param: TfsParamItem;
varParam: Boolean;
begin
varParam := False;
for i := 0 to xi.Count - 1 do
begin
ErrorPos(xi[i]);
if CompareText(xi[i].Name, 'varparam') = 0 then
varParam := True
else if CompareText(xi[i].Name, 'ident') = 0 then
begin
Name := xi[i].Prop['text'];
CheckIdent(v.Prog, Name);
Param := TfsParamItem(CreateVar(xi, v.Prog, Name, nil, True,
varParams or VarParam));
Param.DefValue := Param.Value;
v.Add(Param);
v.Prog.Add(Name, Param);
varParam := False;
end;
end;
end;
begin
if CompareText(xi.Name, 'parameters') <> 0 then Exit;
varParams := False;
for i := 0 to xi.Count - 1 do
begin
s := LowerCase(xi[i].Name);
if s = 'varparams' then
varParams := True
else if s = 'var' then
begin
DoParam(xi[i]);
varParams := False;
end;
end;
end;
procedure TfsILParser.DoProc1(xi: TfsXMLItem; Prog: TfsScript);
var
i: Integer;
s, Name: String;
Proc: TfsProcVariable;
begin
ErrorPos(xi[0]);
Name := xi[0].Prop['text'];
CheckIdent(Prog, Name);
{$IFDEF CPUX64}
Proc := TfsProcVariable.Create(Name, fvtInt64, '', Prog, False);
{$ELSE}
Proc := TfsProcVariable.Create(Name, fvtInt, '', Prog, False);
{$ENDIF}
Proc.SourcePos := PropPos(xi);
Proc.SourceUnit := FUnitName;
Prog.Add(Name, Proc);
for i := 0 to xi.Count - 1 do
begin
s := LowerCase(xi[i].Name);
if s = 'parameters' then
DoParameters(xi[i], Proc);
end;
end;
procedure TfsILParser.DoProc2(xi: TfsXMLItem; Prog: TfsScript);
var
Name: String;
Proc: TfsProcVariable;
begin
Name := xi[0].Prop['text'];
Proc := TfsProcVariable(FindVar(Prog, Name));
DoProgram(xi, Proc.Prog);
end;
procedure TfsILParser.DoFunc1(xi: TfsXMLItem; Prog: TfsScript);
var
i: Integer;
s, Name, TypeName: String;
Typ: TfsVarType;
Func: TfsProcVariable;
begin
Name := '';
TypeName := '';
Typ := fvtVariant;
for i := 0 to xi.Count - 1 do
begin
ErrorPos(xi[i]);
s := LowerCase(xi[i].Name);
if s = 'type' then
begin
TypeName := xi[i].Prop['text'];
Typ := FindType(TypeName);
end
else if s = 'name' then
begin
Name := xi[i].Prop['text'];
CheckIdent(Prog, Name);
end
end;
Func := TfsProcVariable.Create(Name, Typ, TypeName, Prog,
CompareText(TypeName, 'void') <> 0);
Func.SourcePos := PropPos(xi);
Func.SourceUnit := FUnitName;
Prog.Add(Name, Func);
for i := 0 to xi.Count - 1 do
begin
s := LowerCase(xi[i].Name);
if s = 'parameters' then
DoParameters(xi[i], Func);
end;
end;
procedure TfsILParser.DoFunc2(xi: TfsXMLItem; Prog: TfsScript);
var
i: Integer;
s, Name: String;
Func: TfsProcVariable;
begin
Name := '';
for i := 0 to xi.Count - 1 do
begin
s := LowerCase(xi[i].Name);
if s = 'name' then
Name := xi[i].Prop['text'];
end;
Func := TfsProcVariable(FindVar(Prog, Name));
DoProgram(xi, Func.Prog);
end;
procedure TfsILParser.DoAssign(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Stmt: TfsAssignmentStmt;
Designator: TfsDesignator;
Expression: TfsExpression;
Modificator: String;
begin
Designator := nil;
Expression := nil;
try
Modificator := ' ';
Designator := DoDesignator(xi[0], Prog);
i := 1;
if CompareText(xi[1].Name, 'modificator') = 0 then
begin
Modificator := xi[1].Prop['text'];
Inc(i);
end;
Expression := DoExpression(xi[i], Prog);
if Designator.IsReadOnly then
raise Exception.Create(SLeftCantAssigned);
CheckTypeCompatibility(Designator, Expression);
if Modificator = ' ' then
Modificator := Expression.Optimize(Designator);
except
on e: Exception do
begin
if Designator <> nil then
Designator.Free;
if Expression <> nil then
Expression.Free;
raise;
end;
end;
case Modificator[1] of
'+':
Stmt := TfsAssignPlusStmt.Create(Prog, FUnitName, PropPos(xi));
'-':
Stmt := TfsAssignMinusStmt.Create(Prog, FUnitName, PropPos(xi));
'*':
Stmt := TfsAssignMulStmt.Create(Prog, FUnitName, PropPos(xi));
'/':
Stmt := TfsAssignDivStmt.Create(Prog, FUnitName, PropPos(xi));
else
Stmt := TfsAssignmentStmt.Create(Prog, FUnitName, PropPos(xi));
end;
Statement.Add(Stmt);
Stmt.Designator := Designator;
Stmt.Expression := Expression;
Stmt.Optimize;
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoCall(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsCallStmt;
begin
Stmt := TfsCallStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
Stmt.Designator := DoDesignator(xi[0], Prog);
if xi.Count > 1 then
begin
Stmt.Modificator := xi[1].Prop['text'];
if Stmt.Designator.IsReadOnly then
raise Exception.Create(SLeftCantAssigned);
end;
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoIf(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
s: String;
Stmt: TfsIfStmt;
begin
Stmt := TfsIfStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
Stmt.Condition := DoExpression(xi[0], Prog);
for i := 1 to xi.Count - 1 do
begin
s := Lowercase(xi[i].Name);
if s = 'thenstmt' then
DoCompoundStmt(xi[1], Prog, Stmt)
else if s = 'elsestmt' then
DoCompoundStmt(xi[2], Prog, Stmt.ElseStmt);
end;
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Stmt: TfsForStmt;
begin
Stmt := TfsForStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
ErrorPos(xi[0]);
Stmt.Variable := FindVar(Prog, xi[0].Prop['text']);
if not ((Stmt.Variable is TfsVariable) and
(Stmt.Variable.Typ in [fvtInt, fvtInt64, fvtVariant, fvtFloat])) then
raise Exception.Create(SForError);
Stmt.BeginValue := DoExpression(xi[1], Prog);
CheckTypeCompatibility(Stmt.Variable, Stmt.BeginValue);
i := 2;
if CompareText(xi[2].Name, 'downto') = 0 then
begin
Stmt.Down := True;
Inc(i);
end;
Stmt.EndValue := DoExpression(xi[i], Prog);
CheckTypeCompatibility(Stmt.Variable, Stmt.EndValue);
if i + 1 < xi.Count then
DoStmt(xi[i + 1], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoVbFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Stmt: TfsVbForStmt;
begin
Stmt := TfsVbForStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
ErrorPos(xi[0]);
Stmt.Variable := FindVar(Prog, xi[0].Prop['text']);
if not ((Stmt.Variable is TfsVariable) and
(Stmt.Variable.Typ in [fvtInt, fvtInt64, fvtVariant, fvtFloat])) then
raise Exception.Create(SForError);
Stmt.BeginValue := DoExpression(xi[1], Prog);
CheckTypeCompatibility(Stmt.Variable, Stmt.BeginValue);
Stmt.EndValue := DoExpression(xi[2], Prog);
CheckTypeCompatibility(Stmt.Variable, Stmt.EndValue);
i := 3;
if i < xi.Count then
if CompareText(xi[i].Name, 'expr') = 0 then
begin
Stmt.Step := DoExpression(xi[i], Prog);
CheckTypeCompatibility(Stmt.Variable, Stmt.Step);
Inc(i);
end;
if i < xi.Count then
DoStmt(xi[i], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoCppFor(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsCppForStmt;
begin
Stmt := TfsCppForStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
DoStmt(xi[0], Prog, Stmt.FirstStmt);
Stmt.Expression := DoExpression(xi[1], Prog);
DoStmt(xi[2], Prog, Stmt.SecondStmt);
DoStmt(xi[3], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoWhile(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsWhileStmt;
begin
Stmt := TfsWhileStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
Stmt.Condition := DoExpression(xi[0], Prog);
if xi.Count > 1 then
DoStmt(xi[1], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoRepeat(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i, j: Integer;
Stmt: TfsRepeatStmt;
begin
Stmt := TfsRepeatStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
j := xi.Count - 1;
if CompareText(xi[j].Name, 'inverse') = 0 then
begin
Stmt.InverseCondition := True;
Dec(j);
end;
Stmt.Condition := DoExpression(xi[j], Prog);
Dec(j);
for i := 0 to j do
DoStmt(xi[i], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoCase(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Stmt: TfsCaseStmt;
procedure DoCaseSelector(xi: TfsXMLItem);
var
Selector: TfsCaseSelector;
begin
if (CompareText(xi.Name, 'caseselector') <> 0) or (xi.Count <> 2) then Exit;
Selector := TfsCaseSelector.Create(Prog, FUnitName, PropPos(xi));
Stmt.Add(Selector);
Selector.SetExpression := DoSet(xi[0], Prog);
DoStmt(xi[1], Prog, Selector);
end;
begin
Stmt := TfsCaseStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
Stmt.Condition := DoExpression(xi[0], Prog);
for i := 1 to xi.Count - 1 do
DoCaseSelector(xi[i]);
if CompareText(xi[xi.Count - 1].Name, 'caseselector') <> 0 then
DoStmt(xi[xi.Count - 1], Prog, Stmt.ElseStmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoTry(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
Stmt: TfsTryStmt;
begin
Stmt := TfsTryStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
for i := 0 to xi.Count - 1 do
if CompareText(xi[i].Name, 'exceptstmt') = 0 then
begin
Stmt.IsExcept := True;
DoCompoundStmt(xi[i], Prog, Stmt.ExceptStmt);
end
else if CompareText(xi[i].Name, 'finallystmt') = 0 then
DoCompoundStmt(xi[i], Prog, Stmt.ExceptStmt)
else
DoStmt(xi[i], Prog, Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoBreak(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsBreakStmt;
begin
Stmt := TfsBreakStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoContinue(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsContinueStmt;
begin
Stmt := TfsContinueStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoExit(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsExitStmt;
begin
Stmt := TfsExitStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoReturn(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
xi1: TfsXMLItem;
begin
if xi.Count = 1 then { "return expr" }
begin
xi1 := TfsXMLItem.Create;
xi1.Name := 'dsgn';
xi.InsertItem(0, xi1);
with xi1.Add do
begin
Name := 'node';
Text := 'text="Result" pos="' + xi[1].Prop['pos'] + '"';
end;
DoAssign(xi, Prog, Statement);
end;
DoExit(xi, Prog, Statement);
end;
procedure TfsILParser.DoWith(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
d: TfsDesignator;
i, n: Integer;
s: String;
v: TfsVariable;
Stmt: TfsWithStmt;
function CreateUniqueVariable: String;
var
i: Integer;
begin
i := 0;
while (Prog.FindLocal(IntToStr(i)) <> nil) or
(FWithList.IndexOf(IntToStr(i)) <> -1) do
Inc(i);
Result := '_WithList_' + IntToStr(i);
end;
begin
n := xi.Count - 1;
for i := 0 to n - 1 do
begin
d := DoDesignator(xi[i], Prog);
if not ((d.Typ = fvtClass) or (d.Typ = fvtVariant)) then
begin
d.Free;
raise Exception.Create(SClassRequired);
end;
{ create local variable with unique name }
s := CreateUniqueVariable;
v := TfsVariable.Create(s, d.Typ, d.TypeName);
Prog.Add(s, v);
Stmt := TfsWithStmt.Create(Prog, FUnitName, PropPos(xi));
Stmt.Variable := v;
Stmt.Designator := d;
Statement.Add(Stmt);
FWithList.AddObject(s, Stmt);
end;
DoStmt(xi[xi.Count - 1], Prog, Statement);
for i := 0 to n - 1 do
FWithList.Delete(FWithList.Count - 1);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoDelete(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
Stmt: TfsCallStmt;
begin
Stmt := TfsCallStmt.Create(Prog, FUnitName, PropPos(xi));
Statement.Add(Stmt);
Stmt.Designator := DoDesignator(xi[0], Prog, emFree);
FProgram.AddCodeLine(FUnitName, PropPos(xi));
end;
procedure TfsILParser.DoCompoundStmt(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
i: Integer;
begin
for i := 0 to xi.Count - 1 do
DoStmt(xi[i], Prog, Statement);
end;
procedure TfsILParser.DoStmt(xi: TfsXMLItem; Prog: TfsScript; Statement: TfsStatement);
var
s: String;
begin
s := LowerCase(xi.Name);
if s = 'assignstmt' then
DoAssign(xi, Prog, Statement)
else if s = 'callstmt' then
DoCall(xi, Prog, Statement)
else if s = 'ifstmt' then
DoIf(xi, Prog, Statement)
else if s = 'casestmt' then
DoCase(xi, Prog, Statement)
else if s = 'forstmt' then
DoFor(xi, Prog, Statement)
else if s = 'vbforstmt' then
DoVbFor(xi, Prog, Statement)
else if s = 'cppforstmt' then
DoCppFor(xi, Prog, Statement)
else if s = 'whilestmt' then
DoWhile(xi, Prog, Statement)
else if s = 'repeatstmt' then
DoRepeat(xi, Prog, Statement)
else if s = 'trystmt' then
DoTry(xi, Prog, Statement)
else if s = 'break' then
DoBreak(xi, Prog, Statement)
else if s = 'continue' then
DoContinue(xi, Prog, Statement)
else if s = 'exit' then
DoExit(xi, Prog, Statement)
else if s = 'return' then
DoReturn(xi, Prog, Statement)
else if s = 'with' then
DoWith(xi, Prog, Statement)
else if s = 'delete' then
DoDelete(xi, Prog, Statement)
else if s = 'compoundstmt' then
DoCompoundStmt(xi, Prog, Statement)
else if s = 'uses' then
DoUses(xi, Prog)
else if s = 'var' then
DoVar(xi, Prog, Statement)
else if s = 'const' then
DoConst(xi, Prog)
else if s = 'procedure' then
DoProc2(xi, Prog)
else if s = 'function' then
DoFunc2(xi, Prog)
end;
procedure TfsILParser.DoProgram(xi: TfsXMLItem; Prog: TfsScript);
var
TempRoot: TfsXMLItem;
procedure DoFirstPass(xi: TfsXMLItem);
var
i: Integer;
s: String;
begin
for i := 0 to xi.Count - 1 do
begin
s := LowerCase(xi[i].Name);
if s = 'compoundstmt' then
DoFirstPass(xi[i])
else if s = 'procedure' then
DoProc1(xi[i], Prog)
else if s = 'function' then
DoFunc1(xi[i], Prog)
end;
end;
begin
TempRoot := FProgRoot;
FProgRoot := xi;
DoFirstPass(xi);
DoCompoundStmt(xi, Prog, Prog.Statement);
FProgRoot := TempRoot;
end;
end.