Exemple #1
0
        internal override Expression ResolveEntityNames(
            ParserContext parser)
        {
            FunctionDefinition funcDef; // used in multiple places.
            FieldDefinition    fieldDec;

            this.Root = this.Root.ResolveEntityNames(parser);
            Expression root  = this.Root;
            string     field = this.FieldToken.Value;

            if (root is NamespaceReference)
            {
                // already a fully qualified namespace, therefore imports don't matter.
                string         fullyQualifiedName = ((NamespaceReference)root).Template.Name + "." + field;
                TopLevelEntity entity             = this.Owner.FileScope.FileScopeEntityLookup.DoEntityLookup(fullyQualifiedName, parser.CurrentCodeContainer);
                if (entity != null)
                {
                    return(ResolverPipeline.ConvertStaticReferenceToExpression(entity, this.FirstToken, this.Owner));
                }

                NamespaceReferenceTemplate nrt = this.Owner.FileScope.FileScopeEntityLookup.DoNamespaceLookup(fullyQualifiedName, parser.CurrentCodeContainer);
                if (nrt != null)
                {
                    return(new NamespaceReference(this.FirstToken, this.Owner, nrt));
                }

                throw new ParserException(this, "Could not find class or function by the name of: '" + fullyQualifiedName + "'");
            }

            if (root is ClassReference)
            {
                ClassDefinition cd = ((ClassReference)root).ClassDefinition;

                funcDef = cd.GetMethod(field, false);
                if (funcDef != null)
                {
                    if (!funcDef.Modifiers.HasStatic)
                    {
                        string className    = cd.NameToken.Value;
                        string functionName = funcDef.NameToken.Value;

                        throw new ParserException(this.DotToken, "'" + className + "." + functionName + "' is not a static method, but it is being used as though it is static.");
                    }

                    Node.EnsureAccessIsAllowed(this.FieldToken, this.Owner, funcDef);

                    return(new FunctionReference(this.FirstToken, funcDef, this.Owner));
                }

                fieldDec = cd.GetField(field, false);
                if (fieldDec != null)
                {
                    if (!fieldDec.Modifiers.HasStatic)
                    {
                        throw new ParserException(this.DotToken, "Cannot make a static reference to a non-static field.");
                    }

                    Node.EnsureAccessIsAllowed(this.FieldToken, this.Owner, fieldDec);

                    return(new FieldReference(this.FirstToken, fieldDec, this.Owner));
                }

                // TODO: typeof(class name) is less error prone with localization conflicts.
                // However there's a Core.typeOf() method that sort of conflicts.
                // TODO: if this notation is kept, then this needs to be split into two class keywords
                // since they do different things.
                if (field == parser.Keywords.CLASS)
                {
                    return(new ClassReferenceLiteral(this.FirstToken, cd, this.Owner));
                }

                // TODO: nested classes, enums, constants

                // TODO: show spelling suggestions.
                throw new ParserException(this.FieldToken, "No static fields or methods named '" + field + "' on the class " + cd.NameToken.Value + ".");
            }

            if (root is BaseKeyword)
            {
                ClassDefinition thisClass = null;
                if (this.Owner != null)
                {
                    if (this.Owner is FunctionDefinition)
                    {
                        thisClass = this.Owner.Owner as ClassDefinition;
                    }
                    else
                    {
                        thisClass = this.Owner as ClassDefinition;
                    }
                }

                if (thisClass == null)
                {
                    throw new ParserException(root, "'base' keyword can only be used inside classes.");
                }

                ClassDefinition cd = thisClass.BaseClass;
                if (cd == null)
                {
                    throw new ParserException(root, "'base' keyword can only be used inside classes that extend from another class.");
                }

                FunctionDefinition fd = cd.GetMethod(field, true);
                if (fd == null)
                {
                    throw new ParserException(this.DotToken, "Cannot find a method by that name in the base class chain.");
                }

                if (fd.Modifiers.HasStatic)
                {
                    throw new ParserException(this.DotToken, "Cannot reference static methods using 'base' keyword.");
                }

                Node.EnsureAccessIsAllowed(this.DotToken, this.Owner, fd);

                return(new BaseMethodReference(this.FirstToken, this.DotToken, this.FieldToken, this.Owner));
            }

            if (root is ThisKeyword)
            {
                ClassDefinition cd    = null;
                TopLevelEntity  owner = this.TopLevelEntity;
                if (owner is FunctionDefinition)
                {
                    FunctionDefinition functionOwner = (FunctionDefinition)owner;
                    if (functionOwner.Modifiers.HasStatic)
                    {
                        throw new ParserException(this.Root, "'this' keyword cannot be used in static methods.");
                    }

                    if (!(functionOwner.Owner is ClassDefinition))
                    {
                        throw new ParserException(this.Root, "'this' keyword cannot be used in a function that isn't in a class.");
                    }

                    cd = (ClassDefinition)owner.Owner;
                }
                else if (owner is FieldDefinition)
                {
                    if (((FieldDefinition)owner).Modifiers.HasStatic)
                    {
                        throw new ParserException(this.Root, "'this' keyword cannot be used in static fields.");
                    }
                    cd = (ClassDefinition)owner.Owner;
                }
                else if (owner is ConstructorDefinition)
                {
                    if (((ConstructorDefinition)owner).Modifiers.HasStatic)
                    {
                        throw new ParserException(this.Root, "'this', keyword cannot be used in static constructors.");
                    }
                    cd = (ClassDefinition)owner.Owner;
                }
                else
                {
                    throw new ParserException(this.Root, "'this' keyword must be used inside a class.");
                }

                funcDef = cd.GetMethod(field, true);
                if (funcDef != null)
                {
                    if (funcDef.Modifiers.HasStatic)
                    {
                        throw new ParserException(this.DotToken, "This method is static and must be referenced by the class name, not 'this'.");
                    }
                    Node.EnsureAccessIsAllowed(this.DotToken, this.Owner, funcDef);
                    return(new FunctionReference(this.FirstToken, funcDef, this.Owner));
                }

                FieldDefinition fieldDef = cd.GetField(field, true);
                if (fieldDef != null)
                {
                    if (fieldDef.Modifiers.HasStatic)
                    {
                        throw new ParserException(this.DotToken, "This field is static and must be referenced by the class name, not 'this'.");
                    }
                    Node.EnsureAccessIsAllowed(this.DotToken, this.Owner, fieldDef);

                    return(new FieldReference(this.FirstToken, fieldDef, this.Owner));
                }

                // TODO: show suggestions in the error message for anything close to what was typed.
                throw new ParserException(this.FieldToken, "The class '" + cd.NameToken.Value + "' does not have a field named '" + field + "'.");
            }

            if (this.Root is EnumReference)
            {
                EnumDefinition enumDef = ((EnumReference)this.Root).EnumDefinition;

                if (field == parser.Keywords.FIELD_ENUM_LENGTH)
                {
                    return(new IntegerConstant(this.FirstToken, enumDef.Items.Length, this.Owner));
                }
                if (field == parser.Keywords.FIELD_ENUM_MAX)
                {
                    return(new SpecialEntity.EnumMaxFunction(this.FirstToken, enumDef, this.Owner));
                }
                if (field == parser.Keywords.FIELD_ENUM_VALUES)
                {
                    return(new SpecialEntity.EnumValuesFunction(this.FirstToken, enumDef, this.Owner));
                }

                return(new EnumFieldReference(this.FirstToken, enumDef, this.FieldToken, this.Owner));
            }

            // This is done here in the resolver instead of the parser because some unallowed
            // field names (such as .class) are valid.
            if (field == parser.Keywords.CLASS)
            {
                if (this.Root is Variable)
                {
                    throw new ParserException(this.Root, "'" + ((Variable)this.Root).Name + "' is not a class.");
                }
                throw new ParserException(this.DotToken, ".class can only be applied to class names.");
            }
            parser.VerifyIdentifier(this.FieldToken);

            return(this);
        }
