Вы находитесь на странице: 1из 30

Xpres 0.

Documentacin Tcnica

Xpres 0.7

Por Tito Hinostroza

1 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

1 NOTAS SOBRE LA VERSIN 0.7


En esta versin, se convierten arreglos dinmicos en listas de objetos,
porque en pruebas de rendimiento, no se encontr deterioro en la velocidad,
de acceso.
Los registros para describir a las constantes, variables y funciones, se
convierten ahora en clases.
Adems se mejora la modularidad de la librera, separando las
definiciones de constantes, variables y funciones en una nueva unidad:
XpresElements.
Tambin se modifican algunos mtodos para que
referencias a objetos en lugar de manejar ndices a arreglos.

ahora

usen

Con respecto al framework, se puede decir que se pasa ahora a


manejar el concepto de elemento del lenguaje, para abarcar a las
constantes, variables y funciones.

2 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

2 INTRODUCCIN
2.1 Descripcin
Xpres es un framework que permite implementar intrpretes y
compiladores para lenguajes tipados e imperativos, usando el entorno de
desarrollo Lazarus y el compilador Free Pascal.
Xpres es tambin el nombre de un lenguaje de muestra que se ha
creado como una demostracin de las capacidades del framework Xpres para
implementar lenguajes compilados.
Xpres es tambin el nombre del proyecto que se cre para desarrollar
el framework y el lenguaje.
El proyecto Xpres, est basado en la librera SynFacilSyn, que es
usada como analizador lxico.

2.2 Caractersticas
Xpres se caracteriza por:
1.
2.
3.
4.
5.
6.

Ser de cdigo abierto.


Estar desarrollado en Lazarus.
Ser de tamao y funciones relativamente simples.
Estar diseado en base a objetos en la mayora de sus partes.
Trabajar en capas (lexer/parser).
Incluir un lexer considerablemente flexible y rpido (basado en
SynFacilSyn).
7. Poder adaptarse a diversos lenguajes de programacin.
8. Ofrecer una arquitectura de trabajo slida, y escalable.
9. Requerir poco cdigo adicional, para la implementacin de
compiladores o intrpretes.
10. Incluir rutinas prediseadas, que sirven como modelo para la
implementacin final.
11. Incluir documentacin detallada, de modo que sea fcil trabajar
con l.
Entre sus limitaciones estn:
1. El analizador de expresiones est fijado para trabajar con los
operadores como mtodos de los tipos de operandos (Ver seccin
4.7). No trabaja de con operadores independientes.
2. El analizador de expresiones trabaja solo en la forma de notacin de
infijo. No soporta pila polaca.
3. No soporta lenguajes dinmicos. Est orientado a lenguajes tipados.
3 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

4. No permite la implementacin de lenguajes con tipos de datos


complejos.
5. No permite la implementacin de lenguajes orientados a objetos.
6. An est en fase de desarrollo.
Es posible que algunas de estas limitaciones sean salvadas en nuevas
versiones.

2.3 Arquitectura
Xpres, est organizado por capas. El nivel ms bajo es el analizador
lxico, que est implementado en la unidad XpresBas. Por encima de l
est el analizador sintctico y semntico (Parser) implementado en la unidad
XpresParser.
U n id a d e s d e U s u a r io
C d ig o d e im p le m e n t a c i n d e l c o m p ila d o r o in t r p r e t e p a r a
u n le n g u a je p a r t ic u la r .

U n id a d X p r e s P a r s e r

P a rs e r

U n id a d X p r e s B a s

T P E rro r

T C o m p ile r B a s e

U n id a d X p r e s E le m e n ts

Lexer
U n id a d X p r e s T y p e s

TT ype

El tratamiento
se maneja a travs del objeto TPError,
T e x t o de
F u e nerrores
te
definido tambin en la unidad XpresBas.
La unidad XpresTypes, contiene las definiciones de los objetos TType,
que representan a los tipos de datos del lenguaje. El framework Xpres resalta
el manejo de tipos, por eso se define una unidad especial para las
definiciones referidas a tipo. Esta unidad prcticamente no tiene
dependencias, as que se puede considerar una de las unidades
fundamentales de Xpres.
La unidad XpresElements, incluye las definiciones y los contenedores
para las constantes, variables y funciones.
La implementacin de un intrprete/compilador se debe hacer en una
unidad, o varias unidades adicionales, que incluyan a XpresParser y a
XpresTypes. Una implementacin muy sencilla (como la de los ejemplos

4 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

incluidos1), podra requerir solo de una unidad adicional, para implementar


un compilador, incluyendo al generador de cdigo.
Una implementacin ideal solo se reducir a crear una unidad adicional
y poner ah todo el cdigo necesario, sin modificar ninguna de las unidades
de la librera. Sin embargo, en implementaciones ms elaboradas, se tendr
que modificar las unidades de la librera, para adaptarlas a la
implementacin.
Independientemente de si se usa una unidad o varias, debe existir al
menos una unidad que implemente un objeto como subclase de
TCompilerBase. En esta nueva unidad, es donde se debe incluir al generador
de cdigo (o intrprete), de acuerdo al diseo sugerido por Xpres.
Por comodidad, XPres recomienda usar un archivo externo (incluido con
la directiva $I), para la implementacin del generador de cdigo o intrprete.
Se trabaja de esta forma por los siguientes motivos:

Por temas de velocidad, para no crear capas adicionales que


