def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. El operador menos unario se aplica solamente a expresiones que devuelvan enteros y el tipo de retorno siempre será entero. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueba que la expresión a la que se le va aplicar el operador menos unario esté correcta semánticamente y que tenga valor de retorno entero. El tipo del valor de retorno de la expresión representada por este nodo siempre será C{IntegerType}. """ self._scope = scope errors_before = len(errors) self.expression.check_semantics(scope, errors) if errors_before == len(errors): if self.expression.has_return_value(): if self.expression.return_type != IntegerType(): message = 'The expression of the unary minus operator at line {line} ' \ 'does not return an integer value' errors.append(message.format(line=self.line_number)) else: message = 'The expression of the unary minus operator at line {line} ' \ 'does not return a value' errors.append(message.format(line=self.line_number)) self._return_type = IntegerType()
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. Los operadores cuyas clases del árbol de sintáxis abstracta derivan de esta deben recibir en ambos operandos números enteros o ambos cadenas de caracteres. Siempre tienen tipo de retorno entero (1 para el resultado verdadero, 0 para el falso). En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueban semánticamente tanto la expresión de la izquierda como la expresión de la derecha. Luego se comprueba que ambas retornen valor y que el tipo de retorno de ambas sea enteros o cadenas de caracteres. """ self._scope = scope valid_types = (IntegerType(), StringType()) errors_before = len(errors) self.right.check_semantics(scope, errors) if errors_before == len(errors): if self.right.has_return_value(): if self.right.return_type not in valid_types: message = 'Invalid type of right operand in the binary ' \ 'relational operator at line {line}' errors.append(message.format(line=self.line_number)) else: message = 'Invalid use of binary relational operator with a ' \ 'non-valued right expression at line {line}' errors.append(message.format(line=self.line_number)) errors_before = len(errors) self.left.check_semantics(scope, errors) if errors_before == len(errors): if self.left.has_return_value(): if self.left.return_type not in valid_types: message = 'Invalid type of left operand in the binary ' \ 'relational operator at line {line}' errors.append(message.format(line=self.line_number)) else: message = 'Invalid use of binary relational operator with a ' \ 'non-valued left expression at line {line}' errors.append(message.format(line=self.line_number)) if self.right.return_type != self.left.return_type: message = 'Types of left and right operands of the binary ' \ 'relational operator at line {line} does not match' errors.append(message.format(line=self.line_number)) self._return_type = IntegerType()
def generate_code(self, generator): """ Genera el código C correspondiente a la estructura del lenguaje Tiger representada por el nodo. @type generator: C{CodeGenerator} @param generator: Clase auxiliar utilizada en la generación del código C correspondiente a un programa Tiger. @raise CodeGenerationError: Esta excepción se lanzará cuando se produzca algún error durante la generación del código correspondiente al nodo. La excepción contendrá información acerca del error. """ self.scope.generate_code(generator) self.left.generate_code(generator) self.right.generate_code(generator) result_var = generator.define_local(IntegerType().code_type) if isinstance(self.left.return_type, StringType): stmt = '{result} = (pytiger2c_strcmp({left}, {right}) {op} 0);' elif isinstance(self.left, NilType): stmt = '{result} = ((({left_type}) {left}) {op} {right});' elif isinstance(self.right, NilType): stmt = '{result} = ({left} {op} (({right_type}) {right}));' else: stmt = '{result} = ({left} {op} {right});' stmt = stmt.format(result=result_var, op=self._code_operator, left=self.left.code_name, right=self.right.code_name, left_type=self.left.return_type.code_type, right_type=self.right.return_type.code_type) generator.add_statement(stmt) self._code_name = result_var
def generate_code(self, generator): """ Genera el código C correspondiente a la estructura del lenguaje Tiger representada por el nodo. @type generator: C{CodeGenerator} @param generator: Clase auxiliar utilizada en la generación del código C correspondiente a un programa Tiger. @raise CodeGenerationError: Esta excepción se lanzará cuando se produzca algún error durante la generación del código correspondiente al nodo. La excepción contendrá información acerca del error. """ self.scope.generate_code(generator) result_var = generator.define_local(IntegerType().code_type) self.left.generate_code(generator) generator.add_statement( 'if ({left}) {{'.format(left=self.left.code_name)) generator.add_statement('{result} = 1; }}'.format(result=result_var)) generator.add_statement('else {') self.right.generate_code(generator) generator.add_statement( 'if ({right}) {{'.format(right=self.right.code_name)) generator.add_statement('{result} = 1; }}'.format(result=result_var)) generator.add_statement('else {') generator.add_statement('{result} = 0; }}'.format(result=result_var)) generator.add_statement('}') self._code_name = result_var
def _init_types(self): """ Inicializa los tipos básicos del lenguaje Tiger definidos implícitamente en el ámbito raíz. """ self.define_type('int', IntegerType()) self.define_type('string', StringType())
def generate_code(self, generator): """ Genera el código correspondiente a la estructura del lenguaje Tiger representada por el nodo. Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{generate_code} de la clase C{LanguageNode}. """ self.scope.generate_code(generator) self.right.generate_code(generator) self.left.generate_code(generator) # Check integer division by zero. statement = 'if({var} == 0) {{ pytiger2c_error("{msg}"); }}' statement = statement.format(var=self.right.code_name, msg="Integer division by zero.") generator.add_statement(statement) int_code_type = IntegerType().code_type local_var = generator.define_local(int_code_type) statement = '{var} = {left} {operator} {right};' statement = statement.format(var=local_var, left=self.left.code_name, operator=self._operator, right=self.right.code_name) generator.add_statement(statement) self._code_name = local_var
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. Los operadores cuyas clases del árbol de sintáxis abstracta derivan de esta deben recibir operandos enteros y siempre tendrán tipo de retorno entero (1 para el resultado verdadero, 0 para el falso). En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueban semánticamente tanto la expresión de la izquierda como la expresión de la derecha. Luego se comprueba que ambas retornen valor y que el tipo de retorno de ambas sea entero. """ self._scope = scope errors_before = len(errors) self.right.check_semantics(scope, errors) if errors_before == len(errors): if not self.right.has_return_value(): message = 'Invalid use of binary logical operator with a ' \ 'non-valued right expression at line {line}' errors.append(message.format(line=self.line_number)) elif self.right.return_type != IntegerType(): message = 'Invalid use of binary logical operator with a ' \ 'non-integer right value at line {line}' errors.append(message.format(line=self.line_number)) errors_before = len(errors) self.left.check_semantics(scope, errors) if errors_before == len(errors): if not self.left.has_return_value(): message = 'Invalid use of binary logical operator with a ' \ 'non-valued left expression at line {line}' errors.append(message.format(line=self.line_number)) elif self.left.return_type != IntegerType(): message = 'Invalid use of binary logical operator with a ' \ 'non-integer left value at line {line}' errors.append(message.format(line=self.line_number)) self._return_type = IntegerType()
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. El operador por realiza la multiplicación entre los el valor de la expresión que se encuentra a la izquierda por el valor de la derecha. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueban semánticamente tanto la expresión de la izquierda como la expresión de la derecha. Luego se comprueba que ambas retornen valor y que el valor de retorno de ambas sea entero. """ self._scope = scope errors_before = len(errors) self.right.check_semantics(scope, errors) if errors_before == len(errors): if not self.right.has_return_value(): message = 'Invalid use of times operator with a non-valued ' \ 'right expression at line {line}' errors.append(message.format(line=self.line_number)) elif self.right.return_type != IntegerType(): message = 'Invalid use of times operator with a non-integer ' \ 'right value at line {line}' errors.append(message.format(line=self.line_number)) errors_before = len(errors) self.left.check_semantics(scope, errors) if errors_before == len(errors): if not self.left.has_return_value(): message = 'Invalid use of times operator with a non-valued ' \ 'left expression at line {line}' errors.append(message.format(line=self.line_number)) elif self.left.return_type != IntegerType(): message = 'Invalid use of times operator with a non-integer ' \ 'left value at line {line}' errors.append(message.format(line=self.line_number)) self._return_type = IntegerType()
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. Este nodo del árbol de sintáxis abstracta no requiere comprobación semántica, solamente se da valor al tipo de retorno del nodo que siempre será C{IntegerType}. """ self._scope = scope self._return_type = IntegerType()
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se crea un nuevo ámbito que contendrá la definicion de la variable de índice del ciclo como sólo lectura. Este ámbito tendrá como padre el ámbito donde fue definido el ciclo C{for}. Luegoo, se comprueban semánticamente las expresiones correspondientes a los extremos inferiores y superiores del intervalo. Estas expresiones deben tener valor de retorno entero. Además se comprueba semánticamente la expresión que se ejecutará en cada iteración y esta se deja libre de tener o no valor de retorno. """ integer_type = IntegerType() self._scope = Scope(scope) self.scope.define_variable(self.index_name, VariableType(integer_type, True)) errors_before = len(errors) self.lower_expression.check_semantics(self.scope, errors) if errors_before == len(errors): if not self.lower_expression.has_return_value(): message = 'The expression for the lower bound of the for loop ' \ 'at line {line} does not return a value' errors.append(message.format(line=self.line_number)) elif self.lower_expression.return_type != integer_type: message = 'The return type of the expression for the lower bound ' \ 'of the for loop at line {line} is not integer' errors.append(message.format(line=self.line_number)) errors_before = len(errors) self.upper_expression.check_semantics(self.scope, errors) if errors_before == len(errors): if not self.upper_expression.has_return_value(): message = 'The expression for the upper bound of the for loop ' \ 'at line {line} does not return a value' errors.append(message.format(line=self.line_number)) elif self.upper_expression.return_type != integer_type: message = 'The return type of the expression for the upper bound ' \ 'of the for loop at line {line} is not integer' errors.append(message.format(line=self.line_number)) self.expression.check_semantics(self.scope, errors)
def generate_code(self, generator): """ Genera el código correspondiente a la estructura del lenguaje Tiger representada por el nodo. Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{generate_code} de la clase C{LanguageNode}. """ self.scope.generate_code(generator) int_code_type = IntegerType().code_type local_var = generator.define_local(int_code_type) generator.add_statement('{0} = {1};'.format(local_var, self.integer)) self._code_name = local_var
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. Los operadores cuyas clases del árbol de sintáxis abstracta derivan de esta deben recibir en ambos operandos expresiones con valor de retorno y el tipo de estas debe ser el mismo. Siempre tienen tipo de retorno entero (1 para el resultado verdadero, 0 para el falso). En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueban semánticamente tanto la expresión de la izquierda como la expresión de la derecha. Luego se comprueba que ambas retornen valor y que el tipo de retorno de ambas sea el mismo. """ self._scope = scope errors_before = len(errors) self.right.check_semantics(scope, errors) if errors_before == len(errors): if not self.right.has_return_value(): message = 'Invalid use of equality or inequality logical operator ' \ 'with a non-valued right expression at line {line}' errors.append(message.format(line=self.line_number)) self.left.check_semantics(scope, errors) if errors_before == len(errors): if not self.left.has_return_value(): message = 'Invalid use of equality or inequality logical operator ' \ 'with a non-valued left expression at line {line}' errors.append(message.format(line=self.line_number)) if errors_before == len(errors): if self.right.return_type != self.left.return_type: # Check the special case of nil and records. nil can be assigned # to any record type, then r <> nil and r = nil are legal. valid_different_types = (RecordType, NilType) record_and_nil = (isinstance(self.right.return_type, valid_different_types) and isinstance(self.left.return_type, valid_different_types)) if not record_and_nil: message = 'Types of left and right operands of the equality or ' \ 'inequality logical operator at line {line} does not match' errors.append(message.format(line=self.line_number)) self._return_type = IntegerType()
def generate_code(self, generator): """ Genera el código correspondiente a la estructura del lenguaje Tiger representada por el nodo. Para obtener información acerca de los parámetros recibidos por este método consulte la documentación del método C{generate_code} de la clase C{LanguageNode}. """ self.scope.generate_code(generator) self.expression.generate_code(generator) local_var = generator.define_local(IntegerType().code_type) stmt = '{var} = -1 * {expr};'.format(var=local_var, expr=self.expression.code_name) generator.add_statement(stmt) self._code_name = local_var
def _init_functions(self): """ Inicializa las funciónes de la biblioteca standard del lenguaje Tiger definidas implícitamente el ámbito raíz. """ int_type = IntegerType() string_type = StringType() print_type = FunctionType(None, [string_type], ['']) print_type.code_name = 'tiger_print' printi_type = FunctionType(None, [int_type], ['']) printi_type.code_name = 'tiger_printi' flush_type = FunctionType(None, [], []) flush_type.code_name = 'tiger_flush' getchar_type = FunctionType(string_type, [], []) getchar_type.code_name = 'tiger_getchar' ord_type = FunctionType(int_type, [string_type], ['']) ord_type.code_name = 'tiger_ord' chr_type = FunctionType(string_type, [int_type], ['']) chr_type.code_name = 'tiger_chr' size_type = FunctionType(int_type, [string_type], ['']) size_type.code_name = 'tiger_size' substring_type = FunctionType(string_type, [string_type, int_type, int_type], ['', '', '']) substring_type.code_name = 'tiger_substring' concat_type = FunctionType(string_type, [string_type, string_type], ['', '']) concat_type.code_name = 'tiger_concat' not_type = FunctionType(int_type, [int_type], ['']) not_type.code_name = 'tiger_not' exit_type = FunctionType(None, [int_type], ['']) exit_type.code_name = 'tiger_exit' self._members['print'] = print_type self._members['printi'] = printi_type self._members['flush'] = flush_type self._members['getchar'] = getchar_type self._members['ord'] = ord_type self._members['chr'] = chr_type self._members['size'] = size_type self._members['substring'] = substring_type self._members['concat'] = concat_type self._members['not'] = not_type self._members['exit'] = exit_type
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. La estructura de acceso a array del lenguaje Tiger permite obtener el valor de un array en una posición determinada o asignarle un nuevo valor a este array en la misma posición. Esta estructura recibe la expresión que representa el acceso a la instancia de array y la expresión correspondiente a la posición que se quiere acceder. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se verifica que la expresión que se correspondiente al array retorne valor y que este sea del tipo array, luego se comprueba que la expresión de la posición retorne valor y que este sea de tipo entero. En el proceso de comprobación semántica toman valor las propiedades C{return_type} y C{read_only} """ self._scope = scope self.array.check_semantics(self.scope, errors) if self.array.has_return_value(): array_type = self.array.return_type if isinstance(array_type, ArrayType): self._return_type = array_type.fields_types[0] else: self._return_type = None message = 'Invalid array access on a non array type at line {line}' errors.append(message.format(line=self.line_number)) self.position.check_semantics(self.scope, errors) if not self.position.has_return_value(): message = 'The expression for the position in the array does ' \ 'not have a return value at line {line}' errors.append(message.format(line=self.line_number)) elif self.position.return_type != IntegerType(): message = 'Invalid non integer position for array access at line {line}' errors.append(message.format(line=self.line_number)) else: message = 'Invalid array access on a non valued expression at line {line}' errors.append(message.format(line=self.line_number))
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. La expresión C{while-do} recibe una condición y una expresión, de forma que evalua la condición y si esta es distinta de cero, entonces la expresión es ejecutada. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueban semánticamente tanto la condición como la expresión contenidas en este. Luego se comprueba que la condición retorne valor, que el mismo sea de tipo C{IntegerType} y que la expresión no retorne valor. Se reportarán errores semánticos si se encuentran errores durante la comprobación semántica de la condición o la expresión, si la condición no retorna valor, si este valor de retorno no es te tipo C{IntegerType} o si la expresión retorna algún valor. """ self._scope = scope errors_before = len(errors) self.condition.check_semantics(scope, errors) if errors_before == len(errors): # The condition return type must be IntegerType if not self.condition.has_return_value(): message = 'while used with a non-return condition at line {line}' errors.append(message.format(line=self.line_number)) elif self.condition.return_type != IntegerType(): message = 'Invalid type of condition of the while statement at line {line}' errors.append(message.format(line=self.line_number)) errors_before = len(errors) self.expression.check_semantics(scope, errors) if errors_before == len(errors): # The expression must not return value if self.expression.has_return_value(): message = 'while used with a expression with return value at line {line}' errors.append(message.format(line=self.line_number))
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. La expresión seguida de la instrucción C{if}, cuyo nodo del árbol de sintáxis abstracta está almacenado en la propiedad C{condition}, deberá tener valor de retorno entero. La expresión seguida de la instrucción C{then} no debe tener valor de retorno. En la comprobación semántica de este nodo del árbol de sintáxis abstracta primeramente se comprueba que la expresión seguida de la instrucción C{if} esté correcta semánticamente, que tenga valor de retorno y que sea de tipo C{IntegerType}. Luego se comprueba que la expresión seguida de la instrucción C{then} esté correcta semánticamente y que no tenga valor de retorno. """ self._scope = scope # Check semantics of the condition expression. self.condition.check_semantics(scope, errors) if not self.condition.has_return_value(): message = 'The condition of the if-then statement at line {line} ' \ 'does not return a value' errors.append(message.format(line=self.line_number)) elif self.condition.return_type != IntegerType(): message = 'The condition of the if-then statement at line {line} ' \ 'does not return an integer value' errors.append(message.format(line=self.line_number)) # Check semantics of the then expression. self.then_expression.check_semantics(scope, errors) if self.then_expression.has_return_value(): message = 'The then expression of the if-then statement ' \ 'at line {line} should not return a value' errors.append(message.format(line=self.line_number))
def check_semantics(self, scope, errors): """ Para obtener información acerca del resto de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. La creación de una instancia de un tipo array recibe el nombre del tipo de array que se quiere crear, una expresión que corresponde a la cantidad de elementos que va a tener el array y por último una expresión que corresponde al valor con el que se inicializarán todos los miembros de este nuevo array. En la comprobación semántica de este nodo del árbol de sintáxis abstracta se comprueba que el tipo array que se quiere crear ha sido definido en el ámbito correspondiente, se comprueba que la expresión correspondiente a la cantidad de elementos del array tenga valor de retorno y que este sea entero, por último se comprueba que la expresión correspondiente al valor que se le asignará a cada miembro de este nuevo array retorne tipo y que este sea igual al correspondiente a los valores en la declaración del tipo de array. """ self._scope = scope errors_before = len(errors) try: self._return_type = self.scope.get_type_definition(self.type_name) except KeyError: message = 'Undefined type {type} at line {line}' errors.append(message.format(type=self._type_name, line=self.line_number)) return if isinstance(self.return_type, ArrayType): self.count.check_semantics(self.scope, errors) if errors_before != len(errors): return if not self.count.has_return_value(): message = 'Non value expression for the array length at line {line}' errors.append(message.format(line=self.line_number)) return elif self.count.return_type != IntegerType(): message = 'Non integer expression for the array length at line {line}' errors.append(message.format(line=self.line_number)) return self.value.check_semantics(self.scope, errors) if errors_before != len(errors): return if not self.value.has_return_value(): message = 'Non valued expression for the array value at line {line}' errors.append(message.format(line=self.line_number)) elif self.value.return_type == NilType(): if not isinstance(self.return_type.fields_types[0], RecordType): message = 'Invalid nil value for the array value at line {line}' errors.append(message.format(line=self.line_number)) elif self.value.return_type != self.return_type.fields_types[0]: message = 'Incompatible type for the array value at line {line}' errors.append(message.format(line=self.line_number)) else: message = 'Invalid non array type {type_name} at line {line}' errors.append(message.format(type_name = self._type_name, line=self.line_number))
def check_semantics(self, scope, errors): """ Para obtener información acerca de los parámetros recibidos por el método consulte la documentación del método C{check_semantics} en la clase C{LanguageNode}. La expresión seguida de la instrucción C{if}, cuyo nodo del árbol de sintáxis abstracta está almacenado en la propiedad C{condition}, deberá tener valor de retorno entero. Las expresiones seguidas de las instrucciones C{then} y C{else} deben tener el mismo tipo de retorno o no tener valor de retorno ambas. El tipo del valor de retorno de la expresión C{if-then-else} será el mismo que el de estas expresiones o no retornará valor si estas no lo hacen. En la comprobación semántica de este nodo del árbol de sintáxis abstracta primeramente se comprueba que la expresión seguida de la instrucción C{if} esté correcta semánticamente, que tenga valor de retorno y que sea de tipo C{IntegerType}. Luego, se comprueba que las expresiones seguidas de las instrucciones C{then} y C{else} estén correctas semánticamente y que el tipo de su valor de retorno sea el mismo o ambas no tengan valor de retorno. Para finalizar especifica el tipo de retorno que tendrá la expresión, el cual coincidirá con el tipo de las expresiones. """ self._scope = scope # Check semantics of the condition expression. self.condition.check_semantics(scope, errors) if not self.condition.has_return_value(): message = 'The condition of the if-then-else statement at line {line} ' \ 'does not return a value' errors.append(message.format(line=self.line_number)) elif self.condition.return_type != IntegerType(): message = 'The condition of the if-then-else statement at line {line} ' \ 'does not return an integer value' errors.append(message.format(line=self.line_number)) # Check semantics of the then and else expressions. errors_before = len(errors) self.then_expression.check_semantics(scope, errors) self.else_expression.check_semantics(scope, errors) if errors_before == len(errors): then_returns = self.then_expression.has_return_value() else_returns = self.else_expression.has_return_value() if then_returns and else_returns: if self.then_expression.return_type != self.else_expression.return_type: # Check the special case of nil and records. valid_different_types = (RecordType, NilType) record_and_nil = ( isinstance(self.then_expression.return_type, valid_different_types) and isinstance(self.else_expression.return_type, valid_different_types)) if not record_and_nil: message = 'The return type of the expressions of the if-then-else ' \ 'statement at line {line} is not the same' errors.append(message.format(line=self.line_number)) elif then_returns or else_returns: message = 'One of the expressions of the if-then-else statement at ' \ 'line {line} returns but the other does not' errors.append(message.format(line=self.line_number)) # Set the return type of the expression (if any). if then_returns and else_returns: self._return_type = self.then_expression.return_type