Exemple #2
0
        internal override Expression ResolveNames(ParserContext parser)
        {
            if (this.Name == "$$$")
            {
                throw new ParserException(this.FirstToken, "Core function invocations cannot stand alone and must be immediately invoked.");
            }

            if (this.Name.StartsWith("$$"))
            {
                return(new LibraryFunctionReference(this.FirstToken, this.Name.Substring(2), this.Owner));
            }

            if (this.Name == "this" || this.Name == "base")
            {
                TopLevelConstruct container = parser.CurrentCodeContainer;

                if (container is FunctionDefinition)
                {
                    FunctionDefinition funcDef = (FunctionDefinition)this.Owner;
                    if (funcDef.IsStaticMethod)
                    {
                        throw new ParserException(this.FirstToken, "Cannot use '" + this.Name + "' in a static method");
                    }

                    if (funcDef.Owner == null)
                    {
                        throw new ParserException(this.FirstToken, "Cannot use '" + this.Name + "' in a function that isn't a class method.");
                    }
                }

                if (container is FieldDeclaration)
                {
                    if (((FieldDeclaration)container).IsStaticField)
                    {
                        throw new ParserException(this.FirstToken, "Cannot use '" + this.Name + "' in a static field value.");
                    }
                }

                if (container is ConstructorDefinition)
                {
                    ConstructorDefinition constructor = (ConstructorDefinition)container;
                    if (constructor == ((ClassDefinition)constructor.Owner).StaticConstructor) // TODO: This check is silly. Add an IsStatic field to ConstructorDefinition.
                    {
                        throw new ParserException(this.FirstToken, "Cannot use '" + this.Name + "' in a static constructor.");
                    }
                }

                if (this.Name == "this")
                {
                    return(new ThisKeyword(this.FirstToken, this.Owner));
                }
                return(new BaseKeyword(this.FirstToken, this.Owner));
            }

            NamespaceReferenceTemplate nrt = this.Owner.FileScope.FileScopeEntityLookup.DoNamespaceLookup(this.Name, this.Owner);

            if (nrt != null)
            {
                return(new NamespaceReference(this.FirstToken, this.Owner, nrt));
            }

            TopLevelConstruct exec = this.Owner.FileScope.FileScopeEntityLookup.DoEntityLookup(this.Name, this.Owner);

            if (exec != null)
            {
                return(Resolver.ResolverPipeline.ConvertStaticReferenceToExpression(exec, this.FirstToken, this.Owner));
            }

            return(this);
        }
