{******************************************} { } { 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, ''); FList.Insert(ListPos, s); FList.Insert(ListPos, ''); 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(''); 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(''); FList.Add(''); FList.Add(''); FList.Add(''); FList.Add(''); FList.Add(''); 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, ''); FList.Insert(1, ''); FList.Add(''); 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; (* ... - 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 - array and init may be after each - do not handle 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 tag corresponding to our variable } for i := 0 to xi.Count - 1 do if CompareText(xi[i].Prop['text'], Name) = 0 then begin { process and 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.