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); }
private void ResolveAssignment(VariableAssignment assignment, CompilationEnvironment env) { if (assignment.Indices.Count > 1) { env.Messages.AtExpression(assignment.Indices.Last().Index, Messages.MultipleIndices); } if (!assignment.ParentBlockHint .TryGetVariableInfo(assignment.Name, out VariableInfo nameInfo)) { env.Messages.AtToken(assignment.NameToken, Messages.UnrecognizedName, assignment.NameToken.Content); return; } if (!assignment.IsDeclaration) { Block ancestor = assignment.ParentBlockHint; while (ancestor != null && ancestor != nameInfo.Source) { ancestor = ancestor.ParentBlockHint; } if (ancestor == null) { env.Messages.AtToken(assignment.NameToken, Messages.VariableFromDisconnectedBlock); } } if (nameInfo.DeclaredType == null) { // I don't think this should be possible, but this is all // very convoluted. I wish C# was more nullable-aware. throw new Exception("Declaration with Declaration Type"); } SprakType dstType; if (assignment.Indices.Count == 0) { dstType = nameInfo.DeclaredType; } else { if (nameInfo.DeclaredType != SprakType.Array) { env.Messages.AtToken(assignment.NameToken, Messages.CanOnlyIndexArrays, nameInfo.DeclaredType); } // Best we can do with array assignments until generic // type tracking is introduced. dstType = SprakType.Any; } SprakType srcType; if (assignment.Value == null) { srcType = nameInfo.DeclaredType; } else if (assignment.Value.TypeHint != null) { srcType = assignment.Value.TypeHint; } else { srcType = SprakType.Any; } if (assignment.IsDeclaration) { if (assignment.Operator != Operator.Set) { env.Messages.AtToken(assignment.OperatorToken, Messages.InvalidDeclarationOperator); } if (assignment.Indices.Count > 0) { env.Messages.AtExpression(assignment.Indices.First().Index, Messages.InvalidIndexDeclaration); } } else if (!assignment.Operator.IsAssignment) { env.Messages.AtToken(assignment.OperatorToken, Messages.ExpectedAssignmentOperator, assignment.Operator.Text); return; } else if (assignment.Operator.AssignmentOperation != null) { // I doubt I'll bother implementing right-only inputs. // (Like --i) They don't show up much. InputSides inputs; SprakType left; SprakType right; if (!assignment.HasValue) { inputs = InputSides.Left; left = srcType; right = null; } else { inputs = InputSides.Both; left = srcType; right = dstType; } // We need the name of the function to called before assignment. // That may or may not be the same name as that of the operator. string name = assignment.Operator.AssignmentOperation; OperatorTypeSignature signature = new OperatorTypeSignature(left, right, inputs); SignatureLookupResult lookupResult = env.SignatureLookup .TryFindMatch(name, signature); if (lookupResult.Success) { assignment.BuiltInFunctionHint = lookupResult.BuiltInFunction; assignment.OpHint = lookupResult.OpBuilder; srcType = lookupResult.BuiltInFunction.ReturnType; } else { string operation = assignment.ToString(); env.Messages.AtExpression(assignment, Messages.UnresolvedOperation, operation); srcType = SprakType.Any; } } if (!env.AssignmentLookup.IsAssignable(srcType, dstType)) { env.Messages.AtExpression( assignment, Messages.AssignmentTypeMismatch, srcType, dstType); } }