Exemple #3
0
        internal override Expression ResolveTypes(ParserContext parser, TypeResolver typeResolver)
        {
            this.Root.ResolveTypes(parser, typeResolver);

            string field = this.FieldToken.Value;

            if (this.Root is EnumReference)
            {
                throw new System.NotImplementedException();
            }

            ResolvedType rootType = this.Root.ResolvedType;

            // TODO: all of this needs to be localized.
            switch (rootType.Category)
            {
            case ResolvedTypeCategory.NULL:
                throw new ParserException(this.DotToken, "Cannot dereference a field from null.");

            case ResolvedTypeCategory.ANY:
                // ¯\_(ツ)_/¯
                this.ResolvedType = ResolvedType.ANY;
                return(this);

            case ResolvedTypeCategory.INTEGER:
                throw new ParserException(this.DotToken, "Integers do not have any fields.");

            case ResolvedTypeCategory.FLOAT:
                throw new ParserException(this.DotToken, "Floating point decimals do not have any fields.");

            case ResolvedTypeCategory.BOOLEAN:
                throw new ParserException(this.DotToken, "Booleans do not have any fields.");

            case ResolvedTypeCategory.STRING:
                switch (field)
                {
                case "length":
                    this.ResolvedType = ResolvedType.INTEGER;
                    if (this.Root is StringConstant)
                    {
                        return(new IntegerConstant(this.FirstToken, ((StringConstant)this.Root).Value.Length, this.Owner));
                    }
                    return(this);

                case "contains": return(BuildPrimitiveMethod(ResolvedType.BOOLEAN, ResolvedType.STRING));

                case "endsWith": return(BuildPrimitiveMethod(ResolvedType.BOOLEAN, ResolvedType.STRING));

                case "indexOf": return(BuildPrimitiveMethod(ResolvedType.INTEGER, ResolvedType.STRING));

                case "lower": return(BuildPrimitiveMethod(ResolvedType.STRING));

                case "ltrim": return(BuildPrimitiveMethod(ResolvedType.STRING));

                case "replace": return(BuildPrimitiveMethod(ResolvedType.STRING, ResolvedType.STRING, ResolvedType.STRING));

                case "reverse": return(BuildPrimitiveMethod(ResolvedType.STRING));

                case "rtrim": return(BuildPrimitiveMethod(ResolvedType.STRING));

                case "split": return(BuildPrimitiveMethod(ResolvedType.ListOrArrayOf(ResolvedType.STRING), ResolvedType.STRING));

                case "startsWith": return(BuildPrimitiveMethod(ResolvedType.BOOLEAN, ResolvedType.STRING));

                case "trim": return(BuildPrimitiveMethod(ResolvedType.STRING));

                case "upper": return(BuildPrimitiveMethod(ResolvedType.STRING));

                // common mistakes
                case "join":
                    throw new ParserException(this.DotToken, "Strings do not have a .join(list) method. Did you mean to do list.join(string)?");

                case "size":
                    throw new ParserException(this.DotToken, "Strings do not have a .size() method. Did you mean to use .length?");

                default:
                    throw new ParserException(this.DotToken, "Strings do not have that method.");
                }

            case ResolvedTypeCategory.LIST:
                ResolvedType itemType = rootType.ListItemType;
                switch (field)
                {
                case "length":
                    this.ResolvedType = ResolvedType.INTEGER;
                    return(this);

                case "filter":
                case "map":
                    if (this.CompilationScope.IsStaticallyTyped)
                    {
                        // TODO: for Acrylic, this needs to have a way to indicate that resolution should be attempted
                        // again once the function return type is known.
                        throw new System.NotImplementedException();
                    }
                    this.ResolvedType = ResolvedType.ANY;
                    return(this);

                case "add": return(BuildPrimitiveMethod(ResolvedType.VOID, itemType));

                case "choice": return(BuildPrimitiveMethod(ResolvedType.VOID));

                case "clear": return(BuildPrimitiveMethod(ResolvedType.VOID));

                case "clone": return(BuildPrimitiveMethod(rootType));

                case "concat": return(BuildPrimitiveMethod(rootType, rootType));

                case "contains": return(BuildPrimitiveMethod(ResolvedType.BOOLEAN, itemType));

                case "insert": return(BuildPrimitiveMethod(ResolvedType.VOID, ResolvedType.INTEGER, itemType));

                case "join": return(BuildPrimitiveMethodWithOptionalArgs(ResolvedType.STRING, 1, ResolvedType.STRING));

                case "pop": return(BuildPrimitiveMethod(itemType));

                case "remove": return(BuildPrimitiveMethod(ResolvedType.VOID, ResolvedType.INTEGER));

                case "reverse": return(BuildPrimitiveMethod(ResolvedType.VOID));

                case "shuffle": return(BuildPrimitiveMethod(ResolvedType.VOID));

                case "sort": return(BuildPrimitiveMethod(ResolvedType.VOID));

                // common mistakes
                case "count":
                case "size":
                    throw new ParserException(this.DotToken, "Lists do not have a ." + this.FieldToken.Value + "() method. Did you mean to use .length?");

                default:
                    throw new ParserException(this.DotToken, "Lists do not have that method.");
                }

            case ResolvedTypeCategory.DICTIONARY:
                ResolvedType keyType   = rootType.DictionaryKeyType;
                ResolvedType valueType = rootType.DictionaryValueType;
                switch (field)
                {
                case "length":
                    this.ResolvedType = ResolvedType.INTEGER;
                    return(this);

                case "clear": return(BuildPrimitiveMethod(ResolvedType.VOID));

                case "clone": return(BuildPrimitiveMethod(rootType));

                case "contains": return(BuildPrimitiveMethod(ResolvedType.BOOLEAN, keyType));

                case "get": return(BuildPrimitiveMethodWithOptionalArgs(valueType, 1, keyType, valueType));

                case "keys": return(BuildPrimitiveMethod(ResolvedType.ListOrArrayOf(keyType)));

                case "merge": return(BuildPrimitiveMethod(ResolvedType.VOID, rootType));

                case "remove": return(BuildPrimitiveMethod(ResolvedType.VOID, keyType));

                case "values": return(BuildPrimitiveMethod(ResolvedType.ListOrArrayOf(valueType)));

                default:
                    throw new ParserException(this.DotToken, "Dictionaries do not have that field.");
                }

            case ResolvedTypeCategory.CLASS_DEFINITION:
                throw new ParserException(this.DotToken, "Class definitions do not have that field.");

            case ResolvedTypeCategory.FUNCTION_POINTER:
                switch (field)
                {
                case "invoke":
                    return(BuildPrimitiveMethod(ResolvedType.ANY, ResolvedType.ListOrArrayOf(ResolvedType.ANY)));

                default:
                    throw new ParserException(this.DotToken, "Fucntions do not have that field.");
                }

            case ResolvedTypeCategory.INSTANCE:
                FieldDefinition fieldDef = rootType.ClassTypeOrReference.GetField(field, true);
                if (fieldDef != null)
                {
                    if (!Node.IsAccessAllowed(this, fieldDef))
                    {
                        ClassDefinition cd = fieldDef.ClassOwner;
                        throw new ParserException(FieldToken, "The field '" + cd.NameToken.Value + "." + this.FieldToken.Value + "' is not accessible from here due to its access scope.");
                    }

                    this.ResolvedType = fieldDef.ResolvedFieldType;
                    return(this);
                }
                FunctionDefinition funcDef = rootType.ClassTypeOrReference.GetMethod(field, true);
                if (funcDef != null)
                {
                    this.ResolvedType = ResolvedType.GetFunctionType(funcDef);
                    return(this);
                }
                throw new ParserException(this.DotToken, "The class '" + rootType.ClassTypeOrReference.NameToken.Value + "' does not have a field called '" + field + "'.");

            default:
                throw new ParserException(this.DotToken, "This field does not exist.");
            }
        }
