public override string TranslateType(Pastel.Nodes.PType type) { switch (type.RootValue) { case "int": case "char": case "bool": case "double": case "string": case "object": case "void": return(type.RootValue); case "List": return("List<" + this.TranslateType(type.Generics[0]) + ">"); case "Dictionary": return("Dictionary<" + this.TranslateType(type.Generics[0]) + ", " + this.TranslateType(type.Generics[1]) + ">"); case "Array": return(this.TranslateType(type.Generics[0]) + "[]"); default: if (type.Generics.Length > 0) { throw new NotImplementedException(); } return(type.RootValue); } }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { PType type = varScope.GetTypeOfVariable(this.Name); this.ResolvedType = type; if (type == null) { throw new ParserException( this.FirstToken, "The " + (this.IsFunctionInvocation ? "function" : "variable") + " '" + this.Name + "' is not defined."); } return(this); }
internal bool IsIdentical(PastelCompiler compiler, PType other) { if (!this.isTypeFinalized) { this.FinalizeType(compiler); } if (!other.isTypeFinalized) { other.FinalizeType(compiler); } if (this.Category != other.Category) { return(false); } if (this.Generics.Length != other.Generics.Length) { return(false); } if (this.IsStructOrClass) { return(this.structReference == other.structReference || this.classReference == other.classReference); } if (this.RootValue != other.RootValue) { string thisRoot = this.RootValue; string thatRoot = other.RootValue; if (thisRoot == "number" && (thatRoot == "double" || thatRoot == "int")) { return(true); } if (thatRoot == "number" && (thisRoot == "double" || thisRoot == "int")) { return(true); } return(false); } for (int i = this.Generics.Length - 1; i >= 0; --i) { if (!this.Generics[i].IsIdentical(compiler, other.Generics[i])) { return(false); } } return(true); }
public StructDefinition(Token structToken, Token name, IList <PType> argTypes, IList <Token> argNames) { this.FirstToken = structToken; this.NameToken = name; this.ArgTypes = argTypes.ToArray(); this.ArgNames = argNames.ToArray(); this.ArgIndexByName = new Dictionary <string, int>(); for (int i = this.ArgNames.Length - 1; i >= 0; --i) { string argName = this.ArgNames[i].Value; this.ArgIndexByName[argName] = i; } this.Type = new PType(this.FirstToken, name.Value); }
public FunctionDefinition( Token nameToken, PType returnType, IList <PType> argTypes, IList <Token> argNames, IList <Executable> code) { this.FirstToken = returnType.FirstToken; this.NameToken = nameToken; this.ReturnType = returnType; this.ArgTypes = argTypes.ToArray(); this.ArgNames = argNames.ToArray(); this.Code = code.ToArray(); }
private void VerifyArgTypes(PType[] expectedTypes, PastelCompiler compiler) { if (expectedTypes.Length != this.Args.Length) { throw new ParserException(this.OpenParenToken, "This function invocation has the wrong number of parameters. Expected " + expectedTypes.Length + " but found " + this.Args.Length + "."); } for (int i = 0; i < this.Args.Length; ++i) { if (!PType.CheckAssignment(compiler, expectedTypes[i], this.Args[i].ResolvedType)) { throw new ParserException(this.Args[i].FirstToken, "Wrong function arg type. Cannot convert a " + this.Args[i].ResolvedType + " to a " + expectedTypes[i]); } } }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { for (int i = 0; i < this.Args.Length; ++i) { this.Args[i] = this.Args[i].ResolveType(varScope, compiler); } this.Root = this.Root.ResolveType(varScope, compiler); if (this.Root is FunctionReference) { FunctionDefinition functionDefinition = ((FunctionReference)this.Root).Function; this.VerifyArgTypes(functionDefinition.ArgTypes, compiler); this.ResolvedType = functionDefinition.ReturnType; return(this); } else if (this.Root is CoreFunctionReference) { CoreFunctionReference nfr = (CoreFunctionReference)this.Root; CoreFunctionInvocation nfi; if (nfr.Context == null) { nfi = new CoreFunctionInvocation(this.FirstToken, nfr.CoreFunctionId, this.Args, this.Owner); } else { nfi = new CoreFunctionInvocation(this.FirstToken, nfr.CoreFunctionId, nfr.Context, this.Args, this.Owner); } return(nfi.ResolveType(varScope, compiler)); } else if (this.Root is ExtensibleFunctionReference) { return(new ExtensibleFunctionInvocation(this.FirstToken, (ExtensibleFunctionReference)this.Root, this.Args).ResolveType(varScope, compiler)); } else if (this.Root is ConstructorReference) { PType typeToConstruct = ((ConstructorReference)this.Root).TypeToConstruct; typeToConstruct.FinalizeType(compiler); return(new ConstructorInvocation(this.FirstToken, typeToConstruct, this.Args, this.Owner)); } else if (this.Root.ResolvedType.RootValue == "Func") { return(new FunctionPointerInvocation(compiler, this.FirstToken, this.Root, this.Args)); } throw new ParserException(this.OpenParenToken, "This expression cannot be invoked like a function."); }
public FunctionDefinition( Token nameToken, PType returnType, IList <PType> argTypes, IList <Token> argNames, PastelContext context, ClassDefinition nullableClassOwner) // null if not associated with a class { this.Context = context; this.FirstToken = returnType.FirstToken; this.NameToken = nameToken; this.ReturnType = returnType; this.ArgTypes = argTypes.ToArray(); this.ArgNames = argNames.ToArray(); this.ClassDef = nullableClassOwner; }
internal override void ResolveTypes(VariableScope varScope, PastelCompiler compiler) { if (this.Expression != null) { this.Expression = this.Expression.ResolveType(varScope, compiler); if (!PType.CheckReturnType(varScope.RootFunctionDefinition.ReturnType, this.Expression.ResolvedType)) { throw new ParserException(this.Expression.FirstToken, "This expression is not the expected return type of this function."); } } else { if (!this.Expression.ResolvedType.IsIdentical(PType.VOID)) { throw new ParserException(this.FirstToken, "Must return a value in this function."); } } }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { this.Root = this.Root.ResolveType(varScope, compiler); this.Index = this.Index.ResolveType(varScope, compiler); PType rootType = this.Root.ResolvedType; PType indexType = this.Index.ResolvedType; bool badIndex = false; if (rootType.RootValue == "List" || rootType.RootValue == "Array") { badIndex = !indexType.IsIdentical(PType.INT); this.ResolvedType = rootType.Generics[0]; } else if (rootType.RootValue == "Dictionary") { badIndex = !indexType.IsIdentical(rootType.Generics[0]); this.ResolvedType = rootType.Generics[1]; } else if (rootType.RootValue == "string") { badIndex = !indexType.IsIdentical(PType.INT); this.ResolvedType = PType.CHAR; if (this.Root is InlineConstant && this.Index is InlineConstant) { string c = ((string)((InlineConstant)this.Root).Value)[(int)((InlineConstant)this.Index).Value].ToString(); InlineConstant newValue = new InlineConstant(PType.CHAR, this.FirstToken, c); newValue.ResolveType(varScope, compiler); return(newValue); } } else { badIndex = true; } if (badIndex) { throw new ParserException(this.BracketToken, "Cannot index into a " + rootType + " with a " + indexType + "."); } return(this); }
public override string TranslateType(Pastel.Nodes.PType type) { switch (type.RootValue) { case "int": return("int"); case "string": return("int*"); case "bool": return("int"); case "double": return("double"); case "object": return("void*"); case "char": return("int"); case "List": return("List*"); case "Array": return(this.TranslateType(type.Generics[0]) + "*"); case "Dictionary": string keyType = type.Generics[0].RootValue; switch (keyType) { case "int": case "string": return("Dictionary*"); default: throw new NotImplementedException(); } default: break; } char firstChar = type.RootValue[0]; if (firstChar >= 'A' && firstChar <= 'Z') { return(type.RootValue + "*"); } throw new NotImplementedException(); }
internal override void ResolveTypes(VariableScope varScope, PastelCompiler compiler) { this.Value = this.Value.ResolveType(varScope, compiler); this.Target = this.Target.ResolveType(varScope, compiler); if (!PType.CheckAssignment(this.Target.ResolvedType, this.Value.ResolvedType)) { if (this.OpToken.Value != "=" && this.Target.ResolvedType.IsIdentical(PType.DOUBLE) && this.Value.ResolvedType.IsIdentical(PType.INT)) { // You can apply incremental ops such as += with an int to a float and that is fine without explicit conversion in any platform. } else { throw new ParserException(this.OpToken, "Cannot assign a " + this.Value.ResolvedType + " to a " + this.Target.ResolvedType); } } }
private bool IsParentOf(PType moreSpecificTypeOrSame) { if (moreSpecificTypeOrSame == this) { return(true); } if (this.RootValue == "object") { return(true); } if (this.Generics.Length == 0) { // why no treatment of int as a subtype of double? because there needs to be an explicit type conversion // for languages that aren't strongly typed and won't auto-convert. return(this.RootValue == moreSpecificTypeOrSame.RootValue); } // All that's left are Arrays, Lists, and Dictionaries, which must match exactly. return(this.IsIdentical(moreSpecificTypeOrSame)); }
internal bool IsIdenticalOrChildOf(PastelCompiler compiler, PType other) { if (this.IsIdentical(compiler, other)) { return(true); } // only structs or classes should be here if this is to return true. If not, then it's a no. if (!this.IsStructOrClass || !other.IsStructOrClass) { return(false); } if (this.IsStruct != other.IsStruct) { return(false); } if (this.IsStruct) { if (this.StructDef == null || other.StructDef == null) { throw new System.Exception("This check cannot occur without resolving struct information for PTypes."); } StructDefinition walker = this.StructDef; StructDefinition target = other.StructDef; while (walker != null) { if (walker == target) { return(true); } walker = walker.Parent; } } if (this.IsClass) { throw new System.NotImplementedException(); } return(false); }
// Note that this class is instantiated in the ResolveType phase. public FunctionPointerInvocation(PastelCompiler compiler, Token firstToken, Expression root, IList <Expression> Args) : base(firstToken, root.Owner) { this.Root = root; this.Args = Args.ToArray(); this.ResolvedType = this.Root.ResolvedType.Generics[0]; if (this.Root.ResolvedType.Generics.Length - 1 != this.Args.Length) { throw new ParserException(this.Root.FirstToken, "This function has the incorrect number of arguments."); } for (int i = 0; i < this.Args.Length; ++i) { PType expectedArgType = this.Root.ResolvedType.Generics[i + 1]; PType actualArgType = this.Args[i].ResolvedType; if (!actualArgType.IsIdentical(compiler, expectedArgType)) { throw new ParserException(this.Args[i].FirstToken, "Incorrect argument type. Expected " + expectedArgType + " but found " + actualArgType + "."); } } }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { ClassDefinition cd; if (this.Owner is FunctionDefinition funcDef) { cd = funcDef.ClassDef; } else if (this.Owner is FieldDefinition fieldDef) { cd = fieldDef.ClassDef; } else if (this.Owner is ConstructorDefinition constructorDef) { cd = constructorDef.ClassDef; } else { throw new ParserException(this.FirstToken, "Cannot use the expression 'this' outside of classes."); } this.ResolvedType = PType.ForClass(this.FirstToken, cd); return(this); }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { this.Root = this.Root.ResolveType(varScope, compiler); this.Index = this.Index.ResolveType(varScope, compiler); PType rootType = this.Root.ResolvedType; PType indexType = this.Index.ResolvedType; bool badIndex = false; if (rootType.RootValue == "List" || rootType.RootValue == "Array") { badIndex = !indexType.IsIdentical(PType.INT); this.ResolvedType = rootType.Generics[0]; } else if (rootType.RootValue == "Dictionary") { badIndex = !indexType.IsIdentical(rootType.Generics[0]); this.ResolvedType = rootType.Generics[1]; } else if (rootType.RootValue == "string") { badIndex = !indexType.IsIdentical(PType.INT); this.ResolvedType = PType.CHAR; } else { badIndex = true; } if (badIndex) { throw new ParserException(this.BracketToken, "Cannot index into a " + rootType + " with a " + indexType + "."); } return(this); }
internal static bool CheckReturnType(PastelCompiler compiler, PType returnType, PType value) { // This is an assert, not a user-facing exception. Null should never appear here. if (value == null) { throw new ParserException(returnType.FirstToken, "This should not happen."); } if (value.IsIdenticalOrChildOf(compiler, returnType)) { return(true); } if (returnType.Category == TypeCategory.OBJECT) { return(true); } if (returnType.Category == TypeCategory.VOID) { return(false); } if (value.Category == TypeCategory.NULL) { if (returnType.Category == TypeCategory.PRIMITIVE && returnType.TypeName == "string") { return(true); } if (returnType.Generics.Length > 0) { return(true); } if (returnType.IsStructOrClass) { return(true); } } return(false); }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { // The args were already resolved. // This ensures that they match the native function definition PType[] expectedTypes = NativeFunctionUtil.GetNativeFunctionArgTypes(this.Function); bool[] isArgRepeated = NativeFunctionUtil.GetNativeFunctionIsArgTypeRepeated(this.Function); switch (this.Function) { case NativeFunction.FORCE_PARENS: if (this.Args.Length != 1) { throw new ParserException(this.FirstToken, "Expected 1 arg."); } return(new ForcedParenthesis(this.FirstToken, this.Args[0])); case NativeFunction.IS_DEBUG: #if DEBUG bool value = true; #else bool value = false; #endif return(new InlineConstant(PType.BOOL, this.FirstToken, value)); } Dictionary <string, PType> templateLookup = new Dictionary <string, PType>(); int verificationLength = expectedTypes.Length; if (verificationLength > 0 && isArgRepeated[isArgRepeated.Length - 1]) { verificationLength--; } for (int i = 0; i < verificationLength; ++i) { if (!PType.CheckAssignmentWithTemplateOutput(expectedTypes[i], this.Args[i].ResolvedType, templateLookup)) { throw new ParserException(this.Args[i].FirstToken, "Incorrect type. Expected " + expectedTypes[i] + " but found " + this.Args[i].ResolvedType + "."); } } if (expectedTypes.Length < this.Args.Length) { if (isArgRepeated[isArgRepeated.Length - 1]) { PType expectedType = expectedTypes[expectedTypes.Length - 1]; for (int i = expectedTypes.Length; i < this.Args.Length; ++i) { if (!PType.CheckAssignment(expectedType, this.Args[i].ResolvedType)) { throw new ParserException(this.Args[i].FirstToken, "Incorrect type. Expected " + expectedTypes[i] + " but found " + this.Args[i].ResolvedType + "."); } } } else { throw new ParserException(this.FirstToken, "Too many arguments."); } } PType returnType = NativeFunctionUtil.GetNativeFunctionReturnType(this.Function); if (returnType.HasTemplates) { returnType = returnType.ResolveTemplates(templateLookup); } this.ResolvedType = returnType; return(this); }
private NativeFunction DetermineNativeFunctionId(PType rootType, string field) { switch (rootType.RootValue) { case "string": switch (field) { case "CharCodeAt": return(NativeFunction.STRING_CHAR_CODE_AT); case "Contains": return(NativeFunction.STRING_CONTAINS); case "EndsWith": return(NativeFunction.STRING_ENDS_WITH); case "IndexOf": return(NativeFunction.STRING_INDEX_OF); case "Replace": return(NativeFunction.STRING_REPLACE); case "Reverse": return(NativeFunction.STRING_REVERSE); case "Size": return(NativeFunction.STRING_LENGTH); case "Split": return(NativeFunction.STRING_SPLIT); case "StartsWith": return(NativeFunction.STRING_STARTS_WITH); case "SubString": return(NativeFunction.STRING_SUBSTRING); case "SubStringIsEqualTo": return(NativeFunction.STRING_SUBSTRING_IS_EQUAL_TO); case "ToLower": return(NativeFunction.STRING_TO_LOWER); case "ToUpper": return(NativeFunction.STRING_TO_UPPER); case "Trim": return(NativeFunction.STRING_TRIM); case "TrimEnd": return(NativeFunction.STRING_TRIM_END); case "TrimStart": return(NativeFunction.STRING_TRIM_START); default: throw new ParserException(this.FieldName, "Unresolved string method: " + field); } case "Array": switch (field) { case "Join": return(NativeFunction.ARRAY_JOIN); case "Size": return(NativeFunction.ARRAY_LENGTH); default: throw new ParserException(this.FieldName, "Unresolved Array method: " + field); } case "List": switch (field) { case "Add": return(NativeFunction.LIST_ADD); case "Clear": return(NativeFunction.LIST_CLEAR); case "Insert": return(NativeFunction.LIST_INSERT); case "Join": string memberType = rootType.Generics[0].RootValue; switch (memberType) { case "string": return(NativeFunction.LIST_JOIN_STRINGS); case "char": return(NativeFunction.LIST_JOIN_CHARS); default: throw new ParserException(this.FieldName, "Unresolved List<" + memberType + "> method: " + field); } case "Pop": return(NativeFunction.LIST_POP); case "RemoveAt": return(NativeFunction.LIST_REMOVE_AT); case "Reverse": return(NativeFunction.LIST_REVERSE); case "Shuffle": return(NativeFunction.LIST_SHUFFLE); case "Size": return(NativeFunction.LIST_SIZE); default: throw new ParserException(this.FieldName, "Unresolved List method: " + field); } case "Dictionary": switch (field) { case "Contains": return(NativeFunction.DICTIONARY_CONTAINS_KEY); case "Keys": return(NativeFunction.DICTIONARY_KEYS); case "Remove": return(NativeFunction.DICTIONARY_REMOVE); case "Size": return(NativeFunction.DICTIONARY_SIZE); case "Values": return(NativeFunction.DICTIONARY_VALUES); default: throw new ParserException(this.FieldName, "Unresolved Dictionary method: " + field); } default: throw new ParserException(this.FieldName, "Unresolved field."); } }
public ConstructorReference(Token newToken, PType type, ICompilationEntity owner) : base(newToken, owner) { this.TypeToConstruct = type; }
// when a templated type coincides with an actual value, add that template key to the lookup output param. public static bool CheckAssignmentWithTemplateOutput(PType templatedType, PType actualValue, Dictionary <string, PType> output) { if (templatedType.RootValue == "object") { return(true); } // Most cases, nothing to do if (templatedType.IsIdentical(actualValue)) { return(true); } if (templatedType.RootValue.Length == 1) { if (output.ContainsKey(templatedType.RootValue)) { PType requiredType = output[templatedType.RootValue]; // if it's already encountered it better match the existing value if (actualValue.IsIdentical(requiredType)) { return(true); } // It's also possible that this is null, in which case the type must be nullable. if (actualValue.RootValue == "null" && requiredType.IsNullable) { return(true); } return(false); } output[templatedType.RootValue] = actualValue; return(true); } if (templatedType.Generics.Length != actualValue.Generics.Length) { // completely different. don't even try to match templates return(false); } if (templatedType.RootValue != actualValue.RootValue) { if (templatedType.RootValue.Length == 1) { if (output.ContainsKey(templatedType.RootValue)) { // if it's already encountered it better match the existing value if (actualValue.IsIdentical(output[templatedType.RootValue])) { // yup, that's okay } else { return(false); } } else { // first time this type was encountered. output[templatedType.RootValue] = actualValue; } } else { // different type return(false); } } for (int i = 0; i < templatedType.Generics.Length; ++i) { if (!CheckAssignmentWithTemplateOutput(templatedType.Generics[i], actualValue.Generics[i], output)) { return(false); } } return(true); }
public ConstructorReference(Token newToken, PType type) : base(newToken) { this.TypeToConstruct = type; }
private static PType ParseImpl(TokenStream tokens) { Token token = tokens.Pop(); switch (token.Value) { case "int": case "char": case "double": case "bool": case "void": case "string": case "object": return(new PType(token, token.Value)); default: if (!PastelParser.IsValidName(token.Value)) { return(null); } break; } int tokenIndex = tokens.SnapshotState(); bool isError = false; if (tokens.PopIfPresent("<")) { List <PType> generics = new List <PType>(); while (!tokens.PopIfPresent(">")) { if (generics.Count > 0) { if (!tokens.PopIfPresent(",")) { isError = true; break; } } PType generic = ParseImpl(tokens); if (generic == null) { return(null); } generics.Add(generic); } if (!isError) { return(new PType(token, token.Value, generics)); } // If there was an error while parsing generics, then this may still be a valid type. tokens.RevertState(tokenIndex); return(new PType(token, token.Value)); } else { return(new PType(token, token.Value)); } }
internal override Expression ResolveType(VariableScope varScope, PastelCompiler compiler) { for (int i = 0; i < this.Expressions.Length; ++i) { this.Expressions[i] = this.Expressions[i].ResolveType(varScope, compiler); } this.ResolvedType = this.Expressions[0].ResolvedType; for (int i = 0; i < this.Ops.Length; ++i) { PType nextType = this.Expressions[i + 1].ResolvedType; string op = this.Ops[i].Value; if (op == "==" || op == "!=") { if ((nextType.RootValue == this.ResolvedType.RootValue) || (nextType.RootValue == "null" && this.ResolvedType.IsNullable) || (nextType.IsNullable && this.ResolvedType.RootValue == "null") || (nextType.RootValue == "null" && this.ResolvedType.RootValue == "null")) { this.ResolvedType = PType.BOOL; continue; } } string lookup = this.ResolvedType.RootValue + this.Ops[i].Value + nextType.RootValue; switch (lookup) { case "int+int": case "int-int": case "int*int": case "int%int": case "int&int": case "int|int": case "int^int": case "int<<int": case "int>>int": this.ResolvedType = PType.INT; break; case "int+double": case "double+int": case "double+double": case "int-double": case "double-int": case "double-double": case "int*double": case "double*int": case "double*double": case "double%int": case "int%double": case "double%double": this.ResolvedType = PType.DOUBLE; break; case "int>int": case "int<int": case "int>=int": case "int<=int": case "double<int": case "double>int": case "double<=int": case "double>=int": case "int<double": case "int>double": case "int<=double": case "int>=double": case "double<double": case "double>double": case "double<=double": case "double>=double": case "int==int": case "double==double": case "int==double": case "double==int": case "int!=int": case "double!=double": case "int!=double": case "double!=int": case "bool&&bool": case "bool||bool": case "char>char": case "char<char": case "char>=char": case "char<=char": this.ResolvedType = PType.BOOL; break; case "int/int": case "int/double": case "double/int": case "double/double": throw new ParserException(this.Ops[i], "Due to varying platform behavior of / use Core.IntegerDivision(numerator, denominator) or Core.FloatDivision(numerator, denominator)"); default: throw new ParserException(this.Ops[i], "The operator '" + this.Ops[i].Value + "' is not defined for types: " + this.ResolvedType + " and " + nextType + "."); } } return(this); }
public override string TranslateType(Pastel.Nodes.PType type) { return(this.ParentPlatform.TranslateType(type)); }
public CastExpression(Token openParenToken, PType type, Expression expression) : base(openParenToken) { this.Type = type; this.Expression = expression; }
public ConstructorInvocation(Token firstToken, PType type, IList <Expression> args) : base(firstToken) { this.Type = type; this.Args = args.ToArray(); this.ResolvedType = type; }
public InlineConstant(PType type, Token firstToken, object value) : base(firstToken) { this.Type = type; this.ResolvedType = type; this.Value = value; }
public InlineConstant(PType type, Token firstToken, object value, ICompilationEntity owner) : base(firstToken, owner) { this.Type = type; this.ResolvedType = type; this.Value = value; }