1 {
2 XpresElements
3 =============
4 Definiciones para el manejo de los elementos del compilador: funciones, constantes, variables.
5 Todos estos elementos se deberían almacenar en una estrucutura de arbol.
6 Por Tito Hinostroza.
7  }
8 unit XpresElements;
9 {$mode objfpc}{$H+}
10 interface
11 uses
12   Classes, SysUtils, fgl, XpresTypes;
13 
14 type
15   //Tipos de elementos del lenguaje
16   TxpElemType = (eltNone,  //sin tipo
17                  eltMain,  //programa principal
18                  eltVar,   //variable
19                  eltFunc,  //función
20                  eltCons,  //constante
21                  eltType   //tipo
22                 );
23   TFindFuncResult = (TFF_NONE, TFF_PARTIAL, TFF_FULL);
24 
25   TxpElement = class;
26   TxpElements = specialize TFPGObjectList<TxpElement>;
27 
28   { TxpElement }
29   //Clase base para todos los elementos
30   TxpElement = class
31   public
32   private
33 //    amb  : string;      //ámbito o alcance de la constante
34   public
35     name : string;      //nombre de la variable
36     typ  : TType;       //tipo del elemento, si aplica
37     Parent: TxpElement;    //referencia al padre
38     elemType: TxpElemType; //no debería ser necesario
39     Used: integer;      //veces que se usa este nombre
40     elements: TxpElements;  //referencia a nombres anidados, cuando sea función
AddElementnull41     function AddElement(elem: TxpElement): TxpElement;
DuplicateInnull42     function DuplicateIn(list: TObject): boolean; virtual;
FindIdxElemNamenull43     function FindIdxElemName(const eName: string; var idx0: integer): boolean;
44     constructor Create; virtual;
45     destructor Destroy; override;
46   end;
47 
48   TVarAddr = word;
49   TVarBank = byte;
50 
51   //Clase para modelar al bloque principal
52   { TxpEleMain }
53   TxpEleMain = class(TxpElement)
54     constructor Create; override;
55   end;
56 
57   { TxpEleType }
58   //Clase para modelar a los tipos definidos por el usuario
59   { Es diferente a XpresTypes: TType, aunque no se ha hecho un anaálisis profundo }
60   TxpEleType= class(TxpElement)
61     //valores de la constante
62     constructor Create; override;
63   end;
64   TxpEleTypes= specialize TFPGObjectList<TxpEleType>; //lista de variables
65 
66   { TxpEleCon }
67   //Clase para modelar a las constantes
68   TxpEleCon = class(TxpElement)
69     //valores de la constante
70     val : TConsValue;
71     constructor Create; override;
72   end;
73   TxpEleCons = specialize TFPGObjectList<TxpEleCon>; //lista de constantes
74 
75   { TxpEleVar }
76   //Clase para modelar a las variables
77   TxpEleVar = class(TxpElement)
78     //direción física. Usado para implementar un compilador
79     addr: TVarAddr;
80     bank: TVarBank;   //banco o segmento. Usado solo en algunas arquitecturas
81     //Campos usados para implementar el intérprete sin máquina virtual
82     //valores de la variable.
83     valInt  : Int64;    //valor en caso de que sea un entero
84     valUInt : Int64;    //valor en caso de que sea un entero sin signo
85     valFloat: extended; //Valor en caso de que sea un flotante
86     valBool  : Boolean;  //valor  en caso de que sea un booleano
87     valStr  : string;    //valor  en caso de que sea una cadena
88     constructor Create; override;
89   end;
90   TxpEleVars = specialize TFPGObjectList<TxpEleVar>; //lista de variables
91 
92   { TxpEleFun }
93   //Clase para almacenar información de las funciones
94   TxpEleFun = class;
95   TProcExecFunction = procedure(fun: TxpEleFun) of object;  //con índice de función
96   TxpEleFun = class(TxpElement)
97   public
98     pars: array of TType;  //parámetros de entrada
99   public
100     //direción física. Usado para implementar un compilador
101     adrr: integer;  //dirección física
102     //Campos usados para implementar el intérprete sin máquina virtual
103     proc: TProcExecFunction;  //referencia a la función que implementa
104     posF: TPoint;    //posición donde empieza la función en el código fuente
105     procedure ClearParams;
106     procedure CreateParam(parName: string; typ0: TType);
SameParamsnull107     function SameParams(Fun2: TxpEleFun): boolean;
ParamTypesListnull108     function ParamTypesList: string;
DuplicateInnull109     function DuplicateIn(list: TObject): boolean; override;
110     constructor Create; override;
111   end;
112   TxpEleFuns = specialize TFPGObjectList<TxpEleFun>;
113 
114   { TXpTreeElements }
115   {Árbol de elementos. Solo se espera que haya una instacia de este objeto. Aquí es
116   donde se guardará la referencia a todas los elementos (variables, constantes, ..)
117   creados.
118   Este árbol se usa también como un equivalente al NameSpace, porque se usa para
119   buscar los nombres de los elementos, en una estructura en arbol}
120   TXpTreeElements = class
121   private
122     curNode : TxpElement;  //referencia al nodo actual
123     vars    : TxpEleVars;
124     //variables de estado para la búsqueda con FindFirst() - FindNext()
125     curFindName: string;
126     curFindNode: TxpElement;
127     curFindIdx: integer;
128   public
129     main    : TxpEleMain;  //nodo raiz
130     procedure Clear;
AllVarsnull131     function AllVars: TxpEleVars;
CurNodeNamenull132     function CurNodeName: string;
133     //funciones para llenado del arbol
AddElementnull134     function AddElement(elem: TxpElement; verifDuplic: boolean=true): boolean;
135     procedure OpenElement(elem: TxpElement);
ValidateCurElementnull136     function ValidateCurElement: boolean;
137     procedure CloseElement;
138     //Métodos para identificación de nombres
FindFirstnull139     function FindFirst(const name: string): TxpElement;
FindNextnull140     function FindNext: TxpElement;
FindFuncWithParamsnull141     function FindFuncWithParams(const funName: string; const func0: TxpEleFun;
142       var fmatch: TxpEleFun): TFindFuncResult;
FindVarnull143     function FindVar(varName: string): TxpEleVar;
144   public  //constructor y destructror
145     constructor Create; virtual;
146     destructor Destroy; override;
147   end;
148 
149 implementation
150 
151 { TxpElement }
AddElementnull152 function TxpElement.AddElement(elem: TxpElement): TxpElement;
153 {Agrega un elemento hijo al elemento actual. Devuelve referencia. }
154 begin
155   elem.Parent := self;  //actualzia referencia
156   elements.Add(elem);   //agrega a la lista de nombres
157   Result := elem;       //no tiene mucho sentido
158 end;
DuplicateInnull159 function TxpElement.DuplicateIn(list: TObject): boolean;
160 {Debe indicar si el elemento está duplicado en la lista de elementos proporcionada.}
161 var
162   uName: String;
163   ele: TxpElement;
164 begin
165   uName := upcase(name);
166   for ele in TxpElements(list) do begin
167     if upcase(ele.name) = uName then begin
168       exit(true);
169     end;
170   end;
171   exit(false);
172 end;
TxpElement.FindIdxElemNamenull173 function TxpElement.FindIdxElemName(const eName: string; var idx0: integer): boolean;
174 {Busca un nombre en su lista de elementos. Inicia buscando en idx0, hasta el final.
175  Si encuentra, devuelve TRUE y deja en idx0, la posición en donde se encuentra.}
176 var
177   i: Integer;
178   uName: String;
179 begin
180   uName := upcase(eName);
181   //empieza la búsqueda en "idx0"
182   for i := idx0 to elements.Count-1 do begin
183     { TODO : Tal vez sería mejor usar el método de búsqueda interno de la lista,
184      que es más rápido, pero trabaja con listas ordenadas. }
185     if upCase(elements[i].name) = uName then begin
186       //sale dejando idx0 en la posición encontrada
187       idx0 := i;
188       exit(true);
189     end;
190   end;
191   exit(false);
192 end;
193 constructor TxpElement.Create;
194 begin
195   elemType := eltNone;
196 end;
197 destructor TxpElement.Destroy;
198 begin
199   elements.Free;  //por si contenía una lista
200   inherited Destroy;
201 end;
202 
203 { TxpEleMain }
204 constructor TxpEleMain.Create;
205 begin
206   elemType:=eltMain;
207   Parent := nil;  //la raiz no tiene padre
208 end;
209 
210 { TxpEleCon }
211 constructor TxpEleCon.Create;
212 begin
213   elemType:=eltCons;
214 end;
215 
216 { TxpEleVar }
217 constructor TxpEleVar.Create;
218 begin
219   elemType:=eltVar;
220 end;
221 
222 { TxpEleType }
223 constructor TxpEleType.Create;
224 begin
225   elemType:=eltType;
226 end;
227 
228 { TxpEleFun }
229 procedure TxpEleFun.ClearParams;
230 //Elimina los parámetros de una función
231 begin
232   setlength(pars,0);
233 end;
234 procedure TxpEleFun.CreateParam(parName: string; typ0: TType);
235 //Crea un parámetro para la función
236 var
237   n: Integer;
238 begin
239   //agrega
240   n := high(pars)+1;
241   setlength(pars, n+1);
242   pars[n] := typ0;  //agrega referencia
243 end;
TxpEleFun.SameParamsnull244 function TxpEleFun.SameParams(Fun2: TxpEleFun): boolean;
245 {Compara los parámetros de la función con las de otra. Si tienen el mismo número
246 de parámetros y el mismo tipo, devuelve TRUE.}
247 var
248   i: Integer;
249 begin
250   Result:=true;  //se asume que son iguales
251   if High(pars) <> High(Fun2.pars) then
252     exit(false);   //distinto número de parámetros
253   //hay igual número de parámetros, verifica
254   for i := 0 to High(pars) do begin
255     if pars[i] <> Fun2.pars[i] then begin
256       exit(false);
257     end;
258   end;
259   //si llegó hasta aquí, hay coincidencia, sale con TRUE
260 end;
ParamTypesListnull261 function TxpEleFun.ParamTypesList: string;
262 {Devuelve una lista con los nombres de los tipos de los parámetros, de la forma:
263 (byte, word) }
264 var
265   tmp: String;
266   j: Integer;
267 begin
268   tmp := '';
269   for j := 0 to High(pars) do begin
270     tmp += pars[j].name+', ';
271   end;
272   //quita coma final
273   if length(tmp)>0 then tmp := copy(tmp,1,length(tmp)-2);
274   Result := '('+tmp+')';
275 end;
TxpEleFun.DuplicateInnull276 function TxpEleFun.DuplicateIn(list: TObject): boolean;
277 var
278   uName: String;
279   ele: TxpElement;
280 begin
281   uName := upcase(name);
282   for ele in TxpElements(list) do begin
283     if ele = self then Continue;  //no se compara el mismo
284     if upcase(ele.name) = uName then begin
285       //hay coincidencia de nombre
286       if ele.elemType = eltFunc then begin
287         //para las funciones, se debe comparar los parámetros
288         if SameParams(TxpEleFun(ele)) then begin
289           exit(true);
290         end;
291       end else begin
292         //si tiene el mismo nombre que cualquier otro elemento, es conflicto
293         exit(true);
294       end;
295     end;
296   end;
297   exit(false);
298 end;
299 constructor TxpEleFun.Create;
300 begin
301   elemType:=eltFunc;
302 end;
303 
304 { TXpTreeElements }
305 procedure TXpTreeElements.Clear;
306 begin
307   main.elements.Clear;  //esto debe hacer un borrado recursivo
308   curNode := main;      //retorna al nodo principal
309 end;
TXpTreeElements.AllVarsnull310 function TXpTreeElements.AllVars: TxpEleVars;
311 {Devuelve una lista de todas las variables usadas, incluyendo las de las funciones y
312  procedimientos.}
313   procedure AddVars(nod: TxpElement);
314   var
315     ele : TxpElement;
316   begin
317     if nod.elements<>nil then begin
318       for ele in nod.elements do begin
319         if ele.elemType = eltVar then begin
320           vars.Add(TxpEleVar(ele));
321         end else begin
322           if ele.elements<>nil then
323             AddVars(ele);  //recursivo
324         end;
325       end;
326     end;
327   end;
328 begin
329   if vars = nil then begin  //debe estar creada la lista
330     vars := TxpEleVars.Create(false);
331   end else begin
332     vars.Clear;   //por si estaba llena
333   end;
334   AddVars(curNode);
335   Result := vars;
336 end;
CurNodeNamenull337 function TXpTreeElements.CurNodeName: string;
338 {Devuelve el nombre del nodo actual}
339 begin
340   Result := curNode.name;
341 end;
342 //funciones para llenado del arbol
TXpTreeElements.AddElementnull343 function TXpTreeElements.AddElement(elem: TxpElement; verifDuplic: boolean = true): boolean;
344 {Agrega un elemento al nodo actual. Si ya existe el nombre del nodo, devuelve false}
345 begin
346   Result := true;
347   //Verifica si hay conflicto. Solo es necesario buscar en el nodo actual.
348   if verifDuplic and elem.DuplicateIn(curNode.elements) then begin
349     exit(false);  //ya existe
350   end;
351   //agrega el nodo
352   curNode.AddElement(elem);
353 end;
354 procedure TXpTreeElements.OpenElement(elem: TxpElement);
355 {Agrega un elemento y cambia el nodo actual. Este método está reservado para
356 las funciones o procedimientos}
357 begin
358   {las funciones o procedimientos no se validan inicialmente, sino hasta que
359   tengan todos sus parámetros agregados, porque pueden ser sobrecargados.}
360   curNode.AddElement(elem);
361   //Genera otro espacio de nombres
362   elem.elements := TxpElements.Create(true);  //su propia lista
363   curNode := elem;  //empieza a trabajar en esta lista
364 end;
TXpTreeElements.ValidateCurElementnull365 function TXpTreeElements.ValidateCurElement: boolean;
366 {Este método es el complemento de OpenElement(). Se debe llamar cuando ya se
367  tienen creados los parámetros de la función o procedimiento, para verificar
368  si hay duplicidad, en cuyo caso devolverá FALSE}
369 begin
370   //Se asume que el nodo a validar ya se ha abierto, con OpenElement() y es el actual
371   if curNode.DuplicateIn(curNode.Parent.elements) then begin  //busca en el nodo anterior
372     exit(false);
373   end else begin
374     exit(true);
375   end;
376 end;
377 procedure TXpTreeElements.CloseElement;
378 {Sale del nodo actual y retorna al nodo padre}
379 begin
380   if curNode.Parent<>nil then
381     curNode := curNode.Parent;
382 end;
383 //Métodos para identificación de nombres
FindFirstnull384 function TXpTreeElements.FindFirst(const name: string): TxpElement;
385 {Busca un nombre siguiendo la estructura del espacio de nombres (primero en el espacio
386  actual y luego en los espacios padres).
387  Si encuentra devuelve la referencia. Si no encuentra, devuelve NIL.
388 Este es un ejemplo, implementar de acuerdo al lenguaje y reglas de alcance.
389 }
FindFirstInnull390   function FindFirstIn(nod: TxpElement): TxpElement;
391   var
392     idx0: integer;
393   begin
394     curFindNode := nod;  //Busca primero en el espacio actual
395     {Busca con FindIdxElemName() para poder saber donde se deja la exploración y poder
396      retomarla luego con FindNext().}
397     idx0 := 0;  //la primera búsqueda se hace desde el inicio
398     if curFindNode.FindIdxElemName(name, idx0) then begin
399       //Lo encontró, deja estado listo para la siguiente búsqueda
400       curFindIdx:= idx0+1;
401       Result := curFindNode.elements[idx0];
402       exit;
403     end else begin
404       //No encontró
405       if nod.Parent = nil then begin
406         Result := nil;
407         exit;  //no hay espacios padres
408       end;
409       //busca en el espacio padre
410       Result := FindFirstIn(nod.Parent);  //recursividad
411       exit;
412     end;
413   end;
414 begin
415   curFindName := name;     //actualiza para FindNext()
416   Result := FindFirstIn(curNode);
417 end;
TXpTreeElements.FindNextnull418 function TXpTreeElements.FindNext: TxpElement;
419 {Continúa la búsqueda iniciada con FindFirst().}
420 begin
421   //Implementar de acuerdo al lenguaje.
422   Result := nil;
423 end;
TXpTreeElements.FindFuncWithParamsnull424 function TXpTreeElements.FindFuncWithParams(const funName: string; const func0: TxpEleFun;
425   var fmatch: TxpEleFun): TFindFuncResult;
426 {Busca una función que coincida con el nombre "funName" y con los parámetros de func0
427 El resultado puede ser:
428  TFF_NONE   -> No se encuentra.
429  TFF_PARTIAL-> Se encuentra solo el nombre.
430  TFF_FULL   -> Se encuentra y coninciden sus parámetros, actualiza "fmatch".
431 }
432 var
433   tmp: String;
434   ele: TxpElement;
435   hayFunc: Boolean;
436 begin
437   Result := TFF_NONE;   //por defecto
438   hayFunc := false;
439   tmp := UpCase(funName);
440   for ele in curNode.elements do begin
441     if (ele.elemType = eltFunc) and (Upcase(ele.name) = tmp) then begin
442       //coincidencia de nombre, compara parámetros
443       hayFunc := true;  //para indicar que encontró el nombre
444       if func0.SameParams(TxpEleFun(ele)) then begin
445         fmatch := TxpEleFun(ele);  //devuelve ubicación
446         Result := TFF_FULL;     //encontró
447         exit;
448       end;
449     end;
450   end;
451   //si llego hasta aquí es porque no encontró coincidencia
452   if hayFunc then begin
453     //Encontró al menos el nombre de la función, pero no coincide en los parámetros
454     Result := TFF_PARTIAL;
455     {Construye la lista de parámetros de las funciones con el mismo nombre. Solo
456     hacemos esta tarea pesada aquí, porque  sabemos que se detendrá la compilación}
457 {    params := '';   //aquí almacenará la lista
458     for i:=idx0 to high(funcs) do begin  //no debe empezar 1n 0, porque allí está func[0]
459       if Upcase(funcs[i].name)= tmp then begin
460         for j:=0 to high(funcs[i].pars) do begin
461           params += funcs[i].pars[j].name + ',';
462         end;
463         params += LineEnding;
464       end;
465     end;}
466   end;
467 end;
FindVarnull468 function TXpTreeElements.FindVar(varName: string): TxpEleVar;
469 {Busca una variable con el nombre indicado en el espacio de nombres actual}
470 var
471   ele : TxpElement;
472   uName: String;
473 begin
474   uName := upcase(varName);
475   for ele in curNode.elements do begin
476     if (ele.elemType = eltVar) and (upCase(ele.name) = uName) then begin
477       Result := TxpEleVar(ele);
478       exit;
479     end;
480   end;
481   exit(nil);
482 end;
483 //constructor y destructror
484 constructor TXpTreeElements.Create;
485 begin
486   main:= TxpEleMain.Create;  //No debería
487   main.elements := TxpElements.Create(true);  //debe tener lista
488   curNode := main;  //empieza con el nodo principal como espacio de nombres actual
489 end;
490 destructor TXpTreeElements.Destroy;
491 begin
492   main.Destroy;
493   vars.Free;    //por si estaba creada
494   inherited Destroy;
495 end;
496 end.
497 
498