Exemple #4
0
        internal override Expression ResolveNames(
            ParserContext parser)
        {
            FunctionDefinition funcDef; // used in multiple places.
            FieldDeclaration   fieldDec;

            this.Root = this.Root.ResolveNames(parser);
            Expression root  = this.Root;
            string     field = this.StepToken.Value;

            if (root is NamespaceReference)
            {
                // already a fully qualified namespace, therefore imports don't matter.
                string            fullyQualifiedName = ((NamespaceReference)root).Template.Name + "." + field;
                TopLevelConstruct entity             = this.Owner.FileScope.FileScopeEntityLookup.DoEntityLookup(fullyQualifiedName, parser.CurrentCodeContainer);
                if (entity != null)
                {
                    return(ResolverPipeline.ConvertStaticReferenceToExpression(entity, this.FirstToken, this.Owner));
                }

                NamespaceReferenceTemplate nrt = this.Owner.FileScope.FileScopeEntityLookup.DoNamespaceLookup(fullyQualifiedName, parser.CurrentCodeContainer);
                if (nrt != null)
                {
                    return(new NamespaceReference(this.FirstToken, this.Owner, nrt));
                }

                throw new ParserException(this.FirstToken, "Could not find class or function by the name of: '" + fullyQualifiedName + "'");
            }

            if (root is ClassReference)
            {
                ClassDefinition cd = ((ClassReference)root).ClassDefinition;

                funcDef = cd.GetMethod(field, false);
                if (funcDef != null)
                {
                    if (!funcDef.IsStaticMethod)
                    {
                        string className    = cd.NameToken.Value;
                        string functionName = funcDef.NameToken.Value;

                        throw new ParserException(this.DotToken, "'" + className + "." + functionName + "' is not a static method, but it is being used as though it is static.");
                    }

                    return(new FunctionReference(this.FirstToken, funcDef, this.Owner));
                }

                fieldDec = cd.GetField(field, false);
                if (fieldDec != null)
                {
                    if (!fieldDec.IsStaticField)
                    {
                        throw new ParserException(this.DotToken, "Cannot make a static reference to a non-static field.");
                    }

                    return(new FieldReference(this.FirstToken, fieldDec, this.Owner));
                }

                if (field == "class")
                {
                    return(new ClassReferenceLiteral(this.FirstToken, cd, this.Owner));
                }

                // TODO: nested classes, enums, constants

                // TODO: show spelling suggestions.
                throw new ParserException(this.StepToken, "No static fields or methods named '" + field + "' on the class " + cd.NameToken.Value + ".");
            }

            if (root is BaseKeyword)
            {
                ClassDefinition thisClass = null;
                if (this.Owner != null)
                {
                    if (this.Owner is FunctionDefinition)
                    {
                        thisClass = this.Owner.Owner as ClassDefinition;
                    }
                    else
                    {
                        thisClass = this.Owner as ClassDefinition;
                    }
                }

                if (thisClass == null)
                {
                    throw new ParserException(root.FirstToken, "'base' keyword can only be used inside classes.");
                }

                ClassDefinition cd = thisClass.BaseClass;
                if (cd == null)
                {
                    throw new ParserException(root.FirstToken, "'base' keyword can only be used inside classes that extend from another class.");
                }

                FunctionDefinition fd = cd.GetMethod(field, true);
                if (fd == null)
                {
                    throw new ParserException(this.DotToken, "Cannot find a method by that name in the base class chain.");
                }

                if (fd.IsStaticMethod)
                {
                    throw new ParserException(this.DotToken, "Cannot reference static methods using 'base' keyword.");
                }

                return(new BaseMethodReference(this.FirstToken, this.DotToken, this.StepToken, this.Owner));
            }

            if (root is ThisKeyword)
            {
                ClassDefinition cd = null;
                if (this.Owner != null)
                {
                    if (this.Owner is FunctionDefinition)
                    {
                        funcDef = this.Owner as FunctionDefinition;
                        if (funcDef.IsStaticMethod)
                        {
                            throw new ParserException(this.Root.FirstToken, "'this' keyword cannot be used in static methods.");
                        }
                        cd = funcDef.Owner as ClassDefinition;
                        if (cd == null)
                        {
                            throw new ParserException(this.Root.FirstToken, "'this' keyword must be used inside a class.");
                        }
                    }
                    else if (this.Owner is ClassDefinition)
                    {
                        cd = (ClassDefinition)this.Owner;
                    }
                }

                if (cd == null)
                {
                    throw new ParserException(this.Root.FirstToken, "'this' keyword is not allowed here.");
                }

                funcDef = cd.GetMethod(field, true);
                if (funcDef != null)
                {
                    if (funcDef.IsStaticMethod)
                    {
                        throw new ParserException(this.DotToken, "This method is static and must be referenced by the class name, not 'this'.");
                    }
                    return(new FunctionReference(this.FirstToken, funcDef, this.Owner));
                }

                FieldDeclaration fieldDef = cd.GetField(field, true);
                if (fieldDef != null)
                {
                    if (fieldDef.IsStaticField)
                    {
                        throw new ParserException(this.DotToken, "This field is static and must be referenced by the class name, not 'this'.");
                    }

                    return(new FieldReference(this.FirstToken, fieldDef, this.Owner));
                }

                // TODO: show suggestions in the error message for anything close to what was typed.
                throw new ParserException(this.StepToken, "The class '" + cd.NameToken.Value + "' does not have a field named '" + field + "'.");
            }

            // This is done here in the resolver instead of the parser because some unallowed
            // field names (such as .class) are valid.
            if (this.StepToken.Value == parser.Keywords.CLASS)
            {
                if (this.Root is Variable)
                {
                    throw new ParserException(this.Root.FirstToken, "'" + ((Variable)this.Root).Name + "' is not a class.");
                }
                throw new ParserException(this.DotToken, ".class can only be applied to class names.");
            }
            parser.VerifyIdentifier(this.StepToken);

            return(this);
        }
