581 lines
21 KiB
ObjectPascal
581 lines
21 KiB
ObjectPascal
unit Compiler.ScriptCompiler;
|
|
|
|
{
|
|
Inno Setup
|
|
Copyright (C) 1997-2024 Jordan Russell
|
|
Portions by Martijn Laan
|
|
For conditions of distribution and use, see LICENSE.TXT.
|
|
|
|
Script (=[Code]) compiler
|
|
}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, Generics.Collections, uPSUtils;
|
|
|
|
type
|
|
TScriptCompilerOnLineToLineInfo = procedure(const Line: LongInt; var Filename: String; var FileLine: LongInt) of object;
|
|
TScriptCompilerOnUsedLine = procedure(const Filename: String; const Line, Position: LongInt; const IsProcExit: Boolean) of object;
|
|
TScriptCompilerOnUsedVariable = procedure(const Filename: String; const Line, Col, Param1, Param2, Param3: LongInt; const Param4: AnsiString) of object;
|
|
TScriptCompilerOnError = procedure(const Msg: String; const ErrorFilename: String; const ErrorLine: LongInt) of object;
|
|
TScriptCompilerOnWarning = procedure(const Msg: String) of object;
|
|
|
|
TScriptCompiler = class
|
|
private
|
|
FNamingAttribute: String;
|
|
FObsoleteFunctionWarnings: TDictionary<String, String>;
|
|
FExports, FUsedLines: TList;
|
|
FFunctionsFound: TStringList;
|
|
FScriptText: AnsiString;
|
|
FOnLineToLineInfo: TScriptCompilerOnLineToLineInfo;
|
|
FOnUsedLine: TScriptCompilerOnUsedLine;
|
|
FOnUsedVariable: TScriptCompilerOnUsedVariable;
|
|
FOnError: TScriptCompilerOnError;
|
|
FOnWarning: TScriptCompilerOnWarning;
|
|
function FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
|
|
function GetExportCount: Integer;
|
|
procedure PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
|
|
procedure TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
|
|
function CheckExports: Boolean;
|
|
function Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
|
|
property ExportCount: Integer read GetExportCount;
|
|
function ExportFound(const Name: String): Boolean;
|
|
function FunctionFound(const Name: String): Boolean;
|
|
function IsObsoleteFunction(const Name: String): String;
|
|
property NamingAttribute: String write FNamingAttribute;
|
|
property OnLineToLineInfo: TScriptCompilerOnLineToLineInfo write FOnLineToLineInfo;
|
|
property OnUsedLine: TScriptCompilerOnUsedLine write FOnUsedLine;
|
|
property OnUsedVariable: TScriptCompilerOnUsedVariable write FOnUsedVariable;
|
|
property OnError: TScriptCompilerOnError write FOnError;
|
|
property OnWarning: TScriptCompilerOnWarning write FOnWarning;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
SysUtils, Generics.Defaults,
|
|
uPSCompiler, uPSC_dll,
|
|
Compiler.ScriptClasses, Compiler.ScriptFunc;
|
|
|
|
type
|
|
TScriptExport = class
|
|
Name, Decl: String;
|
|
AllowNamingAttribute: Boolean;
|
|
Required: Boolean;
|
|
RequiredFilename: String;
|
|
RequiredLine: LongInt;
|
|
Exported: Boolean;
|
|
end;
|
|
|
|
{---}
|
|
|
|
function PSPascalCompilerOnExternalProc(Sender: TPSPascalCompiler; Decl: TPSParametersDecl; const Name, FExternal: tbtstring): TPSRegProc;
|
|
var
|
|
S: String;
|
|
P: Integer;
|
|
begin
|
|
S := String(FExternal) + ' ';
|
|
P := Pos(' setuponly ', S);
|
|
if P > 0 then begin
|
|
Delete(S, P+1, Length('setuponly '));
|
|
Insert('setup:', S, Pos('@', S)+1);
|
|
end
|
|
else begin
|
|
P := Pos(' uninstallonly ', S);
|
|
if P > 0 then begin
|
|
Delete(S, P+1, Length('uninstallonly '));
|
|
Insert('uninstall:', S, Pos('@', S)+1);
|
|
end;
|
|
end;
|
|
if Pos('@uninstall:files:', S) <> 0 then begin
|
|
Sender.MakeError('', ecCustomError, '"uninstallonly" cannot be used with "files:"');
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
Result := DllExternalProc(Sender, Decl, Name, tbtstring(TrimRight(S)));
|
|
end;
|
|
|
|
function PSPascalCompilerOnApplyAttributeToProc(Sender: TPSPascalCompiler; aProc: TPSProcedure; Attr: TPSAttribute): Boolean;
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
AttrValue: String;
|
|
ScriptExport: TScriptExport;
|
|
B: Boolean;
|
|
I: Integer;
|
|
begin
|
|
ScriptCompiler := TScriptCompiler(Sender.ID);
|
|
if CompareText(String(Attr.AType.Name), ScriptCompiler.FNamingAttribute) = 0 then begin
|
|
if aProc.ClassType <> TPSInternalProcedure then begin
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on external function or procedure', [ScriptCompiler.FNamingAttribute]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end else if Attr.Count <> 1 then begin
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value not found', [ScriptCompiler.FNamingAttribute]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end else begin
|
|
if ScriptCompiler.FindExport(String(TPSInternalProcedure(aProc).Name), '', -1) <> -1 then begin
|
|
{ Don't allow attributes on functions already matching an export (by their name) so that we don't have to deal with this later. }
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute not allowed for function or procedure "%s"', [ScriptCompiler.FNamingAttribute, String(TPSInternalProcedure(aProc).Name)]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end else begin
|
|
AttrValue := String(GetString(Attr.Values[0], B));
|
|
I := ScriptCompiler.FindExport(AttrValue, String(Sender.MakeDecl(TPSInternalProcedure(aProc).Decl)), -1);
|
|
if I <> -1 then begin
|
|
{ The name from the attribute and the function prototype are both ok. }
|
|
ScriptExport := ScriptCompiler.FExports[I];
|
|
if not ScriptExport.AllowNamingAttribute then begin
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" not allowed', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end else begin
|
|
ScriptExport.Exported := True;
|
|
Result := True;
|
|
end;
|
|
end else if ScriptCompiler.FindExport(AttrValue, '', -1) <> -1 then begin
|
|
{ The name from the attribute is ok but the function prototype is not. }
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid function or procedure prototype for attribute value "%s"', [AttrValue]))) do
|
|
SetCustomPos(TPSInternalProcedure(aProc).DeclarePos, TPSInternalProcedure(aProc).DeclareRow, TPSInternalProcedure(aProc).DeclareCol);
|
|
Result := False;
|
|
end else begin
|
|
{ The name from the attribute is not ok. }
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute value "%s" invalid', [ScriptCompiler.FNamingAttribute, AttrValue]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end;
|
|
end;
|
|
end;
|
|
end else
|
|
Result := True;
|
|
end;
|
|
|
|
function PSPascalCompilerOnApplyAttributeToType(Sender: TPSPascalCompiler; aType: TPSType; Attr: TPSAttribute): Boolean;
|
|
var
|
|
NamingAttribute: String;
|
|
begin
|
|
NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
|
|
if CompareText(String(Attr.AType.Name), NamingAttribute) = 0 then begin
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('"%s" attribute cannot be used on types', [NamingAttribute]))) do
|
|
SetCustomPos(Attr.DeclarePos, Attr.DeclareRow, Attr.DeclareCol);
|
|
Result := False;
|
|
end else
|
|
Result := True;
|
|
end;
|
|
|
|
function PSPascalCompilerOnUses(Sender: TPSPascalCompiler; const Name: tbtstring): Boolean;
|
|
var
|
|
NamingAttribute: String;
|
|
begin
|
|
if Name = 'SYSTEM' then begin
|
|
RegisterDll_Compiletime(Sender);
|
|
Sender.OnExternalProc := PSPascalCompilerOnExternalProc;
|
|
ScriptClassesLibraryRegister_C(Sender);
|
|
ScriptFuncLibraryRegister_C(Sender, TScriptCompiler(Sender.ID).FObsoleteFunctionWarnings);
|
|
NamingAttribute := TScriptCompiler(Sender.ID).FNamingAttribute;
|
|
if NamingAttribute <> '' then begin
|
|
with Sender.AddAttributeType do begin
|
|
OrgName := tbtstring(NamingAttribute);
|
|
with AddField do begin
|
|
FieldOrgName := 'Name';
|
|
FieldType := Sender.FindType('String');
|
|
end;
|
|
OnApplyAttributeToProc := PSPascalCompilerOnApplyAttributeToProc;
|
|
OnApplyAttributeToType := PSPascalCompilerOnApplyAttributeToType;
|
|
end;
|
|
end;
|
|
Result := True;
|
|
end else begin
|
|
Sender.MakeError('', ecUnknownIdentifier, '');
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function PSPascalCompilerOnExportCheck(Sender: TPSPascalCompiler; Proc: TPSInternalProcedure; const ProcDecl: tbtstring): Boolean;
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
begin
|
|
ScriptCompiler := TScriptCompiler(Sender.ID);
|
|
|
|
ScriptCompiler.FFunctionsFound.Add(String(Proc.Name));
|
|
|
|
{ Try and see if the function name matches an export and if so,
|
|
see if one of the prototypes for that name matches. }
|
|
|
|
I := ScriptCompiler.FindExport(String(Proc.Name), String(Procdecl), -1);
|
|
if I <> -1 then begin
|
|
{ The function name is a match and the function prototype is ok. }
|
|
ScriptExport := ScriptCompiler.FExports[I];
|
|
ScriptExport.Exported := True;
|
|
Result := True;
|
|
end else if ScriptCompiler.FindExport(String(Proc.Name), '', -1) <> -1 then begin
|
|
{ The function name is a match but the function prototype is not. }
|
|
with Sender.MakeError('', ecCustomError, tbtstring(Format('Invalid prototype for ''%s''', [Proc.OriginalName]))) do
|
|
SetCustomPos(Proc.DeclarePos, Proc.DeclareRow, Proc.DeclareCol);
|
|
Result := False;
|
|
end else
|
|
Result := True; { The function name is not a match - this is a user function. }
|
|
end;
|
|
|
|
function PSPascalCompilerOnBeforeOutput(Sender: TPSPascalCompiler): Boolean;
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
Decl: TPSParametersDecl;
|
|
Msg: String;
|
|
begin
|
|
ScriptCompiler := Sender.ID;
|
|
Result := True;
|
|
|
|
{ Try and see if required but non found exports match any built in function
|
|
names and if so, see if the prototypes also match }
|
|
|
|
for I := 0 to ScriptCompiler.FExports.Count-1 do begin
|
|
ScriptExport := ScriptCompiler.FExports[I];
|
|
if ScriptExport.Required and not ScriptExport.Exported then begin
|
|
Decl := Sender.UseExternalProc(tbtstring(ScriptExport.Name));
|
|
if Decl <> nil then begin
|
|
if CompareText(ScriptExport.Decl, String(Sender.MakeDecl(Decl))) = 0 then
|
|
ScriptExport.Exported := True
|
|
else begin
|
|
if Assigned(ScriptCompiler.FOnError) then begin
|
|
Msg := Format('Function or procedure ''%s'' prototype is incompatible', [ScriptExport.Name]);
|
|
ScriptCompiler.FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
|
|
end;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function PSPascalCompilerOnWriteLine2(Sender: TPSPascalCompiler; Position: Cardinal; IsProcExit: Boolean): Boolean;
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
Filename: String;
|
|
Line, Col: LongInt;
|
|
begin
|
|
ScriptCompiler := Sender.ID;
|
|
|
|
if Assigned(ScriptCompiler.FOnUsedLine) then begin
|
|
ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
|
|
if ScriptCompiler.FUsedLines.IndexOf(Pointer(Line)) = -1 then begin
|
|
ScriptCompiler.FUsedLines.Add(Pointer(Line));
|
|
Filename := '';
|
|
if Assigned(ScriptCompiler.FOnLineToLineInfo) then
|
|
ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
|
|
ScriptCompiler.FOnUsedLine(Filename, Line, Position, IsProcExit);
|
|
Result := True;
|
|
end else
|
|
Result := False;
|
|
end else
|
|
Result := True;
|
|
end;
|
|
|
|
procedure PSPascalCompilerOnUseVariable(Sender: TPSPascalCompiler; VarType: TPSVariableType; VarNo: Longint; ProcNo, Position: Cardinal; const PropData: tbtstring);
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
Filename: String;
|
|
Line, Col: LongInt;
|
|
begin
|
|
ScriptCompiler := Sender.ID;
|
|
|
|
if Assigned(ScriptCompiler.FOnUsedVariable) then begin
|
|
ScriptCompiler.PSPositionToLineCol(Position, Line, Col);
|
|
Filename := '';
|
|
if Assigned(ScriptCompiler.FOnLineToLineInfo) then
|
|
ScriptCompiler.FOnLineToLineInfo(Line, Filename, Line);
|
|
ScriptCompiler.FOnUsedVariable(Filename, Line, Col, LongInt(VarType), ProcNo, VarNo, PropData);
|
|
end;
|
|
end;
|
|
|
|
procedure PSPascalCompilerOnUseRegProc(Sender: TPSPascalCompiler; Position: Cardinal; const Name: tbtstring);
|
|
var
|
|
ScriptCompiler: TScriptCompiler;
|
|
WarningMessage: String;
|
|
begin
|
|
ScriptCompiler := Sender.ID;
|
|
|
|
if Assigned(ScriptCompiler.FOnWarning) then begin
|
|
WarningMessage := ScriptCompiler.IsObsoleteFunction(String(Name));
|
|
if WarningMessage <> '' then
|
|
ScriptCompiler.TriggerWarning(Position, 'Hint', WarningMessage);
|
|
end;
|
|
end;
|
|
|
|
{---}
|
|
|
|
constructor TScriptCompiler.Create;
|
|
begin
|
|
FObsoleteFunctionWarnings := TDictionary<String, String>.Create(TIStringComparer.Ordinal);
|
|
FExports := TList.Create();
|
|
FUsedLines := TList.Create();
|
|
FFunctionsFound := TStringList.Create();
|
|
end;
|
|
|
|
destructor TScriptCompiler.Destroy;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
FFunctionsFound.Free();
|
|
FUsedLines.Free();
|
|
for I := 0 to FExports.Count-1 do
|
|
TScriptExport(FExports[I]).Free();
|
|
FExports.Free();
|
|
FObsoleteFunctionWarnings.Free();
|
|
end;
|
|
|
|
procedure TScriptCompiler.PSPositionToLineCol(Position: LongInt; var Line, Col: LongInt);
|
|
|
|
function FindNewLine(const S: AnsiString; const Start: Integer): Integer;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
for I := Start to Length(S) do
|
|
if S[I] = #10 then begin
|
|
Result := I - Start + 1;
|
|
Exit;
|
|
end;
|
|
Result := 0;
|
|
end;
|
|
|
|
var
|
|
LineStartPosition, LineLength: LongInt;
|
|
begin
|
|
Inc(Position);
|
|
|
|
Line := 1;
|
|
LineStartPosition := 1;
|
|
LineLength := FindNewLine(FScriptText, LineStartPosition);
|
|
|
|
while (LineLength <> 0) and (Position > LineLength) do begin
|
|
Inc(Line);
|
|
Inc(LineStartPosition, LineLength);
|
|
Dec(Position, LineLength);
|
|
LineLength := FindNewLine(FScriptText, LineStartPosition);
|
|
end;
|
|
|
|
{ Convert Position from the UTF8 encoded ANSI string index to a UTF-16 string index }
|
|
Position := Length(UTF8ToString(Copy(FScriptText, LineStartPosition, Position - 1))) + 1;
|
|
Col := Position;
|
|
end;
|
|
|
|
procedure TScriptCompiler.TriggerWarning(const Position: LongInt; const WarningType, WarningMessage: String);
|
|
var
|
|
Line, Col: LongInt;
|
|
Filename, Msg: String;
|
|
begin
|
|
PSPositionToLineCol(Position, Line, Col);
|
|
Filename := '';
|
|
if Assigned(FOnLineToLineInfo) then
|
|
FOnLineToLineInfo(Line, Filename, Line);
|
|
Msg := '';
|
|
if Filename <> '' then
|
|
Msg := Msg + Filename + ', ';
|
|
Msg := Msg + Format('Line %d, Column %d: [%s] %s', [Line, Col, WarningType, WarningMessage]);
|
|
FOnWarning(Msg);
|
|
end;
|
|
|
|
procedure TScriptCompiler.AddExport(const Name, Decl: String; const AllowNamingAttribute, Required: Boolean; const RequiredFilename: String; const RequiredLine: LongInt);
|
|
var
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
begin
|
|
I := FindExport(Name, Decl, -1);
|
|
if I <> -1 then begin
|
|
ScriptExport := FExports[I];
|
|
if Required and not ScriptExport.Required then begin
|
|
ScriptExport.Required := True;
|
|
ScriptExport.RequiredFilename := RequiredFilename;
|
|
ScriptExport.RequiredLine := RequiredLine;
|
|
end;
|
|
ScriptExport.AllowNamingAttribute := ScriptExport.AllowNamingAttribute and AllowNamingAttribute;
|
|
Exit;
|
|
end;
|
|
|
|
ScriptExport := TScriptExport.Create();
|
|
ScriptExport.Name := Name;
|
|
ScriptExport.Decl := Decl;
|
|
ScriptExport.AllowNamingAttribute := AllowNamingAttribute;
|
|
ScriptExport.Required := Required;
|
|
if Required then begin
|
|
ScriptExport.RequiredFilename := RequiredFilename;
|
|
ScriptExport.RequiredLine := RequiredLine;
|
|
end;
|
|
FExports.Add(ScriptExport);
|
|
end;
|
|
|
|
function TScriptCompiler.FindExport(const Name, Decl: String; const IgnoreIndex: Integer): Integer;
|
|
var
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FExports.Count-1 do begin
|
|
ScriptExport := FExports[I];
|
|
if ((Name = '') or (CompareText(ScriptExport.Name, Name) = 0)) and
|
|
((Decl = '') or (CompareText(ScriptExport.Decl, Decl) = 0)) and
|
|
((IgnoreIndex = -1) or (I <> IgnoreIndex)) then begin
|
|
Result := I;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := -1;
|
|
end;
|
|
|
|
function TScriptCompiler.CheckExports: Boolean;
|
|
var
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
Msg: String;
|
|
begin
|
|
Result := True;
|
|
for I := 0 to FExports.Count-1 do begin
|
|
ScriptExport := FExports[I];
|
|
if ScriptExport.Required and not ScriptExport.Exported then begin
|
|
if Assigned(FOnError) then begin
|
|
{ Either the function wasn't present or it was present but matched another export }
|
|
if FindExport(ScriptExport.Name, '', I) <> -1 then
|
|
Msg := Format('Required function or procedure ''%s'' found but not with a compatible prototype', [ScriptExport.Name])
|
|
else
|
|
Msg := Format('Required function or procedure ''%s'' not found', [ScriptExport.Name]);
|
|
FOnError(Msg, ScriptExport.RequiredFilename, ScriptExport.RequiredLine);
|
|
end;
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TScriptCompiler.Compile(const ScriptText: String; var CompiledScriptText, CompiledScriptDebugInfo: tbtString): Boolean;
|
|
var
|
|
PSPascalCompiler: TPSPascalCompiler;
|
|
L, Line, Col: LongInt;
|
|
Filename, Msg: String;
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
|
|
FScriptText := Utf8Encode(ScriptText);
|
|
|
|
for I := 0 to FExports.Count-1 do
|
|
TScriptExport(FExports[I]).Exported := False;
|
|
FFunctionsFound.Clear;
|
|
|
|
PSPascalCompiler := TPSPascalCompiler.Create();
|
|
|
|
try
|
|
PSPascalCompiler.ID := Self;
|
|
PSPascalCompiler.AllowNoBegin := True;
|
|
PSPascalCompiler.AllowNoEnd := True;
|
|
PSPascalCompiler.BooleanShortCircuit := True;
|
|
PSPascalCompiler.AllowDuplicateRegister := False;
|
|
PSPascalCompiler.UTF8Decode := True;
|
|
PSPascalCompiler.AttributesOpenTokenID := CSTI_Less;
|
|
PSPascalCompiler.AttributesCloseTokenID := CSTI_Greater;
|
|
|
|
PSPascalCompiler.OnUses := PSPascalCompilerOnUses;
|
|
PSPascalCompiler.OnExportCheck := PSPascalCompilerOnExportCheck;
|
|
PSPascalCompiler.OnBeforeOutput := PSPascalCompilerOnBeforeOutput;
|
|
DefaultCC := ClStdCall;
|
|
FUsedLines.Clear();
|
|
PSPascalCompiler.OnWriteLine2 := PSPascalCompilerOnWriteLine2;
|
|
PSPascalCompiler.OnUseVariable := PSPascalCompilerOnUseVariable;
|
|
PSPascalCompiler.OnUseRegProc := PSPascalCompilerOnUseRegProc;
|
|
|
|
if not PSPascalCompiler.Compile(FScriptText) then begin
|
|
if Assigned(FOnError) then begin
|
|
for L := 0 to PSPascalCompiler.MsgCount-1 do begin
|
|
if PSPascalCompiler.Msg[L] is TPSPascalCompilerError then begin
|
|
PSPositionToLineCol(PSPascalCompiler.Msg[L].Pos, Line, Col);
|
|
Filename := '';
|
|
if Assigned(FOnLineToLineInfo) then
|
|
FOnLineToLineInfo(Line, Filename, Line);
|
|
Msg := Format('Column %d:'#13#10'%s', [Col, PSPascalCompiler.Msg[L].ShortMessageToString]);
|
|
FOnError(Msg, Filename, Line);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
Exit;
|
|
end else begin
|
|
if not CheckExports() then
|
|
Exit;
|
|
|
|
if not PSPascalCompiler.GetOutput(CompiledScriptText) then begin
|
|
if Assigned(FOnError) then begin
|
|
Msg := 'GetOutput failed';
|
|
FOnError(Msg, '', 0);
|
|
end;
|
|
Exit;
|
|
end;
|
|
|
|
if not PSPascalCompiler.GetDebugOutput(CompiledScriptDebugInfo) then begin
|
|
if Assigned(FOnError) then begin
|
|
Msg := 'GetDebugOutput failed';
|
|
FOnError(Msg, '', 0);
|
|
end;
|
|
Exit;
|
|
end;
|
|
|
|
if Assigned(FOnWarning) then
|
|
for L := 0 to PSPascalCompiler.MsgCount-1 do
|
|
TriggerWarning(PSPascalCompiler.Msg[L].Pos,
|
|
String(PSPascalCompiler.Msg[L].ErrorType),
|
|
String(PSPascalCompiler.Msg[L].ShortMessageToString));
|
|
end;
|
|
|
|
Result := True;
|
|
finally
|
|
PSPascalCompiler.Free();
|
|
end;
|
|
end;
|
|
|
|
function TScriptCompiler.ExportFound(const Name: String): Boolean;
|
|
var
|
|
ScriptExport: TScriptExport;
|
|
I: Integer;
|
|
begin
|
|
for I := 0 to FExports.Count-1 do begin
|
|
ScriptExport := FExports[I];
|
|
if CompareText(ScriptExport.Name, Name) = 0 then begin
|
|
Result := ScriptExport.Exported;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
Result := False;
|
|
end;
|
|
|
|
function TScriptCompiler.FunctionFound(const Name: String): Boolean;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := False;
|
|
for I := 0 to FFunctionsFound.Count-1 do begin
|
|
if CompareText(FFunctionsFound[I], Name) = 0 then begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TScriptCompiler.GetExportCount: Integer;
|
|
begin
|
|
Result := FExports.Count;
|
|
end;
|
|
|
|
function TScriptCompiler.IsObsoleteFunction(const Name: String): String;
|
|
begin
|
|
FObsoleteFunctionWarnings.TryGetValue(Name, Result);
|
|
end;
|
|
|
|
end.
|