RTL refactoring
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -45,7 +45,10 @@ uses
|
|||||||
Myc.Ast.Script.Print in '..\Src\AST\Myc.Ast.Script.Print.pas',
|
Myc.Ast.Script.Print in '..\Src\AST\Myc.Ast.Script.Print.pas',
|
||||||
Myc.Ast.Json.Schema.Old in '..\Src\AST\Myc.Ast.Json.Schema.Old.pas',
|
Myc.Ast.Json.Schema.Old in '..\Src\AST\Myc.Ast.Json.Schema.Old.pas',
|
||||||
Myc.Ast.Attributes in 'Myc.Ast.Attributes.pas',
|
Myc.Ast.Attributes in 'Myc.Ast.Attributes.pas',
|
||||||
Myc.Ast.Json.Schema in '..\Src\AST\Myc.Ast.Json.Schema.pas';
|
Myc.Ast.Json.Schema in '..\Src\AST\Myc.Ast.Json.Schema.pas',
|
||||||
|
Myc.Ast.RTL.Math in '..\Src\AST\Myc.Ast.RTL.Math.pas',
|
||||||
|
Myc.Ast.RTL.DateTime in '..\Src\AST\Myc.Ast.RTL.DateTime.pas',
|
||||||
|
Myc.Ast.RTL.Series in '..\Src\AST\Myc.Ast.RTL.Series.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,9 @@
|
|||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Json.Schema.Old.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Json.Schema.Old.pas"/>
|
||||||
<DCCReference Include="Myc.Ast.Attributes.pas"/>
|
<DCCReference Include="Myc.Ast.Attributes.pas"/>
|
||||||
<DCCReference Include="..\Src\AST\Myc.Ast.Json.Schema.pas"/>
|
<DCCReference Include="..\Src\AST\Myc.Ast.Json.Schema.pas"/>
|
||||||
|
<DCCReference Include="..\Src\AST\Myc.Ast.RTL.Math.pas"/>
|
||||||
|
<DCCReference Include="..\Src\AST\Myc.Ast.RTL.DateTime.pas"/>
|
||||||
|
<DCCReference Include="..\Src\AST\Myc.Ast.RTL.Series.pas"/>
|
||||||
<BuildConfiguration Include="Base">
|
<BuildConfiguration Include="Base">
|
||||||
<Key>Base</Key>
|
<Key>Base</Key>
|
||||||
</BuildConfiguration>
|
</BuildConfiguration>
|
||||||
|
|||||||
@@ -72,6 +72,26 @@ type
|
|||||||
property Signature: string read FSignature;
|
property Signature: string read FSignature;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Decorates a native function to be exported to the Myc runtime.
|
||||||
|
// 'IsPure' hints the compiler that the function has no side effects.
|
||||||
|
TRtlExportAttribute = class(TCustomAttribute)
|
||||||
|
type
|
||||||
|
TPurity = (Pure, Impure);
|
||||||
|
public
|
||||||
|
Name: string;
|
||||||
|
IsPure: Boolean;
|
||||||
|
constructor Create(const AName: string); overload;
|
||||||
|
constructor Create(const AName: string; APurity: TPurity); overload;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Decorates a class function to be exported as a constant value.
|
||||||
|
// The function is invoked once at registration time.
|
||||||
|
TRtlConstAttribute = class(TCustomAttribute)
|
||||||
|
public
|
||||||
|
Name: string;
|
||||||
|
constructor Create(const AName: string);
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{ AstTagAttribute }
|
{ AstTagAttribute }
|
||||||
@@ -116,4 +136,28 @@ begin
|
|||||||
FSignature := ASignature;
|
FSignature := ASignature;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Attribute Implementations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
constructor TRtlExportAttribute.Create(const AName: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
Name := AName;
|
||||||
|
IsPure := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TRtlExportAttribute.Create(const AName: string; APurity: TPurity);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
Name := AName;
|
||||||
|
IsPure := APurity = Pure;
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TRtlConstAttribute.Create(const AName: string);
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
Name := AName;
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
+82
-593
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,59 @@
|
|||||||
|
unit Myc.Ast.RTL.DateTime;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.SysUtils,
|
||||||
|
System.DateUtils,
|
||||||
|
Myc.Data.Scalar,
|
||||||
|
Myc.Data.Value,
|
||||||
|
Myc.Ast.Attributes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TRtlDateTimeFunctions = record
|
||||||
|
public
|
||||||
|
[TRtlExport('now', Impure)]
|
||||||
|
[AstDoc('Returns the current system timestamp.')]
|
||||||
|
[AstSignature('() -> datetime')]
|
||||||
|
class function Now(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('date', Impure)]
|
||||||
|
[AstDoc('Returns the current date or constructs a date from Y, M, D.')]
|
||||||
|
[AstSignature('() -> datetime')]
|
||||||
|
[AstSignature('(number, number, number) -> datetime')]
|
||||||
|
class function Date(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TRtlDateTimeFunctions }
|
||||||
|
|
||||||
|
class function TRtlDateTimeFunctions.Now(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
begin
|
||||||
|
Result := TScalar.FromDateTime(System.SysUtils.Now);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlDateTimeFunctions.Date(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
|
||||||
|
function GetInt(const V: TDataValue; ArgIndex: Integer): Word;
|
||||||
|
begin
|
||||||
|
if V.AsScalar.Kind <> TScalar.TKind.Ordinal then
|
||||||
|
raise EArgumentException.CreateFmt('Date argument %d must be an Integer.', [ArgIndex]);
|
||||||
|
Result := V.AsScalar.Value.AsInt64;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Length(Args) = 3 then
|
||||||
|
begin
|
||||||
|
var y := GetInt(Args[0], 1);
|
||||||
|
var m := GetInt(Args[1], 2);
|
||||||
|
var d := GetInt(Args[2], 3);
|
||||||
|
Result := TScalar.FromDateTime(EncodeDate(y, m, d));
|
||||||
|
end
|
||||||
|
else if Length(Args) = 0 then
|
||||||
|
Result := TScalar.FromDateTime(System.SysUtils.Date)
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Date expects 0 or 3 arguments (Year, Month, Day).');
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,270 @@
|
|||||||
|
unit Myc.Ast.RTL.Math;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.SysUtils,
|
||||||
|
System.Math,
|
||||||
|
Myc.Data.Scalar,
|
||||||
|
Myc.Data.Value,
|
||||||
|
Myc.Ast.Attributes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TRtlMathFunctions = record
|
||||||
|
public
|
||||||
|
// --- Math Functions ---
|
||||||
|
|
||||||
|
[TRtlExport('abs', Pure)]
|
||||||
|
[AstDoc('Returns the absolute value of a number.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Abs(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('round', Pure)]
|
||||||
|
[AstDoc('Rounds a float to the nearest integer.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Round(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('trunc', Pure)]
|
||||||
|
[AstDoc('Returns the integer part of a float.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Trunc(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('ceil', Pure)]
|
||||||
|
[AstDoc('Returns the smallest integer >= the argument.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Ceil(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('floor', Pure)]
|
||||||
|
[AstDoc('Returns the largest integer <= the argument.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Floor(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('sign', Pure)]
|
||||||
|
[AstDoc('Returns -1 for negative, 1 for positive, 0 for zero.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Sign(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('sqrt', Pure)]
|
||||||
|
[AstDoc('Returns the square root of a number.')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Sqrt(const Arg: TScalar): TScalar; static;
|
||||||
|
|
||||||
|
[TRtlExport('pow', Pure)]
|
||||||
|
[AstDoc('Returns Base raised to the power of Exponent.')]
|
||||||
|
[AstSignature('(number, number) -> number')]
|
||||||
|
class function Pow(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('random', Impure)]
|
||||||
|
[AstDoc('Returns a random number. No args: [0..1). Arg N: Integer [0..N).')]
|
||||||
|
[AstSignature('() -> float')]
|
||||||
|
[AstSignature('(number) -> number')]
|
||||||
|
class function Random(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
// --- Static Specializations ---
|
||||||
|
|
||||||
|
[TRtlExport('abs', Pure)]
|
||||||
|
class function Abs_Ordinal_Ordinal(A: Int64): Int64; static;
|
||||||
|
[TRtlExport('abs', Pure)]
|
||||||
|
class function Abs_Float_Float(A: Double): Double; static;
|
||||||
|
|
||||||
|
[TRtlExport('round', Pure)]
|
||||||
|
class function Round_Ordinal_Ordinal(A: Int64): Int64; static;
|
||||||
|
[TRtlExport('round', Pure)]
|
||||||
|
class function Round_Float_Ordinal(A: Double): Int64; static;
|
||||||
|
|
||||||
|
[TRtlExport('trunc', Pure)]
|
||||||
|
class function Trunc_Ordinal_Ordinal(A: Int64): Int64; static;
|
||||||
|
[TRtlExport('trunc', Pure)]
|
||||||
|
class function Trunc_Float_Ordinal(A: Double): Int64; static;
|
||||||
|
|
||||||
|
[TRtlExport('sqrt', Pure)]
|
||||||
|
class function Sqrt_Ordinal_Float(A: Int64): Double; static;
|
||||||
|
[TRtlExport('sqrt', Pure)]
|
||||||
|
class function Sqrt_Float_Float(A: Double): Double; static;
|
||||||
|
|
||||||
|
[TRtlExport('pow', Pure)]
|
||||||
|
class function Pow_Ordinal_Ordinal_Float(A, B: Int64): Double; static;
|
||||||
|
[TRtlExport('pow', Pure)]
|
||||||
|
class function Pow_Float_Float_Float(A, B: Double): Double; static;
|
||||||
|
[TRtlExport('pow', Pure)]
|
||||||
|
class function Pow_Ordinal_Float_Float(A: Int64; B: Double): Double; static;
|
||||||
|
[TRtlExport('pow', Pure)]
|
||||||
|
class function Pow_Float_Ordinal_Float(A: Double; B: Int64): Double; static;
|
||||||
|
|
||||||
|
[TRtlExport('random', Impure)]
|
||||||
|
class function Random_Void_Float: Double; static;
|
||||||
|
[TRtlExport('random', Impure)]
|
||||||
|
class function Random_Ordinal_Ordinal(Limit: Int64): Int64; static;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TRtlMathFunctions }
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Abs(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(System.Abs(Arg.Value.AsInt64));
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime: Result := TScalar.FromDouble(System.Abs(Arg.Value.AsDouble));
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Abs requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Round(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := Arg;
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime:
|
||||||
|
begin
|
||||||
|
var val: Double := Arg.Value.AsDouble;
|
||||||
|
Result := TScalar.FromInt64(System.Round(val));
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Round requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Trunc(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := Arg;
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime:
|
||||||
|
begin
|
||||||
|
var val: Double := Arg.Value.AsDouble;
|
||||||
|
Result := TScalar.FromInt64(System.Trunc(val));
|
||||||
|
end;
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Trunc requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Ceil(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := Arg;
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime: Result := TScalar.FromInt64(System.Math.Ceil(Arg.Value.AsDouble));
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Ceil requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Floor(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := Arg;
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime: Result := TScalar.FromInt64(System.Math.Floor(Arg.Value.AsDouble));
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Floor requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Sign(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
case Arg.Kind of
|
||||||
|
TScalar.TKind.Ordinal: Result := TScalar.FromInt64(System.Math.Sign(Arg.Value.AsInt64));
|
||||||
|
TScalar.TKind.Float, TScalar.TKind.DateTime: Result := TScalar.FromInt64(System.Math.Sign(Arg.Value.AsDouble));
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Sign requires a numeric argument.');
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Sqrt(const Arg: TScalar): TScalar;
|
||||||
|
begin
|
||||||
|
var val: Double := Arg; // Implicit cast handles Ordinal and Float
|
||||||
|
Result := TScalar.FromDouble(System.Sqrt(val));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Pow(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 2 then
|
||||||
|
raise EArgumentException.Create('Pow requires 2 arguments (Base, Exponent).');
|
||||||
|
|
||||||
|
var baseVal: Double := Args[0].AsScalar;
|
||||||
|
var expVal: Double := Args[1].AsScalar;
|
||||||
|
Result := TScalar.FromDouble(System.Math.Power(baseVal, expVal));
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Random(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
begin
|
||||||
|
if Length(Args) = 0 then
|
||||||
|
Result := TScalar.FromDouble(System.Random)
|
||||||
|
else if (Length(Args) = 1) and (Args[0].AsScalar.Kind = TScalar.TKind.Ordinal) then
|
||||||
|
Result := TScalar.FromInt64(System.Random(Integer(Args[0].AsScalar.Value.AsInt64)))
|
||||||
|
else
|
||||||
|
raise EArgumentException.Create('Random expects 0 arguments or 1 integer argument.');
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- Static Specializations ---
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Abs_Ordinal_Ordinal(A: Int64): Int64;
|
||||||
|
begin
|
||||||
|
Result := System.Abs(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Abs_Float_Float(A: Double): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Abs(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Round_Ordinal_Ordinal(A: Int64): Int64;
|
||||||
|
begin
|
||||||
|
Result := A;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Round_Float_Ordinal(A: Double): Int64;
|
||||||
|
begin
|
||||||
|
Result := System.Round(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Trunc_Ordinal_Ordinal(A: Int64): Int64;
|
||||||
|
begin
|
||||||
|
Result := A;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Trunc_Float_Ordinal(A: Double): Int64;
|
||||||
|
begin
|
||||||
|
Result := System.Trunc(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Sqrt_Ordinal_Float(A: Int64): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Sqrt(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Sqrt_Float_Float(A: Double): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Sqrt(A);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Pow_Ordinal_Ordinal_Float(A, B: Int64): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Math.Power(A, B);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Pow_Float_Float_Float(A, B: Double): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Math.Power(A, B);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Pow_Ordinal_Float_Float(A: Int64; B: Double): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Math.Power(A, B);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Pow_Float_Ordinal_Float(A: Double; B: Int64): Double;
|
||||||
|
begin
|
||||||
|
Result := System.Math.Power(A, B);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Random_Void_Float: Double;
|
||||||
|
begin
|
||||||
|
Result := System.Random;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlMathFunctions.Random_Ordinal_Ordinal(Limit: Int64): Int64;
|
||||||
|
begin
|
||||||
|
Result := System.Random(Limit);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
unit Myc.Ast.RTL.Series;
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.SysUtils,
|
||||||
|
System.Generics.Collections,
|
||||||
|
Myc.Data.Scalar,
|
||||||
|
Myc.Data.Value,
|
||||||
|
Myc.Data.Series,
|
||||||
|
Myc.Ast.Attributes;
|
||||||
|
|
||||||
|
type
|
||||||
|
TRtlSeriesFunctions = record
|
||||||
|
public
|
||||||
|
// --- Functional / Series ---
|
||||||
|
|
||||||
|
[TRtlExport('memoize', Pure)]
|
||||||
|
[AstDoc('Creates a memoized version of a function for performance.')]
|
||||||
|
[AstSignature('((any) -> any) -> ((any) -> any)')]
|
||||||
|
class function Memoize(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('map', Pure)]
|
||||||
|
[AstDoc('Applies a function to every element of a series.')]
|
||||||
|
[AstSignature('(series, (any) -> any) -> series')]
|
||||||
|
class function Map(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('reduce', Pure)]
|
||||||
|
[AstDoc('Collapses a series into a single value using a reducer function.')]
|
||||||
|
[AstSignature('(series, any, (any, any) -> any) -> any')]
|
||||||
|
class function Reduce(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('where', Pure)]
|
||||||
|
[AstDoc('Filters a series based on a predicate function.')]
|
||||||
|
[AstSignature('(series, (any) -> boolean) -> series')]
|
||||||
|
class function Where(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
|
||||||
|
[TRtlExport('any', Pure)]
|
||||||
|
[AstDoc('Returns true if at least one element satisfies the predicate.')]
|
||||||
|
[AstSignature('(series, (any) -> boolean) -> boolean')]
|
||||||
|
class function Any(const Args: TArray<TDataValue>): TDataValue; static;
|
||||||
|
end;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
{ TRtlSeriesFunctions }
|
||||||
|
|
||||||
|
class function TRtlSeriesFunctions.Memoize(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
var
|
||||||
|
funcToMemoize: TDataValue.TFunc;
|
||||||
|
memoizedFunc: TDataValue.TFunc;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 1 then
|
||||||
|
raise EArgumentException.Create('Memoize requires exactly one argument.');
|
||||||
|
if Args[0].Kind <> vkMethod then
|
||||||
|
raise EArgumentException.Create('The argument to Memoize must be a function.');
|
||||||
|
|
||||||
|
// We store the dictionary inside a TObjVal wrapped in a TDataValue to keep it alive
|
||||||
|
// as part of the closure's captured state (or rather, accessible via the closure).
|
||||||
|
// Note: In a pure closure implementation, capturing a local variable would be enough,
|
||||||
|
// but here we ensure explicit lifetime management via the TDataValue ownership mechanism if needed.
|
||||||
|
var cCache: TDataValue;
|
||||||
|
cCache.FromObj(TDictionary<Int64, TDataValue>.Create);
|
||||||
|
|
||||||
|
funcToMemoize := Args[0].AsMethod();
|
||||||
|
|
||||||
|
memoizedFunc :=
|
||||||
|
function(const AArgs: TArray<TDataValue>): TDataValue
|
||||||
|
var
|
||||||
|
argScalar: TScalar;
|
||||||
|
key: Int64;
|
||||||
|
begin
|
||||||
|
if (Length(AArgs) <> 1) or (AArgs[0].Kind <> vkScalar) then
|
||||||
|
raise EArgumentException.Create('This memoized function can only be called with a single scalar argument.');
|
||||||
|
|
||||||
|
argScalar := AArgs[0].AsScalar;
|
||||||
|
if argScalar.Kind <> TScalar.TKind.Ordinal then
|
||||||
|
raise EArgumentException.Create('This memoized function expects an ordinal argument for caching.');
|
||||||
|
|
||||||
|
key := argScalar.Value.AsInt64;
|
||||||
|
|
||||||
|
// Unsafe cast is safe here because we created it above
|
||||||
|
var cache := TDictionary<Int64, TDataValue>(cCache.AsObject);
|
||||||
|
|
||||||
|
if cache.TryGetValue(key, Result) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
Result := funcToMemoize(AArgs);
|
||||||
|
cache.Add(key, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := TDataValue(memoizedFunc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlSeriesFunctions.Map(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
var
|
||||||
|
sourceArg: TDataValue;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 2 then
|
||||||
|
raise EArgumentException.Create('Map requires exactly two arguments: a series and a function.');
|
||||||
|
|
||||||
|
sourceArg := Args[0];
|
||||||
|
if not (sourceArg.Kind in [vkSeries]) then
|
||||||
|
raise EArgumentException.Create('The first argument to Map must be a series.');
|
||||||
|
|
||||||
|
if Args[1].Kind <> vkMethod then
|
||||||
|
raise EArgumentException.Create('The second argument to Map must be a function.');
|
||||||
|
|
||||||
|
Result := TDataValue.Map(sourceArg.AsSeries, Args[1].AsMethod());
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlSeriesFunctions.Reduce(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
var
|
||||||
|
sourceArg: TDataValue;
|
||||||
|
sourceSeries: ISeries;
|
||||||
|
accumulator: TDataValue;
|
||||||
|
reducerFunc: TDataValue.TFunc;
|
||||||
|
i: Integer;
|
||||||
|
currentItem: TScalar;
|
||||||
|
reducerArgs: TArray<TDataValue>;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 3 then
|
||||||
|
raise EArgumentException.Create('Reduce requires exactly three arguments: a series, an initial value, and a reducer function.');
|
||||||
|
|
||||||
|
sourceArg := Args[0];
|
||||||
|
if sourceArg.Kind <> vkSeries then
|
||||||
|
raise EArgumentException.Create('The first argument to Reduce must be a series.');
|
||||||
|
|
||||||
|
if Args[2].Kind <> vkMethod then
|
||||||
|
raise EArgumentException.Create('The third argument to Reduce must be a function.');
|
||||||
|
|
||||||
|
sourceSeries := sourceArg.AsSeries;
|
||||||
|
accumulator := Args[1];
|
||||||
|
reducerFunc := Args[2].AsMethod();
|
||||||
|
|
||||||
|
// Reduce typically iterates from oldest to newest (index count-1 downto 0 in standard array,
|
||||||
|
// but TSeries logic might define 0 as newest. Assuming standard iteration order here).
|
||||||
|
// Note: Based on previous TSeries implementation, Index 0 is newest.
|
||||||
|
// Usually Reduce goes chronologically: Oldest (High) -> Newest (0).
|
||||||
|
for i := sourceSeries.Count - 1 downto 0 do
|
||||||
|
begin
|
||||||
|
currentItem := sourceSeries.Items[i];
|
||||||
|
reducerArgs := [accumulator, TDataValue(currentItem)];
|
||||||
|
accumulator := reducerFunc(reducerArgs);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := accumulator;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlSeriesFunctions.Where(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
var
|
||||||
|
sourceArg: TDataValue;
|
||||||
|
sourceSeries: ISeries;
|
||||||
|
predicateFunc: TDataValue.TFunc;
|
||||||
|
matchingIndices: TList<Integer>;
|
||||||
|
i: Integer;
|
||||||
|
item: TScalar;
|
||||||
|
predicateResult: TDataValue;
|
||||||
|
indexSeries: ISeries;
|
||||||
|
mapperFunc: TDataValue.TFunc;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 2 then
|
||||||
|
raise EArgumentException.Create('Where requires exactly two arguments: a series and a predicate function.');
|
||||||
|
|
||||||
|
sourceArg := Args[0];
|
||||||
|
if sourceArg.Kind <> vkSeries then
|
||||||
|
raise EArgumentException.Create('The first argument to Where must be a series.');
|
||||||
|
|
||||||
|
if Args[1].Kind <> vkMethod then
|
||||||
|
raise EArgumentException.Create('The second argument to Where must be a function.');
|
||||||
|
|
||||||
|
sourceSeries := sourceArg.AsSeries;
|
||||||
|
predicateFunc := Args[1].AsMethod();
|
||||||
|
|
||||||
|
matchingIndices := TList<Integer>.Create;
|
||||||
|
try
|
||||||
|
// We scan all items. Order doesn't strictly matter for building the index list
|
||||||
|
// if we just want to filter, but keeping order is good.
|
||||||
|
// Iterating 0 to Count-1 (Newest to Oldest) or vice versa depends on desired output series order.
|
||||||
|
// TIndexSeries usually expects indices.
|
||||||
|
for i := 0 to sourceSeries.Count - 1 do
|
||||||
|
begin
|
||||||
|
item := sourceSeries.Items[i];
|
||||||
|
// Perf warning: Allocation of array for every call
|
||||||
|
predicateResult := predicateFunc([TDataValue(item)]);
|
||||||
|
|
||||||
|
if (predicateResult.Kind = vkScalar) and (Boolean(predicateResult.AsScalar)) then
|
||||||
|
matchingIndices.Add(i);
|
||||||
|
end;
|
||||||
|
|
||||||
|
indexSeries := TIndexSeries.Create(matchingIndices.ToArray);
|
||||||
|
finally
|
||||||
|
matchingIndices.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Create a lazy mapper that looks up the original value based on the index
|
||||||
|
mapperFunc :=
|
||||||
|
function(const AArgs: TArray<TDataValue>): TDataValue
|
||||||
|
var
|
||||||
|
idx: Integer;
|
||||||
|
begin
|
||||||
|
idx := AArgs[0].AsScalar.Value.AsInt64;
|
||||||
|
Result := TDataValue(sourceSeries.Items[idx]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
// Result is a new series containing only the filtered items
|
||||||
|
Result := TDataValue.Map(indexSeries, mapperFunc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlSeriesFunctions.Any(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
var
|
||||||
|
sourceArg: TDataValue;
|
||||||
|
sourceSeries: ISeries;
|
||||||
|
predicateFunc: TDataValue.TFunc;
|
||||||
|
i: Integer;
|
||||||
|
item: TScalar;
|
||||||
|
predicateResult: TDataValue;
|
||||||
|
begin
|
||||||
|
if Length(Args) <> 2 then
|
||||||
|
raise EArgumentException.Create('Any requires exactly two arguments: a series and a predicate function.');
|
||||||
|
|
||||||
|
sourceArg := Args[0];
|
||||||
|
if sourceArg.Kind <> vkSeries then
|
||||||
|
raise EArgumentException.Create('The first argument to Any must be a series.');
|
||||||
|
|
||||||
|
if Args[1].Kind <> vkMethod then
|
||||||
|
raise EArgumentException.Create('The second argument to Any must be a function.');
|
||||||
|
|
||||||
|
sourceSeries := sourceArg.AsSeries;
|
||||||
|
predicateFunc := Args[1].AsMethod();
|
||||||
|
|
||||||
|
for i := 0 to sourceSeries.Count - 1 do
|
||||||
|
begin
|
||||||
|
item := sourceSeries.Items[i];
|
||||||
|
predicateResult := predicateFunc([TDataValue(item)]);
|
||||||
|
|
||||||
|
if (predicateResult.Kind = vkScalar) and (Boolean(predicateResult.AsScalar)) then
|
||||||
|
begin
|
||||||
|
Result := TScalar.FromBoolean(True);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := TScalar.FromBoolean(False);
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
+321
-417
@@ -2,9 +2,6 @@ unit Myc.Ast.RTL;
|
|||||||
|
|
||||||
interface
|
interface
|
||||||
|
|
||||||
// This unit is intended to be included in a 'uses' clause.
|
|
||||||
// It self-registers its functions via its initialization section.
|
|
||||||
|
|
||||||
uses
|
uses
|
||||||
System.SysUtils,
|
System.SysUtils,
|
||||||
System.Generics.Collections,
|
System.Generics.Collections,
|
||||||
@@ -19,27 +16,11 @@ uses
|
|||||||
Myc.Ast.Types;
|
Myc.Ast.Types;
|
||||||
|
|
||||||
type
|
type
|
||||||
// Defines the export parameters for a native RTL function.
|
//==============================================================================================
|
||||||
TRtlExportAttribute = class(TCustomAttribute)
|
// Internal Structures
|
||||||
type
|
//==============================================================================================
|
||||||
TPurity = (Pure, Impure);
|
|
||||||
public
|
|
||||||
Name: string;
|
|
||||||
IsPure: Boolean;
|
|
||||||
constructor Create(const AName: string); overload;
|
|
||||||
constructor Create(const AName: string; APurity: TPurity); overload;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Defines a constant export (via class function).
|
// Key used to identify specific overloads of a function based on argument types.
|
||||||
// The registry will INVOKE this function at startup and register the RESULT as a value.
|
|
||||||
TRtlConstAttribute = class(TCustomAttribute)
|
|
||||||
public
|
|
||||||
Name: string;
|
|
||||||
constructor Create(const AName: string);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Defines the key for the static bootstrap cache.
|
|
||||||
type
|
|
||||||
TStaticSignatureKey = record
|
TStaticSignatureKey = record
|
||||||
public
|
public
|
||||||
Name: string;
|
Name: string;
|
||||||
@@ -47,13 +28,14 @@ type
|
|||||||
constructor Create(const AName: string; const AArgTypes: TArray<IStaticType>);
|
constructor Create(const AName: string; const AArgTypes: TArray<IStaticType>);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// Comparer for the static signature key
|
// Equality comparer for TStaticSignatureKey to be used in dictionaries.
|
||||||
TStaticSignatureKeyComparer = class(TEqualityComparer<TStaticSignatureKey>)
|
TStaticSignatureKeyComparer = class(TEqualityComparer<TStaticSignatureKey>)
|
||||||
public
|
public
|
||||||
function Equals(const Left, Right: TStaticSignatureKey): Boolean; override;
|
function Equals(const Left, Right: TStaticSignatureKey): Boolean; override;
|
||||||
function GetHashCode(const Value: TStaticSignatureKey): Integer; override;
|
function GetHashCode(const Value: TStaticSignatureKey): Integer; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Holds a reference to a specialized, type-safe native function wrapper.
|
||||||
TSpecializedMethod = record
|
TSpecializedMethod = record
|
||||||
public
|
public
|
||||||
Target: TDataValue.TFunc;
|
Target: TDataValue.TFunc;
|
||||||
@@ -62,21 +44,22 @@ type
|
|||||||
constructor Create(const ATarget: TDataValue.TFunc; const AReturnType: IStaticType; AIsPure: Boolean);
|
constructor Create(const ATarget: TDataValue.TFunc; const AReturnType: IStaticType; AIsPure: Boolean);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
// The cache holding TDataValue.TFunc wrappers for static native functions.
|
// Cache for static specializations.
|
||||||
TStaticBootstrapCache = TDictionary<TStaticSignatureKey, TSpecializedMethod>;
|
TStaticBootstrapCache = TDictionary<TStaticSignatureKey, TSpecializedMethod>;
|
||||||
|
|
||||||
|
// Metadata for a registered function, including dynamic fallback and signatures.
|
||||||
TRtlFunctionInfo = class
|
TRtlFunctionInfo = class
|
||||||
public
|
public
|
||||||
DynamicWrapper: TDataValue.TFunc;
|
DynamicWrapper: TDataValue.TFunc;
|
||||||
Signatures: TList<IMethodSignature>;
|
Signatures: TList<IMethodSignature>;
|
||||||
Doc: string; // Documentation string from AstDoc attribute
|
Doc: string;
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TRtlFunctionMap = TDictionary<string, TRtlFunctionInfo>;
|
TRtlFunctionMap = TDictionary<string, TRtlFunctionInfo>;
|
||||||
|
|
||||||
// Container for constant metadata
|
// Metadata for a registered constant.
|
||||||
TRtlConstantInfo = record
|
TRtlConstantInfo = record
|
||||||
Name: string;
|
Name: string;
|
||||||
Value: TDataValue;
|
Value: TDataValue;
|
||||||
@@ -85,14 +68,18 @@ type
|
|||||||
constructor Create(const AName: string; const AValue: TDataValue; const AType: IStaticType; const ADoc: string);
|
constructor Create(const AName: string; const AValue: TDataValue; const AType: IStaticType; const ADoc: string);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
//==============================================================================================
|
||||||
//== Library Registration (RTTI-based)
|
// RTL Registry
|
||||||
//--------------------------------------------------------------------------------------------------
|
//==============================================================================================
|
||||||
|
|
||||||
// A helper record to encapsulate the RTTI-based registration logic.
|
// Central registry logic using RTTI to scan provider classes.
|
||||||
TRtlRegistry = record
|
TRtlRegistry = record
|
||||||
|
private
|
||||||
|
// Function pointer types for static wrappers
|
||||||
type
|
type
|
||||||
// --- Static Wrapper Signatures ---
|
TNativeDataValueFunc = function(const Args: TArray<TDataValue>): TDataValue;
|
||||||
|
TNativeScalarFunc = function(Arg: TScalar): TScalar;
|
||||||
|
|
||||||
TNativeFunc_O_O = function(A: Int64): Int64;
|
TNativeFunc_O_O = function(A: Int64): Int64;
|
||||||
TNativeFunc_F_F = function(A: Double): Double;
|
TNativeFunc_F_F = function(A: Double): Double;
|
||||||
TNativeFunc_F_O = function(A: Double): Int64;
|
TNativeFunc_F_O = function(A: Double): Int64;
|
||||||
@@ -112,15 +99,13 @@ type
|
|||||||
private
|
private
|
||||||
class var
|
class var
|
||||||
FStaticBootstrap: TStaticBootstrapCache;
|
FStaticBootstrap: TStaticBootstrapCache;
|
||||||
class var
|
|
||||||
FStaticFuncMap: TRtlFunctionMap;
|
FStaticFuncMap: TRtlFunctionMap;
|
||||||
class var
|
|
||||||
FStaticConstList: TList<TRtlConstantInfo>;
|
FStaticConstList: TList<TRtlConstantInfo>;
|
||||||
|
|
||||||
class constructor Create;
|
class constructor Create;
|
||||||
class destructor Destroy;
|
class destructor Destroy;
|
||||||
|
|
||||||
// --- Wrapper Creation Helpers ---
|
// Wrapper generators for specific function signatures
|
||||||
class function CreateWrapper_O_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
class function CreateWrapper_O_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||||
class function CreateWrapper_F_F(CodeAddress: Pointer): TDataValue.TFunc; static;
|
class function CreateWrapper_F_F(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||||
class function CreateWrapper_F_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
class function CreateWrapper_F_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||||
@@ -136,19 +121,14 @@ type
|
|||||||
class function CreateWrapper_FO_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
class function CreateWrapper_FO_O(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||||
class function CreateWrapper_TT_T(CodeAddress: Pointer): TDataValue.TFunc; static;
|
class function CreateWrapper_TT_T(CodeAddress: Pointer): TDataValue.TFunc; static;
|
||||||
|
|
||||||
// --- RTTI Helpers ---
|
// Helpers
|
||||||
class function RttiTypeToStaticType(const AType: TRttiType): IStaticType; static;
|
|
||||||
class function TValueToDataValue(const V: TValue): TDataValue; static;
|
class function TValueToDataValue(const V: TValue): TDataValue; static;
|
||||||
|
class function RttiTypeToStaticType(const AType: TRttiType): IStaticType; static;
|
||||||
class function ParseStaticSuffix(
|
class function ParseStaticSuffix(
|
||||||
const AMethodName: string;
|
const AMethodName: string;
|
||||||
out AArgTypes: TArray<IStaticType>;
|
out AArgTypes: TArray<IStaticType>;
|
||||||
out AReturnType: IStaticType
|
out AReturnType: IStaticType
|
||||||
): Boolean; static;
|
): Boolean; static;
|
||||||
|
|
||||||
public
|
|
||||||
class procedure RegisterAll(const AScope: IExecutionScope); static;
|
|
||||||
|
|
||||||
class function CreateStaticWrapper(
|
class function CreateStaticWrapper(
|
||||||
method: TRttiMethod;
|
method: TRttiMethod;
|
||||||
retType: IStaticType;
|
retType: IStaticType;
|
||||||
@@ -156,7 +136,14 @@ type
|
|||||||
rttiParams: TArray<TRttiParameter>
|
rttiParams: TArray<TRttiParameter>
|
||||||
): TDataValue.TFunc; static;
|
): TDataValue.TFunc; static;
|
||||||
|
|
||||||
// Public accessor for the TStaticSpecializer
|
public
|
||||||
|
// Scans a specific provider type for attributes and registers them internally.
|
||||||
|
class procedure Scan<T>; static;
|
||||||
|
|
||||||
|
// Dumps all registered functions and constants into the execution scope.
|
||||||
|
class procedure RegisterAll(const AScope: IExecutionScope); static;
|
||||||
|
|
||||||
|
// API for the type checker to retrieve specialized methods.
|
||||||
class function GetStaticSpecialization(const AName: string; const AArgTypes: TArray<IStaticType>): TSpecializedMethod; static;
|
class function GetStaticSpecialization(const AName: string; const AArgTypes: TArray<IStaticType>): TSpecializedMethod; static;
|
||||||
class procedure RegisterStaticSpecialization(
|
class procedure RegisterStaticSpecialization(
|
||||||
const AName: string;
|
const AName: string;
|
||||||
@@ -165,6 +152,7 @@ type
|
|||||||
); static;
|
); static;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
// Main entry point for library registration.
|
||||||
procedure RegisterRtlFunctions(const AScope: IExecutionScope);
|
procedure RegisterRtlFunctions(const AScope: IExecutionScope);
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@@ -173,39 +161,14 @@ uses
|
|||||||
System.Hash,
|
System.Hash,
|
||||||
System.StrUtils,
|
System.StrUtils,
|
||||||
Myc.Ast.Attributes,
|
Myc.Ast.Attributes,
|
||||||
Myc.Ast.RTL.Core;
|
Myc.Ast.RTL.Core,
|
||||||
|
Myc.Ast.RTL.Math,
|
||||||
|
Myc.Ast.RTL.Series,
|
||||||
|
Myc.Ast.RTL.DateTime;
|
||||||
|
|
||||||
type
|
//==================================================================================================
|
||||||
TNativeDataValueFunc = function(const Args: TArray<TDataValue>): TDataValue;
|
// Helper Structure Implementations
|
||||||
TNativeScalarFunc = function(Arg: TScalar): TScalar;
|
//==================================================================================================
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
//== Native Function Implementation (Core Logic)
|
|
||||||
//--------------------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
constructor TRtlExportAttribute.Create(const AName: string);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
Self.Name := AName;
|
|
||||||
Self.IsPure := False; // Default to impure (safe)
|
|
||||||
end;
|
|
||||||
|
|
||||||
constructor TRtlExportAttribute.Create(const AName: string; APurity: TPurity);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
Self.Name := AName;
|
|
||||||
Self.IsPure := APurity = Pure;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TRtlConstAttribute }
|
|
||||||
|
|
||||||
constructor TRtlConstAttribute.Create(const AName: string);
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
Name := AName;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TStaticSignatureKey }
|
|
||||||
|
|
||||||
constructor TStaticSignatureKey.Create(const AName: string; const AArgTypes: TArray<IStaticType>);
|
constructor TStaticSignatureKey.Create(const AName: string; const AArgTypes: TArray<IStaticType>);
|
||||||
begin
|
begin
|
||||||
@@ -213,8 +176,6 @@ begin
|
|||||||
ArgTypes := AArgTypes;
|
ArgTypes := AArgTypes;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TStaticSignatureKeyComparer }
|
|
||||||
|
|
||||||
function TStaticSignatureKeyComparer.Equals(const Left, Right: TStaticSignatureKey): Boolean;
|
function TStaticSignatureKeyComparer.Equals(const Left, Right: TStaticSignatureKey): Boolean;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@@ -223,42 +184,28 @@ begin
|
|||||||
exit(False);
|
exit(False);
|
||||||
if Length(Left.ArgTypes) <> Length(Right.ArgTypes) then
|
if Length(Left.ArgTypes) <> Length(Right.ArgTypes) then
|
||||||
exit(False);
|
exit(False);
|
||||||
|
|
||||||
// Compare types
|
|
||||||
for i := 0 to High(Left.ArgTypes) do
|
for i := 0 to High(Left.ArgTypes) do
|
||||||
begin
|
|
||||||
if not Left.ArgTypes[i].IsEqual(Right.ArgTypes[i]) then
|
if not Left.ArgTypes[i].IsEqual(Right.ArgTypes[i]) then
|
||||||
exit(False);
|
exit(False);
|
||||||
end;
|
|
||||||
|
|
||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TStaticSignatureKeyComparer.GetHashCode(const Value: TStaticSignatureKey): Integer;
|
function TStaticSignatureKeyComparer.GetHashCode(const Value: TStaticSignatureKey): Integer;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i, hash, ptrHash: Integer;
|
||||||
hash: Integer;
|
|
||||||
ptrHash: Integer;
|
|
||||||
begin
|
begin
|
||||||
// 1. Hash the function name to get the initial value
|
|
||||||
hash := THashBobJenkins.GetHashValue(Value.Name);
|
hash := THashBobJenkins.GetHashValue(Value.Name);
|
||||||
|
|
||||||
// 2. Iteratively combine the hash of each argument type
|
|
||||||
for i := 0 to High(Value.ArgTypes) do
|
for i := 0 to High(Value.ArgTypes) do
|
||||||
begin
|
begin
|
||||||
if Assigned(Value.ArgTypes[i]) then
|
if Assigned(Value.ArgTypes[i]) then
|
||||||
ptrHash := Value.ArgTypes[i].GetHashCode
|
ptrHash := Value.ArgTypes[i].GetHashCode
|
||||||
else
|
else
|
||||||
ptrHash := 0;
|
ptrHash := 0;
|
||||||
|
|
||||||
hash := THashBobJenkins.GetHashValue(ptrHash, SizeOf(Integer), hash);
|
hash := THashBobJenkins.GetHashValue(ptrHash, SizeOf(Integer), hash);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
Result := hash;
|
Result := hash;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TRtlFunctionInfo }
|
|
||||||
|
|
||||||
constructor TRtlFunctionInfo.Create;
|
constructor TRtlFunctionInfo.Create;
|
||||||
begin
|
begin
|
||||||
inherited Create;
|
inherited Create;
|
||||||
@@ -273,8 +220,6 @@ begin
|
|||||||
inherited Destroy;
|
inherited Destroy;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TRtlConstantInfo }
|
|
||||||
|
|
||||||
constructor TRtlConstantInfo.Create(const AName: string; const AValue: TDataValue; const AType: IStaticType; const ADoc: string);
|
constructor TRtlConstantInfo.Create(const AName: string; const AValue: TDataValue; const AType: IStaticType; const ADoc: string);
|
||||||
begin
|
begin
|
||||||
Name := AName;
|
Name := AName;
|
||||||
@@ -283,8 +228,6 @@ begin
|
|||||||
Doc := ADoc;
|
Doc := ADoc;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TSpecializedMethod }
|
|
||||||
|
|
||||||
constructor TSpecializedMethod.Create(const ATarget: TDataValue.TFunc; const AReturnType: IStaticType; AIsPure: Boolean);
|
constructor TSpecializedMethod.Create(const ATarget: TDataValue.TFunc; const AReturnType: IStaticType; AIsPure: Boolean);
|
||||||
begin
|
begin
|
||||||
Target := ATarget;
|
Target := ATarget;
|
||||||
@@ -292,7 +235,153 @@ begin
|
|||||||
IsPure := AIsPure;
|
IsPure := AIsPure;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TRtlRegistry }
|
//==================================================================================================
|
||||||
|
// TRtlRegistry Implementation
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
class constructor TRtlRegistry.Create;
|
||||||
|
begin
|
||||||
|
FStaticBootstrap := TStaticBootstrapCache.Create(TStaticSignatureKeyComparer.Create);
|
||||||
|
FStaticFuncMap := TRtlFunctionMap.Create;
|
||||||
|
FStaticConstList := TList<TRtlConstantInfo>.Create;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class destructor TRtlRegistry.Destroy;
|
||||||
|
var
|
||||||
|
funcInfo: TRtlFunctionInfo;
|
||||||
|
begin
|
||||||
|
for funcInfo in FStaticFuncMap.Values do
|
||||||
|
funcInfo.Free;
|
||||||
|
FStaticFuncMap.Free;
|
||||||
|
FStaticConstList.Free;
|
||||||
|
FStaticBootstrap.Free;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class procedure TRtlRegistry.Scan<T>;
|
||||||
|
var
|
||||||
|
ctx: TRttiContext;
|
||||||
|
typ: TRttiType;
|
||||||
|
method: TRttiMethod;
|
||||||
|
attribute: TCustomAttribute;
|
||||||
|
exportAttr: TRtlExportAttribute;
|
||||||
|
constAttr: TRtlConstAttribute;
|
||||||
|
docString, rtlName: string;
|
||||||
|
argTypes: TArray<IStaticType>;
|
||||||
|
retType: IStaticType;
|
||||||
|
methodRecord: TSpecializedMethod;
|
||||||
|
funcInfo: TRtlFunctionInfo;
|
||||||
|
constInfo: TRtlConstantInfo;
|
||||||
|
rttiParams: TArray<TRttiParameter>;
|
||||||
|
isDynamic: Boolean;
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
ctx := TRttiContext.Create;
|
||||||
|
try
|
||||||
|
typ := ctx.GetType(TypeInfo(T));
|
||||||
|
if typ = nil then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
for method in typ.GetMethods do
|
||||||
|
begin
|
||||||
|
if method.MethodKind <> mkClassFunction then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
exportAttr := nil;
|
||||||
|
constAttr := nil;
|
||||||
|
docString := '';
|
||||||
|
|
||||||
|
// Extract attributes
|
||||||
|
for attribute in method.GetAttributes do
|
||||||
|
begin
|
||||||
|
if attribute is TRtlExportAttribute then
|
||||||
|
exportAttr := attribute as TRtlExportAttribute
|
||||||
|
else if attribute is TRtlConstAttribute then
|
||||||
|
constAttr := attribute as TRtlConstAttribute
|
||||||
|
else if attribute is AstDocAttribute then
|
||||||
|
docString := AstDocAttribute(attribute).Description;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- CASE A: CONSTANT (via class function) ---
|
||||||
|
if Assigned(constAttr) then
|
||||||
|
begin
|
||||||
|
var val: TValue := method.Invoke(nil, []);
|
||||||
|
var dataVal: TDataValue := TValueToDataValue(val);
|
||||||
|
var staticType: IStaticType := RttiTypeToStaticType(method.ReturnType);
|
||||||
|
constInfo := TRtlConstantInfo.Create(constAttr.Name, dataVal, staticType, docString);
|
||||||
|
FStaticConstList.Add(constInfo);
|
||||||
|
continue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
// --- CASE B: FUNCTION ---
|
||||||
|
if not Assigned(exportAttr) then
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rtlName := exportAttr.Name;
|
||||||
|
if not FStaticFuncMap.TryGetValue(rtlName, funcInfo) then
|
||||||
|
begin
|
||||||
|
funcInfo := TRtlFunctionInfo.Create;
|
||||||
|
FStaticFuncMap.Add(rtlName, funcInfo);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if (funcInfo.Doc = '') and (docString <> '') then
|
||||||
|
funcInfo.Doc := docString;
|
||||||
|
|
||||||
|
rttiParams := method.GetParameters;
|
||||||
|
isDynamic :=
|
||||||
|
(Length(rttiParams) = 1)
|
||||||
|
and (method.ReturnType.Handle = TypeInfo(TDataValue))
|
||||||
|
and (rttiParams[0].ParamType.Handle = TypeInfo(TArray<TDataValue>));
|
||||||
|
|
||||||
|
if isDynamic then
|
||||||
|
begin
|
||||||
|
// Dynamic wrapper (Interpreted mode)
|
||||||
|
if Assigned(funcInfo.DynamicWrapper) then
|
||||||
|
raise EInvalidOpException.CreateFmt('Duplicate dynamic fallback defined for %s', [rtlName]);
|
||||||
|
|
||||||
|
var wrapperFactory :=
|
||||||
|
function(CodeAddress: Pointer): TDataValue.TFunc
|
||||||
|
begin
|
||||||
|
Result :=
|
||||||
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
|
begin
|
||||||
|
Result := TNativeDataValueFunc(CodeAddress)(Args);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
funcInfo.DynamicWrapper := wrapperFactory(method.CodeAddress);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
// Static wrapper (Compiled mode / Fast path)
|
||||||
|
if not ParseStaticSuffix(method.Name, argTypes, retType) then
|
||||||
|
begin
|
||||||
|
// Fallback to RTTI if name convention fails
|
||||||
|
SetLength(argTypes, Length(rttiParams));
|
||||||
|
for i := 0 to High(rttiParams) do
|
||||||
|
argTypes[i] := RttiTypeToStaticType(rttiParams[i].ParamType);
|
||||||
|
retType := RttiTypeToStaticType(method.ReturnType);
|
||||||
|
end;
|
||||||
|
|
||||||
|
var sig := TMethodSignature.Create(argTypes, retType);
|
||||||
|
funcInfo.Signatures.Add(sig);
|
||||||
|
|
||||||
|
var wrapper := CreateStaticWrapper(method, retType, argTypes, rttiParams);
|
||||||
|
if Assigned(wrapper) then
|
||||||
|
begin
|
||||||
|
methodRecord := TSpecializedMethod.Create(wrapper, retType, exportAttr.IsPure);
|
||||||
|
RegisterStaticSpecialization(rtlName, argTypes, methodRecord);
|
||||||
|
|
||||||
|
// Use static wrapper as fallback for interpreter if no dynamic wrapper exists
|
||||||
|
if (not Assigned(funcInfo.DynamicWrapper)) and (Length(argTypes) = 1) and (argTypes[0].Kind = stUnknown) then
|
||||||
|
funcInfo.DynamicWrapper := wrapper;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise ENotImplemented.CreateFmt('Wrapper for %s not implemented', [method.Name]);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
ctx.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
class function TRtlRegistry.TValueToDataValue(const V: TValue): TDataValue;
|
class function TRtlRegistry.TValueToDataValue(const V: TValue): TDataValue;
|
||||||
begin
|
begin
|
||||||
@@ -310,299 +399,14 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
class constructor TRtlRegistry.Create;
|
// --- Wrapper Implementations ---
|
||||||
var
|
|
||||||
ctx: TRttiContext;
|
|
||||||
rtlType: TRttiType;
|
|
||||||
method: TRttiMethod;
|
|
||||||
attribute: TCustomAttribute;
|
|
||||||
exportAttr: TRtlExportAttribute;
|
|
||||||
constAttr: TRtlConstAttribute;
|
|
||||||
rtlName: string;
|
|
||||||
argTypes: TArray<IStaticType>;
|
|
||||||
retType: IStaticType;
|
|
||||||
methodRecord: TSpecializedMethod;
|
|
||||||
funcInfo: TRtlFunctionInfo;
|
|
||||||
constInfo: TRtlConstantInfo;
|
|
||||||
rttiParams: TArray<TRttiParameter>;
|
|
||||||
isDynamic: Boolean;
|
|
||||||
i: Integer;
|
|
||||||
docString: string;
|
|
||||||
begin
|
|
||||||
// 1. Create global caches
|
|
||||||
FStaticBootstrap := TStaticBootstrapCache.Create(TStaticSignatureKeyComparer.Create);
|
|
||||||
FStaticFuncMap := TRtlFunctionMap.Create;
|
|
||||||
FStaticConstList := TList<TRtlConstantInfo>.Create;
|
|
||||||
|
|
||||||
// 2. Perform RTTI Scan
|
|
||||||
ctx := TRttiContext.Create;
|
|
||||||
try
|
|
||||||
rtlType := ctx.GetType(TypeInfo(TRtlFunctions));
|
|
||||||
|
|
||||||
// --- SCAN METHODS ---
|
|
||||||
// Includes Class Functions used as Constants!
|
|
||||||
for method in rtlType.GetMethods do
|
|
||||||
begin
|
|
||||||
if method.MethodKind <> mkClassFunction then
|
|
||||||
continue;
|
|
||||||
|
|
||||||
exportAttr := nil;
|
|
||||||
constAttr := nil;
|
|
||||||
docString := '';
|
|
||||||
|
|
||||||
for attribute in method.GetAttributes do
|
|
||||||
begin
|
|
||||||
if attribute is TRtlExportAttribute then
|
|
||||||
exportAttr := attribute as TRtlExportAttribute
|
|
||||||
else if attribute is TRtlConstAttribute then
|
|
||||||
constAttr := attribute as TRtlConstAttribute
|
|
||||||
else if attribute is AstDocAttribute then
|
|
||||||
docString := AstDocAttribute(attribute).Description;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// --- CASE A: CONSTANT (via class function) ---
|
|
||||||
if Assigned(constAttr) then
|
|
||||||
begin
|
|
||||||
// Since this is a static method (class function), we can invoke it with nil instance
|
|
||||||
// to get the value immediately.
|
|
||||||
var val: TValue := method.Invoke(nil, []);
|
|
||||||
var dataVal: TDataValue := TValueToDataValue(val);
|
|
||||||
var staticType: IStaticType := RttiTypeToStaticType(method.ReturnType);
|
|
||||||
|
|
||||||
constInfo := TRtlConstantInfo.Create(constAttr.Name, dataVal, staticType, docString);
|
|
||||||
FStaticConstList.Add(constInfo);
|
|
||||||
continue; // Done with this method
|
|
||||||
end;
|
|
||||||
|
|
||||||
// --- CASE B: FUNCTION ---
|
|
||||||
if not Assigned(exportAttr) then
|
|
||||||
continue;
|
|
||||||
|
|
||||||
rtlName := exportAttr.Name;
|
|
||||||
|
|
||||||
if not FStaticFuncMap.TryGetValue(rtlName, funcInfo) then
|
|
||||||
begin
|
|
||||||
funcInfo := TRtlFunctionInfo.Create;
|
|
||||||
FStaticFuncMap.Add(rtlName, funcInfo);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Update documentation (if not already set or override)
|
|
||||||
if (funcInfo.Doc = '') and (docString <> '') then
|
|
||||||
funcInfo.Doc := docString;
|
|
||||||
|
|
||||||
// Check Signature Type (Dynamic vs Static)
|
|
||||||
rttiParams := method.GetParameters;
|
|
||||||
isDynamic :=
|
|
||||||
(Length(rttiParams) = 1)
|
|
||||||
and (method.ReturnType.Handle = TypeInfo(TDataValue))
|
|
||||||
and (rttiParams[0].ParamType.Handle = TypeInfo(TArray<TDataValue>));
|
|
||||||
|
|
||||||
if isDynamic then
|
|
||||||
begin
|
|
||||||
if Assigned(funcInfo.DynamicWrapper) then
|
|
||||||
raise EInvalidOpException.CreateFmt('Duplicate dynamic fallback defined for %s', [rtlName]);
|
|
||||||
|
|
||||||
var wrapperFactory :=
|
|
||||||
function(CodeAddress: Pointer): TDataValue.TFunc
|
|
||||||
begin
|
|
||||||
Result :=
|
|
||||||
function(const Args: TArray<TDataValue>): TDataValue
|
|
||||||
begin
|
|
||||||
Result := TNativeDataValueFunc(CodeAddress)(Args);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
funcInfo.DynamicWrapper := wrapperFactory(method.CodeAddress);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
// This is a STATIC SPECIALIZATION
|
|
||||||
if not ParseStaticSuffix(method.Name, argTypes, retType) then
|
|
||||||
begin
|
|
||||||
SetLength(argTypes, Length(rttiParams));
|
|
||||||
for i := 0 to High(rttiParams) do
|
|
||||||
argTypes[i] := RttiTypeToStaticType(rttiParams[i].ParamType);
|
|
||||||
retType := RttiTypeToStaticType(method.ReturnType);
|
|
||||||
end;
|
|
||||||
|
|
||||||
var sig := TMethodSignature.Create(argTypes, retType);
|
|
||||||
funcInfo.Signatures.Add(sig);
|
|
||||||
|
|
||||||
var wrapper := CreateStaticWrapper(method, retType, argTypes, rttiParams);
|
|
||||||
if Assigned(wrapper) then
|
|
||||||
begin
|
|
||||||
methodRecord := TSpecializedMethod.Create(wrapper, retType, exportAttr.IsPure);
|
|
||||||
RegisterStaticSpecialization(rtlName, argTypes, methodRecord);
|
|
||||||
|
|
||||||
// If this is a generic wrapper (e.g. Abs(TScalar)) and no DynamicWrapper exists yet,
|
|
||||||
// use this wrapper as the interpreter fallback!
|
|
||||||
if (not Assigned(funcInfo.DynamicWrapper)) and (Length(argTypes) = 1) and (argTypes[0].Kind = stUnknown) then
|
|
||||||
begin
|
|
||||||
funcInfo.DynamicWrapper := wrapper;
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise ENotImplemented.CreateFmt('Static native method wrapper for %s() not implemented', [method.Name]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
finally
|
|
||||||
ctx.Free;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class destructor TRtlRegistry.Destroy;
|
|
||||||
var
|
|
||||||
funcInfo: TRtlFunctionInfo;
|
|
||||||
begin
|
|
||||||
for funcInfo in FStaticFuncMap.Values do
|
|
||||||
funcInfo.Free;
|
|
||||||
FStaticFuncMap.Free;
|
|
||||||
|
|
||||||
FStaticConstList.Free;
|
|
||||||
FStaticBootstrap.Free;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class function TRtlRegistry.GetStaticSpecialization(const AName: string; const AArgTypes: TArray<IStaticType>): TSpecializedMethod;
|
|
||||||
var
|
|
||||||
key: TStaticSignatureKey;
|
|
||||||
begin
|
|
||||||
key.Name := AName;
|
|
||||||
key.ArgTypes := AArgTypes;
|
|
||||||
FStaticBootstrap.TryGetValue(key, Result);
|
|
||||||
end;
|
|
||||||
|
|
||||||
class procedure TRtlRegistry.RegisterStaticSpecialization(
|
|
||||||
const AName: string;
|
|
||||||
const AArgTypes: TArray<IStaticType>;
|
|
||||||
const AMethod: TSpecializedMethod
|
|
||||||
);
|
|
||||||
var
|
|
||||||
key: TStaticSignatureKey;
|
|
||||||
begin
|
|
||||||
key := TStaticSignatureKey.Create(AName, AArgTypes);
|
|
||||||
FStaticBootstrap.AddOrSetValue(key, AMethod);
|
|
||||||
end;
|
|
||||||
|
|
||||||
class function TRtlRegistry.RttiTypeToStaticType(const AType: TRttiType): IStaticType;
|
|
||||||
begin
|
|
||||||
if not Assigned(AType) then
|
|
||||||
exit(TTypes.Unknown);
|
|
||||||
|
|
||||||
if AType.Handle = TypeInfo(Int64) then
|
|
||||||
Result := TTypes.Ordinal
|
|
||||||
else if AType.Handle = TypeInfo(Double) then
|
|
||||||
Result := TTypes.Float
|
|
||||||
else if AType.Handle = TypeInfo(string) then
|
|
||||||
Result := TTypes.Text
|
|
||||||
else if (AType.Handle = TypeInfo(TScalar)) or (AType.Handle = TypeInfo(TDataValue)) then
|
|
||||||
Result := TTypes.Unknown
|
|
||||||
else
|
|
||||||
Result := TTypes.Unknown;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class function TRtlRegistry.ParseStaticSuffix(
|
|
||||||
const AMethodName: string;
|
|
||||||
out AArgTypes: TArray<IStaticType>;
|
|
||||||
out AReturnType: IStaticType
|
|
||||||
): Boolean;
|
|
||||||
var
|
|
||||||
parts: TArray<string>;
|
|
||||||
i: Integer;
|
|
||||||
t: IStaticType;
|
|
||||||
|
|
||||||
function ParseType(const S: string): IStaticType;
|
|
||||||
begin
|
|
||||||
if SameText(S, 'Ordinal') then
|
|
||||||
Result := TTypes.Ordinal
|
|
||||||
else if SameText(S, 'Float') then
|
|
||||||
Result := TTypes.Float
|
|
||||||
else if SameText(S, 'Keyword') then
|
|
||||||
Result := TTypes.Keyword
|
|
||||||
else if SameText(S, 'Boolean') then
|
|
||||||
Result := TTypes.Boolean
|
|
||||||
else if SameText(S, 'DateTime') then
|
|
||||||
Result := TTypes.DateTime
|
|
||||||
else
|
|
||||||
Result := TTypes.Unknown;
|
|
||||||
end;
|
|
||||||
|
|
||||||
begin
|
|
||||||
Result := False;
|
|
||||||
AReturnType := TTypes.Unknown;
|
|
||||||
AArgTypes := nil;
|
|
||||||
|
|
||||||
parts := AMethodName.Split(['_']);
|
|
||||||
|
|
||||||
if Length(parts) < 2 then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
AReturnType := ParseType(parts[High(parts)]);
|
|
||||||
if AReturnType.Kind = stUnknown then
|
|
||||||
exit;
|
|
||||||
|
|
||||||
SetLength(AArgTypes, Length(parts) - 2);
|
|
||||||
if Length(AArgTypes) = 0 then
|
|
||||||
begin
|
|
||||||
Result := True;
|
|
||||||
exit;
|
|
||||||
end;
|
|
||||||
|
|
||||||
for i := 1 to High(parts) - 1 do
|
|
||||||
begin
|
|
||||||
t := ParseType(parts[i]);
|
|
||||||
if t.Kind = stUnknown then
|
|
||||||
begin
|
|
||||||
AArgTypes := nil;
|
|
||||||
AReturnType := TTypes.Unknown;
|
|
||||||
exit(False);
|
|
||||||
end;
|
|
||||||
AArgTypes[i - 1] := t;
|
|
||||||
end;
|
|
||||||
|
|
||||||
Result := True;
|
|
||||||
end;
|
|
||||||
|
|
||||||
class procedure TRtlRegistry.RegisterAll(const AScope: IExecutionScope);
|
|
||||||
var
|
|
||||||
rtlName: string;
|
|
||||||
funcInfo: TRtlFunctionInfo;
|
|
||||||
constInfo: TRtlConstantInfo;
|
|
||||||
staticType: IStaticType;
|
|
||||||
pair: TPair<string, TRtlFunctionInfo>;
|
|
||||||
begin
|
|
||||||
// Register Functions
|
|
||||||
for pair in FStaticFuncMap do
|
|
||||||
begin
|
|
||||||
rtlName := pair.Key;
|
|
||||||
funcInfo := pair.Value;
|
|
||||||
|
|
||||||
if funcInfo.Signatures.Count > 0 then
|
|
||||||
staticType := TTypes.CreateMethodSet(funcInfo.Signatures.ToArray)
|
|
||||||
else
|
|
||||||
staticType := nil;
|
|
||||||
|
|
||||||
var wrapper := funcInfo.DynamicWrapper; // Default is Void
|
|
||||||
|
|
||||||
// Pass documentation to scope definition
|
|
||||||
AScope.Define(rtlName, wrapper, staticType, funcInfo.Doc);
|
|
||||||
end;
|
|
||||||
|
|
||||||
// Register Constants
|
|
||||||
for constInfo in FStaticConstList do
|
|
||||||
begin
|
|
||||||
AScope.Define(constInfo.Name, constInfo.Value, constInfo.StaticType, constInfo.Doc);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
// --- Wrapper Generation ---
|
|
||||||
|
|
||||||
class function TRtlRegistry.CreateWrapper_O_O(CodeAddress: Pointer): TDataValue.TFunc;
|
class function TRtlRegistry.CreateWrapper_O_O(CodeAddress: Pointer): TDataValue.TFunc;
|
||||||
begin
|
begin
|
||||||
Result :=
|
Result :=
|
||||||
function(const Args: TArray<TDataValue>): TDataValue
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
var
|
var
|
||||||
A: Int64;
|
A, Res: Int64;
|
||||||
Res: Int64;
|
|
||||||
begin
|
begin
|
||||||
A := Args[0].AsScalar.Value.AsInt64;
|
A := Args[0].AsScalar.Value.AsInt64;
|
||||||
Res := TNativeFunc_O_O(CodeAddress)(A);
|
Res := TNativeFunc_O_O(CodeAddress)(A);
|
||||||
@@ -615,8 +419,7 @@ begin
|
|||||||
Result :=
|
Result :=
|
||||||
function(const Args: TArray<TDataValue>): TDataValue
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
var
|
var
|
||||||
A: Double;
|
A, Res: Double;
|
||||||
Res: Double;
|
|
||||||
begin
|
begin
|
||||||
A := Args[0].AsScalar.Value.AsDouble;
|
A := Args[0].AsScalar.Value.AsDouble;
|
||||||
Res := TNativeFunc_F_F(CodeAddress)(A);
|
Res := TNativeFunc_F_F(CodeAddress)(A);
|
||||||
@@ -728,8 +531,7 @@ begin
|
|||||||
function(const Args: TArray<TDataValue>): TDataValue
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
var
|
var
|
||||||
A: Int64;
|
A: Int64;
|
||||||
B: Double;
|
B, Res: Double;
|
||||||
Res: Double;
|
|
||||||
begin
|
begin
|
||||||
A := Args[0].AsScalar.Value.AsInt64;
|
A := Args[0].AsScalar.Value.AsInt64;
|
||||||
B := Args[1].AsScalar.Value.AsDouble;
|
B := Args[1].AsScalar.Value.AsDouble;
|
||||||
@@ -791,8 +593,7 @@ begin
|
|||||||
Result :=
|
Result :=
|
||||||
function(const Args: TArray<TDataValue>): TDataValue
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
var
|
var
|
||||||
A: String;
|
A, B: String;
|
||||||
B: String;
|
|
||||||
begin
|
begin
|
||||||
A := Args[0].AsText;
|
A := Args[0].AsText;
|
||||||
B := Args[1].AsText;
|
B := Args[1].AsText;
|
||||||
@@ -800,6 +601,77 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
class function TRtlRegistry.RttiTypeToStaticType(const AType: TRttiType): IStaticType;
|
||||||
|
begin
|
||||||
|
if not Assigned(AType) then
|
||||||
|
exit(TTypes.Unknown);
|
||||||
|
if AType.Handle = TypeInfo(Int64) then
|
||||||
|
Result := TTypes.Ordinal
|
||||||
|
else if AType.Handle = TypeInfo(Double) then
|
||||||
|
Result := TTypes.Float
|
||||||
|
else if AType.Handle = TypeInfo(string) then
|
||||||
|
Result := TTypes.Text
|
||||||
|
else
|
||||||
|
Result := TTypes.Unknown;
|
||||||
|
end;
|
||||||
|
|
||||||
|
class function TRtlRegistry.ParseStaticSuffix(
|
||||||
|
const AMethodName: string;
|
||||||
|
out AArgTypes: TArray<IStaticType>;
|
||||||
|
out AReturnType: IStaticType
|
||||||
|
): Boolean;
|
||||||
|
var
|
||||||
|
parts: TArray<string>;
|
||||||
|
i: Integer;
|
||||||
|
|
||||||
|
function ParseType(const S: string): IStaticType;
|
||||||
|
begin
|
||||||
|
if SameText(S, 'Ordinal') then
|
||||||
|
Result := TTypes.Ordinal
|
||||||
|
else if SameText(S, 'Float') then
|
||||||
|
Result := TTypes.Float
|
||||||
|
else if SameText(S, 'Keyword') then
|
||||||
|
Result := TTypes.Keyword
|
||||||
|
else if SameText(S, 'Boolean') then
|
||||||
|
Result := TTypes.Boolean
|
||||||
|
else if SameText(S, 'DateTime') then
|
||||||
|
Result := TTypes.DateTime
|
||||||
|
else
|
||||||
|
Result := TTypes.Unknown;
|
||||||
|
end;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
AReturnType := TTypes.Unknown;
|
||||||
|
AArgTypes := nil;
|
||||||
|
parts := AMethodName.Split(['_']);
|
||||||
|
|
||||||
|
// Format: Name_Arg1_Arg2_Return
|
||||||
|
if Length(parts) < 2 then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
AReturnType := ParseType(parts[High(parts)]);
|
||||||
|
if AReturnType.Kind = stUnknown then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
SetLength(AArgTypes, Length(parts) - 2);
|
||||||
|
if Length(AArgTypes) = 0 then
|
||||||
|
begin
|
||||||
|
Result := True;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
for i := 1 to High(parts) - 1 do
|
||||||
|
begin
|
||||||
|
AArgTypes[i - 1] := ParseType(parts[i]);
|
||||||
|
if AArgTypes[i - 1].Kind = stUnknown then
|
||||||
|
begin
|
||||||
|
AArgTypes := nil;
|
||||||
|
exit(False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
Result := True;
|
||||||
|
end;
|
||||||
|
|
||||||
class function TRtlRegistry.CreateStaticWrapper(
|
class function TRtlRegistry.CreateStaticWrapper(
|
||||||
method: TRttiMethod;
|
method: TRttiMethod;
|
||||||
retType: IStaticType;
|
retType: IStaticType;
|
||||||
@@ -814,13 +686,9 @@ begin
|
|||||||
|
|
||||||
case Length(argTypes) of
|
case Length(argTypes) of
|
||||||
0:
|
0:
|
||||||
begin
|
|
||||||
if retType.Kind = stFloat then
|
if retType.Kind = stFloat then
|
||||||
Result := CreateWrapper_V_F(ptr);
|
Result := CreateWrapper_V_F(ptr);
|
||||||
end;
|
|
||||||
|
|
||||||
1:
|
1:
|
||||||
begin
|
|
||||||
if (argTypes[0].Kind = stOrdinal) and (retType.Kind = stOrdinal) then
|
if (argTypes[0].Kind = stOrdinal) and (retType.Kind = stOrdinal) then
|
||||||
Result := CreateWrapper_O_O(ptr)
|
Result := CreateWrapper_O_O(ptr)
|
||||||
else if (argTypes[0].Kind = stFloat) and (retType.Kind = stFloat) then
|
else if (argTypes[0].Kind = stFloat) and (retType.Kind = stFloat) then
|
||||||
@@ -831,25 +699,19 @@ begin
|
|||||||
Result := CreateWrapper_O_F(ptr)
|
Result := CreateWrapper_O_F(ptr)
|
||||||
else if (argTypes[0].Kind = stUnknown) and (rttiParams[0].ParamType.Handle = TypeInfo(TScalar)) then
|
else if (argTypes[0].Kind = stUnknown) and (rttiParams[0].ParamType.Handle = TypeInfo(TScalar)) then
|
||||||
begin
|
begin
|
||||||
// This is the wrapper for: class function(Arg: TScalar): TScalar;
|
|
||||||
var wrapperFactory :=
|
var wrapperFactory :=
|
||||||
function(CodeAddress: Pointer): TDataValue.TFunc
|
function(CodeAddress: Pointer): TDataValue.TFunc
|
||||||
begin
|
begin
|
||||||
Result :=
|
Result :=
|
||||||
function(const Args: TArray<TDataValue>): TDataValue
|
function(const Args: TArray<TDataValue>): TDataValue
|
||||||
var
|
|
||||||
argScalar: TScalar;
|
|
||||||
begin
|
begin
|
||||||
if (Length(Args) <> 1) or (Args[0].Kind <> vkScalar) then
|
if (Length(Args) <> 1) or (Args[0].Kind <> vkScalar) then
|
||||||
raise EArgumentException.Create('Invalid argument for TScalar function.');
|
raise EArgumentException.Create('Invalid argument');
|
||||||
argScalar := Args[0].AsScalar;
|
Result := TDataValue(TNativeScalarFunc(CodeAddress)(Args[0].AsScalar));
|
||||||
Result := TDataValue(TNativeScalarFunc(CodeAddress)(argScalar));
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
Result := wrapperFactory(ptr);
|
Result := wrapperFactory(ptr);
|
||||||
end;
|
end;
|
||||||
end;
|
|
||||||
|
|
||||||
2:
|
2:
|
||||||
begin
|
begin
|
||||||
var k1 := argTypes[0].Kind;
|
var k1 := argTypes[0].Kind;
|
||||||
@@ -861,54 +723,96 @@ begin
|
|||||||
if rk = stOrdinal then
|
if rk = stOrdinal then
|
||||||
Result := CreateWrapper_OO_O(ptr)
|
Result := CreateWrapper_OO_O(ptr)
|
||||||
else if rk = stFloat then
|
else if rk = stFloat then
|
||||||
Result := CreateWrapper_OO_F(ptr); // Divide
|
Result := CreateWrapper_OO_F(ptr);
|
||||||
end
|
end
|
||||||
else if (k1 = stFloat) and (k2 = stFloat) then
|
else if (k1 = stFloat) and (k2 = stFloat) then
|
||||||
begin
|
begin
|
||||||
if rk = stFloat then
|
if rk = stFloat then
|
||||||
Result := CreateWrapper_FF_F(ptr)
|
Result := CreateWrapper_FF_F(ptr)
|
||||||
else if rk = stOrdinal then
|
else if rk = stOrdinal then
|
||||||
Result := CreateWrapper_FF_O(ptr); // Comparisons
|
Result := CreateWrapper_FF_O(ptr);
|
||||||
end
|
end
|
||||||
else if (k1 = stOrdinal) and (k2 = stFloat) then
|
else if (k1 = stOrdinal) and (k2 = stFloat) then
|
||||||
begin
|
begin
|
||||||
if rk = stFloat then
|
if rk = stFloat then
|
||||||
Result := CreateWrapper_OF_F(ptr)
|
Result := CreateWrapper_OF_F(ptr)
|
||||||
else if rk = stOrdinal then
|
else if rk = stOrdinal then
|
||||||
Result := CreateWrapper_OF_O(ptr); // Comparisons
|
Result := CreateWrapper_OF_O(ptr);
|
||||||
end
|
end
|
||||||
else if (k1 = stFloat) and (k2 = stOrdinal) then
|
else if (k1 = stFloat) and (k2 = stOrdinal) then
|
||||||
begin
|
begin
|
||||||
if rk = stFloat then
|
if rk = stFloat then
|
||||||
Result := CreateWrapper_FO_F(ptr)
|
Result := CreateWrapper_FO_F(ptr)
|
||||||
else if rk = stOrdinal then
|
else if rk = stOrdinal then
|
||||||
Result := CreateWrapper_FO_O(ptr); // Comparisons
|
Result := CreateWrapper_FO_O(ptr);
|
||||||
end
|
end
|
||||||
else if (k1 = stKeyword) and (k2 = stKeyword) and (rk = stOrdinal) then
|
else if (k1 = stKeyword) and (k2 = stKeyword) and (rk = stOrdinal) then
|
||||||
begin
|
Result := CreateWrapper_OO_O(ptr)
|
||||||
// Keywords are passed as Int64 (index)
|
|
||||||
Result := CreateWrapper_OO_O(ptr); // e.g. Equal_Keyword_Keyword
|
|
||||||
end
|
|
||||||
else if (k1 = stText) and (k2 = stText) then
|
else if (k1 = stText) and (k2 = stText) then
|
||||||
begin
|
Result := CreateWrapper_TT_T(ptr);
|
||||||
Result := CreateWrapper_TT_T(ptr); // e.g. String concat
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
else
|
|
||||||
Assert(false, 'Wrapper not implemented');
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure RegisterRtlFunctions(const AScope: IExecutionScope);
|
class function TRtlRegistry.GetStaticSpecialization(const AName: string; const AArgTypes: TArray<IStaticType>): TSpecializedMethod;
|
||||||
|
var
|
||||||
|
key: TStaticSignatureKey;
|
||||||
|
begin
|
||||||
|
key.Name := AName;
|
||||||
|
key.ArgTypes := AArgTypes;
|
||||||
|
FStaticBootstrap.TryGetValue(key, Result);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class procedure TRtlRegistry.RegisterStaticSpecialization(
|
||||||
|
const AName: string;
|
||||||
|
const AArgTypes: TArray<IStaticType>;
|
||||||
|
const AMethod: TSpecializedMethod
|
||||||
|
);
|
||||||
|
var
|
||||||
|
key: TStaticSignatureKey;
|
||||||
|
begin
|
||||||
|
key := TStaticSignatureKey.Create(AName, AArgTypes);
|
||||||
|
FStaticBootstrap.AddOrSetValue(key, AMethod);
|
||||||
|
end;
|
||||||
|
|
||||||
|
class procedure TRtlRegistry.RegisterAll(const AScope: IExecutionScope);
|
||||||
|
var
|
||||||
|
rtlName: string;
|
||||||
|
funcInfo: TRtlFunctionInfo;
|
||||||
|
constInfo: TRtlConstantInfo;
|
||||||
|
staticType: IStaticType;
|
||||||
|
pair: TPair<string, TRtlFunctionInfo>;
|
||||||
begin
|
begin
|
||||||
AScope.Define('true', TDataValue(TScalar.FromBoolean(True)), TTypes.Boolean, 'Boolean true.');
|
AScope.Define('true', TDataValue(TScalar.FromBoolean(True)), TTypes.Boolean, 'Boolean true.');
|
||||||
AScope.Define('false', TDataValue(TScalar.FromBoolean(False)), TTypes.Boolean, 'Boolean false.');
|
AScope.Define('false', TDataValue(TScalar.FromBoolean(False)), TTypes.Boolean, 'Boolean false.');
|
||||||
|
|
||||||
|
for pair in FStaticFuncMap do
|
||||||
|
begin
|
||||||
|
rtlName := pair.Key;
|
||||||
|
funcInfo := pair.Value;
|
||||||
|
if funcInfo.Signatures.Count > 0 then
|
||||||
|
staticType := TTypes.CreateMethodSet(funcInfo.Signatures.ToArray)
|
||||||
|
else
|
||||||
|
staticType := nil;
|
||||||
|
|
||||||
|
AScope.Define(rtlName, funcInfo.DynamicWrapper, staticType, funcInfo.Doc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
for constInfo in FStaticConstList do
|
||||||
|
AScope.Define(constInfo.Name, constInfo.Value, constInfo.StaticType, constInfo.Doc);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure RegisterRtlFunctions(const AScope: IExecutionScope);
|
||||||
|
begin
|
||||||
TRtlRegistry.RegisterAll(AScope);
|
TRtlRegistry.RegisterAll(AScope);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
initialization
|
initialization
|
||||||
// Register this library's functions with the central AST factory.
|
TRtlRegistry.Scan<TRtlCoreFunctions>;
|
||||||
|
TRtlRegistry.Scan<TRtlMathFunctions>;
|
||||||
|
TRtlRegistry.Scan<TRtlDateTimeFunctions>;
|
||||||
|
TRtlRegistry.Scan<TRtlSeriesFunctions>;
|
||||||
|
|
||||||
TAst.RegisterLibrary(RegisterRtlFunctions);
|
TAst.RegisterLibrary(RegisterRtlFunctions);
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|||||||
Reference in New Issue
Block a user