コード例 #1
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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);
            }
        }
コード例 #2
0
        public FormalDescriptor AddFormal(string name, CFlatType type, string modifier)
        {
            var descriptor = new FormalDescriptor(type, name, modifier);

            CurrentScope.Descriptors.Add(name, descriptor);
            return(descriptor);
        }
コード例 #3
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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.");
        }
コード例 #4
0
        /// <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);
        }
コード例 #5
0
        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);
        }
コード例 #6
0
 private void BoxIfNeeded(CFlatType t)
 {
     if (t.NeedsBoxing)
     {
         _gen.Emit(OpCodes.Box, t.CilType);
     }
 }
コード例 #7
0
        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);
        }
コード例 #8
0
ファイル: SystemMethod.cs プロジェクト: goric/cflat
 public SystemMethod(string name, CFlatType returnType, Dictionary <string, CFlatType> formals)
 {
     Name     = name;
     FuncInfo = new TypeFunction(name)
     {
         ReturnType = returnType,
         Formals    = formals
     };
 }
コード例 #9
0
        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);
        }
コード例 #10
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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));
            }
        }
コード例 #11
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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));
            }
        }
コード例 #12
0
        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);
        }
コード例 #13
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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));
            }
        }
コード例 #14
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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));
            }
        }
コード例 #15
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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));
            }
        }
コード例 #16
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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.");
            }
        }
コード例 #17
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
 /// <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);
     }
 }
コード例 #18
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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.");
            }
        }
コード例 #19
0
 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);
     }
 }
コード例 #20
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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));
            }
        }
コード例 #21
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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));
            }
        }
コード例 #22
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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));
            }
        }
コード例 #23
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
 /// <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());
     }
 }
コード例 #24
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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.");
            }
        }
コード例 #25
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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));
            }
        }
コード例 #26
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        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);
            }
        }
コード例 #27
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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);
            }
        }
コード例 #28
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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.");
            }
        }
コード例 #29
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
 /// <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));
 }
コード例 #30
0
ファイル: ThirdPass.cs プロジェクト: goric/cflat
        /// <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));
            }
        }