Exemple #5
0
 public FunctionReference(Token token, FunctionDefinition funcDef, TopLevelConstruct owner)
     : base(token, owner)
 {
     this.FunctionDefinition = funcDef;
 }
Exemple #6
0
        private void EnsureModifiersAndTypeSignatureConsistencyForClassMethods()
        {
            ClassDefinition classDef     = (ClassDefinition)this.Owner;
            ClassDefinition baseClass    = classDef.BaseClass;
            bool            hasBaseClass = baseClass != null;

            if (hasBaseClass)
            {
                FieldDefinition hiddenField = baseClass.GetField(this.NameToken.Value, true);
                if (hiddenField != null)
                {
                    throw new ParserException(this, "This function definition hides a field from a parent class.");
                }
            }

            if (!hasBaseClass)
            {
                if (this.Modifiers.HasOverride)
                {
                    throw new ParserException(this, "Cannot mark a method as 'override' if it has no base class.");
                }
            }
            else
            {
                FunctionDefinition overriddenFunction = baseClass.GetMethod(this.NameToken.Value, true);
                if (overriddenFunction != null)
                {
                    if (!this.Modifiers.HasOverride)
                    {
                        if (this.CompilationScope.IsCrayon)
                        {
                            // TODO: just warn if not present
                        }
                        else
                        {
                            throw new ParserException(this, "This function hides another function from a parent class. If overriding is intentional, use the 'override' keyword.");
                        }
                    }

                    if (overriddenFunction.Modifiers.HasStatic)
                    {
                        throw new ParserException(this, "Cannot override a static method.");
                    }

                    if (overriddenFunction.Modifiers.AccessModifierType != this.Modifiers.AccessModifierType)
                    {
                        throw new ParserException(this, "This method defines a different access modifier than its overridden parent.");
                    }

                    if (overriddenFunction.Modifiers.AccessModifierType == AccessModifierType.PRIVATE)
                    {
                        throw new ParserException(this, "Cannot override a private method.");
                    }

                    if (overriddenFunction.Modifiers.HasInternal &&
                        overriddenFunction.CompilationScope != this.CompilationScope)
                    {
                        throw new ParserException(this, "Cannot override this method. It is marked as internal and is located in a different assembly.");
                    }

                    if (overriddenFunction.ResolvedReturnType != this.ResolvedReturnType)
                    {
                        if (overriddenFunction.ResolvedReturnType == ResolvedType.ANY &&
                            this.ResolvedReturnType == ResolvedType.OBJECT)
                        {
                            // This is fine.
                        }
                        else
                        {
                            throw new ParserException(this, "This function returns a different type than its overridden parent.");
                        }
                    }

                    if (overriddenFunction.ArgTypes.Length != this.ArgTypes.Length)
                    {
                        throw new ParserException(this, "This function has a different number of arguments than its overridden parent.");
                    }

                    if (overriddenFunction.MinArgCount != this.MinArgCount)
                    {
                        throw new ParserException(this, "This function has a different number of optional arguments than its overridden parent.");
                    }

                    // TODO: if you are overridding a function between a statically and dynamically typed languagte,
                    // the dynamic language should pick up the types from the statically typed language or the
                    // statically typed language should require "object" types in its signature for the other direction.
                    for (int i = 0; i < this.ArgTypes.Length; ++i)
                    {
                        ResolvedType expected = overriddenFunction.ResolvedArgTypes[i];
                        ResolvedType actual   = this.ResolvedArgTypes[i];
                        if (actual != expected &&
                            !(actual == ResolvedType.OBJECT && expected == ResolvedType.ANY))
                        {
                            throw new ParserException(this, "This function has arguments that are different types than its overridden parent.");
                        }
                    }
                }
                else
                {
                    if (this.Modifiers.HasOverride)
                    {
                        throw new ParserException(this, "This function is marked as 'override', but there is no method in a parent class with the same name.");
                    }
                }
            }
        }
