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; TypeContext tc = parser.TypeContext; if (rootType == tc.ANY) { this.ResolvedType = tc.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 != tc.OBJECT && actualType == tc.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 == tc.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 == tc.FLOAT) > 0) { this.ResolvedType = tc.FLOAT; } else { this.ResolvedType = tc.INTEGER; } break; case "floor": case "sign": this.ResolvedType = tc.INTEGER; break; default: this.ResolvedType = tc.FLOAT; break; } } if (this.ResolvedType == tc.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 = tc.OBJECT; } return(this); } throw new System.NotImplementedException(); }
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); }
public FunctionReference(Token token, FunctionDefinition funcDef, Node owner) : base(token, owner) { this.FunctionDefinition = funcDef; }
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. } }
private void EnsureModifiersAndTypeSignatureConsistencyForClassMethods(TypeContext tc) { 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 == tc.ANY && this.ResolvedReturnType == tc.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 == tc.OBJECT && expected == tc.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."); } } } }
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); }
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; TypeContext tc = parser.TypeContext; // 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 = parser.TypeContext.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 = tc.INTEGER; if (this.Root is StringConstant) { return(new IntegerConstant(this.FirstToken, ((StringConstant)this.Root).Value.Length, this.Owner)); } return(this); case "contains": return(BuildPrimitiveMethod(tc.BOOLEAN, tc.STRING)); case "endsWith": return(BuildPrimitiveMethod(tc.BOOLEAN, tc.STRING)); case "indexOf": return(BuildPrimitiveMethod(tc.INTEGER, tc.STRING)); case "lower": return(BuildPrimitiveMethod(tc.STRING)); case "ltrim": return(BuildPrimitiveMethod(tc.STRING)); case "replace": return(BuildPrimitiveMethod(tc.STRING, tc.STRING, tc.STRING)); case "reverse": return(BuildPrimitiveMethod(tc.STRING)); case "rtrim": return(BuildPrimitiveMethod(tc.STRING)); case "split": return(BuildPrimitiveMethod(ResolvedType.ListOrArrayOf(tc.STRING), tc.STRING)); case "startsWith": return(BuildPrimitiveMethod(tc.BOOLEAN, tc.STRING)); case "trim": return(BuildPrimitiveMethod(tc.STRING)); case "upper": return(BuildPrimitiveMethod(tc.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 = tc.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 = tc.ANY; return(this); case "add": return(BuildPrimitiveMethod(tc.VOID, itemType)); case "choice": return(BuildPrimitiveMethod(tc.VOID)); case "clear": return(BuildPrimitiveMethod(tc.VOID)); case "clone": return(BuildPrimitiveMethod(rootType)); case "concat": return(BuildPrimitiveMethod(rootType, rootType)); case "contains": return(BuildPrimitiveMethod(tc.BOOLEAN, itemType)); case "insert": return(BuildPrimitiveMethod(tc.VOID, tc.INTEGER, itemType)); case "join": return(BuildPrimitiveMethodWithOptionalArgs(tc.STRING, 1, tc.STRING)); case "pop": return(BuildPrimitiveMethod(itemType)); case "remove": return(BuildPrimitiveMethod(tc.VOID, tc.INTEGER)); case "reverse": return(BuildPrimitiveMethod(tc.VOID)); case "shuffle": return(BuildPrimitiveMethod(tc.VOID)); case "sort": return(BuildPrimitiveMethod(tc.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 = tc.INTEGER; return(this); case "clear": return(BuildPrimitiveMethod(tc.VOID)); case "clone": return(BuildPrimitiveMethod(rootType)); case "contains": return(BuildPrimitiveMethod(tc.BOOLEAN, keyType)); case "get": return(BuildPrimitiveMethodWithOptionalArgs(valueType, 1, keyType, valueType)); case "keys": return(BuildPrimitiveMethod(ResolvedType.ListOrArrayOf(keyType))); case "merge": return(BuildPrimitiveMethod(tc.VOID, rootType)); case "remove": return(BuildPrimitiveMethod(tc.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(tc.ANY, ResolvedType.ListOrArrayOf(tc.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."); } }