puedan deteriorar el rendimiento.
Por temas de modularidad. Para que el texto del generador de
cdigo, se maneje en un solo archivo, y sea ms fcil de escribir y
mantener.
Porque resulta complicado separar el generador de cdigo como
una unidad independiente, ya que est fuertemente relacionada
con las rutinas del analizador sintctico.

Consideremos una implementacin sencilla (como la mostrada en los


ejemplos) que solo usa una unidad adicional (llamada aqu Parser.pas) para
la implementacin de un compilador con su generador de cdigo. El
diagrama de unidades ser el siguiente:

U n id a d P a r s e r . p a s
C d ig o d e im p le m e n t a c i n d e l
c o m p ila d o r .

A r c h iv o G e n C o d . p a s
G e n e r a d o r d e C d ig o

U n id a d X p r e s P a r s e r

U n id a d
X p r e s E le m e n ts
U n id a d X p r e s B a s

U n id a d X p r e s T y p e s

Los proyectos de los ejemplos usan un archivo adicional, incluido con la directiva $I, pero
en la prctica se trata de una sola unidad.

5 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

En la unidad Parser, se debe definir la clase final con la que se va a


trabajar, agregando los mtodos o propiedades requeridas, para una
implementacin en particular.
El archivo incluido (generador de Cdigo) es llamado GenCod.pas,
pero puede tener cualquier nombre.

6 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

3 ANLISIS LXICO
El analizador lxico (lexer) est contenido en la unidad XpresBas, y
est basado en el resaltador de sintaxis TSynFacilSyn, al cual usa como
analizador lxico para construir la primera capa de accesos al contenido.
Tener al analizador lxico ya desarrollado, es una gran ayuda, porque nos
evita tener que lidiar con el manejo de extraccin de tokens.

3.1 Contextos
Para acceder a un texto cualquiera, se usa el objeto TContext, que usa
un objeto TSynFacilSyn, para acceder al Texto Fuente.
La clase TContext, representa a un objeto Contexto, que vendra a ser
algo as como el medio que usaremos para ir extrayendo los tokens del texto
o archivo fuente. Tambin puede funcionar como el contenedor del texto
fuente, dependiendo de cmo se haya creado.
El siguiente diagrama muestra los niveles del anlisis lxico.
U n id a d X p r e s B a s

U n id a d S y n F a c ilH ig h lig h t e r

T C o n te x t

S y n F a c ilS y n

T e x to F u e n te
Como un contexto trabaja sobre TSynFacilSyn, lo que ve son tokens.
Todo lo que se extrae de un contexto son Tokens. No existe un nivel por
debajo de los token en un Contexto.
Todo contenido a leer, debe ser cargado o asignado un objeto TContext,
para trabajar. La fuente de datos a usar pude ser de tres tipos: Una cadena
de texto, un archivo de texto, o un objeto TStrings.
Para usar un contexto, primero hay que crearlo y usar alguno de los
siguientes mtodos para definir su contenido:
//Fija el contenido del contexto con cadena
procedure SetSource(txt : string);
//Fija contenido a partir de una lista
procedure SetSource(lins: Tstrings; MakeCopy: boolean = false);
//Fija el contenido del contexto con archivo
procedure SetSourceF(file0: string);

7 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Con estos mtodos se pueden crear contextos a partir de una cadena,


una lista, o un archivo. Cuando se usa una lista para crear el contexto, se
tiene la opcin de crear una copia interna o usar solo la referencia a la lista
indicada, ahorrando as espacio de almacenamiento y mejorando la
velocidad.
En los otros casos, siempre se crea una copia local de los datos en el
objeto TContext.
Los contextos se crearon como una capa de abstraccin entre los datos
fuente y el Lexer. Manejar contextos nos ayuda en hacer ms transparente la
extraccin de tokens desde una fuente cualquiera. Adems nos facilitar el
manejar varios archivos fuente como entrada para el Lexer, o mezclar
fuentes de texto de tipos diversos como cadenas y archivos.
Visto desde fuera un contexto, es como una caja negra del cual se
pueden extraer tokens, abstrayndose del origen del texto.
Si se quisiera realizar la exploracin de tokens desde un SynEdit,
usando TContext, el cdigo sera similar a este:
xCon := TContext.Create;
xCon.DefSyn(xLex);
xCon.SetSource(SynEdit1.Lines);
while not xCon.Eof do begin
//El token actual es xCon.Token
//El tipo de token actual es xCon.TokenType
//El bloque actual es xCon.Block
xCon.Next;
end;
xCon.Destroy;

No se han definido muchos mtodos sobre la clase TContexts, porque


no se ha diseado para ser usada directamente sino a travs del objeto
TContexts.

3.2 TContexts
En XpresBas, existe una capa ms de abstraccin, a travs de la clase
TContexts. Esta clase permite administrar varios contextos y cambiar la
posicin de lectura entre ellos, de forma transparente. Pare ello maneja una
lista de contextos de tipo TContext, y siempre mantiene una referencia al
T C o n te x ts
contexto actual.

8 de 30

T C o n te x t

T C o n te x t

T e x to F u e n te

T e x to F u e n te

T C o n te x t

T e x t o F u 07/07/2016
e n te

Xpres 0.7

Documentacin Tcnica

Toda la lectura de archivos fuente, se har a travs de un objeto


