Skip to content

Especificación

Extracción de Requisitos

En la descripción del lenguaje no están expresamente indicados los requisitos de esta fase ya que, por lo conocido de éstos, se sobreentienden. De todas formas, por completitud, se incluyen a continuación:

  • La sentencia print deberá obtener el valor de la expresión y a continuación enviarlo a la salida estándar.
  • La sentencia asignación deberá obtener el valor de la expresión de la derecha y, a continuación, guardarlo en la dirección de memoria que represente la expresión de la izquierda.
  • El valor de una expresión aritmética es el resultado de aplicar el operador indicado a los valores de sus dos expresiones, obteniendo un valor del mismo tipo que estos.

Creación de la Especificación

Introducción

Dado que el objetivo de esta fase es generar código, se necesita un metalenguaje que exprese de manera precisa qué código se va a generar ante cualquier estructura del lenguaje. El metalenguaje que se usará en esta fase es la especificación de código. Y dicha especificación no es mas que un conjunto de funciones de código.

En matemáticas, una función asocia un elemento de un dominio X en un elemento del codominio Y.

f: X → Y

En una función de código, el dominio debe ser un símbolo de la gramática abtracta y el codominio un conjunto de instrucciones del lenguaje de salida. La notación, a diferencia de la función matemática, es la siguiente:

funcion⟦dominio⟧ = instrucciones

Por ejemplo, para indicar que una función value recibe como parámetro una expression, se indicaría así:

bnf
value⟦variable⟧ = instrucciones

Es aconsejable que el nombre de una función indique el qué hacen las instrucciones que produce si se ejecutan. Por tanto, en el caso anterior, el nombre indica que las instrucciones que genera son aquellas que, al ejecutarlas, calculen el valor de la expresión sobre la que se aplique.

Dado que a una categoría pueden pertenecer distintos nodos (por ejemplo, a la categoría expression pertenecen los nodos arithmetic, variable, intLiteral, ... ), para definir completamente un función de código hay que definir una plantilla de código por cada nodo que pertenezca al dominio de la función.

TIP

La relación entre una función de código y sus plantillas de código recuerda a la relación entre un método de un interface y la implementación del mismo en cada uno de las clases que lo implementan.

Documento Inicial

Para hacer una especificación de código hay que indicar todas las funciones del mismo (nombre y dominio) y las plantillas de cada una de ellas.

En vez de comenzar con un documento vacío, se puede utilizar el esqueleto de dicho metalenguaje generado por VGen en vgen.output/skeleton.specifications. Ahí se generó un fichero Code Specification.template.html en el cual ya aparece la estructura de la especificación de código.

En el esqueleto generado se puede ver:

  • En la columna de la izquierda se ha generado una función de código ƒi para cada símbolo o categoría de la gramática abstracta.
  • En la segunda columna aparece una plantilla de código para cada nodo de la categoría.

Una vez abierto dicho documento en Word (o editor equivalente) se deberán realizar las siguientes tareas para completar la especificación:

  • En la primera columna, se cambia el nombre de las funciones (ƒ1, ƒ2, …) para que expresen mejor la labor que realiza el código que generan (run, execute, value, ...).
  • En la segunda columna, se completará las plantillas. Cada plantilla deberá indicar qué código genera dicha función para dicho nodo.

Formato de las Plantillas de Código

Las plantillas de código están formadas básicamente por dos elementos: el código que hay que generar y llamadas a otras funciones de código.

Supóngase la siguiente plantilla:

plaintext
value⟦variable → name:string⟧ =
     address⟦variable⟧
     LOAD<variable.type>
  • La plantilla anterior comienza con una llamada a la función address, indicando que en este punto hay que generar el mismo código que genera dicha función.
  • Finalmente indica que hay que generar la intrucción LOAD. El código a generar se pondrá en mayúsculas en este tutorial

En la especificación anterior se usará, además, una notación específica para la generación del lenguaje MAPL. En este lenguaje existen versiones de una misma instrucción para distintos tipos. Estas se diferencia por la última letra de la instrucción. Así, las instrucciones LOADI y LOADF cargan un entero y un real, respectivamente.

Para no tener que estar escribiendo todas las versiones de una instrucción, se ha usado un atajo con el siguiente formato:

INSTRUCTION<type_expression>

La notación anterior, en función del tipo de la expresión, generará la versión adecuada de la instrucción en función del tipo. Por ejemplo:

LOAD<int>	→	LOADI
LOAD<float>	→	LOADF

Especificación

Una vez definidas todas las funciones y sus plantillas, el resultado obtenido es el siguiente:

Code Functions

FunctionsCode Templates
run⟦programrun⟦program → varDefinition* statement*⟧ =
     metadata⟦program⟧
     execute⟦statementi
     HALT
execute⟦statementexecute⟦print → expression⟧ =
     #LINE {end.line}
     value⟦expression⟧
     OUT<expression.type>

execute⟦assignment → left:expression right:expression⟧ =
     #LINE {end.line}
     address⟦left⟧
     value⟦right⟧
     STORE<left.type>

value⟦expressionvalue⟦arithmetic → left:expression operator:string right:expression⟧ =
     value⟦left⟧
     value⟦right⟧
     if operator == "+"
         ADD<arithmetic.type>
     if operator == "-"
         SUB<arithmetic.type>
     if operator == "*"
         MUL<arithmetic.type>
     if operator == "/"
         DIV<arithmetic.type>

value⟦variable → name:string⟧ =
     address⟦variable⟧
     LOAD<variable.type>

value⟦intLiteral → intValue:int⟧ =
     PUSH {intValue}

value⟦floatLiteral → floatValue:float⟧ =
     PUSHF {floatValue}

address⟦expression⟧address⟦arithmetic → left:expression operator:string right:expression⟧ =
    

address⟦variable → name:string⟧ =
     PUSHA {variable.varDefinition.address}

address⟦intLiteral → intValue:int⟧ =
    

address⟦floatLiteral → floatValue:float⟧ =
    

metadata⟦programmetadata⟦program → varDefinition* statement*⟧ =
     #SOURCE {source_file}
     metadata⟦varDefinitioni
metadata⟦varDefinitionmetadata⟦varDefinition → type name:string⟧ =
     #GLOBAL {name}: {maplType(type)}

Auxiliary Functions

NameDescription
maplType(type)Returns the MAPL equivalent to the MLang type argument:
    intType -> int
    floatType -> float

La función address debe definir, como toda función, una plantilla por cada nodo de su dominio (las expression). Sin embargo, si el programa es válido, sólo es posible que una de ellas sea invocada. Por ejemplo, no tiene sentido pedir la dirección (address) de un literal entero. Por tanto, se documenta en la especificación que el invocar estas plantillas debería producir un error.