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