public T GetVariable <T>(string name) where T : Value { SprakType type = SprakType.NetToSprakLookup[typeof(T)]; return((T)GetVariable(name, type)); }
public Value GetVariable(string name, SprakType targetType) { Value rawResult = GetVariable(name); _converter.TryConvertValue(rawResult, targetType, out Value result); return(result); }
public bool TryConvertValue(Value source, SprakType destinationType, out Value destination) { if (source.Type == destinationType) { destination = source; return(true); } if (destinationType == SprakType.Any) { destination = source; return(true); } if (destinationType == SprakType.String) { destination = source.ToSprakString(); return(true); } ConversionTypeSignature signature = new ConversionTypeSignature(source.Type, destinationType); SignatureLookupResult result = _resolver.TryFindMatch(signature); if (!result.Success) { destination = null; return(false); } destination = result.BuiltInFunction.Call(new Value[] { source }, _context); return(true); }
public bool CanAccept(OperatorTypeSignature other, AssignmentResolver resolver) { if (other.Inputs != Inputs) { return(false); } if (Inputs == InputSides.Both || Inputs == InputSides.Left) { SprakType destination = LeftParam; SprakType source = other.LeftParam; if (!resolver.IsAssignable(source, destination)) { return(false); } } if (Inputs == InputSides.Both || Inputs == InputSides.Right) { SprakType destination = RightParam; SprakType source = other.RightParam; if (!resolver.IsAssignable(source, destination)) { return(false); } } return(true); }
public OperatorTypeSignature( SprakType left, SprakType right, InputSides inputs) { LeftParam = left; RightParam = right; Inputs = inputs; }
private bool IsDirect(SprakType source, SprakType destination) { // Obviously if the destination accepts "any" then the current value is okay // The source being "any" is a problem - there is no way of checking. // Current the principle source of "any" is the array indexer, since arrays can contain any combination of values. // At somepoint, the assignment resolver should be modified so that // a source of any yields "IsDirect" as true, but "IsPerfect" as // false, perhaps? Or maybe this is a special case better handled // by the code using the resolution. if (destination == SprakType.Any || source == SprakType.Any) { return(true); } if (destination == source) { return(true); } if (destination == SprakType.String) { return(true); } return(false); }
private void ParseReturnType(MethodInfo info, out SprakType returnType) { if (!SprakType.NetToSprakLookup.TryGetValue(info.ReturnType, out returnType)) { string returnName = info.ReturnType.Name; string methodName = info.DeclaringType.Name + "." + info.Name; string message = $"Unable to recognize return type {returnName} of {methodName} as a Sprak type"; throw new ArgumentException(message); } }
private void AddOperator(MethodInfo methodInfo, SprakOperatorAttribute meta) { BuiltInFunction function = new BuiltInFunction(methodInfo, this); if (!Operator.TryParse(out Operator op, name: meta.OperatorName)) { string message = $"{MethodName(methodInfo)} is declared to be an unrecognized operator: \"{meta.OperatorName}\""; throw new ArgumentException(message); } int paramCount = function.Parameters.Count; bool binary = meta.InputsHint == InputSides.Both; int requiredCount = binary ? 2 : 1; if (paramCount != requiredCount) { string desc = binary ? "binary" : "unary"; string message = $"{MethodName(methodInfo)} was declared to be the {desc} operator \"{meta.OperatorName}\", " + $"but has {function.Parameters.Count} Sprak arguments"; throw new ArgumentException(message); } InputSides inputs = meta.InputsHint; SprakType left = null; SprakType right = null; switch (inputs) { case InputSides.Both: left = function.Parameters[0]; right = function.Parameters[1]; break; case InputSides.Left: left = function.Parameters[0]; break; case InputSides.Right: right = function.Parameters[0]; break; } OperatorTypeSignature typeSignature = new OperatorTypeSignature(left, right, inputs); OperatorSignature signature = new OperatorSignature(op.Name, typeSignature); _operators.Add(signature, function); }
public Value PopValue(SprakType type) { Value value = PopValue(); if (!_converter.TryConvertValue(value, type, out Value result)) { PushValue(value); // This is a recoverable runtime error, so restore the stack. throw new SprakRuntimeException(Messages.UnexpectedType, type, value.Type, value); } return(result); }
public bool NextIsType(out SprakType type) { if (Next(out Token token) && token.Type == TokenType.Type) { type = token.AssertType(); MoveNext(); return(true); } type = null; return(false); }
public static SprakType AssertType(this Token token) { token.AssertType(TokenType.Type); if (SprakType.TryParse(token.Content, out SprakType result)) { return(result); } else { throw new TokenCheckException(token, $"Failed to parse Sprak Type: {token.Type}"); } }
public bool IsAssignable(SprakType source, SprakType destination) { if (IsDirect(source, destination)) { return(true); } HashSet <SprakType> possibleSources; if (!_possibleConversions.TryGetValue(destination, out possibleSources)) { return(false); } return(possibleSources.Contains(destination)); }
public bool IsPerfect(SprakType source, SprakType destination) { if (IsDirect(source, destination)) { return(true); } ConversionTypeSignature signature = new ConversionTypeSignature(source, destination); if (_informationLoss.TryGetValue(signature, out bool perfect)) { return(perfect); } else { throw new ArgumentException($"Unrecognized conversion from {source} to {destination}"); } }
public bool CanAccept(FunctionTypeSignature target, AssignmentResolver resolver) { if (target.Parameters.Count != Parameters.Count) { return(false); } for (int i = 0; i < Parameters.Count; i++) { SprakType destination = Parameters[i]; SprakType source = target.Parameters[i]; if (!resolver.IsAssignable(source, destination)) { return(false); } } return(true); }
private void ParseParameters(MethodInfo info, out bool acceptsContext, out List <SprakType> types, out List <string> names) { types = new List <SprakType>(); names = new List <string>(); acceptsContext = false; ParameterInfo[] parameters = info.GetParameters(); if (parameters.Length == 0) { return; } int index = 0; if (parameters[0].ParameterType == typeof(ExecutionContext)) { acceptsContext = true; index++; } while (index < parameters.Length) { if (SprakType.TryGetSprak(parameters[index].ParameterType, out SprakType sprakType)) { types.Add(sprakType); names.Add(parameters[index].Name); } else { string paramName = parameters[index].Name; string typeName = parameters[index].ParameterType.Name; string methodName = info.DeclaringType.Name + "." + info.Name; string message = $"Unable to recogize the type of parameter [{index} ({paramName}) of {methodName}], {typeName}, as a Sprak type"; throw new ArgumentException(message); } index++; } }
public BuiltInFunction(MethodInfo methodInfo, Library parent) { _methodInfo = methodInfo; string name = methodInfo .GetCustomAttribute <SprakNameOverride>() ?.Name; name ??= methodInfo.Name; List <SprakType> types; List <string> names; ParseParameters(methodInfo, out _acceptsContext, out types, out names); Signature = new FunctionSignature(parent.UniqueID, name, new FunctionTypeSignature(types)); ParameterNames = names; ParseReturnType(methodInfo, out SprakType returnType); ReturnType = returnType; }
public FunctionParameter(string name, SprakType type) { Name = name; Type = type; }
private void Update(Expression expression, CompilationEnvironment env) { // Type hints can be dependent on child expressions. // (They are indie of parents, and neighbours) Update(expression.GetSubExpressions(), env); switch (expression) { case FunctionCall function: ResolveCallAndTypeHint(function, env); break; case Group group: group.TypeHint = group.Value.TypeHint; break; case LiteralArrayGet array: array.TypeHint = SprakType.Array; break; case Indexer indexer: if (!(indexer.SourceExpression is VariableReference)) { env.Messages.AtExpression(indexer.SourceExpression, Messages.IndexerNotSupported); } SprakType sourceType = indexer.SourceExpression.TypeHint; // The source should be an array if (sourceType != null && sourceType != SprakType.Array && sourceType != SprakType.Any) { env.Messages.AtExpression(indexer.SourceExpression, Messages.CanOnlyIndexArrays, sourceType.Text); } // The index should be a number SprakType indexType = indexer.IndexExpression.TypeHint; if (indexType != null && indexType != SprakType.Number && indexType != SprakType.Any) { env.Messages.AtExpression(indexer.IndexExpression, Messages.IndexShouldBeNumber, indexType.Text); } // This is unfortunate, and to be worked on // later. indexer.TypeHint = SprakType.Any; break; case LiteralGet literal: literal.TypeHint = literal.Value.Type; break; case OperatorCall op: ResolveCallAndTypeHint(op, env); break; case Return ret: if (ret.HasValue) { ret.TypeHint = ret.Value.TypeHint; } Block parent = ret.ParentBlockHint; while (parent != null && !(parent.Header is FunctionHeader)) { parent = parent.ParentBlockHint; } if (parent == null) { env.Messages.AtToken(ret.ReturnToken, Messages.ReturnOutsideFunction); } else { SprakType src = ret.TypeHint; SprakType dst = ((FunctionHeader)parent.Header).ReturnType; if (ret.HasValue) { if (dst == SprakType.Unit) { env.Messages.AtExpression(ret.Value, Messages.ReturnValueFromVoid); } else if (src != null) { if (!env.AssignmentLookup.IsAssignable(src, dst)) { env.Messages.AtExpression(ret.Value, Messages.IncompatibleReturnValue, src.Text, dst.Text); } } } else { if (dst != SprakType.Unit) { env.Messages.AtToken(ret.ReturnToken, Messages.MissingReturnValue); } } } break; case VariableAssignment assignment: ResolveAssignment(assignment, env); break; case VariableReference variable: if (variable.ParentBlockHint.TryGetVariableInfo(variable.Name, out VariableInfo info)) { Block refAncestor = variable.ParentBlockHint; while (refAncestor != null && refAncestor != info.Source) { refAncestor = refAncestor.ParentBlockHint; } if (refAncestor == null) { env.Messages.AtToken(variable.Token, Messages.VariableFromDisconnectedBlock); } else if (variable.StartToken.Start < info.DeclarationEnd) { env.Messages.AtToken(variable.Token, Messages.ReferenceBeforeDefinition, variable.Name); } variable.TypeHint = info.DeclaredType; } else { env.Messages.AtToken(variable.Token, Messages.UnrecognizedName, variable.Name); variable.TypeHint = null; } break; } }
private void ResolveCallAndTypeHint(OperatorCall call, CompilationEnvironment env) { if (!Operator.TryParse(out Operator op, text: call.OperatorText)) { env.Messages.AtToken(call.OperatorToken, Messages.UnrecognizedOperator, call.OperatorText); return; } if (op.IsAssignment) { env.Messages.AtToken(call.OperatorToken, Messages.IncorrectUseOfAssignment, op.Text); return; } call.TypeHint = null; if (call.LeftInput != null && call.LeftInput.TypeHint == null) { return; } if (call.RightInput != null && call.RightInput.TypeHint == null) { return; } SprakType left = call.LeftInput?.TypeHint; SprakType right = call.RightInput?.TypeHint; InputSides inputs; if (left != null && right != null) { inputs = InputSides.Both; } else if (left != null) { inputs = InputSides.Left; } else { inputs = InputSides.Right; } OperatorTypeSignature signature = new OperatorTypeSignature(left, right, inputs); SignatureLookupResult lookupResult; lookupResult = env.SignatureLookup .TryFindMatch(op.Name, signature); if (lookupResult.Success) { call.BuiltInFunctionHint = lookupResult.BuiltInFunction; call.TypeHint = lookupResult.FunctionInfo?.ReturnType; } else { string operation = $"({call.LeftInput?.TypeHint?.Text})" + $" {call.OperatorToken.Content}" + $" ({call.RightInput?.TypeHint?.Text})"; env.Messages.AtToken(call.OperatorToken, Messages.UnresolvedOperation, operation); } }
public FunctionInfo(FunctionHeader header) { Signature = header.Signature; ReturnType = header.ReturnType; }
public FunctionInfo(FunctionSignature signature, SprakType returnType) { Signature = signature; ReturnType = returnType; }
private void RenderType(SprakType type) { Write(type?.InternalName, Theme.Expressions.Type); }
public VariableInfo(SprakType type, int offset, Block source) { DeclaredType = type; DeclarationEnd = offset; Source = source; }
public void AssertType(out SprakType type) { Assert(out Token current); type = current.AssertType(); }
private void UpdateBlock(Block block, Messenger messenger, Dictionary <FunctionSignature, FunctionInfo> functions) { if (block.ScopeHint == null) { block.ScopeHint = new Scope(); } if (block.Header is IfHeader ifHeader) { ApplyCombinedScope(ifHeader); } Scope scope = block.ScopeHint; // Three steps // First: check if the header declares any variables - function arguments, for example switch (block.Header) { case FunctionHeader function: for (int i = 0; i < function.ParameterCount; i++) { string name = function.ParameterNames[i]; SprakType type = function.ParameterTypes[i]; scope.VariableDeclarations .Add(name, new VariableInfo(type, -1, block)); } break; case LoopHeader loop: string loopVarName = null; if (loop.HasName) { loopVarName = loop.Name; } else if (loop.IsForeach) { loopVarName = "@"; } if (loopVarName != null) { SprakType loopVarType; if (loop.IsRange) { loopVarType = SprakType.Number; } else { loopVarType = SprakType.Any; } // we need an "undetermined" type here scope.VariableDeclarations .Add(loopVarName, new VariableInfo(loopVarType, -1, block)); } break; } // Second: check if variables or functions have been declared in the statements of the block. // If there is a subblock, apply this update to that block as well. // Note that a subblock can a function declaration, but any other declarations in that subblock belong to its hints, // not the the ones for the current block. foreach (Expression expression in block.Statements) { switch (expression) { case VariableAssignment assignment: if (assignment.IsDeclaration) { if (scope.VariableDeclarations.ContainsKey(assignment.Name)) { messenger.AtToken(assignment.NameToken, Messages.DuplicateVariable, assignment.Name); } else { scope.VariableDeclarations.Add(assignment.Name, new VariableInfo(assignment.DeclarationType, assignment.EndToken.End, block)); } } break; case Block subBlock: if (subBlock.Header is FunctionHeader function) { if (functions.ContainsKey(function.Signature)) { messenger.AtToken(function.NameToken, Messages.DuplicateFunction, function.Name); } else { functions.Add(function.Signature, new FunctionInfo(function)); } } UpdateBlock(subBlock, messenger, functions); break; } } block.ScopeHint = scope; }
public T PopValue <T>() where T : Value { SprakType target = SprakType.NetToSprakLookup[typeof(T)]; return(PopValue(target) as T); }