/// <summary> /// Checks if the variable already exists. This does not allow for shadowing of variables, maybe as an /// enhancement, cause it wouldn't be that hard. /// /// Cflat also allows for declarations and assignments on the same line, so this will process the InitialValue sub tree /// as well. /// </summary> /// <param name="n"></param> public override void VisitDeclLocal(ASTDeclarationLocal n) { if (!_scopeMgr.HasSymbol(n.ID)) { CFlatType lhs = CheckSubTree(n.Type); //Check if the code is also assigning a value on the same line bool valid = true; if (n.InitialValue != null) { CFlatType rhs = CheckSubTree(n.InitialValue); if (rhs is TypeFunction && ((TypeFunction)rhs).IsConstructor) { rhs = new TypeClass(((TypeFunction)rhs).Name); } if (!IsValidAssignment(lhs, rhs)) { valid = false; ReportError(n.Location, "Invalid assignment, type mismatch. Expected: {0} Got: {1}", TypeToFriendlyName(lhs), TypeToFriendlyName(rhs)); } } if (valid) { _scopeMgr.AddLocal(n.ID, lhs, _currentMethod); } } else { ReportError(n.Location, "The identifier '{0}' already exists", n.ID); } }
public FormalDescriptor AddFormal(string name, CFlatType type, string modifier) { var descriptor = new FormalDescriptor(type, name, modifier); CurrentScope.Descriptors.Add(name, descriptor); return(descriptor); }
private CFlatType TypeCheckIncrementDecrement(ASTExpression expr, string operatorName, LexLocation loc) { //totally cheating here. the grammar should not even allow this to happen. if (expr is ASTIdentifier) { ASTIdentifier identifier = (ASTIdentifier)expr; //need to set a flag so that the code generator can store back the result identifier.IsLeftHandSide = true; CFlatType t = CheckSubTree(identifier); if (t.IsNumeric) { return(t); } else { ReportError(loc, "The {0} operator requires an instance of a numeric datatype", operatorName); } } else { ReportError(loc, "The {0} operator requires an instance of a numeric datatype", operatorName); } /* note: the ReportError method will always throw, so this part of the code should not be reached, * unless of course we change the ReportError method to not throw or try to implement some error recovery * strategy... * */ throw new InternalCompilerException("This part of the code should not be reachable."); }
/// <summary> /// create a descriptor and add it to the current scope /// </summary> /// <param name="name"></param> /// <param name="t"></param> /// <param name="parent"></param> /// <returns></returns> public ClassDescriptor AddClass(string name, CFlatType t, ClassDescriptor parent = null) { ClassDescriptor cd = new ClassDescriptor(t, parent, name, CurrentScope); CurrentScope.Descriptors.Add(name, cd); return(cd); }
public ArrayDescriptor AddArray(string name, CFlatType type, CFlatType baseType, int lower, int upper) { var descriptor = new ArrayDescriptor(type, baseType, name, lower, upper); CurrentScope.Descriptors.Add(name, descriptor); return(descriptor); }
private void BoxIfNeeded(CFlatType t) { if (t.NeedsBoxing) { _gen.Emit(OpCodes.Box, t.CilType); } }
public LocalDescriptor AddLocal(string name, CFlatType type, TypeFunction containingMethod) { var descriptior = new LocalDescriptor(type, name); CurrentScope.Descriptors.Add(name, descriptior); containingMethod.AddLocal(name, type); return(descriptior); }
public SystemMethod(string name, CFlatType returnType, Dictionary <string, CFlatType> formals) { Name = name; FuncInfo = new TypeFunction(name) { ReturnType = returnType, Formals = formals }; }
public MemberDescriptor AddMember(string name, CFlatType type, TypeClass containingClass, List <string> modifiers = null) { var descriptor = new MemberDescriptor(type, name, containingClass.Descriptor); if (modifiers != null) { descriptor.Modifiers.AddRange(modifiers); } CurrentScope.Descriptors.Add(name, descriptor); containingClass.Descriptor.Fields.Add(descriptor); return(descriptor); }
public override void VisitIfThen(ASTIfThen n) { CFlatType condType = CheckSubTree(n.Condition); if (condType is TypeBool) { CheckSubTree(n.Then); } else { ReportError(n.Location, "If statement must evaluate to boolean. Got type '{0}'", TypeToFriendlyName(condType)); } }
/// <summary> /// Pretty simple, just ensure that the condition is a boolean. /// </summary> /// <param name="n"></param> public override void VisitWhile(ASTWhile n) { CFlatType condType = CheckSubTree(n.Condition); if (condType is TypeBool) { CheckSubTree(n.Body); } else { ReportError(n.Location, "While loop condition must be a boolean. Got type '{0}'", TypeToFriendlyName(condType)); } }
public MethodDescriptor AddMethod(string name, CFlatType type, TypeClass containingClass, List <String> modifiers = null, bool isCFlat = false) { var md = new MethodDescriptor(type, name, containingClass.Descriptor); md.IsCFlatMethod = isCFlat; if (modifiers != null) { md.Modifiers.AddRange(modifiers); } CurrentScope.Descriptors.Add(name, md); containingClass.Descriptor.Methods.Add(md); return(md); }
public override void VisitNot(ASTNot n) { CFlatType t = CheckSubTree(n.Expression); if (t is TypeBool) { n.CFlatType = t; _lastSeenType = t; } else { ReportError(n.Location, "Operand for the not operator must be a boolean. Got type '{0}'", TypeToFriendlyName(t)); } }
public override void VisitNeg(ASTNegative n) { CFlatType t = CheckSubTree(n.Expression); if (t.IsNumeric) { n.CFlatType = t; _lastSeenType = t; } else { ReportError(n.Location, "Only numeric datatypes can be negated. Got type '{0}'", TypeToFriendlyName(t)); } }
/// <summary> /// Binary operators are not supported for classes, only primatives. /// </summary> /// <param name="n"></param> public override void VisitAdd(ASTAdd n) { CFlatType lhs = CheckSubTree(n.Left); CFlatType rhs = CheckSubTree(n.Right); if (lhs.IsNumeric && rhs.IsNumeric) { _lastSeenType = n.CFlatType = Supertype(lhs, rhs); } else { ReportError(n.Location, "Invalid operands for addition. Got types '{0}' and '{1}'.", TypeToFriendlyName(lhs), TypeToFriendlyName(rhs)); } }
public override void VisitDerefArray(ASTDereferenceArray n) { CFlatType arrType = CheckSubTree(n.Array); CFlatType index = CheckSubTree(n.Index); if (arrType.IsArray && index is TypeInt) { _lastSeenType = n.CFlatType = ((TypeArray)arrType).BaseType; } else { ReportError(n.Location, "Array index must be an integer."); } }
/// <summary> /// Takes two types and returns which one is the super class of the two. /// If both types are the same, then of course the first is returned. /// If the types are totally unrelated (that is, neither is a supertype), then null is returned (or maybe it should throw?) /// </summary> /// <param name="t1"></param> /// <param name="t2"></param> /// <returns></returns> private CFlatType Supertype(CFlatType t1, CFlatType t2) { if (t1.IsSupertype(t2)) { return(t2); } else if (t2.IsSupertype(t1)) { return(t1); } else { return(null); } }
public override void VisitConcatenate(ASTConcatenate n) { CFlatType lhs = CheckSubTree(n.Left); CFlatType rhs = CheckSubTree(n.Right); // we'll allow for implicit conversion from numeric types to strings for concat purposes if ((lhs.IsConcatenatable) && (rhs.IsConcatenatable)) { _lastSeenType = n.CFlatType = new TypeString(); } else { ReportError(n.Location, "Operands for concatenation must be strings, characters, or integers."); } }
public Type LookupCilType(CFlatType type) { if (type is TypeClass) { string name = (type as TypeClass).ClassName; return(TypeBuilderMap[name].Builder); } else if (type is TypeArray) { return(LookupCilType(type.BaseType).MakeArrayType()); } else { return(type.CilType); } }
private void TypeCheckEquality(ASTBinary n) { CFlatType lhs = CheckSubTree(n.Left); CFlatType rhs = CheckSubTree(n.Right); if (lhs.IsSupertype(rhs) || rhs.IsSupertype(lhs)) { //we're good _lastSeenType = n.CFlatType = new TypeBool(); } else { //invalid ReportError(n.Location, "Types '{0}' and '{1}' are not compatible for equality.", TypeToFriendlyName(lhs), TypeToFriendlyName(rhs)); } }
/// <summary> /// For now, this will only allow Classes to have fields and methods. /// So primitives like int, real, bool won't have any. /// </summary> /// <param name="n"></param> public override void VisitDerefField(ASTDereferenceField n) { //we're processing something of the form: lvalue.identifier //Make sure the lvalue is a type and the identifier exists CFlatType lhs = CheckSubTree(n.Object); if (lhs.IsClass) { TypeClass lvalue = (TypeClass)lhs; var descriptor = (ClassDescriptor)_scopeMgr.Find(lvalue.ClassName, p => p is ClassDescriptor); //check if a field exists. MemberDescriptor memberDesc = _scopeMgr.Find(n.Field, d => d is MemberDescriptor, descriptor.Scope) as MemberDescriptor; if (memberDesc != null && descriptor.Fields.Contains(memberDesc)) { if (memberDesc.Modifiers.Contains(PRIVATE_MODIFIER, StringComparer.InvariantCultureIgnoreCase)) { if (!_scopeMgr.HasSymbolShallow(n.Field)) { ReportError(n.Location, "Cannot reference the private member '{0}' on class '{1}' from class '{2}'", memberDesc.Name, memberDesc.ContainingClass.Name, _currentClass.ClassName); } } if (memberDesc != null) { //hooray, code is valid n.Descriptor = memberDesc; n.CFlatType = memberDesc.Type; _lastSeenType = memberDesc.Type; } else { ReportError(n.Location, "'{0}' is not a field for type '{1}'", n.Field, TypeToFriendlyName(lvalue)); } } else { ReportError(n.Location, "Field '{0}' does not exist for type '{1}'", n.Field, TypeToFriendlyName(lvalue)); } } else { ReportError(n.Location, "Type '{0}' does not support fields.", TypeToFriendlyName(lhs)); } }
public override void VisitReturn(ASTReturn n) { CFlatType actual = CheckSubTree(n.ReturnValue); CFlatType expected = _currentMethod.ReturnType; if (actual.IsSupertype(expected)) { n.CFlatType = expected; _lastSeenType = expected; _currentMethod.RegisterReturnStatement(); } else { ReportError(n.Location, "Type mismatch in return statement. Expected: {0} Got: {1}", TypeToFriendlyName(expected), TypeToFriendlyName(actual)); } }
/// <summary> /// This kind of logic should probably be in the ToString() methods of TypeClass /// and TypeArray... /// </summary> /// <param name="t"></param> /// <returns></returns> private string TypeToFriendlyName(CFlatType t) { if (t.IsClass) { return(((TypeClass)t).ClassName); } else if (t.IsArray) { TypeArray arr = (TypeArray)t; string baseType = TypeToFriendlyName(arr.BaseType); return(String.Concat(baseType, "[]")); } else { return(t.ToString()); } }
/// <summary> /// Checks to make sure that both the upper and lower bounds are integers /// </summary> /// <param name="n"></param> public override void VisitInstantiateArray(ASTInstantiateArray n) { n.Lower.CFlatType = CheckSubTree(n.Lower); n.Upper.CFlatType = CheckSubTree(n.Upper); if ((n.Lower.CFlatType is TypeInt) && (n.Upper.CFlatType is TypeInt)) { CFlatType elementType = CheckSubTree(n.Type); TypeArray arrType = new TypeArray(elementType); _lastSeenType = arrType; n.CFlatType = arrType; } else { ReportError(n.Location, "Bounds of an array must be integers."); } }
/// <summary> /// Makes sure the left hand side of the assignment is a supertype of the right hand side. /// </summary> /// <param name="n"></param> public override void VisitAssign(ASTAssign n) { n.LValue.IsLeftHandSide = true; //this is needed when we generate the IL. CFlatType lhs = CheckSubTree(n.LValue); CFlatType rhs = CheckSubTree(n.Expr); if (IsValidAssignment(lhs, rhs)) { //check if the assignment is to a readonly method parameter CheckForReadonlyAssignments(n); //I believe we don't really do anything when the source code is correct in this case. n.CFlatType = new TypeVoid(); _lastSeenType = n.CFlatType; } else { ReportError(n.Location, "Invalid assignment, type mismatch. Expected: {0} Got: {1}", TypeToFriendlyName(lhs), TypeToFriendlyName(rhs)); } }
private void TypeCheckBooleanOperator(ASTBinary n, string errorMessage) { bool valid = false; CFlatType lhs = CheckSubTree(n.Left); if (lhs is TypeBool) { CFlatType rhs = CheckSubTree(n.Right); if (rhs is TypeBool) { _lastSeenType = n.CFlatType = new TypeBool(); valid = true; } } if (!valid) { ReportError(n.Location, errorMessage); } }
/// <summary> /// This method looks kinda ugly because I'm using it for all binary operations, which may actually typecheck /// to different types /// </summary> /// <param name="n"></param> /// <param name="errorMessage"></param> /// <param name="resultingType">The type that the expression will evaluate as. This is optional, so if you /// don't pass it in, the compiler will use the supertype of the two operands. /// </param> private void TypeCheckNumericBinary(ASTBinary n, string errorMessage, CFlatType resultingType = null) { bool valid = false; CFlatType lhs = CheckSubTree(n.Left); if (lhs.IsNumeric) { CFlatType rhs = CheckSubTree(n.Right); if (rhs.IsNumeric) { //if the parameter was not passed in, we'll figure it out resultingType = resultingType ?? Supertype(lhs, rhs); n.CFlatType = resultingType; _lastSeenType = resultingType; valid = true; } } if (!valid) { ReportError(n.Location, errorMessage); } }
/// <summary> /// /// </summary> /// <param name="n"></param> public override void VisitFor(ASTFor n) { //define the block scope now to incapsulate the declared variable. BeginForLoopBlock(); CFlatType initType = CheckSubTree(n.InitialExpr); n.InitialExpr.CFlatType = initType; TypeBool condition = CheckSubTree(n.Conditional) as TypeBool; if (condition != null) { n.Conditional.CFlatType = condition; CFlatType loopType = CheckSubTree(n.LoopExpr); n.LoopExpr.CFlatType = loopType; CheckSubTree(n.Body); } else { ReportError(n.Location, "The condition of a for loop must be a boolean."); } }
/// <summary> /// Returns if the assignment is valid, i.e. if the expression: /// lhs = rhs; /// can be evaluated. /// /// Returns true if both sides are numeric, of if the left hand side is a super type of the right hand side. /// </summary> /// <param name="lhs"></param> /// <param name="rhs"></param> /// <returns></returns> private bool IsValidAssignment(CFlatType lhs, CFlatType rhs) { return((lhs.IsNumeric && rhs.IsNumeric) || rhs.IsSupertype(lhs)); }
/// <summary> /// VisitInvoke and VisitDereferenceField are very similiar, so this should probably be refactored out /// into a common method. /// </summary> /// <param name="n"></param> public override void VisitInvoke(ASTInvoke n) { //Make sure the lvalue is a type and the method name exists CFlatType lhs = CheckSubTree(n.Object); if (lhs.IsClass) { TypeClass lvalue = (TypeClass)lhs; var descriptor = (ClassDescriptor)_scopeMgr.Find(lvalue.ClassName, p => p is ClassDescriptor); //check if a method with the given name exists in the scope. //This needs to check not only the class's shallow scope, but all the parents as well. MethodDescriptor methodDesc = _scopeMgr.Find(n.Method, d => d is MethodDescriptor, descriptor.Scope) as MethodDescriptor; //if (methodDesc != null && (descriptor.Methods.Contains(methodDesc) || methodDesc.IsCFlatMethod)) if (methodDesc != null) { if (methodDesc.Modifiers.Contains(PRIVATE_MODIFIER, StringComparer.InvariantCultureIgnoreCase)) { if (!_scopeMgr.HasSymbol(n.Method, _currentClass.Scope)) { ReportError(n.Location, "Cannot access the private method {0} in class {1} from class {2}", methodDesc.Name, methodDesc.ContainingClass.Name, _currentClass.ClassName); } } //check if the arguments match TypeFunction method = (TypeFunction)methodDesc.Type; //visit any actuals that need processing CheckSubTree(n.Actuals); //collect the actuals var curMethDesc = _scopeMgr.Find(_currentMethod.Name, d => d is MethodDescriptor) as MethodDescriptor; ActualBuilder builder = new ActualBuilder(curMethDesc); n.Actuals.Visit(builder); if (method.AcceptCall(builder.Actuals)) //if the types check { CheckActualsHaveReadonly(methodDesc, builder, n); CheckReadonlyNotPassedAsModifiable(methodDesc, builder, n); n.Descriptor = methodDesc; n.CFlatType = method.ReturnType; _lastSeenType = method.ReturnType; //check if we're processing an exit instruction, and if so, this counts as a return for the current block. if (IsExitMethod(n)) { _currentMethod.RegisterReturnStatement(); } } else { ReportError(n.Location, "Invalid parameters for method '{0}.{1}'", TypeToFriendlyName(lvalue), n.Method); } } else { ReportError(n.Location, "Method '{0}' does not exist for type '{1}'", n.Method, TypeToFriendlyName(lvalue)); } } else { ReportError(n.Location, "Type '{0}' does not support methods.", TypeToFriendlyName(lhs)); } }