Exemple #7
0
        private IConstantValue SimplifyFunctionCall(FunctionDefinition func, Expression[] args)
        {
            if (args.Length == 0)
            {
                return(null);
            }

            for (int i = 0; i < args.Length; ++i)
            {
                if (!(args[i] is IConstantValue))
                {
                    return(null);
                }
            }

            int        iValue = 0;
            double     fValue = 0.0;
            bool       bValue = false;
            double     output = 0.0;
            string     lib    = func.Assembly.CanonicalKey;
            string     name   = func.NameToken.Value;
            Expression winner;

            switch (lib)
            {
            case "en:Core":
                switch (this.Args.Length + ":" + name)
                {
                // TODO: implement these...
                case "1:chr":
                case "1:ord":
                case "1:parseFloat":
                case "1:parseInt":
                case "1:typeof":
                    return(null);

                case "1:isString":
                case "1:isNumber":
                    if (this.Args[0] is StringConstant && name == "isString")
                    {
                        bValue = true;
                    }
                    else if (this.Args[0] is IntegerConstant || this.Args[0] is FloatConstant && name == "isNumber")
                    {
                        bValue = true;
                    }
                    return(new BooleanConstant(this.FirstToken, bValue, this.Owner));
                }
                break;

            case "en:Math":
                double[] nums = new double[this.Args.Length];
                // Make sure we're only dealing with numbers.
                for (int i = 0; i < this.Args.Length; ++i)
                {
                    if (this.Args[i] is IntegerConstant)
                    {
                        nums[i] = ((IntegerConstant)this.Args[i]).Value;
                    }
                    else if (this.Args[i] is FloatConstant)
                    {
                        nums[i] = ((FloatConstant)this.Args[i]).Value;
                    }
                    else
                    {
                        throw new ParserException(this.Args[i], "This is not a valid input for " + name + ".");
                    }
                }

                switch (this.Args.Length + ":" + name)
                {
                case "3:ensureRange":
                    double value = nums[0];
                    double r1    = nums[1];
                    double r2    = nums[2];
                    if (r1 > r2)         // swap the values if given in wrong order
                    {
                        winner = value <r2 ? args[2] : value> r1 ? args[1] : args[0];
                    }
                    else
                    {
                        winner = value <r1 ? args[1] : value> r2 ? args[2] : args[0];
                    }
                    if (winner is IntegerConstant)
                    {
                        return(new IntegerConstant(this.FirstToken, ((IntegerConstant)winner).Value, this.Owner));
                    }
                    return(new FloatConstant(this.FirstToken, ((FloatConstant)winner).Value, this.Owner));

                case "1:abs":
                    if (args[0] is IntegerConstant)
                    {
                        iValue = ((IntegerConstant)args[0]).Value;
                        return(new IntegerConstant(this.FirstToken, iValue < 0 ? -iValue : iValue, this.Owner));
                    }
                    fValue = nums[0];
                    return(new FloatConstant(this.FirstToken, fValue < 0 ? -fValue : fValue, this.Owner));

                case "2:arctan":
                case "2:min":
                case "2:max":
                    double arg1 = nums[0];
                    double arg2 = nums[1];

                    if (name == "arctan")
                    {
                        output = System.Math.Atan2(arg1, arg2);
                        if (output < 0)
                        {
                            output += TWO_PI;
                        }
                        return(new FloatConstant(this.FirstToken, output, this.Owner));
                    }

                    winner = (arg1 == arg2)
                                ? args[0]
                                : name == "min"
                                    ? (arg1 < arg2 ? args[0] : args[1])
                                    : (arg1 < arg2 ? args[1] : args[0]);
                    if (winner is IntegerConstant)
                    {
                        return(new IntegerConstant(this.FirstToken, ((IntegerConstant)winner).Value, this.Owner));
                    }
                    return(new FloatConstant(this.FirstToken, ((FloatConstant)winner).Value, this.Owner));

                case "1:arctan":
                case "1:arcsin":
                case "1:arccos":
                case "1:floor":
                case "1:cos":
                case "1:log2":
                case "1:log10":
                case "1:sign":
                case "1:sin":
                case "1:tan":
                case "1:ln":
                    if (args.Length != 1)
                    {
                        return(null);
                    }
                    double input = nums[0];
                    switch (name)
                    {
                    case "floor":
                        return(new IntegerConstant(this.FirstToken, (int)System.Math.Floor(input), this.Owner));

                    case "arctan":
                        output = System.Math.Atan(input);
                        if (output < 0)
                        {
                            output += TWO_PI;
                        }
                        break;

                    case "cos": output = System.Math.Cos(input); break;

                    case "sin": output = System.Math.Sin(input); break;

                    case "arccos":
                    case "arcsin":
                        if (input < -1 || input > 1)
                        {
                            throw new ParserException(
                                      this,
                                      "Cannot calculate " + name + " of a value outside of the range of -1 and 1");
                        }
                        output = name == "arccos"
                                        ? System.Math.Acos(input)
                                        : System.Math.Asin(input);
                        if (output < 0)
                        {
                            output += TWO_PI;
                        }
                        break;

                    case "sign":
                        return(new IntegerConstant(
                                   this.FirstToken,
                                   input < 0 ? -1 : input > 0 ? 1 : 0,
                                   this.Owner));

                    case "tan":
                        try
                        {
                            output = System.Math.Tan(input);
                        }
                        catch (System.Exception)
                        {
                            throw new ParserException(this, "Cannot calculate tan of this value. tan produces undefined results.");
                        }
                        break;

                    case "log10":
                    case "log2":
                    case "ln":
                        if (input < 0)
                        {
                            throw new ParserException(this, "Cannot calculate the log of a negative number.");
                        }
                        output = name == "log10"
                                        ? CalculateLogWithIntegerBase(input, 10)
                                        : name == "log2"
                                            ? CalculateLogWithIntegerBase(input, 2)
                                            : System.Math.Log(input);
                        break;
                    }

                    return(new FloatConstant(this.FirstToken, output, this.Owner));
                }
                break;
            }

            return(null);
        }
