I mam pytanie odnośnie tego, czy mógłby mi ktoś poradzić jak zrobić obsługę zmiennych środowiskowych?
Pisane z palca:
Type TSymbolType = (stType, stFunction, stVariable, stConstant, ...);
Type TSymbol = Class // "fizyczny symbol"
Private
fType: TSymbolType;
fName: String;
fToken: PToken; // weź pod uwagę, że musi to być wskaźnik na token, a nie sam rekord/klasa
Public
Constructor TSymbol.Create(Typ: TSymbolType; Name: String; Token: PToken);
Property Name: String read fName; // zasadniczo nazwa symbolu (np.zmiennej/klasy/typu/wtf) nie powinna być modyfikowana, gdy już jest ustalona, stąd jest to read-only.
Property Token: PToken read fToken; // j/w
End;
Type TSymbolList = TList<TSymbol>; // lista symbolów (np.zadeklarowanych zmiennych), używana np.w funkcji:
(*
Type TFunction = Class(TSymbol)
Private
{ ... }
SymbolList: TSymbolList;
{ ... }
End;
*)
{ ... }
Type TVariable = Class;
{ ... }
Type TReadEvent = Function(const Variable: TVariable): Variant;
Type TWriteEvent = Procedure(const Variable: TVariable; const Value: Variant);
Type TVariable = Class(TSymbol) // pojedyncza zmienna
Private
fType: TVariableType; // bool/char/int/float/string i co tam jeszcze tylko masz
fRange: TRange; // blok, w którym zmienna jest widzialna (tyczy się wyłącznie zmiennych lokalnych, no, chyba, że masz klasy/przestrzenie nazw i/lub pozwalasz na bloki poza funkcjami (czyli np."begin..end" poza jakąś funkcją) :P)
fValue: Variant; // używane, gdy 'fOnRead' oraz 'fOnWrite' są równe 'nil', jest to właściwa wartość zmiennej
fOnRead: TReadEvent;
fOnWrite: TWriteEvent;
Function getValue: Variant;
Procedure setValue(const Value: Variant);
Public
Constructor Create(Name: String; Token: PToken; Typ: TVariableType; onRead: TReadEvent; onWrite: TWriteEvent);
Property Value: Variant read getValue write setValue;
Property Typ: TVariableType read fType;
End;
(* TSymbol.Create *)
Constructor TSymbol.Create(Typ: TSymbolType; Name: String; Token: PToken);
Begin
fType := Typ;
fName := Name;
fToken := Token;
End;
(* TVariable.Create *)
Constructor TVariable.Create(Name: String; Token: PToken; Typ: TVariableType; onRead: TReadEvent; onWrite: TWriteEvent);
Begin
inherited Create(stVariable, Name, Token);
fType := Typ;
fOnRead := onRead;
fOnWrite := onWrite;
End;
(* TVariable.getValue *)
Function TVariable.getValue: Variant;
Begin
if (fOnRead = nil) Then
Exit(fValue) Else
Exit(fOnRead(self));
End;
(* TVariable.setVaule *)
Procedure TVariable.setValue(const Value: Variant);
Begin
if (fOnWrite = nil) Then
fValue := Value Else
fOnWrite(self, Value);
End;
{ ... }
// EnvVarRead
Function EnvVarRead(const Variable: TVariable): Variant;
Begin
Result := GetEnvVarValue(Variable.Name); // patrz link do artykułu powyżej
End;
// EnvVarWrite
Procedure EnvVarWrite(const Variable: TVariable; const Value: Variant);
Begin
SetEnvVarValue(Variable.Name, Value); // patrz link do artykułu powyżej
End;
{ ... }
// FetchEnvironmentVariableList
Function FetchEnvironmentVariableList: TStringList;
Begin
Result := TStringList.Create;
// to sobie napisz na podstawie funkcji "GetAllEnvVars" z artykułu: http://www.delphidabbler.com/articles?article=6
End;
// BeforeParse
Procedure BeforeParse;
Var EnvList: TStringList;
Tmp: String;
Begin
// AddGlobalVar(DeclToken: PToken; VarName: String; onRead: TReadEvent; onWrite: TWriteEvent);
Try
EnvList := FetchEnvironmentList;
For Tmp in EnvList Do
AddGlobalVar(nil, Tmp, EnvVarRead, EnvVarWrite);
Finally
EnvList.Free;
End;
End;
Jakoś tak to wyglądałoby (oczywiście w uproszczeniu), nie wiem jak Twój parser wygląda od strony budowy, stąd tyle kodu :P
Bo z tego co wiem, to w Delphi nie można deklarować zmiennych dynamicznych.
Zależy, co masz na myśli. Są typy generyczne...
Btw, kompilatory są znacznie fajniejsze od parserów zarówno w pisaniu, jak i działaniu ;>