internal override Expression ResolveTypes(ParserContext parser, TypeResolver typeResolver) { int argCount = this.ArgTypes.Length; this.ResolvedArgTypes = new ResolvedType[argCount]; for (int i = 0; i < argCount; ++i) { ResolvedType argType = typeResolver.ResolveType(this.ArgTypes[i]); this.ResolvedArgTypes[i] = argType; // TODO: this variableId will not always be non-null when the argument in a lambda is used by a // closure of another lambda inside the lambda. I'll have to change my strategy here. Possibly // tracking the VariableId's as another Args field. VariableId variableId = this.VariableScope.GetVarId(this.Args[i]); variableId.ResolvedType = argType; } foreach (Executable ex in this.Code) { ex.ResolveTypes(parser, typeResolver); } // TODO: how do you define the lambda return type in Acrylic? Snoop the nested returns, maybe? ResolvedType returnType = ResolvedType.ANY; this.ResolvedType = ResolvedType.GetFunctionType(returnType, this.ResolvedArgTypes, 0); return(this); }
internal override Expression ResolveTypes(ParserContext parser, TypeResolver typeResolver) { ResolvedType returnType = this.FunctionDefinition.ResolvedReturnType; ResolvedType[] argTypes = this.FunctionDefinition.ResolvedArgTypes; int optionalCount = argTypes.Length == 0 || this.FunctionDefinition.DefaultValues[argTypes.Length - 1] == null ? 0 : this.FunctionDefinition.DefaultValues.Where(e => e != null).Count(); this.ResolvedType = ResolvedType.GetFunctionType(returnType, argTypes, optionalCount); return(this); }
internal override Expression ResolveTypes(ParserContext parser, TypeResolver typeResolver) { this.ResolvedType = ResolvedType.GetFunctionType(this.FunctionDefinition); return(this); }
private Expression BuildPrimitiveMethodWithOptionalArgs(ResolvedType returnType, int optionalCount, params ResolvedType[] argTypes) { return(new PrimitiveMethodReference(this.Root, this.DotToken, this.FieldToken, ResolvedType.GetFunctionType(returnType, argTypes, optionalCount), this.Owner)); }
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."); } }