TContexts, y no abriendo contextos individuales. Inclusive si se maneja una
sola fuente de entrada (un archivo o StringList), es recomendable hacerlo a
travs de TContexts.
Los contextos en TContexts, se crean de acuerdo a la necesidad.
TContexts, tiene otra forma de crear contextos, bsicamente son las
mismas funciones de TContext, pero con nombres diferentes:
procedure NewContextFromFile(arc0: String);
procedure NewContextFromFile(arc0: String; lins: Tstrings);
procedure NewContextFromTxt(txt: string; arc0: String);

El procedimiento para iniciar la lectura de un archivo fuente es crear


una instancia de TContexts:
cIn := TContexts.Create(xLex); //Crea lista de Contextos
cIn.NewContextFromFile(NombArchivo);

//Crea nuevo contenido

while not cIn.Eof do begin


//El token actual se lee en cIn.tok
//y el tipo se lee en cIn.tokType
...
cIn.Next;
end;

//pasa al siguiente token

...
cIn.Destroy; //Limpia lista de Contextos

Existen diversas operaciones que se pueden realizar con un objeto


TContexts, las ms comunes son las referidas a la lectura de tokens del
contexto de entrada.
En la exploracin normal de un contexto, puede surgir la necesidad de
retroceder en la exploracin del texto fuente. Para ello existen la propiedad
PosAct, que permite guardar la posicin actual de lectura en el contexto de
entrada y luego retomarla, desde cualquier otra parte, inclusive cuando se
puede estar explorando otro archivo.
La forma de trabajo es simple:
posCxt := cIn.PosAct;

9 de 30

//Guarda por si lo necesita

07/07/2016

Xpres 0.7
cIn.Next;
cIn.Next;
...

Documentacin Tcnica
//explora
//explora

cIn.PosAct := posCxt;

10 de 30

//Retorna a la posicin guardada

07/07/2016

Xpres 0.7

Documentacin Tcnica

4 FUNCIONALIDADES PRE-DEFINIDAS
Para la implementacin directa de un compilador/intrprete para un
lenguaje determinado, se debe verificar que el lenguaje sea compatible con
el framework. Para ello se deben cumplir ciertos requerimientos y
mantenerse dentro de las limitaciones.
Como siempre suele pasar, es posible romper alguna (o todas) de estas
limitaciones impuestas, pero eso requerir un trabajo considerable, en
modificar las rutinas bsicas del framework.

4.1 Requerimientos del lenguaje


El framework Xpres, permite implementar diversos lenguajes de
programacin que pueden usar la maquinaria interna (lexer, parser,
generador de cdigo) del framework. Sin embargo, estos lenguajes de
programacin, deben cumplir ciertos requerimientos:

Ser insensible a la caja (podra cambiarse con cierto trabajo)


Ser un lenguaje imperativo.
Ser un lenguaje tipado.
Todas las variables deben ser declaradas explcitamente.
El lenguaje debe seguir la estructura del framework
Las expresiones deben seguir la estructura del framework.
Debe definir sus operadores por tipo (Ver Seccin 5.4 - Definiendo
los Operadores), no generales.
Debe usar tipos que cuadren en los tipos base predefinidos.

4.2 Caractersticas Soportadas


Existen algunas funcionalidades que vienen ya soportadas por la
librera, para la implementacin de lenguajes de programacin. Estas son:

Evaluacin completa de expresiones (sin incluir la generacin de


cdigo).
Reconocimiento de declaracin de funciones.
Sobrecarga de funciones.
Manejo del espacio de nombres (NAMESPACE).

4.3 Declaracin de Variables


Xpres, est pensado en lenguajes tipados, as que ha sido diseado
para trabajar con variables que requieran declararse antes de usarse.
11 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Lo usual es que las variables se declaren al inicio, antes de ejecutar


algn tipo de cdigo. Pero se puede aceptar que el lenguaje maneje
declaraciones en medio del cdigo.
Xpres incluye rutinas para la deteccin de la declaracin de variables,
basadas en la sintaxis de Pascal, pero son rutinas bastante simples y casi con
seguridad, tendrn que re-escribirse.

4.4 Estructura del Lenguaje del Framework


Xpres, espera que el lenguaje a implementar tenga un estructura
basada en bloques expresiones y tokens. El texto de un cdigo fuente se
maneja a diversos niveles:
B lo q u e s

E x p r e s io n e s

Tokens

En el primer nivel, lo que se espera es leer un bloque, que se leer


como un conjunto de expresiones.
Existe un par de normas a seguir en el lenguaje:

Los Bloques son conjuntos de expresiones y delimitadores de


expresiones, siguiendo reglas predefinidas del lenguaje.
Las Expresiones son conjuntos de Operandos y Operadores,
siguiendo reglas predefinidas en el lenguaje. Si no se cumplen las
reglas, se tendr un error de sintaxis.

As, podramos trazar un diagrama ms detallado:


B lo q u e s

E x p r e s io n e s

O p e ra n d o s

12 de 30

D e lim it a d o r e s
d e e x p r e s i n

O p e ra d o re s

Tokens

07/07/2016

Xpres 0.7

Documentacin Tcnica

4.5 Categoras de tipos


