unit ASObjStore;
{$WARN SYMBOL_PLATFORM OFF}
{$WARN DUPLICATE_CTOR_DTOR OFF}
interface
uses
SysUtils, Types, Classes, Graphics, Generics.Defaults,
Generics.Collections, ASNum, ASPixmap, ASTable, ASObjects, ASStructs,
ASKernelDefs, GenHelpers;
type
TVariableMetadata = record
Name: string;
Description: string;
Time: TDateTime;
Attributes: TIdentAttribs;
end;
TAlgosimVariable = class
strict private
FName: string;
FDescription: string;
FTime: TDateTime;
FObject: TAlgosimObject;
FAttributes: TIdentAttribs;
procedure SetValue(const Value: TAlgosimObject);
procedure SetDescription(const Value: string);
procedure SetName(const Value: string);
function GetValue: TAlgosimObject; inline;
function GetProtected: Boolean; inline;
procedure SetProtected(AProtected: Boolean); inline;
function GetSystem: Boolean; inline;
procedure SetSystem(ASystem: Boolean); inline;
private
procedure ProtectionError;
public
constructor Create; overload;
constructor Create(AObject: TAlgosimObject;
const AName: string; const ADescription: string = '';
AProtected: Boolean = False; ASystem: Boolean = False); overload;
destructor Destroy; override;
property Name: string read FName write SetName;
property Description: string read FDescription write SetDescription;
property Time: TDateTime read FTime;
property Value: TAlgosimObject read GetValue write SetValue;
property ObjRef: TAlgosimObject read FObject;
property Attributes: TIdentAttribs read FAttributes write FAttributes;
property IsProtected: Boolean read GetProtected write SetProtected;
property IsSystem: Boolean read GetSystem write SetSystem;
function CreateStructure: TAlgosimStructure;
function CreateMetadataStructure: TAlgosimStructure;
function Metadata: TVariableMetadata;
function Extract: TAlgosimObject;
end;
TLValuePathItem = TSubscript;
TLValueData = class(TList<TLValuePathItem>)
end;
TAlgosimObjStore = class
private
Objs: TObjectDictionary<string, TAlgosimVariable>;
public
function TryGetVariable(const AName: string;
out AVariable: TAlgosimVariable): Boolean;
procedure SetVariable(const AName: string;
AValue: TAlgosimObject; const ADescription: string = '';
AProtected: Boolean = False);
procedure ForceSetVariable(const AName: string;
AValue: TAlgosimObject; const ADescription: string = '';
AProtected: Boolean = False; ASystem: Boolean = False);
function GetVariableList: TArray<string>;
procedure Clear;
constructor Create; virtual;
destructor Destroy; override;
end;
TStoreStackRes = record
Store: TAlgosimObjStore;
Success: Boolean;
procedure Clear;
end;
TObjStoreStack = class
strict private
FStores: TList<TAlgosimObjStore>;
function GetStore(Index: Integer): TAlgosimObjStore; inline;
function GetCount: Integer; inline;
function GetObjRefOrValue(const ALValueData: TLValueData;
out AValue: TAlgosimObject; AAsValue: Boolean): TStoreStackRes;
public
constructor Create; overload; virtual;
constructor Create(const AStores: array of TAlgosimObjStore); overload; virtual;
function Push(AStore: TAlgosimObjStore): Integer;
function Pop: TAlgosimObjStore;
function Top: TAlgosimObjStore; inline;
function Bottom: TAlgosimObjStore; inline;
procedure Clear;
destructor Destroy; override;
function TryGetVariable(const AName: string;
out AVariable: TAlgosimVariable): TAlgosimObjStore;
function GetValue(const AName: string;
out AValue: TAlgosimObject): TAlgosimObjStore; overload;
function GetValue(const ALValueData: TLValueData;
out AValue: TAlgosimObject): TStoreStackRes; overload;
function GetObjRef(const AName: string;
out AValue: TAlgosimObject): TAlgosimObjStore; overload;
function GetObjRef(const ALValueData: TLValueData;
out AValue: TAlgosimObject): TStoreStackRes; overload;
function TryRemoveVariable(const AName: string): TAlgosimObjStore;
function SetVariable(const AName: string;
AValue: TAlgosimObject): TAlgosimObjStore; overload;
function SetVariable(const ALValueData: TLValueData;
AValue: TAlgosimObject): TAlgosimObjStore; overload;
function GetVariableList: TArray<string>;
property Store[Index: Integer]: TAlgosimObjStore read GetStore; default;
property Count: Integer read GetCount;
end;
implementation
uses
Math, StrUtils;
constructor TAlgosimVariable.Create;
begin
FTime := Now;
end;
constructor TAlgosimVariable.Create(AObject: TAlgosimObject;
const AName, ADescription: string; AProtected, ASystem: Boolean);
begin
Create;
FName := AName;
FDescription := ADescription;
FObject := AObject;
if AProtected then
Include(FAttributes, iaProtected);
if ASystem then
Include(FAttributes, iaSystem);
end;
function TAlgosimVariable.CreateStructure: TAlgosimStructure;
begin
Result := ASOVariable(FName, FDescription, FTime, FObject, IsProtected);
end;
function TAlgosimVariable.CreateMetadataStructure: TAlgosimStructure;
begin
Result := ASOVariableMetadata(FName, FDescription, FTime, IsProtected);
end;
destructor TAlgosimVariable.Destroy;
begin
FObject.Free;
inherited;
end;
function TAlgosimVariable.Extract: TAlgosimObject;
begin
TMover<TAlgosimObject>.Move(Result, FObject);
end;
function TAlgosimVariable.GetProtected: Boolean;
begin
Result := iaProtected in FAttributes;
end;
function TAlgosimVariable.GetSystem: Boolean;
begin
Result := iaSystem in FAttributes;
end;
function TAlgosimVariable.GetValue: TAlgosimObject;
begin
Result := FObject.Clone;
end;
function TAlgosimVariable.Metadata: TVariableMetadata;
begin
Result.Name := FName;
Result.Description := FDescription;
Result.Time := FTime;
Result.Attributes := FAttributes;
end;
procedure TAlgosimVariable.ProtectionError;
begin
raise EVariableProtectionException.CreateFmt(SVarProtectionError, [FName]);
end;
procedure TAlgosimVariable.SetDescription(const Value: string);
begin
if IsProtected then
ProtectionError;
FDescription := Value;
end;
procedure TAlgosimVariable.SetName(const Value: string);
begin
if IsProtected then
ProtectionError;
FName := Value;
end;
procedure TAlgosimVariable.SetProtected(AProtected: Boolean);
begin
if AProtected then
Include(FAttributes, iaProtected)
else
Exclude(FAttributes, iaProtected)
end;
procedure TAlgosimVariable.SetSystem(ASystem: Boolean);
begin
if ASystem then
Include(FAttributes, iaSystem)
else
Exclude(FAttributes, iaSystem)
end;
procedure TAlgosimVariable.SetValue(const Value: TAlgosimObject);
begin
if IsProtected then
begin
Value.Free;
ProtectionError;
Exit;
end;
FTime := Now;
FreeAndNil(FObject);
FObject := Value;
end;
procedure TAlgosimObjStore.Clear;
begin
Objs.Clear;
end;
constructor TAlgosimObjStore.Create;
begin
Objs := TObjectDictionary<string, TAlgosimVariable>.Create([doOwnsValues]);
end;
destructor TAlgosimObjStore.Destroy;
begin
Objs.Free;
inherited;
end;
function TAlgosimObjStore.GetVariableList: TArray<string>;
begin
Result := Objs.Keys.ToArray;
end;
procedure TAlgosimObjStore.SetVariable(const AName: string;
AValue: TAlgosimObject; const ADescription: string = '';
AProtected: Boolean = False);
var
Variable: TAlgosimVariable;
begin
if Objs.TryGetValue(AName, Variable) then
begin
Variable.Value := AValue;
Variable.Description := ADescription;
Variable.IsProtected := AProtected;
end
else
Objs.Add(AName, TAlgosimVariable.Create(AValue, AName, ADescription, AProtected));
end;
procedure TAlgosimObjStore.ForceSetVariable(const AName: string;
AValue: TAlgosimObject; const ADescription: string;
AProtected, ASystem: Boolean);
var
Variable: TAlgosimVariable;
begin
if Objs.TryGetValue(AName, Variable) then
begin
Variable.IsProtected := False;
Variable.Value := AValue;
Variable.Description := ADescription;
Variable.IsProtected := AProtected;
Variable.IsSystem := ASystem;
end
else
Objs.Add(AName, TAlgosimVariable.Create(AValue, AName, ADescription, AProtected, ASystem));
end;
function TAlgosimObjStore.TryGetVariable(const AName: string;
out AVariable: TAlgosimVariable): Boolean;
begin
Result := Objs.TryGetValue(AName, AVariable);
end;
constructor TObjStoreStack.Create;
begin
FStores := TList<TAlgosimObjStore>.Create;
end;
procedure TObjStoreStack.Clear;
begin
FStores.Clear;
end;
constructor TObjStoreStack.Create(const AStores: array of TAlgosimObjStore);
var
i: Integer;
begin
Create;
FStores.Capacity := Max(FStores.Capacity, Length(AStores));
for i := 0 to High(AStores) do
FStores.Add(AStores[i]);
end;
destructor TObjStoreStack.Destroy;
begin
FStores.Free;
inherited;
end;
function TObjStoreStack.GetCount: Integer;
begin
Result := FStores.Count;
end;
function TObjStoreStack.GetStore(Index: Integer): TAlgosimObjStore;
begin
Result := FStores[Index];
end;
function TObjStoreStack.Pop: TAlgosimObjStore;
begin
if FStores.Count > 0 then
begin
Result := FStores[FStores.Count - 1];
FStores.Count := FStores.Count - 1;
end
else
Result := nil;
end;
function TObjStoreStack.Push(AStore: TAlgosimObjStore): Integer;
begin
Result := FStores.Add(AStore);
end;
function TObjStoreStack.TryRemoveVariable(const AName: string): TAlgosimObjStore;
var
Variable: TAlgosimVariable;
begin
Result := TryGetVariable(AName, Variable);
if Assigned(Result) then
if Variable.IsProtected then
Variable.ProtectionError
else
Result.Objs.Remove(AName);
end;
function TObjStoreStack.SetVariable(const AName: string;
AValue: TAlgosimObject): TAlgosimObjStore;
var
Variable: TAlgosimVariable;
begin
Result := TryGetVariable(AName, Variable);
if Assigned(Result) then
Variable.Value := AValue
else
begin
try
Result := Top;
if Result = nil then
raise EVariableException.Create(SObjectStoreStackEmpty);
CheckIdentName(AName);
except
AValue.Free;
raise;
end;
Top.Objs.Add(AName, TAlgosimVariable.Create(AValue, AName));
end;
end;
function TObjStoreStack.SetVariable(const ALValueData: TLValueData;
AValue: TAlgosimObject): TAlgosimObjStore;
var
HighIndex: Integer;
BaseIdent: string;
Variable: TAlgosimVariable;
i: Integer;
ref: TAlgosimObject;
CurPath: string;
mbrname: string;
mbridx: Integer;
OwnsValue: Boolean;
begin
OwnsValue := True;
try
if ALValueData = nil then
raise EExpressionException.Create(SUnassignedLValueData);
if ALValueData.Count = 0 then
raise EExpressionException.Create(SEmptyLValueData);
HighIndex := ALValueData.Count - 1;
if (ALValueData[HighIndex].Kind <> skIdentifier) or ALValueData[HighIndex].Ident.IsEmpty then
raise EExpressionException.Create(SInvalidLValueDataRoot);
BaseIdent := ALValueData[HighIndex].Ident;
if ALValueData.Count = 1 then
begin
OwnsValue := False;
Exit(SetVariable(BaseIdent, AValue));
end;
Result := TryGetVariable(BaseIdent, Variable);
if Result = nil then
raise EUnknownIdentifier.CreateFmt(SUnknownIdentifier, [BaseIdent]);
if Variable.IsProtected then
Variable.ProtectionError;
ref := Variable.ObjRef;
CurPath := BaseIdent;
for i := HighIndex - 1 downto 0 do
if ALValueData[i].Kind = skIdentifier then
begin
if not (ref is TAlgosimStructure) then
raise EAlgosimObjectException.CreateFmt(SObjectNotAStructure, [CurPath]);
mbrname := ALValueData[i].Ident;
mbridx := TAlgosimStructure(ref).IndexOfName(mbrname);
if mbridx = -1 then
raise EStructureException.CreateFmt(SNamedStructNoMemberFound,
[CurPath, mbrname]);
if i = 0 then
begin
OwnsValue := False;
TAlgosimStructure(ref).Values[mbridx] := AValue;
Exit;
end;
ref := TAlgosimStructure(ref).Members[mbridx].Value;
CurPath := CurPath + ALValueData[i].ToString;
end
else
begin
if i = 0 then
begin
OwnsValue := False;
ref.SetSubscript(ALValueData[i], AValue);
Exit;
end;
ref := ref.GetSubscriptedRef(ALValueData[i]);
CurPath := CurPath + ALValueData[i].ToString;
end;
finally
if OwnsValue then AValue.Free;
end;
end;
function TObjStoreStack.Top: TAlgosimObjStore;
begin
if FStores.Count > 0 then
Result := FStores[FStores.Count - 1]
else
Result := nil;
end;
function TObjStoreStack.Bottom: TAlgosimObjStore;
begin
if FStores.Count > 0 then
Result := FStores[0]
else
Result := nil;
end;
function TObjStoreStack.GetObjRef(const AName: string;
out AValue: TAlgosimObject): TAlgosimObjStore;
var
i: Integer;
Variable: TAlgosimVariable;
begin
for i := FStores.Count - 1 downto 0 do
if FStores[i].Objs.TryGetValue(AName, Variable) then
begin
AValue := Variable.ObjRef;
Exit(FStores[i]);
end;
raise EUnknownIdentifier.CreateFmt(SUnknownIdentifier, [AName]);
end;
function TObjStoreStack.GetObjRef(const ALValueData: TLValueData;
out AValue: TAlgosimObject): TStoreStackRes;
begin
Result := GetObjRefOrValue(ALValueData, AValue, False);
end;
function TObjStoreStack.GetObjRefOrValue(const ALValueData: TLValueData;
out AValue: TAlgosimObject; AAsValue: Boolean): TStoreStackRes;
var
HighIndex: Integer;
BaseIdent: string;
Variable: TAlgosimVariable;
i: Integer;
ref: TAlgosimObject;
begin
Result.Clear;
if ALValueData = nil then
raise EExpressionException.Create(SUnassignedLValueData);
if ALValueData.Count = 0 then
raise EExpressionException.Create(SEmptyLValueData);
HighIndex := ALValueData.Count - 1;
if (ALValueData[HighIndex].Kind <> skIdentifier) or ALValueData[HighIndex].Ident.IsEmpty then
raise EExpressionException.Create(SInvalidLValueDataRoot);
BaseIdent := ALValueData[HighIndex].Ident;
Result.Store := TryGetVariable(BaseIdent, Variable);
if Result.Store = nil then
raise EUnknownIdentifier.CreateFmt(SUnknownIdentifier, [BaseIdent]);
if Variable.IsProtected and not AAsValue then
begin
Result.Success := False;
Exit;
end;
ref := Variable.ObjRef;
for i := HighIndex - 1 downto 0 do
begin
if (i = 0) and AAsValue then
begin
AValue := ref.GetSubscriptedValue(ALValueData[i]);
Result.Success := True;
Exit;
end;
Result.Success := ref.TryGetSubscriptedRef(ALValueData[i], ref);
if not Result.Success then
begin
AValue := nil;
Exit;
end;
end;
if AAsValue then
AValue := ref.Clone
else
AValue := ref;
Result.Success := True;
end;
function TObjStoreStack.GetValue(const AName: string;
out AValue: TAlgosimObject): TAlgosimObjStore;
begin
Result := GetObjRef(AName, AValue);
AValue := AValue.Clone;
end;
function TObjStoreStack.GetValue(const ALValueData: TLValueData;
out AValue: TAlgosimObject): TStoreStackRes;
begin
Result := GetObjRefOrValue(ALValueData, AValue, True);
end;
function TObjStoreStack.GetVariableList: TArray<string>;
var
D: TDictionary<string, Pointer>;
L: TList<string>;
S: string;
i: Integer;
begin
D := TDictionary<string, Pointer>.Create;
try
L := TList<string>.Create;
try
for i := FStores.Count - 1 downto 0 do
for s in FStores[i].GetVariableList do
if not D.ContainsKey(s) then
begin
L.Add(s);
D.Add(s, nil);
end;
Result := L.ToArray;
TArray.Sort<string>(Result);
finally
L.Free;
end;
finally
D.Free;
end;
end;
function TObjStoreStack.TryGetVariable(const AName: string;
out AVariable: TAlgosimVariable): TAlgosimObjStore;
var
i: Integer;
begin
for i := FStores.Count - 1 downto 0 do
if FStores[i].Objs.TryGetValue(AName, AVariable) then
Exit(FStores[i]);
Result := nil;
end;
procedure TStoreStackRes.Clear;
begin
Self.Store := nil;
Self.Success := False;
end;
end.