Exemple #8
0
        internal override Expression ResolveTypes(ParserContext parser, TypeResolver typeResolver)
        {
            this.Root = this.Root.ResolveTypes(parser, typeResolver);

            for (int i = 0; i < this.Args.Length; ++i)
            {
                this.Args[i] = this.Args[i].ResolveTypes(parser, typeResolver);
            }

            ResolvedType rootType = this.Root.ResolvedType;

            if (rootType == ResolvedType.ANY)
            {
                this.ResolvedType = ResolvedType.ANY;
                return(this);
            }

            if (rootType.Category == ResolvedTypeCategory.FUNCTION_POINTER)
            {
                int maxArgCount = rootType.FunctionArgs.Length;
                int minArgCount = maxArgCount - rootType.FunctionOptionalArgCount;
                if (this.Args.Length < minArgCount || this.Args.Length > maxArgCount)
                {
                    throw new ParserException(this.ParenToken, "This function has the incorrect number of arguments.");
                }

                for (int i = 0; i < this.Args.Length; ++i)
                {
                    if (!this.Args[i].ResolvedType.CanAssignToA(rootType.FunctionArgs[i]))
                    {
                        throw new ParserException(this.Args[i], "Incorrect argument type.");
                    }
                }
                this.ResolvedType = rootType.FunctionReturnType;

                if (!this.CompilationScope.IsStaticallyTyped && this.Root is FunctionReference)
                {
                    FunctionDefinition fn = ((FunctionReference)this.Root).FunctionDefinition;
                    if (fn.CompilationScope.IsStaticallyTyped)
                    {
                        for (int i = 0; i < this.Args.Length; ++i)
                        {
                            ResolvedType actualType   = this.Args[i].ResolvedType;
                            ResolvedType expectedType = fn.ResolvedArgTypes[i];
                            if (expectedType != ResolvedType.OBJECT && actualType == ResolvedType.ANY)
                            {
                                this.Args[i] = new Cast(this.Args[i].FirstToken, expectedType, this.Args[i], this.Owner, false);
                            }
                        }
                    }
                }

                // TODO: this is temporary until the Math library is converted to Acrylic
                if (this.ResolvedType == ResolvedType.ANY &&
                    this.Root is FunctionReference &&
                    ((FunctionReference)this.Root).FunctionDefinition.CompilationScope.Metadata.ID == "Math")
                {
                    FunctionReference func = (FunctionReference)this.Root;
                    switch (func.FunctionDefinition.NameToken.Value)
                    {
                    case "abs":
                    case "min":
                    case "max":
                    case "ensureRange":
                        if (this.Args.Count(ex => ex.ResolvedType == ResolvedType.FLOAT) > 0)
                        {
                            this.ResolvedType = ResolvedType.FLOAT;
                        }
                        else
                        {
                            this.ResolvedType = ResolvedType.INTEGER;
                        }
                        break;

                    case "floor":
                    case "sign":
                        this.ResolvedType = ResolvedType.INTEGER;
                        break;

                    default:
                        this.ResolvedType = ResolvedType.FLOAT;
                        break;
                    }
                }

                if (this.ResolvedType == ResolvedType.ANY &&
                    this.CompilationScope.IsStaticallyTyped)
                {
                    // ANY types are not allowed in statically typed compilation scopes.
                    // Convert this into an object and require the user to perform any specific casts.
                    this.ResolvedType = ResolvedType.OBJECT;
                }

                return(this);
            }

            throw new System.NotImplementedException();
        }
        internal override Expression Resolve(ParserContext parser)
        {
            for (int i = 0; i < this.Args.Length; ++i)
            {
                this.Args[i] = this.Args[i].Resolve(parser);
            }

            if (this.Root is Variable)
            {
                string varName = ((Variable)this.Root).Name;

                if (parser.GetClass(varName) != null)
                {
                    throw new ParserException(this.ParenToken, "Cannot invoke a class like a function. To construct a new class, the \"new\" keyword must be used.");
                }
            }

            this.Root = this.Root.Resolve(parser);

            // TODO: this is hardcoded just for Math.floor(numeric constant). Eventually, it'd be nice
            // for a few common functions to have a compile-time codepath here.
            // e.g. Core.parseInt, Math.sin, etc.
            if (this.Root is FunctionReference && this.Args.Length == 1)
            {
                FunctionDefinition funcDef = ((FunctionReference)this.Root).FunctionDefinition;
                if (funcDef.Library != null &&
                    funcDef.Library.CanonicalKey == "en:Math" &&
                    funcDef.NameToken.Value == "floor")
                {
                    Expression arg0 = this.Args[0];
                    if (arg0 is IntegerConstant)
                    {
                        int integerValue = ((IntegerConstant)arg0).Value;
                        return(new IntegerConstant(this.FirstToken, integerValue, this.Owner));
                    }

                    if (arg0 is FloatConstant)
                    {
                        double floatValue   = ((FloatConstant)arg0).Value;
                        int    integerValue = (int)floatValue;
                        return(new IntegerConstant(this.FirstToken, integerValue, this.Owner));
                    }
                }
            }

            if (this.Root is SpecialEntity)
            {
                if (this.Root is SpecialEntity.EnumMaxFunction)
                {
                    int max = ((SpecialEntity.EnumMaxFunction) this.Root).GetMax();
                    return(new IntegerConstant(this.Root.FirstToken, max, this.Owner));
                }

                if (this.Root is SpecialEntity.EnumValuesFunction)
                {
                    int[]             rawValues = ((SpecialEntity.EnumValuesFunction) this.Root).GetValues();
                    List <Expression> values    = new List <Expression>();
                    foreach (int rawValue in rawValues)
                    {
                        values.Add(new IntegerConstant(this.Root.FirstToken, rawValue, this.Owner));
                    }
                    return(new ListDefinition(this.FirstToken, values, this.Owner));
                }
            }

            return(this);
        }