Xpres, permite crear cualquier cantidad de tipos de datos especficos
para el lenguaje de programacin que deseemos implementar, pero estos
tipos deben estar dentro de las categoras existentes dentro de Xpres, y
estas categoras si son fijas:
TCatType=(
t_integer, //nmeros enteros
t_uinteger, //enteros sin signo
t_float, // de coma flotante
t_string, //cadena de caracteres
t_boolean, //booleano
t_enum
//enumerado
);
Las tres primeras categoras sirven para representar a tipos numricos,
y se pueden definir con hasta 64 bits de precisin.
Suponiendo que queremos implementar los tipos numricos de C:
char, usnigned char y short, estos deben ser definidos como:
char -> t_integer de 1 byte.
unsigned char -> t_uinteger de 1 byte.
short -> t_integer de 2 bytes.
Como vemos, cada categora pueden alojar a diversos tipos de datos.
Solo las categoras numricas estn medianamente implementadas en
la presente versin de Xpres. Las categoras adicionales no estn an bien
estudiadas.

4.6 Reconocimiento de constantes numricas


Las constantes numricas son los nmeros que se escriben de forma
literal en el cdigo fuente. Los casos ms comunes son constantes enteras o
en coma flotante:
123 1.23 0.25
Xpres, reconoce las constantes numricas viendo que el token ledo
tenga el tipo tkNumber. Esto significa que las constantes numricas las
reconoce el analizador lxico. As que si se debe reconocer diversos

13 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

formatos, como 1.2e+23 o 0x123, estos se deben definir en el lexer, usando


el resaltador SynfacilSyn.
Entonces el primer paso en el reconocimiento de constantes numricas
es definirlas como de tipo tkNumber, en el analizador lxico (lexer). Esto se
hace por lo general en el constructor de la clase compilador (derivada de
TCompilerBase):
constructor TCompiler.Create;
begin
inherited Create;
//Define la sintaxis
//Se puede definir aqu pero es preferible en un procedimiento aparte.
xLex.ClearMethodTables;
//limpa tabla de mtodos
xLex.ClearSpecials;
//para empezar a definir tokens
//crea tokens por contenido
xLex.DefTokIdentif('[$A-Za-z_]', '[A-Za-z0-9_]*');
xLex.DefTokContent('[0-9]', '[0-9.]*', tkNumber);
//nmeros
...
end;

Una vez que le hemos enseado al lexer a reconocer nmeros, el


trabajo del analizador sintctico se reduce a detectar los tokens de tipo
tkNumber, para saber que est ante una constante numrica. El siguiente
paso, sera obtener el valor adecuado a partir de ese nmero, que desde el
punto de vista del analizador sintctico, no es ms que una serie de
caracteres sin sentido.
El analizador sintctico, debe convertir las constantes numricas a
variables Pascal con el valor del nmero ya ledo.
Esta tarea no es tan sencilla como parece, porque no solo se trata de
convertir con un StrToInt(), o con un StrToFloat(), sino que se debe tener en
cuenta que:
1. Pueden existir diversos formatos de nmeros especiales, como
b00111 o 08A0h.
2. El nmero ledo debe ser asignado a una categora (t_integer,
t_uinteger o t_float) y a un tipo (de los tipos definidos para el lenguaje).
La primera consideracin solo es cuestin de algoritmos de conversin
de formato, as que no representara gran problema. Adems si el lexer pudo
reconocer al nmero, no es difcil que el analizador sintctico lo pueda
convertir.
La segunda consideracin si puede convertirse en una tarea pica,
sobre todo si el lenguaje a implementar maneja diversos tipos de nmeros.

14 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Como ejemplo de implementacin, la clase TCompilerBase, implementa


un
mtodo
sencillo
para
reconocer
constantes
numricas:
TCompilerBase.TipDefecNumber().
Su trabajo se limita a reconocer los formatos simples de enteros
t_integer y flotantes t_float (expresados como cantidades con punto
decimal).
Adems TipDefecNumber(), asigna el tipo numrico explorando los
tipos numricos definidos y eligiendo el tipo ms pequeo que pueda alojar
al nmero. Es decir, si se encuentra el token 123 y el lenguaje solo ha
definido enteros de 2 bytes y 4 bytes, TipDefecNumber() detectar a 123
como el tipo entero de 4 bytes.
Si el lenguaje es sencillo TipDefecNumber, podra bastar para el
procesamiento de constantes numricas. De otra forma ser necesario sobre
escribir TipDefecNumber().

4.7 Expresiones aritmticas


Xpres, tiene incluido un cdigo potente para el manejo de las
expresiones. Las expresiones pueden manejar diversos tipos de datos, no
solo las aritmticas.
Las expresiones soportadas pro Xpres son las que tienen notacin de
infijo (no pila polaca o pila polaca inversa).
Es decir, son soportadas las expresiones de tipo:
1+1
2 + 3*5
(1+2)*5
Que es la forma ms comn de evaluacin en los lenguajes de
programacin.
Las expresiones en notacin de pila polaca, como las siguientes:
11+
3*52+
No estn soportadas por el framework.
B lo q u e s
Como ya se indic, las expresiones se componen de operandos y
operadores:
D e lim it a d o r e s
E x p r e s io n e s

O p e ra n d o s

d e e x p r e s i n

O p e ra d o re s

15 de 30

07/07/2016
Tokens

Xpres 0.7

Documentacin Tcnica

Una expresin tpica sera la siguiente:

O p e ra n d o

O p e ra d o re s

O p e ra n d o s

D e lim it a d o r

