private SignatureSearcher.Checks <OperatorTypeSignature> GetChecks(OperatorTypeSignature target) { return(new SignatureSearcher.Checks <OperatorTypeSignature> { Matches = (option) => option.CanAccept(target, _assignmentResolver), ExactComparison = CheckExact(target), LosslessComparison = CheckLossless(target) }); }
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); }
private Func <OperatorTypeSignature, int> CheckLossless(OperatorTypeSignature target) { int KeyFunction(OperatorTypeSignature subject) { int key = 0;; if (!_assignmentResolver.IsPerfect(target.LeftParam, subject.LeftParam)) { key++; } if (!_assignmentResolver.IsPerfect(target.RightParam, subject.RightParam)) { key++; } return(key); } return(KeyFunction); }
private Func <OperatorTypeSignature, int> CheckExact(OperatorTypeSignature target) { int KeyFunction(OperatorTypeSignature subject) { int key = 0;; if (target.LeftParam != subject.LeftParam) { key++; } if (target.RightParam != subject.RightParam) { key++; } return(key); } return(KeyFunction); }
public SignatureLookupResult TryFindMatch(string name, OperatorTypeSignature typeSignature) { SignatureLookupResult result = new SignatureLookupResult(); var checks = GetChecks(typeSignature); Dictionary <OperatorTypeSignature, Func <Op> > opOptions; if (_operatorBindingLookup.TryGetOptions(name, out opOptions)) { var searchResult = SignatureSearcher.Search(opOptions, checks, x => x); if (searchResult.Success) { result.Success = true; result.Ambiguous = searchResult.Ambiguous; result.OpBuilder = searchResult.Value; return(result); } } Dictionary <OperatorTypeSignature, BuiltInFunction> builtinOptions; if (_builtInOperatorLookup.TryGetOptions(name, out builtinOptions)) { var searchResult = SignatureSearcher.Search(builtinOptions, checks, x => x); if (searchResult.Success) { result.Success = true; result.Ambiguous = searchResult.Ambiguous; result.FunctionInfo = searchResult.Value.Info; result.BuiltInFunction = searchResult.Value; return(result); } } return(result); }
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); } }
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); } }