public override void CheckSemantic(Scope scope, IList<Error> errors) { //El 'for' define un nuevo scope Scope = new Scope(scope); //Anadimos una variable de tipo entero de solo lectura al scope del 'for' Scope.Add(VariableName, IntegerType.Create(), true); //Evaluamos la expresion de inicio y la de fin. //OJO: Con el Scope que veniamos usando hasta ahora, pues la variable del 'for' no es visible para estas expresiones int errorsCount = errors.Count; ExpressionInitial.CheckSemantic(scope, errors); ExpressionFinal.CheckSemantic(scope, errors); //Si ocurrio algun error, lo mas probable es que no se haya asignado un tipo de retorno a estas expresiones, por lo que terminamos if (errorsCount != errors.Count) { return; } //Comprobamos que retornen valores las expresiones anteriores y que sea entero ErrorsHelper.CheckIfReturnTypeIsInt(ExpressionInitial, errors, "The expression that defines the first value in the 'for' must return a value", "The expression that defines the first value in the 'for' must return an integer"); ErrorsHelper.CheckIfReturnTypeIsInt(ExpressionFinal, errors, "The expression that defines the last value in the 'for' must return a value", "The expression that defines the last value in the 'for' must return an integer"); //Comprobamos el cuerpo Body.CheckSemantic(Scope, errors); //Comprobamos que retorne algun valor if (Body.ReturnsValue()) { errors.Add(new MessageError(Line, Column, "Expression body of 'for' must not return a value")); } }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobamos que lo de la izquierda sea un record //Luego que exista el campo al que estamos accediendo int errorsCount = errors.Count; //Chequeamos semanticamente la expresion a la que estamos accediendo Left.CheckSemantic(scope, errors); if (errorsCount != errors.Count) { return; } //Si no retorna valor la expresion ErrorsHelper.CheckIfReturnsValue(Left, errors, "The expression being accessed must return a value"); if (errorsCount != errors.Count) { return; } //Si no es un record if (!(Left.ReturnType is RecordType)) { errors.Add(new UnexpectedTypeError(Line, Column, Left.ReturnType, "record")); return; } //Si es un record, comprobamos que exista ese campo... var record = (RecordType) Left.ReturnType; if (!record.Fields.ContainsKey(FieldName)) { errors.Add(new FieldNotFoundError(Line, Column, record.Name, FieldName)); } else { //El tipo de retorno sera el mismo que el del campo del record ReturnType = record.Fields[FieldName]; } }
/// <summary> /// Chequea semanticamente el cuerpo de la funcion /// Ademas, comprueba que el tipo de retorno especificado coincide con el del cuerpo de la funcion /// </summary> /// <param name="scope"></param> /// <param name="errors"></param> public void CheckBodySemantic(Scope scope, IList<Error> errors) { int errorsCount = errors.Count; Body.CheckSemantic(Scope, errors); //Si ocurrio algun error... if (errorsCount != errors.Count) { return; } //Comprobamos que el tipo de retorno especificado coincida con el del cuerpo de la funcion if (Type != null) { var type = scope.GetType(Type); //Si la expresion no retorna ningun valor if (!Body.ReturnsValue()) { errors.Add(new NonValueAssignmentError(Line, Column, Name)); } //Comprobamos que el tipo del cuerpo de la funcion sea igual al especificado else if (type != Body.ReturnType) { //Si no pasa que el tipo es 'nil' y puede ser asignado (es decir, si el tipo no es 'nil', o si el tipo es 'nil' y no puede ser asignado if (!(Body.ReturnType is NilType && NilType.CanBeAssignedTo(type))) { errors.Add(new UnexpectedTypeError(Line, Column, type, Body.ReturnType)); } } //TODO: Es posible retornar Nil aqui? } }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //ReturnType = Else.ReturnType Condition.CheckSemantic(scope, errors); //Comprobamos que Condition retorne un entero ErrorsHelper.CheckIfReturnTypeIsInt(Condition, errors, "The condition must return a value", "The condition must return an integer"); int errorsCount = errors.Count; ThenBody.CheckSemantic(scope, errors); if (errorsCount != errors.Count) { return; } //Si no existe Else... if (ElseBody == null) { //Si retorna valor... if (ThenBody.ReturnsValue()) { errors.Add(new MessageError(Line, Column, "'then' expression must not return a value")); } } //Si existe el else... else { //Ambas expresiones deben, o retornar el mismo tipo, o no retornar valor ElseBody.CheckSemantic(scope, errors); //Comprobamos si ambos no retornan tipo if (!ThenBody.ReturnsValue() && !ElseBody.ReturnsValue()) { //Entonces esta expresion no retorna nada ReturnType = null; return; } //Si retornan tipos diferentes... if (ThenBody.ReturnType != ElseBody.ReturnType) { //Si alguno de los dos es nil... if (ThenBody.ReturnType is NilType || ElseBody.ReturnType is NilType) { var nonNilType = ThenBody.ReturnType is NilType ? ElseBody.ReturnType : ThenBody.ReturnType; //Si no pasa que el tipo es 'nil' y puede ser asignado (es decir, si el tipo no es 'nil', o si el tipo es 'nil' y no puede ser asignado) if (!(NilType.CanBeAssignedTo(nonNilType))) { errors.Add(new MessageError(Line, Column, "In if-then-else expressions, both 'then' and 'else' expressions must either return no value or be of the same type")); } } else { errors.Add(new MessageError(Line, Column, "In if-then-else expressions, both 'then' and 'else' expressions must either return no value or be of the same type")); } } //Si retornan el mismo tipo else { ReturnType = ThenBody.ReturnType; } } }
private void AddStringVariable(string name, Scope s = null) { if (s == null) _scope.Add(name, StringType.Create()); else { s.Add(name, StringType.Create()); } }
public void CheckExistence_OneTypeInParent_TwoScopes() { var s = new Scope(); s.Add(StringType.Create()); _scope = new Scope(s); Assert.That(_scope.ExistsType("string")); Assert.That(!_scope.ExistsType("string", false)); }
public void CheckExistence_OneVariableInParent_TwoScopes() { var s = new Scope(); AddStringVariable("x", s); _scope = new Scope(s); Assert.That(_scope.ExistsVariableOrCallable("x")); Assert.That(!_scope.ExistsVariableOrCallable("x", false)); }
public void Two_Scopes() { var s = new Scope(); s.Add("x", StringType.Create()); Scope.Parent = s; var ast = Utils.BuildAST(@"let var x := 10 in x + 2 end"); ast.CheckSemantic(Scope, Errors); Assert.That(Errors.Count == 0); Assert.That(ast.ReturnType is IntegerType); }
public Scope(Scope parent) { DefinedTypes = new Dictionary<string, TigerType>(); DefinedVariables = new Dictionary<string, Variable>(); DefinedCallables = new Dictionary<string, Callable>(); Parent = parent; InvalidCallableNames = new List<string>(); //TODO: Eliminar, y subirlo para el scope padre InvalidTypeNames = new List<string> {"string", "int"}; }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Chequeamos la semantica del cuerpo de la asignacion //Otro nodo se encargara de comprobar que el nombre del campo sea correcto, el tipo, etc Body.CheckSemantic(scope, errors); ErrorsHelper.CheckIfReturnsValue(Body, errors, string.Format( "The expression to be assigned to the field '{0}' must return a value", FieldName)); ReturnType = Body.ReturnType; }
public override void CheckSemantic(Scope scope, IList<Error> errors) { Scope = scope; //Comprobamos que exista la variable con ese nombre en el scope if (!scope.ExistsVariable(Name)) { errors.Add(new UndefinedVariableError(Line, Column, Name)); } else { var variable = scope.GetVariable(Name); //Asignamos el tipo de retorno ReturnType = variable.Type; } }
public void AlreadyExistingType_ButInOuterScope() { var s = new Scope(); s.Add(new RecordType("someRecord", new Fields())); Scope.Parent = s; var ast = (LetInEndNode)Utils.BuildAST("let type someRecord = array of string in end"); ast.CheckSemantic(Scope, Errors); Assert.That(Errors.Count == 0); Assert.That(ast.Scope.ExistsType("someRecord")); Assert.That(ast.Scope.GetType("someRecord") is ArrayType); var type = (ArrayType)ast.Scope.GetType("someRecord"); Assert.That(type.ElementsType is StringType); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Chequeamos la semantica del cuerpo int errorsCount = errors.Count; Body.CheckSemantic(scope, errors); if (errorsCount != errors.Count) { return; } //Comprobamos que la expresion retorne algun valor, y que sea entero ErrorsHelper.CheckIfReturnTypeIsInt(Body, errors); //El tipo de retorno de esta expresion es entero ReturnType = IntegerType.Create(); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Si no hay expresiones, no se hace nada. El tipo de retorno sigue siendo Null if (Sequence.Count == 0) { return; } //Chequeamos semanticamente cada expresion foreach (var expr in Sequence) { expr.CheckSemantic(scope, errors); } //El tipo de retorno sera el de la ultima expresion ReturnType = Sequence[Sequence.Count - 1].ReturnType; }
/// <summary> /// Chequea que ambos operandos sean enteros y que retornen valor /// Importante: Los nodos que difieran en el chequeo semantico, deben sobreescribirlo /// </summary> /// <param name="scope"></param> /// <param name="errors"></param> public override void CheckSemantic(Scope scope, IList<Error> errors) { //Se comprueban ambos nodos (izquierda y derecha) int errorsCount = errors.Count; Left.CheckSemantic(scope, errors); Right.CheckSemantic(scope, errors); //Si ocurrio algun error if (errorsCount != errors.Count) { return; } //Comprobamos que ambas expresiones retornan valor ErrorsHelper.CheckIfReturnsValue(Left, errors, GetNoValueMessage("Left")); ErrorsHelper.CheckIfReturnsValue(Right, errors, GetNoValueMessage("Right")); if (errorsCount != errors.Count) { return; } //Comprobamos que ambas expresiones sean del tipo permitido por este nodo CheckIfAreTypesAllowed(errors); if (errorsCount != errors.Count) { return; } //Chequeamos que ambas expresiones retornen el mismo tipo (en Tiger) if (Left.ReturnType != Right.ReturnType) { //Si no retornan el mismo tipo, pero si un tipo es nil if (Left.ReturnType is NilType || Right.ReturnType is NilType) { //Comprobamos que el otro tipo sea 'compatible' con nil var nonNilType = Left.ReturnType is NilType ? Right.ReturnType : Left.ReturnType; if (!NilType.CanBeAssignedTo(nonNilType)) { errors.Add(new OperatorError(Line, Column, OperatorName, Left.ReturnType, Right.ReturnType)); } } else { errors.Add(new OperatorError(Line, Column, OperatorName, Left.ReturnType, Right.ReturnType)); } } //Si ambos tipos son nil else if (Left.ReturnType is NilType && Right.ReturnType is NilType) { errors.Add(new MessageError(Line, Column, "The type of the two expressions cannot be inferred because 'nil' was used")); } //El tipo de retorno de esta expresion sera entero ReturnType = IntegerType.Create(); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobando que exista el record if (!scope.ExistsType(Name)) { errors.Add(new UndefinedTypeError(Line, Column, Name)); return; } var type = scope.GetType(Name); var array = type as ArrayType; //Si el tipo que obtuvimos no es un record... if (array == null) { errors.Add(new UnexpectedTypeError(Line, Column, type, Name)); return; } //El tipo de retorno sera el de la definicion del array ReturnType = array; int errorsCount = errors.Count; Count.CheckSemantic(scope, errors); //Si hubo errores if (errorsCount != errors.Count) { return; } //Si la expresion que da la cantidad de elementos del array no es entera ErrorsHelper.CheckIfReturnTypeIsInt(Count, errors, string.Format("The size expression of the array '{0}' must return a value", Name), string.Format("The size expression of the array '{0}' must be an integer", Name)); errorsCount = errors.Count; InitialValue.CheckSemantic(scope, errors); if (errorsCount != errors.Count) { return; } //Que el tipo de InitialValue sea igual al tipo definido en el array if (array.ElementsType != InitialValue.ReturnType) { //Si no pasa que el tipo es 'nil' y puede ser asignado (es decir, si el tipo no es 'nil', o si el tipo es 'nil' y no puede ser asignado if (!(InitialValue.ReturnType is NilType && NilType.CanBeAssignedTo(array.ElementsType))) { errors.Add(new UnexpectedTypeError(Line, Column, InitialValue.ReturnType, array.ElementsType)); } } }
public override void CheckSemantic(Scope scope, IList<Error> errors) { int errorsCount = errors.Count; //Chequear que el tipo que almacenara el array exista en el scope if (!scope.ExistsType(TypeName)) { errors.Add(new UndefinedTypeError(Line, Column, TypeName)); } //Si ocurrio algun error if (errorsCount != errors.Count) { return; } //Anadimos el nuevo tipo definido al scope scope.Add(new ArrayType(Name, scope.GetType(TypeName)), updateIfExists: true); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //La condicion debe retornar entero //El cuerpo no puede retornar valor //No retorna nada int errorsCount = errors.Count; Condition.CheckSemantic(scope, errors); Body.CheckSemantic(scope, errors); //La condicion debe retornar un entero ErrorsHelper.CheckIfReturnTypeIsInt(Condition, errors, "The condition must return a value", "The condition must return an integer"); //El cuerpo no puede retornar valor if (Body.ReturnsValue()) { errors.Add(new MessageError(Line, Column, "Expression body of 'while' must not return a value")); } }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobamos que lo de la izquierda sea un record //Luego que exista el campo al que estamos accediendo int errorsCount = errors.Count; //Chequeamos semanticamente la expresion a la que estamos accediendo Left.CheckSemantic(scope, errors); if (errorsCount != errors.Count) { return; } //Chequeamos que el indice sea entero Index.CheckSemantic(scope, errors); ErrorsHelper.CheckIfReturnsValue(Index, errors, "The index expression must return a value"); if (!(Index.ReturnType is IntegerType)) { errors.Add(new UnexpectedTypeError(Line, Column, Index.ReturnType, "int")); } if (errorsCount != errors.Count) { return; } //Si no retorna valor la expresion ErrorsHelper.CheckIfReturnsValue(Left, errors, "The expression being accessed must return a value"); if (errorsCount != errors.Count) { return; } //Si no es un record if (!(Left.ReturnType is ArrayType)) { errors.Add(new UnexpectedTypeError(Line, Column, Left.ReturnType, "record")); return; } //Si es un record, comprobamos que exista ese campo... var array = (ArrayType)Left.ReturnType; //El tipo de retorno, sera el tipo de retorno de los elementos que almacena el array ReturnType = array.ElementsType; }
public override void CheckSemantic(Scope scope, IList<Error> errors) { int errorsCount = errors.Count; //Comprobamos que este correctamente Left Left.CheckSemantic(scope, errors); //Comprobamos que este correctamente Body Body.CheckSemantic(scope, errors); //Si ocurrio algun error, paramos if (errorsCount != errors.Count) { return; } //Comprobamos que ambas expresiones retornen valor... ErrorsHelper.CheckIfReturnsValue(Left, errors, "The expression that is being assigned a value must return a value"); ErrorsHelper.CheckIfReturnsValue(Body, errors, "The expression to be assigned must return a value"); if (errorsCount != errors.Count) { return; } //Si 'Left' es una variable, y es de solo lectura => Error! if (Left is VariableAccessNode) { var varAccessNode = (VariableAccessNode) Left; //Buscamos la variable a la que estamos accediendo en el scope var variable = scope.GetVariable(varAccessNode.Name); if (variable.ReadOnly) { errors.Add(new MessageError(Line, Column, string.Format("Cannot assign value to read-only variable '{0}'", variable.Name))); } } //Si el tipo de retorno es diferente if (Body.ReturnType != Left.ReturnType) { //Si se esta asignando nil, comprobar que el tipo lo permita if (!(Body.ReturnType is NilType) || !NilType.CanBeAssignedTo(Left.ReturnType)) { errors.Add(new UnexpectedTypeError(Line, Column, Body.ReturnType, Left.ReturnType)); } } //Esta expresion no retorna valor }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobamos que la funcion exista if (!scope.ExistsCallable(Name)) { errors.Add(new UndefinedFunctionError(Line, Column, Name)); return; } //Obtenemos la funcion que llamaremos var callable = scope.GetCallable(Name); if (callable.Fields.Count != Parameters.Count) { errors.Add(new MessageError(Line, Column, "The arguments of the call do not match the parameters of the defined function")); //Para poder continuar el chequeo semantico y detectar mas errores en el programa... AssignReturnType(scope, callable); return; } for (int i = 0; i < Parameters.Count; i++) { var parameter = Parameters[i]; var field = callable.Fields[i]; //Chequeamos la semantica del argumento parameter.CheckSemantic(scope, errors); ErrorsHelper.CheckIfReturnsValue(parameter, errors, string.Format("The argument '{0}' must return a value", i)); if (parameter.ReturnsValue()) { //Comprobamos que el tipo de retorno coincida con la definicion de la funcion var definedType = scope.GetType(field.TypeId); var returnType = parameter.ReturnType; //Si el tipo de retorno es diferente... if (definedType != returnType) { errors.Add(new MessageError(Line, Column, string.Format("Argument '{0}' is not assignable to parameter type '{1}'", returnType, definedType))); } } } //El tipo de retorno es el tipo de retorno de la funcion al definirse (si es funcion) AssignReturnType(scope, callable); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { IList<ExpressionNode> ancestors = new List<ExpressionNode>(); var parent = Parent; bool found = false; while (parent != null) { //Vamos guardando el camino... ancestors.Add(parent); //Si encontramos a un ciclo, paramos if (parent is LoopNode) { found = true; break; } //Si encontramos una declaracion de funcion/procedimiento, sin haber encontrado un ciclo, no es valido... if (parent is CallableDeclarationNode) { var dec = (CallableDeclarationNode) parent; errors.Add(new MessageError(Line, Column, string.Format("'break' expression is inside inside the function/procedure '{0}'", dec.Name))); return; } parent = parent.Parent; } //Si llegamos al nodo raiz y no encontramos un ciclo... if (!found) { errors.Add(new MessageError(Line, Column, string.Format("'break' expression must be inside either a 'while' or 'for' expression"))); return; } //Marcamos todos los nodos del camino como 'rompibles' foreach (var expr in ancestors) { expr.IsBreakable = true; } }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobar que no haya sido definido este tipo en este mismo scope //Comprobar que no haya dos campos con un mismo nombre //Comprobar que todos los tipos de los campos esten definidos int errorsCount = errors.Count; IDictionary<string, TigerType> fields = new Dictionary<string, TigerType>(); foreach (var field in Fields) { bool errorInField = false; //Comprobando que el tipo de este campo este definido if (!scope.ExistsType(field.TypeId)) { errors.Add(new UndefinedTypeError(Line, Column, field.TypeId)); errorInField = true; } //Comprobando que no exista ya un campo con este nombre if (fields.ContainsKey(field.Id)) { errors.Add(new DuplicateFieldError(Line, Column, field.Id)); errorInField = true; } if (!errorInField) { fields.Add(field.Id, scope.GetType(field.TypeId)); } } //Si ocurrio un error if (errorsCount != errors.Count) { return; } //Si no ocurrio ningun error, actualizamos la definicion de este tipo //Guardamos el record en este nodo para usarlo en la generacion de codigo RecordType = new RecordType(Name, new Fields(fields)); scope.Add(RecordType, updateIfExists: true); }
public override void CheckSemantic(Scope scope, IList<Error> errors) { //Comprobamos que no exista una variable/funcion en este mismo scope definida if (scope.ExistsVariableOrCallable(Name, false) || scope.IsCallableNameInvalid(Name)) { errors.Add(new AlreadyDefinedError(Line, Column, Name)); return; } int errorsCount = errors.Count; IList<string> parameters = new List<string>(); //Creamos el scope de la nueva funcion Scope = new Scope(scope); //Chequeamos los parametros... foreach (var field in Fields) { bool errorInField = false; //Comprobar que el tipo exista if (!scope.ExistsType(field.TypeId)) { errors.Add(new UndefinedTypeError(Line, Column, field.TypeId)); errorInField = true; } //Si existe un parametro con ese mismo nombre if (parameters.Contains(field.Id)) { errors.Add(new DuplicateParameterError(Line, Column, field.Id)); errorInField = true; } else { parameters.Add(field.Id); } //Anadimos este parametro al scope de la funcion if (!errorInField) { Scope.Add(field.Id, scope.GetType(field.TypeId)); } } //Si me especificaron un tipo y este no existe if (Type != null && !scope.ExistsType(Type)) { errors.Add(new UndefinedTypeError(Line, Column, Type)); } //Si hubo errores en los parametros no seguimos o en la definicion explicita del tipo de la funcion... if (errorsCount != errors.Count) { return; } //Si estamos aqui es porque no hubo ningun error anteriormente //OJO: No chequeamos la semantica del cuerpo de la funcion. Lo haremos en una segunda pasada //Si no hubo ningun problema... if (errorsCount == errors.Count) { Callable = new Callable(Name, Fields, Type); scope.AddCallable(Callable); } }
public void Init() { _scope = new Scope(); }
private void ProcessVariableDeclarations(IList<DeclarationNode> group, Scope scope, IList<Error> errors) { //Las variables no necesitan un trato especial foreach (var dec in @group) { dec.CheckSemantic(scope, errors); } }
private void ProcessTypeDeclarations(IList<DeclarationNode> groupOfDeclarations, Scope scope, IList<Error> errors) { int errorsCount = 0; //Anadimos los encabezados de las declaraciones de los tipos //Y a la vez, comprobamos que no exista un tipo con ese nombre (en este scope solamente) foreach (var dec in groupOfDeclarations) { if (scope.ExistsType(dec.Name, false) || scope.IsTypeNameInvalid(dec.Name)) { errors.Add(new AlreadyDefinedError(dec.Line, dec.Column, dec.Name)); break; } scope.Add(null, dec.Name); } //Si ocurrio algun error, paramos if (errorsCount != errors.Count) { return; } //Separamos los alias y las declaraciones de array/records.. //Listado de declaraciones que no son alias var notAliasDeclarations = groupOfDeclarations.Where(x => !(x is AliasDeclarationNode)).ToList(); //Listadoo de declaraciones que son alias var aliasDeclarations = groupOfDeclarations.OfType<AliasDeclarationNode>().ToList(); /* Procesamos los alias primero, y obtenemos varios conjuntos de alias * En un mismo conjunto estaran los alias que hacen referencia entre ellos * El primer elemento del conjunto sera el alias por el que se empezo a analizar * El ultimo elemento sera el que desembocó en un tipo existente/valido * * Ejemplo: * * type r = {x : a} * type a = b * type c = r * type b = c * * Aqui obtendremos un solo conjunto con las declaraciones: {a = b} {b = c} {c = r} */ IList<List<AliasDeclarationNode>> aliasesGroups = ProcessAliasesGroup(aliasDeclarations, scope, errors); //Si ocurrio algun error, paramos if (errorsCount != errors.Count) { return; } //Chequeamos semanticamente cada tipo (que no sea alias) //Todos los tipos posibles a los que pueden hacer referencia han sido anadidos al scope foreach (var declaration in notAliasDeclarations) { declaration.CheckSemantic(scope, errors); } //Si hubo algun error if (errorsCount != errors.Count) { return; } //Por cada grupo de alias, vamos de atras hacia delante resolviendo los tipos //Pues el ultimo en cada grupo es el que apuntaba hacia un tipo valido (y no hacia otro alias) foreach (var aliasGroup in aliasesGroups) { for (int i = aliasGroup.Count - 1; i >= 0; i--) { var alias = aliasGroup[i]; //Actualizamos la definicion del tipo en el scope alias.UpdateDefinition(scope); } } //Actualizamos las declaraciones de los no-alias foreach (var declaration in notAliasDeclarations) { var t = (TypeDeclarationNode) declaration; //Actualizamos la definicion del tipo en el scope t.UpdateDefinition(scope); } }
private void ProcessCallableDeclarations(IList<DeclarationNode> group, Scope scope, IList<Error> errors) { //Por cada procedimiento o funcion en el bloque... int errorsCount = errors.Count; foreach (CallableDeclarationNode c in group) { //Chequeamos la semantica de la funcion. //OJO: No chequea la semantica del cuerpo de la funcion c.CheckSemantic(scope, errors); //Si ocurrio algun error chequeando la semantica de la funcion (en la 1ra pasada) if (errorsCount != errors.Count) { return; } } //Hacemos una segunda pasada para chequear los cuerpos de las funciones (una vez ya definidas formalmente todas las funciones del bloque) //Y ver que el tipo de retorno de la funcion sea igual al especificado foreach (CallableDeclarationNode c in group) { errorsCount = errors.Count; c.CheckBodySemantic(scope, errors); //Si hubo algun error, eliminamos esa funcion del scope. TODO: Necesario? if (errorsCount != errors.Count) { scope.DefinedCallables.Remove(c.Name); } } }
/// <summary> /// Encargado de recibir un conjunto de declaraciones de tipo alias (pertenecientes a un mismo bloque) /// y chequear que no haya ciclos, etc. /// </summary> /// <param name="aliasDeclarations">Conjunto de alias</param> /// <param name="scope"></param> /// <param name="errors"></param> /// <returns>Varios conjunticos de alias donde en cada conjunto estan los relacionados. Ademas, estan en el orden que se fueron resolviendo</returns> private IList<List<AliasDeclarationNode>> ProcessAliasesGroup(IList<AliasDeclarationNode> aliasDeclarations, Scope scope, IList<Error> errors) { //Donde guardaremos los conjunticos var result = new List<List<AliasDeclarationNode>>(); //Si no hay ningun Alias declaration if (aliasDeclarations.Count == 0) { return result; } //Creamos el primer conjunto result.Add(new List<AliasDeclarationNode>()); //Convertimos los alias a un diccionario (para facilitar el manejo) de la forma: //{ Name : AliasDeclarationNode } var dictAliases = aliasDeclarations.ToDictionary(x => x.Name, x => x); //Cogemos el primer alias AliasDeclarationNode alias = aliasDeclarations[0]; //Mientras queden alias por analizar while (dictAliases.Count > 0) { //Tipo al que apunta var type = alias.AliasType; //Si no existe el tipo, paramos if (!scope.ExistsType(type)) { errors.Add(new UndefinedTypeError(Line, Column, type)); break; } //Si el tipo ya ha sido definido correctamente o si no ha sido definido completamente pero no apunta a ningun alias (ej. apunta a un record que esta por definirse) if (IsFullyDefined(type, scope) || DoesNotPointToAlias(type, aliasDeclarations)) { //Cogemos el tipo al que apunta var t = scope.GetType(type); //Anadimos este alias diciendo que apunta a aquel tipo scope.Add(t, alias.Name, updateIfExists: true); //Anadimos el alias al grupo actual result[result.Count - 1].Add(alias); //Lo eliminamos de los procesados (ya acabamos con el) dictAliases.Remove(alias.Name); //Ya actualizamos el que apunta a un tipo que no es alias //Ahora vamos de atras hacia delante por la cadena y actualizamos el tipo de todos for (int i = result[result.Count - 1].Count - 1; i >= 0; i--) { //Cogemos el i-esimo alias var a = result[result.Count - 1][i]; //Cogemos el tipo al que apunta t = scope.GetType(a.AliasType); //Actualizamos el alias, diciendo que va a ser del mismo tipo al que el apunta scope.Add(t, a.Name, updateIfExists: true); //Ya no procesaremos mas este alias dictAliases.Remove(a.Name); } //Creamos un nuevo grupo result.Add(new List<AliasDeclarationNode>()); if (dictAliases.Count != 0) { //Si todavia quedan alias por analizar, nos quedamos con uno cualquiera alias = dictAliases[dictAliases.First().Key]; } } //Si el tipo apunta no ha sido 100% definido y esta apuntando a un tipo que es una alias... else { //Lo anadimos al grupo actual result[result.Count - 1].Add(alias); //Cogemos el proximo (al que el apunta) alias = dictAliases[alias.AliasType]; //Si ese tipo al que apunta ya ha sido anadido en este grupo => Hay un ciclo //Notar que si estuviera en otro grupo, no hubiera problema, porque significa que ya fue resuelto correctamente sin siclos if (result[result.Count - 1].Contains(alias)) { //Existe un ciclo y terminamo errors.Add(new CyclicAliasesError(alias.Line, alias.Column)); break; } } } return result; }
/// <summary> /// Procesa un conjunto de declaraciones de un mismo tipo /// </summary> /// <param name="group"></param> /// <param name="scope"> </param> private void Process(IList<DeclarationNode> group, Scope scope, IList<Error> errors) { var first = group[0]; //TODO: Refactorizar: Usar clases en vez de if - else if - else if... //Si es un grupo de declaraciones de variables if (first is VariableDeclarationNode) { ProcessVariableDeclarations(group, scope, errors); } //Si es un grupo de declaraciones de funciones/procedimientos else if (first is CallableDeclarationNode) { ProcessCallableDeclarations(group, scope, errors); } //Si es un grupo de declaraciones de tipos (records, arrays, etc) else { ProcessTypeDeclarations(group, scope, errors); } }