x := x + 1;
Esta nomenclatura, es bastante conocida y usada en muchos lenguajes
de programacin, sin embargo, la forma como Xpres trata a las expresiones,
es un tanto particular2.
Los operadores se deben definir para los tipos de datos que formarn
parte de las expresiones. Es decir, que los operadores, no existen como
elementos independientes (a nivel sintctico) de los operandos.
Cuando se implementa un lenguaje, primero se crean los tipos, y luego
los operadores que se aplican a esos tipos. Nunca se crean los operadores
independientemente de los tipos (excepto a nivel lexico).
Para clarificar la idea, imaginemos que estamos definiendo un lenguaje
y queremos que pueda sumar enteros. Para que Xpres pueda brindarle el
soporte de su maquinaria de expresiones, primero se define el tipo entero:
tipoEntero := CreateType('entero', t_integer, 1);

//define tipo entero

Luego se debe definir el operador suma:


oprSuma := tipoEntero.CreateOperator('+',5,'suma');

Y luego indicar una operacin de ese operador aplicado al tipo entero:


oprSuma.CreateOperation(tipoEntero, @int_suma_int);

Desde este punto de vista, la expresin:


2

Muy parecida a la forma como se maneja en el lenguaje Ruby.

16 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

1+2
Se evala considerando que para el operador 1, existe una operacin
definida con el smbolo +, que se aplica al operador 2.
Vindolo de esta forma, los operandos se ven como objetos 3 y los
operadores como simple llamadas a mtodos, usando como parmetros a
otros operandos.

No se implementan clases y objetos dentro de Xpres, de forma nativa.

17 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

4.8 El analizador de expresiones


Una de las facilidades que ofrece las libreras de Xpres, es el analizador
de expresiones.
El analizador de expresiones, se encuentra implementado en la unidad
XpresParser, y funciona de acuerdo al esquema indicado en la seccin 4.5.
Este evaluador, no es precisamente un evaluador, en el sentido de que
calcule el resultado de una expresin (aunque bien podra hacerlo), sino que
reconoce las expresiones (aritmticas o de cualquier tipo que se haya
definido), y las procesa de acuerdo a las reglas definidas para los tipos de
datos creados.
Esto es una gran ayuda, porque as nos libera del trabajo tedioso que
representara implementar desde cero, un analizador de expresiones especial
para un intrprete/compilador.
El analizador de expresiones, funciona de la siguiente forma:
O n E x p r S ta rt( )

A n a liz a d o r d e
e x p r e s io n e s

L la m a a
m to d o s
s e n c illo s d e
e v a lu a c i n .
O n E x p rE n d ()

e x p r e s i n d e e n tr a d a :
1 + 2se
* 3 ^ 2llama al analizador de expresiones, a travs del
Cada vez que

mtodo GetExpression(), este toma del contexto de entrada y la va


desmenuzando de acuerdo a las reglas de precedencia, definidas para los
operadores de los tipos existentes.
Antes de evaluar a una expresin, se realiza una llamada al mtodo
OnExprStart(), y al terminar de evaluar, se hace una llamada al mtodo
OnExprEnd(). Estos mtodos sirven para poder realizar alguna tarea de
preparacin o finalizacin al momento de empezar o terminar de evaluar las
expresiones. Entre estos dos eventos, el analizador de expresiones generar
diversos eventos de acuerdo a la expresin evaluada.
Inicialmente, cuando no se ha creado ningn tipo, el analizador de
expresiones, no reconocer, siquiera los tokens bsicos 1 o 20 como
nmeros. Debe primero crearse algn tipo numrico para alojar a estas
constantes.

18 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

4.8.1 Tipos de operandos


Ya vimos que las expresiones se componen de operandos y operadores.
Como la definicin de expresin es recursiva, se puede considerar que una
expresin es un operando en s.
Todos los operandos que maneja el analizador de expresiones, se
clasifican en 3 grupos:
1. Constantes.
2. Variables.
3. Expresiones.
Estos estn identificados por el tipo TCatOperan:
TCatOperan = (
coConst =%00,
coVariab=%01,
coExpres=%10
funcin).
);

//Constante. Inlcuyendo expresiones de constantes evaluadas.


//Variable. Variable nica.
//Expresin. Algo que requiere clculo (incluyendo a una

Las constantes son literales de algn tipo,


perfectamente definido. Son ejemplos de constantes:

cuyo

valor,

est

123, 123.0, 1.2e+23, cadena


Las constantes pueden ser de cualquier tipo que se haya definido.
Los operandos de tipo variable, son expresiones de un solo operando
en la cual ese operando es la referencia a una variable. Eso significa que el
valor de la expresin, se obtiene leyendo solamente el valor que tiene
almacenado la variable en cualquier momento.
Los operandos de tipo expresin son aquellos que por lo general tienen
ms de un operando, o involucran llamadas a funciones. En trminos de un
compilador, se dira que son expresiones que requieren trabajo de clculo
para obtener su valor.
La siguiente tabla resume cmo se obtiene el valor de los operandos,
de acuerdo a su tipo:
Tipo de
Operando
coConst
coVariab
coExpres

19 de 30

Ejemplos
3.1415
cadena
x
variable1
x+1
mximo(1,2)

Mtodo para obtener su valor


Se puede determinar a priori. Se puede leer
directamente en tiempo de compilacin.
No se puede determinar a priori. Se lee
accediendo a la posicin de la memoria en
donde se guarda la variable.
No se puede determinar a priori. Se necesita
hacer primero un clculo para evaluar el valor

