Академический Документы
Профессиональный Документы
Культура Документы
• 1958, Strong y otros proponen una solución al problema de que un compilador fuera portable, y
esta era dividir al compilador en dos fases “front end” (analiza el programa fuente) y “back end”
(genera código objeto para la máquina objeto).
• Aparece BNF (Backus-1960, Naur-1963, Knuth-1964) como una guía para el desarrollo del
análisis sintáctico
• 1959, Sheridan describe un método de parsing de FORTRAN para introducir paréntesis en una
expresión
• A mitad de los 70’s Johnson crea YACC para UNIX (generador de analizadores sintácticos)
Traductor. Cualquier programa que toma como entrada un texto escrito en un lenguaje
llamado fuente y da como salida un programa equivalente en otro lenguaje, el lenguaje
objeto.
Si el lenguaje fuente de un lenguaje de programación de alto nivel y el objeto un lenguaje de
bajo nivel (ensamblador o código de máquina), al traductor se le denomina compilador.
Intérprete. Es un programa que no genera un programa equivalente, sino que toma una
sentencia del programa fuente en un lenguaje de alto nivel y la traduce al código equivalente
y al mismo tiempo lo ejecuta.
En un principio debido a la escasez de memoria se utilizaban más los intérpretes, ahora se
usan más los compiladores (a excepción de JAVA)
Conceptos básicos
2
Gcódigo máquina
• Compilador también define como aquel traductor que tiene como entrada una sentencia en
lenguaje formal y como salida tiene un fichero ejecutable, es decir, realiza una traducción de un
código de alto nivel a código máquina (también se entiende por compilador aquel programa que
proporciona un fichero objeto en lugar del ejecutable final).
Hay miles de lenguajes fuente, desde los lenguajes de programación tradicionales, hasta los
lenguajes especializados que han surgido virtualmente en todas las áreas de aplicación de la
información.
3
Estructura de un compilador
Estructura de un compilador.
Estos componentes léxicos son los operadores, identificadores y palabras reservadas del
lenguaje. Por lo tanto, transforma en flujo de caracteres en un flujo de objetos tokens,
ignorando los comentarios y los espacios en blanco y tabulaciones. Esta primera fase es capaz
de detectar errores de tipo léxico.
Por ejemplo, si en lugar de poner if pusiéramos ifi, el analizador léxico no usaría el token
predefinido IF para esa secuencia de caracteres, sino que entendería que es un identificador
cualquiera (como pudiera ser el nombre de una variable).
Esta etapa detecta errores sintácticos. Siguiendo el ejemplo anterior, si hubiésemos puesto ifi
en lugar de if, esta etapa detectaría que la estructura de un bloque if no es así, detectando así
el error en el código fuente. A la siguiente fase le manda árboles de sintaxis abstracta.
Por ejemplo, un árbol cuyo nodo raíz es PROGRAMA, que se compone de una lista de clases.
Cada una de estas serian arboles que describen los distintos atributos y métodos de la misma,
y así sucesivamente. Esta fase no puede detectar los errores.
- Análisis Semántico: Comprobación de validez semántica. Esta es una de las fases más
complejas del proceso de compilación. Tiene que realizar varias tareas: la resolución de
nombres, el tipo de las expresiones y la evaluación L/R. La resolución de nombres consiste en
comprobar que cada identificador que usemos (ya sea una variable local, parámetro, atributo o
5
método) ha sido previamente declarado y además, si es visible desde el ámbito actual.
Tanto la etapa de análisis como la de síntesis acceden a esta estructura, por lo que se halla 6
muy acoplada al resto de fases del compilador. Por ello conviene dotar a la tabla de símbolos
de una interfaz lo suficientemente genérica como para permitir el cambio de las estructuras
TIPOS DE COMPILADORES
• Compiladores cruzados: generan código para un sistema distinto del que están
funcionando.
• Compiladores optimizadores: realizan cambios en el código para mejorar su
eficiencia, pero manteniendo la funcionalidad del programa original.
• Compiladores de una sola pasada: generan el código máquina a partir de una única
lectura del código fuente.
• Compiladores de varias pasadas: necesitan leer el código fuente varias veces antes
de poder producir el código máquina.
• Compiladores JIT (Just In Time): forman parte de un intérprete (traductor que realiza la
operación de compilación paso a paso), y compilan partes del código según se
necesitan.
Partes de un compilador
Front End: es la parte que analiza el código fuente, comprueba su validez, genera el
árbol de derivación y rellena los valores de la tabla de símbolos. Esta parte suele ser
independiente de la plataforma o sistema para el cual se vaya a compilar.
Back End: es la parte que genera el código máquina, específico de una plataforma, a
partir de los resultados de la fase de análisis, realizada por el Front End.
Esta división permite que el mismo Back End se utilice para generar el código máquina de 7
varios lenguajes de programación distintos y que el mismo Front End que sirve para analizar el
El código que genera el Back End normalmente no se puede ejecutar directamente, sino que
necesita ser enlazado por un programa enlazador (linker).
Definición: El analizador léxico (scanner), lee un texto fuente y lo transforma en una secuencia
ordenada de elementos léxicamente válidos. Un caracter o conjunto de estos que constituya un
componente léxico se llama lexema (token). Como componentes léxicos consideramos:
palabras reservadas, separadores, operadores, identificadores, constantes y signos de
puntuación.
Normalmente se implementa como una función de éste componentes léxicos que utiliza el
analizador sintáctico para hacer el análisis. Esta interacción suele aplicarse convirtiendo al
analizador léxico en una subrutina o corrutina del analizador sintáctico. Recibida la orden
“Dame el siguiente componente léxico”del analizador sintáctico, el léxico lee los caracteres de
entrada hasta que pueda identificar el siguiente componente léxico, el cual devuelve al
sintáctico según el formato convenido (figura anterior).
Analizador léxico lee la secuencia de caracteres del programa fuente, carácter a carácter, y los
agrupa para formar unidades con significado propio, los componentes léxicos (tokens en
ingles). Estos componentes léxicos representan:
El analizador léxico opera bajo petición del analizador sintáctico devolviendo un componente
léxico conforme el analizador sintáctico lo va necesitando para avanzar en la gramática. Los
componentes léxicos son los símbolos terminales de la gramática.
Otras funciones:
• Manejo del fichero de entrada del programa fuente: abrirlo, leer sus caracteres, cerrarlo
y gestionar posibles errores de lectura.
• Eliminar comentarios, espacios en blanco, tabuladores y saltos de línea (caracteres no
validos para formar un token).
• Inclusión de ficheros: # include ...
• La expansión de macros y funciones inline: # define ...
• Contabilizar el número de líneas y columnas para emitir mensajes de error.
• Reconocimiento y ejecución de las directivas de compilación (por ejemplo, para depurar
u optimizar el código fuente).
• Rechazar un carácter o conjunto de estos que no concuerden con patrones
especificados
• Entendamos como patrón una expresión regular que se define en el lenguaje.
• Ignorar comentarios, espacios en blanco y tabuladores.
• Gestionar errores, contando los saltos de línea y asociando los mensajes de error con el
número de la línea del archivo fuente donde se producen.
• Guardar tokens junto con su atributo en una tabla de símbolos. Este atributo es
información adicional relevante, habitualmente con relación a los identificadores.
10
Componentes Toquen, Patrones, Lexemas
Token: “nombre “que se da a cada componente léxico.
Los componentes léxicos se suelen definir como un tipo enumerado. Se codifican como
enteros. También se suele almacenar la cadena de caracteres que se acaba de reconocer (el
lexema), que se usara posteriormente para el análisis semántico. 11
Cualquier carácter.
Opcionalidad ?
12
r? indica que la expresión r puede aparecer o no. En el caso de que aparezca solo lo haría una
vez.
Elaborado por: Beatriz Pasaca| Noveno “A”
Aspectos prácticos en la implementación de un analizador léxico
Un lenguaje de programación puede tener del orden de 50 palabras reservadas. Para evitar
tener un AFD demasiado grande las palabras reservadas se reconocen como identificadores
(una palabra reservada también es una letra seguida de mas letras) y se comprueba antes de
decidir el tipo de token si se trata de una palabra reservada o de un identificador consultando
una tabla previamente inicializada con las palabras reservadas. Este método es recomendable
cuando el número de palabras reservadas es grande.
Los errores léxicos se detectan cuando el analizador léxico intenta reconocer componentes
léxicos y la cadena de caracteres de la entrada no encaja con ningún patrón. Son situaciones
en las que usa un carácter invalido (@, $,",>,...), que no pertenece al vocabulario del lenguaje
de programación, al escribir mal un identificador, palabra reservada u operador.
Errores léxicos típicos son:
Si en el momento de detectar el error ya hemos pasado por algún estado final ejecutamos la acción
correspondiente al último estado final visitado con el lexema formado hasta que salimos de él; el
resto de caracteres leídos se devuelven al flujo de entrada y se vuelve al estado inicial.
Si no hemos pasado por ningún estado final, advertimos que el carácter encontrado no se
esperaba, lo eliminamos y proseguimos con el análisis.
Si se sabe que el siguiente componente léxico es una palabra reservada (en la gramática
esperamos una palabra reservada) es posible corregir la palabra mal escrita. Por ejemplo, si se
ha escrito hwile en vez de while o fi en vez de if, intercambiando caracteres adyacentes.
ANALIZADOR SINTÁCTICO
Definición: Es la fase del analizador que se encarga de chequear la secuencia de tokens que
representa al texto de entrada, en base a una gramática dada. En caso de que el programa de
entrada sea válido, suministra el árbol sintáctico que lo reconoce en base a una representación
computacional. Este árbol es el punto de partida de la fase posterior de la etapa de análisis: el
analizador semántico.
Para obtener en lógica el rigor y precisión deseados, se introduce un lenguaje formal. Éste es
un lenguaje artificial, con unas reglas gramaticales explícitas que indican qué sucesiones de
signos del alfabeto son fórmulas y unas reglas semánticas también explícitas, que determinan
cuando una fórmula es verdadera bajo una determinada interpretación. Éste lenguaje, pues, se
utiliza como vehículo para el razonamiento.
El análisis sintáctico es una aplicación que resulta del estudio de la Teoría de Autómatas y
Lenguajes Formales. El análisis sintáctico es la base del Demostrador de Teoremas, ya que le
permite analizar los componentes léxicos y sintácticos que forman una fórmula para después
poderla evaluar, o no en caso de error, correctamente.
Visión General: Todo lenguaje de programación obedece a unas reglas que describen la
estructura sintáctica de los programas bien formados que acepta. Se puede describir la sintaxis
de las construcciones de los lenguajes de programación por medio de gramáticas de contexto
libre.
Las gramáticas formales ofrecen ventajas significativas a los diseñadores de lenguajes y a los
desarrolladores de compiladores:
• Incorpora acciones semánticas en las que colocar el resto de fases del compilador
(excepto el analizador léxico): desde el análisis semántico hasta la generación de
código.
• Informa de la naturaleza de los errores sintácticos que encuentra e intenta recuperarse
de ellos para continuar la compilación.
• Controla el flujo de tokens reconocidos por parte del analizador léxico.
El analizador obtiene una cadena de componentes léxicos del analizador léxico, como se
muestra en la siguiente figura, y comprueba si la cadena puede ser generada por la gramática
del lenguaje fuente. El analizador sintáctico informará de cualquier error de sintaxis de manera
inteligente. También debería recuperarse de los errores que ocurren frecuentemente para
poder continuar procesando el resto de su entrada.
17
Gramáticas
Las fórmulas de L se construyen siguiendo unas sencillas reglas de formación. Dichas reglas
extraen del conjunto de filas de signos del alfabeto aquellas a las que llamamos fórmulas.
Cuádrupla G= ( å t, å n, S, P).
Donde:
Arboles de Ambigüedad
Supongamos que tenemos la gramática:
19
La sentencia id*id+id, tiene distintas derivaciones:
Puede suceder que una misma cadena tenga asociado más de un _árbol de derivación. Por
ejemplo, la cadena anterior tiene asociados dos _arboles, cada uno con un significado distinto:
20
• Gramáticas de tipo 0 (sin restricciones), que incluye a todas las gramáticas formales.
Estas gramáticas generan todos los lenguajes capaces de ser reconocidos por una
máquina de Turing. Los lenguajes son conocidos como lenguajes recursivamente
enumerables. Nótese que esta categoría es diferente de la de los lenguajes recursivos,
cuya decisión puede ser realizada por una máquina de Turing que se detenga.
• Gramáticas de tipo 1 (gramáticas sensibles al contexto) generan los lenguajes sensibles
Cada lenguaje regular es a su vez libre del contexto, asimismo un lenguaje libre del contexto es
también dependiente del contexto, éste es recursivo y a su vez, recursivamente enumerable.
Las inclusiones son, sin embargo, propias, es decir, existen en cada nivel lenguajes que no
están en niveles anteriores.
Las gramáticas de tipo 2 o Libres de contexto, son las mas útiles para lenguajes de
programación, en la actualidad son la manera estándar de representar la estructura de los
lenguajes desprogramación.
1. Los terminales son los símbolos básicos con que se forman las cadenas.
2. Los no terminales son variables sintácticas que denotan conjuntos de cadenas. Los no
terminales definen conjuntos de cadenas que ayudan a definir el lenguaje generado por la
gramática.
3. En una gramática, un no terminal es considerado como el símbolo inicial, y el conjunto de
cadenas que representan es el lenguaje definido por la gramática.
4. Las producciones de una gramática especifican cómo se pueden combinar los terminales
y los no terminales para formar cadenas. Cada producción consta de un terminal,
22
seguido por algún símbolo y seguida por una cadena de no terminales y terminales.
Notación BNF
Elaborado por: Beatriz Pasaca| Noveno “A”
Utilizada para representar las gramáticas independientes de contexto, que es una escritura
gramatical para especificar lenguajes de programación.
En esta notación se deben seguir las siguientes convenciones:
- S::=λ
Sea la gramática: S → AA
A → AAA | a | bA |Ab
S →AA→ bAA → bbAA → bbaA → bbabA → bbabAAA → bbabaAA → bbabaAbA→ bbabaabA → babaaba
Arboles de derivación
Sea la gramática: S → AB
A → aA | a
B → bB | b
Se puede concluir que la gramática anteriormente definida no es ambigua, ya que a pesar de tener
derivaciones diferentes, posee un solo árbol de derivación.
Sea la gramática: S → SbS | ScS | a , construir el árbol de derivación para la cadena abaca
Derivaciones:
1. S → SbS → abS → abScS → abaca
2. S → SbS → SbScS → abaca
3. S → ScS → SbScS → abaca
• Toda derivación en una gramática regular, es a la vez una derivación por la izquierda y por la
derecha sin tener en cuenta si tenemos una gramática regular por la derecha o por la izquierda.
Gramática:
1. A → bAA | aC | B
2. B → aSS | BC
3. C→ CC | λ
4. S → SS | C A
Para ello reemplazamos la cadena vacía en todos los no terminales que la produzcan así:
1. A → bAA | aC | B
A → bAA | a λ | B | a λ
A → bAA | a | B | a
2. B → aSS | BC | B λ
B → aSS | BC | B
3. C→ CC | C λ
C→ CC | C 26
4. S → SS |CA| λA
S → SS | C A | A
Elaborado por: Beatriz Pasaca| Noveno “A”
La gramática resultante será:
A → bAA | a | B
B → aSS | BC | B
C→ CC | C
S → SS | C A | A
A → bAA | a | aSS | BC
B → aSS | BC
C→ CC
S → SS | C A | bAA | a | aSS | BC
Para esta gramática el no terminal no generativo es C ya que no produce terminal alguno, entonces
eliminamos todas las producciones que lo contengan:
A → bAA | a | aSS | BC
B → aSS | BC
C→ CC
S → SS | C A | bAA | a | aSS | BC
A → bAA | a | aSS
B → aSS
C→ CC
27
S → SS | bAA | aSS | a
A → bAA | a | aSS
B → aSS
C→ CC
S → SS | bAA | aSS | a
P→G
G → | GG | F
T→T+i|i
S→i
EJERCICIO
B → aBC
E → aBEd
Gramática:
<inicio> → _ <guion>
<inicio> → l <letra>
<guion> → l <letra> | n <numero>
<letra> → n <numero> | l_<guion> | λ
<numero> → l <letra>
• Derivación de la cadena _a
S → bA | aB S → DbA | DaB
A → bAA | aS |a A → DbD1 | DaS |Da
B → aBB | bS | b B →DaD2 | bS | b
Da → a
Db → b
D1→ AA
D2 → BB
Gramática 1:
S → AB| Cb
A → f | Al
B → t | CF
D→a|t
Gramática 2:
S → aA| λ
A → aA | bB | λ
B → bB
Gramática 3:
S → a| aA | B | E
A → aB | λ
31
B → Aa
E → bED
D → ccc
Gramática 4:
S → ABaC
A → AB
B → b |λ
C→D|λ
D→d
Análisis
Es el proceso que permite determinar si una cadena puede ser generada por una gramática. En
nuestro caso es el proceso que permite determinar si una fórmula está bien generada o no.
Este proceso es fundamental en el Demostrador de Teoremas ya que una cadena que no sea
una fórmula no servirá para realizar los razonamientos o pruebas. Por tanto es importante
definir precisamente un analizador para esta tarea.
32
Desde el punto de vista formal un analizador es un Autómata equivalente a una gramática, que
reconoce la estructura de una cadena de componentes léxicos.
Otras:
• Reaccionar frente a los errores e intentar acotar la propagación de los errores (intentar
evitar que un error produzca muchos mensajes de error).
• Hacer los siguientes pasos del compilador más independientes de la sintaxis del lenguaje.
El proceso de análisis sintáctico y la ejecución son ahora dos pasos completamente separados,
no se procederá a la ejecución del código de cualquier archivo hasta que éste en su totalidad,
así como todo el código requerido se haya analizado completa y satisfactoriamente.
Uno de los nuevos requisitos introducidos con esta separación es que todos los archivos
requeridos y de inclusión tienen que ser sintácticamente completos ahora. Ya no es permitida
la separación de diferentes segmentos de una estructura de control a través de varios archivos.
Esto quiere decir que ahora no puede iniciar un ciclo for o while, una sentencia if o un bloque
switch en un archivo, y tener el final del ciclo, sentencias else, endif, case o break en un archivo
diferente.
Aun es perfectamente legal incluir código adicional al interior de ciclos u otras estructuras de
control, únicamente las palabras claves de control y los corchetes correspondientes {___}
tienen que estar en la misma unidad de compilación (archivo o cadena procesada por eval ()). 33
Algo más que ya no es posible, aunque es rara veces visto en código PHP 3, es devolver
valores desde un archivo requerido devolver un valor desde un archivo de inclusión es posible
aun.
Un analizador sintáctico, tal y como se ha enfocado en esta librería es una clase que recibe lo
siguiente:
2. Una gramática libre de contexto donde el símbolo de inicio solo produce una variable.
3. Una función de síntesis para cada producción, encargada de sintetizar los atributos
necesarios.
5. Una instancia de una clase de léxico que derive de la clase abstracta de léxico básico.
El analizador ira leyendo del léxico y aplicando las reducciones necesarias (llamando a las
funciones de síntesis proporcionadas). El resultado habitual es un árbol sintáctico construido de
manera ascendente. Cada nodo de ese árbol es del tipo genérico que se le pasa como
parámetro al parser mencionado en la lista anterior.
Analizar sintácticamente una tira o cadena de tokens no es más que encontrar para ella el árbol
sintáctico o de derivación que tiene como raíz el axioma de la gramática, y como nodos
terminales la sucesión ordenada de símbolos que componen la cadena analizada.
34
En caso de no existir este árbol sintáctico, la cadena no pertenecerá al lenguaje, y el analizador
sintáctico ha de emitir el correspondiente mensaje de error.
Análisis descendente:
Análisis ascendente:
Los análisis con retroceso se basan en la prueba sistemática de todas las alternativas posibles,
dando marcha atrás tan pronto como se detecte que el camino seguido es erróneo.
Pueden usarse para cualquier gramática de contexto libre, aunque tienen tres grandes
inconvenientes:
35
Primero, emplean mucho más tiempo para el análisis que los demás analizadores,
dependiendo éste incluso de la ordenación de las reglas gramaticales.
Los métodos más eficientes de análisis (tanto ascendente como descendente) no funcionan
para todas las gramáticas de contexto libre, sino sólo para las gramáticas que cumplen unas
determinadas condiciones.
Para representar el árbol sintáctico que conduce hasta una cadena se asigna a cada regla de
la gramática un número. Se define el parse como la secuencia ordenada de números (de
reglas) aplicadas para construir dicho árbol.
• El parse-izquierdo: Son los números de las reglas de derivación izquierda utilizadas para
generar la cadena a partir del axioma, por tanto correspondiente a un análisis descendente.
• El parse-derecho: Son los números de las reglas de derivación derecha utilizadas para
generar la cadena a partir del axioma, en orden inverso. El tomar el orden inverso viene
condicionado por ser el análisis ascendente el que normalmente utiliza las reglas de derivación
derecha, con lo que el orden en el que aparecen al realizar el análisis es invertido.
Para comprender cada uno de los pasos del algoritmo descrito anteriormente tomaremos como
ejemplo la siguiente gramática:
Ejercicio resuelto en clases:
I -> * I | id A
A -> Ɛ | [E]
E -> I | cte
1. Como inicialización, asociamos a todos los no terminales distintos del inicial el conjunto
vacío y al símbolo inicial le asociamos el conjunto f ($), para que se cumpla la ecuación
(1).
Después, añadimos los símbolos que aparecen inmediatamente después de cada no terminal:
Ahora aplicamos ordenadamente la ecuación (3) hasta que no haya modificaciones. Así
39
Con la regla (E)(I), aumentamos los siguientes de (I):
Si volvemos a repasar las reglas, vemos que los siguientes no varían, así que podemos dar por
terminado el proceso.
SLR:
La mayor inconveniencia de los analizadores LL1 es el conjunto bastante limitado de los
lenguajes LL1. Un conjunto bastante más amplio, que incluye casi todos los lenguajes de
programación, es el SLR.
Definición: Un lenguaje de tipo SLR si existe un analizador SLR que permita analizarlo.
Comentario: Al igual que el analizador SLR, éste se construye siguiendo un algoritmo, por lo
tanto no podemos hablar de tautología.
¿Qué hace?
40
En general, un analizador sintáctico es un autómata a pila. El analizador sintáctico:
Inicializa el compilador y AS en particular.
Plan de desarrollo
Etapas:
4. Añadir al analizador atributos y acciones (en la parte del analizador sintáctico solo la
producción de la salida, tal como se explica en los requisitos de prueba).
Propiamente hablando, sólo el segundo y el tercer punto forman el analizador sintáctico por sí.
El resto es analizador semántico y el generador de código. En todos los modos se requiere una
vista general al definir como vamos a realizar el analizador sintáctico.
ANALIZADOR SEMÁNTICO.
Introducción.
41
Es aquel que se ocupa de analizar si la sentencia de caracteres tiene algún significado. Se
pueden encontrar sentencias que son sintácticamente correctas pero que no se pueden
El análisis semántico utiliza como entrada el árbol sintáctico detectado por el análisis sintáctico
para comprobar restricciones de tipo y otras limitaciones semánticas y preparar la generación
de código. El instrumento más utilizado para conseguirlo es la gramática de atributos.
Gramáticas semánticas
Una gramática semántica es una gramática libre de contexto en la cual la elección de símbolos
no terminales y de reglas de producción son regidos tanto por funciones sintácticas como
semánticas. Muchos símbolos no terminales representan elementos semánticos; esto es, que
su posición depende también del tema que se este tratando. Una gramática semántica puede
ser usada por un sistema de análisis gramatical de la misma forma en que pudiera ser usada
una gramática estrictamente sintáctica. Las principales ventajas son las siguientes:
• Debido a lo anterior, el análisis gramatical puede ser muy lento y ocupar mucho
espacio en memoria.
42
Se puede observar que las gramáticas semánticas pueden ser útiles para producir interfaces de
subconjuntos de lenguaje natural.
Una gramática con atributos es una gramática de contexto libre cuyos símbolos pueden
tener asociados atributos y las producciones pueden tener asociadas reglas de evaluación
de los atributos.
• Los valores de los atributos deberán estar asociados con un dominio de valores.
• Por lo general las gramáticas con atributos se escriben en forma tabular, con cada
regla Gramatical enumerada con el conjunto de ecuaciones de atributo o reglas
semánticas asociadas con esa regla de manera siguiente:
… ….
Ejemplo:
exp term term * factor factor * factor (exp) * facto (exp – term ) * factor
(term – term ) * factor (factor – term) * factor (numero – term ) * factor
(numero –factor) * factor (numero –numero) * factor ( numero –numero) *numero
Luego de ello podemos construir el árbol de análisis gramatical para (34-3)*42, mostrando
cálculos del atributo val.
44
Nótese que el atributo principal de una exp o term o factor es su valor numérico (val) el
cual va ha formar parte de las ecuaciones para las reglas semánticas que se muestran a
continuación:
45
46
FUENTES PRIMARIAS:
FUENTES SECUNDARIAS:
48