Exemple #10
0
        internal override void Resolve(ParserContext parser)
        {
            for (int i = 0; i < this.Fields.Length; ++i)
            {
                FieldDefinition field = this.Fields[i];
                field.Resolve(parser);
                this.Fields[i] = field;
            }

            for (int i = 0; i < this.Methods.Length; ++i)
            {
                FunctionDefinition funcDef = this.Methods[i];
                funcDef.Resolve(parser);
                this.Methods[i] = funcDef;
            }

            this.Constructor.Resolve(parser);

            if (this.StaticConstructor != null)
            {
                this.StaticConstructor.Resolve(parser);
            }

            bool hasABaseClass        = this.BaseClass != null;
            bool callsBaseConstructor = this.Constructor.BaseToken != null;

            if (hasABaseClass && callsBaseConstructor)
            {
                Expression[] defaultValues = this.BaseClass.Constructor.DefaultValues;
                int          maxValues     = defaultValues.Length;
                int          minValues     = 0;
                for (int i = 0; i < maxValues; ++i)
                {
                    if (defaultValues[i] == null)
                    {
                        minValues++;
                    }
                    else
                    {
                        break;
                    }
                }
                int baseArgs = this.Constructor.BaseArgs.Length;
                if (baseArgs < minValues || baseArgs > maxValues)
                {
                    throw new ParserException(this.Constructor.BaseToken, "Invalid number of arguments passed to base constructor.");
                }
            }
            else if (hasABaseClass && !callsBaseConstructor)
            {
                if (this.BaseClass.Constructor != null)
                {
                    if (this.BaseClass.Constructor.MinArgCount > 0)
                    {
                        throw new ParserException(this, "The base class of this class has a constructor which must be called.");
                    }
                }
            }
            else if (!hasABaseClass && callsBaseConstructor)
            {
                throw new ParserException(this.Constructor.BaseToken, "Cannot call base constructor without a base class.");
            }
            else // (!hasABaseClass && !callsBaseConstructor)
            {
                // yeah, that's fine.
            }
        }