07/07/2016

Xpres 0.7

Documentacin Tcnica
final de la expresin

4.8.2 Secuencia de eventos


El analizador de expresiones digiere las expresiones y como salida,
genera llamadas a eventos, en una secuencia lgica. La idea es que el
generador de cdigo (o intrprete) se implemente como respuesta a estos
eventos.
Para mostrar el funcionamiento
consideremos un ejemplo en donde:

del analizador

de expresiones,

Se ha definido un tipo numrico llamado byte,


Se ha definido la operacin suma, con el smbolo +, al tipo byte,
aplicado a otro byte (byte + byte)
La presencia de la operacin suma se pone a 1.
Se ha definido la operacin producto, con el smbolo *, al tipo
byte, aplicado a otro byte (byte * byte)
La presencia de la operacin producto se pone a 2.

Bajo estas consideraciones, analicemos los siguientes casos:


EXPRESI
N
5
x
5+1
5+x
5+2*x

DESCRIPCIN

EVENTOS LLAMADOS

Constante nica
<byte>.OnLoad()
Variable nica
<byte>.OnLoad()
Suma de constantes
<byte>.proc(const
Suma de constante y <byte>.proc(const
variable.
Precedencia al final
<byte>.proc(const
<byte>.proc(const

+ const)
+ variab.)
* variab.)
+ expres.)

CATEGORA
coConst
coVariab
coExpres*
coExpres
coExpres

(*) En el caso de compiladores, por temas de optimizacin se suele


evaluar primero esta expresin y devolver una sola constante, con categora
coConst.
Los eventos son llamados en el orden de precedencia que se haya
definido para los operadores. Las operaciones con operadores que tengan
mayor precedencia, sern llamados primero.
As, en nuestro ejemplo, la expresin: 5+2*x, genera primero una
llamada a la operacin de producto, antes que a la de la suma, porque el
operador *, se defini con mayor precedencia.
Los parntesis pueden forzar el orden de evaluacin. Por ejemplo en la
expresin: (5+2)*3, los parntesis obligarn al analizador de expresiones a
procesar primero la expresin (5+2).

20 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Aunque son muchos los eventos que puede generar el analizador de


expresiones, los tipos de eventos son pocos. El siguiente diagrama
esquematiza los tipos de eventos generados:
E v a lu a c i n d e
o p e r a c io n e s
b in a r ia s

A n a liz a d o r d e
e x p r e s io n e s

E v a lu a c i n d e
o p e r a c io n e s
u n a r ia s
E v a lu a c i n d e
f u n c io n e s

T x O p e r a tio n . p r o c ( )

< N o im p le m e n ta d o >

T fu n c .p ro c ()

e x p r e s i n d e e n t r a d a :
m a x (1 ,2 )+ 2 * 3

La mayora de veces se llamar al evento TxOperation.proc,


(configurado mediante TOperator.CreateOperation() - Ver seccin 5.5), para
pedirle al generador de cdigo que genere el cdigo para la evaluacin de
une operacin binaria simple.

4.9 Generador de cdigo/ Intrprete


El generador de cdigo no se implementa dentro del framework Xpres.
Debe ser creado cuando se implemente un compilador o intrprete.
El generador de cdigo, se usar para cuando se desee implementar
un compilador. Si lo que se desea es implementar un lenguaje interpretado,
en lugar del generador de cdigo, lo que se requiere implementar es un
intrprete.
Aunque ambos casos pudieran parecer muy diferentes, en realidad
comparten mucho de comn.
En el caso del generador de cdigo, si vemos el tipo de salida este
puede ser:

Generador a cdigo internedio. Como los que van a ser ejecutados


por una mquina virtual.
Generador a cdigo nativo. Los que van a generar programas
ejecutables.

Adems considerando el tipo de salida, el generador de cdigo puede


ser:

21 de 30

Orientado a registro. Usado generalmente cuando la arquitectura


del procesador destino, contiene varios registros.
07/07/2016

Xpres 0.7

22 de 30

Documentacin Tcnica

Orientado a pila. Cuando la arquitectura del procesador o mquina


virtual destino, maneje pilas.

07/07/2016

Xpres 0.7

Documentacin Tcnica

5 IMPLEMENTACIN DE COMPILADOR/INTRPRETE
5.1 Preparar el espacio de trabajo
Para implementacin de un compilador/intrprete, primero se debe
tener definido el lenguaje (y que debe cumplir con los requerimientos
indicados en la seccin 4.1).
Luego se debe crear la estructura de archivos para el nuevo proyecto o
tomar como base uno de los proyectos de demostracin que se incluye en la
librera Xpres.
Si queremos crear un proyecto desde cero, debemos primero crear un
proyecto en blanco e incluir a los archivos: XpresBas.pas y XpresParser.pas,
Para que estas unidades funciones se debe incluir las libreras:

SynfacilUtils 0.4 o superior.


MisUtils 0.3 o superior.

En este proyecto agregar una unidad nueva llamada: Parser.pas.


Luego crear un archivo nuevo llamado GenCod.pas, que es un archivo que
es referenciado como archivo a incluir, desde dentro de XpresParser.pas.
La estructura de archivos de este proyecto debe quedar de la siguiente
forma:
U n id a d P a r s e r . p a s
{$ I G e n C o d .p a s }

A r c h iv o G e n C o d .p a s

U n id a d X p r e s P a r s e r . p a s

U n id a d X p r e s B a s .p a s

A r c h iv o s d e la s lib r e r a s
S y n F a c ilU t ils y M is U t ils

El lenguaje debe definirse principalmente en el archivo GenCod.pas


Luego de tener el entorno de trabajo listo, queda pendiente realizar los
siguientes pasos:
1. Definicin de Tokens
23 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

2. Definicin de Tipos
3. Definicin de Operadores
4. Definicin de Operaciones

5.2 Definiendo los Tokens


Como en todo lenguaje, primero se deben definir los elementos bsicos
de la sintaxis que son los tipos de tokens a trabajar.
La definicin del lenguaje debe hacerse usando SynFacilSyn, como
herramienta. Por lo tanto se debe usar los mtodos de definicin de lenguaje
de SynFacilSyn, que puede ser un archivo XML externo o usando cdigo (Para
mayor informacin, revisar la documentacin de SynFacilSyn).
Existen elementos que ya se encuentran definidos en SynFacilSyn y
que no se podrn cambiar (y que por lo general no ser necesario). Estos son
la definicin de espacios y saltos de lnea. Estos tokens viene ya predefinidos
y no se cambiarn.
Los primeros elementos a definir (segn SynFacilSyn y por lgica) son
los identificadores, porque estos elementos son bloques bsicos de
construccin de la mayora de lenguajes de programacin.
Otro elemento importante son los nmeros. Por lo general deben seguir
el estndar normal de los nmeros en lenguajes de programacin. Esta
definicin debe incluir a los nmeros enteros o en coma flotante, si es que el
lenguaje lo soporta. Se recomienda que la definicin de nmeros en coma
flotante, reconozca tambin la notacin cientfica: 1.23e-23.
Otros elementos importantes son las cadenas y lo comentarios. Casi
todos los lenguajes admiten la definicin de cadenas y comentarios. Los
comentarios, suelen incluir los de tipo uni-lnea y multi-lnea. Por ejemplo en
lenguaje C++, los comentarios de una sola lnea se definen con el smbolo //
y los de varias lneas con los smbolos /* y */.
La definicin de tokens se debe hacer buscando la facilidad para
realizar el anlisis lxico y sintctico (no pensando en usar el resaltador para
mostrar los tokens coloreados en un editor).
Tambin se debe considerar que la implementacin pueda ejecutarse
rpidamente. Bajo este aspecto, podra convenir aprovechar la velocidad de
reconocimiento que tiene SynFacilSyn, para que nos ayude a identificar
diversos elementos de la sintaxis, no solo a las palabras reservadas.

5.3 Definiendo los Tipos

24 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Una vez definidos los elementos del lenguaje, se tiene que definir los
tipos de datos que manejar el lenguaje. Todos los tipos se crean como
subtipos, de los tipos base.
Los tipos base son:
1. Nmeros Enteros (de hasta 64 bits)
2. Nmeros Enteros sin signo (de hasta 64 bits) No soportado
actualmente
3. Nmeros de coma flotante (de hasta 64 bits)
4. Cadenas
5. Booleanos.
6. Enumerados
Todos los tipos que maneje el lenguaje, deben poder definirse a partir
de estos tipos bsicos, pero no necesariamente deben incluir a todos y en
toda su extensin.
As por ejemplo, podramos simplemente tener un lenguaje con enteros
de hasta 16 bits y sin nmeros en coma flotante (como en una
implementacin para microcontroladores sencillos).
El siguiente cdigo crea un tipo entero de 16 bits, llamado int:
ClearTypes; //para empezar a definir los tipos
tipInt := CreateType('int', t_integer, 2);
//de 2 bytes

El tamao de los tipos enteros, se especifica en cantidad de bytes y


puede ir desde 1 hasta 8.
Al definir los tipos, identificamos tambin a las constantes.
Por defecto el framework, est ya preparado para reconocer algunas
constantes de los tipos base predefinidos, as si encuentra un nmero como
1234, lo identificar por defecto como un entero de 16 bits, esperando que
se haya definido ese tipo (o un tipo de mayor rango), para poder procesarlo,
de otra forma un mensaje de error similar a No se ha definido un tipo para
alojar a esta constante.
Cuando el sistema encuentra una constante numrica, la lee
internamente en un almacenamiento de hasta 64 bits, luego determina el
tamao ms pequeo que lo alojara, y finalmente busca si se encuentra un
tipo igual o de mayor rango al requerido. Este proceso est implementado en
el mtodo TipDefecNumber().

5.4 Definiendo los Operadores


Al definir los tipos de datos, estamos activando el reconocimiento de
operandos, pero an no podemos definir expresiones.
25 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Los tipos por si solos no permitirn crear expresiones. Necesitaremos


primero crear operadores.
A diferencia de muchos lenguajes, los operadores no se definen
independientemente de los tipos. Aqu los operadores se deben definir para
cada tipo con el que queramos crear expresiones (que por lo general son
todos los tipos definidos).
Por ejemplo, si hemos creado el tipo int16, que es un entero de 16
bits, es seguro que deseremos poder sumar, restar, multiplicar y dividir
estos nmeros enteros. Entonces debemos definir los operadores +, -,
* y /, para el tipo int16.
SI tuviramos otro tipo int8, tambin se deben crear los operadores
para este tipo. Los operadores se crean siempre por tipo, no de forma global.
Esto implica que todos los operadores estn asociados a un tipo, no
existen por s solos. Un tipo, sin embargo, no necesita de un operador para
existir.

5.5 Definiendo las Operaciones


Aunque pueda parecer extrao, tener tipos y operadores definidos, no
es suficiente para poder definir expresiones, dentro de Xpres. Se requiere
adems que se definan sobre qu tipo de dato, se aplican los operadores.
A este proceso se le llama crear Operaciones.
Se puede esquematizar el proceso de la siguiente forma:
Tipo
Tipo -> Operador
Tipo -> Operador -> Tipo
Esta forma de definicin de operadores, se puede asemejar a la
forma en que se definen los mtodos para una clase, en la
programacin orientada a objetos. Los operadores vendran a ser
los mtodos y el operando, despus del operador, es como un
parmetro del mtodo.

5.6 Definiendo Funciones


Al crear al lenguaje desde cero, es conveniente dotarle de algunas
funciones ya predefinidas. A estas funciones les llamaremos, funciones del
sistema.

26 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Las funciones del sistema, se crean de la misma forma a como se crea


cualquier funcin.
Se usa el mtodo CreateSysFunction() para crear la funcin, y el
mtodo
CreateParam(), para crear los parmetros de la funcin. A
continuacin vemos cmo definir la funcin puts, con u parmetro de
cadena:
f := CreateSysFunction('puts', tipInt, @fun_puts);
f.CreateParam('',tipStr);

Las funciones se representan como objetos dentro de la tabla de


funciones.
CreateSysFunction(), requiere que se especifique la funcin de
tratamiento a la funcin que estamos implementando. En nuestro ejemplo
se ha definido fun_puts(), como funcin de tratamiento, y se ejecutara
cuando el compilador, encuentre una llamada a esta funcin definida (y que
no tenga error en los tipos de los parmetros).
Se pueden crear as funciones con muchos parmetros o sin
parmetros. Cada parmetro, se crea especificando su tipo. Este tipo debe
haber sido previamente creado en la sintaxis.
Es posible definir funciones con el mismo nombre, pero no con los
mismos parmetros, porque Xpres soporta la sobrecarga de funciones. En el
siguiente cdigo se sobrecarga a la funcin puts, para que soporte
parmetros de cadena o enteros:
f := CreateSysFunction('puts', tipInt, @fun_puts);
f.CreateParam('',tipStr);
f := CreateSysFunction('puts', tipInt, @fun_putsI);
f.CreateParam('',tipInt);
if FindDuplicFunction then exit; //error

La funcin FindDuplicFunction(), permite verificar si la ltima funcin


creada est duplicada (mismo nombre, cantidad y tipo de parmetros). Se
deber usar ms que nada cuando se analice las declaraciones que haga el
programador en el cdigo fuente.

5.7 Creando el Compilador


Sea que vayamos a implementar un intrprete, compilador o alguna
suerte de mquina virtual, el mtodo de trabajo es el mismo. Se necesita
crear una clase derivada de la clase TCompiler:
TCompiler = class(TCompilerBase)
...
end;

27 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

Esta clase dar lugar a nuestro objeto compilador, que debe ser nico y
que nos servir para realizar todo el procesamiento del texto fuente.
Para implementar correctamente al compilador, es necesario sobreescribir algunos mtodos cruciales, entre ellos:
constructor Create; override;
destructor Destroy; override;
procedure CompileCurBlock; override;
procedure TipDefecString(var Op: TOperand; tokcad: string); override;

El constructor se usa para definir la sintaxis del lenguaje.

28 de 30

07/07/2016

Xpres 0.7

Documentacin Tcnica

6 PROCESAMIENTO DEL CDIGO FUENTE


Recordando la estructura de un lenguaje de programacin en Xpres,
tenamos el siguiente diagrama:
B lo q u e s

E x p r e s io n e s

O p e ra n d o s

D e lim it a d o r e s
d e e x p r e s i n

O p e ra d o re s

Tokens

Cada elemento, se extrae con una funcin particular.


Los bloques se extraen con la funcin: CompileCurBlock(), que har
repetidas llamadas a GetExpression(), para extraer una a una las expresiones
del bloque.
Los operandos, se extraen con la funcin GetOperand().

6.1 Expresiones y Operandos


Como se indic, los operandos y operadores conforman a las
expresiones.
Bsicamente a las expresiones, se les considera como un conjunto
ordenado, de al menos un operando, unidos por operadores. En otras
palabras, una expresin sera:
<OPERANDO> <Operador> < OPERANDO > <Operador> ...
Las expresiones y los operandos se consideran como objetos de tipo
TOperand.
Los operandos se consideran a su vez, como sub-expresiones.
Consideremos los siguientes tipos de operandos:
OPERANDOS
5
cadena
true
29 de 30

DESCRIPCIN
Constante.
cualquiera
predefinidos.

CATERGORA DE
OPERANDO
Puede
ser
de coConst
de
los
tipos

07/07/2016

Xpres 0.7
var1
x
(1+x)
x++
log(x)
atan(x+2/3)

30 de 30

Documentacin Tcnica
Variable. Puede ser de cualquiera coVariable
de los tipos predefinidos.
Expresin. Resultado de una coExpres
expresin.
Expresin. Resultado de una coExpres
funcin.

07/07/2016

Вам также может понравиться