Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Source; | |
| 3 | use Logging; | |
| 4 | ||
| 5 | use Symbols = Semantic.Symbols; | |
| 6 | ||
| 7 | use Ghul.Pipes; | |
| 8 | ||
| 9 | // The overloads a named-argument call resolved to, together with | |
| 10 | // the permutation that reorders the supplied arguments into | |
| 11 | // parameter order: `permutation[formal_index]` is the source | |
| 12 | // index of the supplied argument that fills that parameter. | |
| 13 | // More than one overload can survive name matching (overloads | |
| 14 | // that differ only in parameter types); the type-driven overload | |
| 15 | // resolver disambiguates between them. | |
| 16 | class NAMED_ARGUMENT_BINDING is | |
| 17 | targets: Collections.List[Symbols.Function]; | |
| 18 | permutation: Collections.List[int]; | |
| 19 | ||
| 20 | init(targets: Collections.List[Symbols.Function], permutation: Collections.List[int]) is | |
| 21 | super.init(); | |
| 22 | ||
| 23 | self.targets = targets; | |
| 24 | self.permutation = permutation; | |
| 25 | si | |
| 26 | si | |
| 27 | ||
| 28 | // Resolves a named-argument call (`f(width = 800, height = 600)`) | |
| 29 | // against a function group: culls to the overloads whose formal | |
| 30 | // parameter names match the supplied names and computes the | |
| 31 | // permutation into parameter order. Name matching needs only the | |
| 32 | // formal names, never the argument types, so it runs ahead of the | |
| 33 | // type-driven overload resolver, which is then restricted to the | |
| 34 | // surviving overloads. | |
| 35 | class NAMED_ARGUMENT_BINDER is | |
| 36 | _logger: Logger; | |
| 37 | ||
| 38 | init(logger: Logger) is | |
| 39 | super.init(); | |
| 40 | ||
| 41 | _logger = logger; | |
| 42 | si | |
| 43 | ||
| 44 | bind( | |
| 45 | location: LOCATION, | |
| 46 | group: Symbols.FUNCTION_GROUP, | |
| 47 | argument_names: Collections.List[Trees.Identifiers.Identifier], | |
| 48 | want_instance: bool | |
| 49 | ) -> NAMED_ARGUMENT_BINDING? is | |
| 50 | // An overload survives name-culling when every supplied | |
| 51 | // name is one of its parameters. `exact` overloads have no | |
| 52 | // other parameters; `omitting` ones have parameters left | |
| 53 | // unsupplied (filled with `default` per the call site). | |
| 54 | let exact = Collections.LIST[Symbols.Function](); | |
| 55 | let omitting = Collections.LIST[Symbols.Function](); | |
| 56 | ||
| 57 | for f in group.functions do | |
| 58 | if (want_instance \/ !f.is_instance) /\ _covers_all_names(f, argument_names) then | |
| 59 | if f.argument_names.count == argument_names.count then | |
| 60 | exact.add(f); | |
| 61 | elif _omitted_have_defaults(f, argument_names) then | |
| 62 | omitting.add(f); | |
| 63 | fi | |
| 64 | fi | |
| 65 | od | |
| 66 | ||
| 67 | let supplied = argument_names |> map(n => n.name) |> join(); | |
| 68 | ||
| 69 | // An exact overload always beats one that needs defaults. | |
| 70 | if exact.count > 0 then | |
| 71 | let permutation = _permutation_for(exact[0], argument_names); | |
| 72 | ||
| 73 | if permutation |> any(source_index => source_index < 0) then | |
| 74 | _logger.error(location, "call to {group.name} with named arguments {supplied} cannot be bound"); | |
| 75 | ||
| 76 | return null; | |
| 77 | fi | |
| 78 | ||
| 79 | // Overloads that share a name set but order it | |
| 80 | // differently (`f(a, b)` and `f(b, a)`) would bind the | |
| 81 | // same call two ways - genuinely ambiguous. | |
| 82 | for f in exact do | |
| 83 | if !_same_permutation(permutation, _permutation_for(f, argument_names)) then | |
| 84 | _logger.error(location, "call to {group.name} with named arguments {supplied} is ambiguous"); | |
| 85 | ||
| 86 | return null; | |
| 87 | fi | |
| 88 | od | |
| 89 | ||
| 90 | return NAMED_ARGUMENT_BINDING(exact, permutation); | |
| 91 | fi | |
| 92 | ||
| 93 | if omitting.count == 0 then | |
| 94 | _logger.error(location, "no overload of {group.name} takes named arguments {supplied}"); | |
| 95 | ||
| 96 | return null; | |
| 97 | fi | |
| 98 | ||
| 99 | // More than one overload would need defaulting - which | |
| 100 | // parameters are omitted is genuinely ambiguous. | |
| 101 | if omitting.count > 1 then | |
| 102 | _logger.error(location, "call to {group.name} with named arguments {supplied} is ambiguous"); | |
| 103 | ||
| 104 | return null; | |
| 105 | fi | |
| 106 | ||
| 107 | return NAMED_ARGUMENT_BINDING(omitting, _permutation_for(omitting[0], argument_names)); | |
| 108 | si | |
| 109 | ||
| 110 | // True when every supplied name is a parameter of `f`. `f` may | |
| 111 | // still have further parameters the call leaves unsupplied. | |
| 112 | _covers_all_names( | |
| 113 | f: Symbols.Function, | |
| 114 | argument_names: Collections.List[Trees.Identifiers.Identifier] | |
| 115 | ) -> bool is | |
| 116 | if !f.are_arguments_declared then | |
| 117 | return false; | |
| 118 | fi | |
| 119 | ||
| 120 | for supplied in argument_names do | |
| 121 | if !(f.argument_names |> any(formal => formal =~ supplied.name)) then | |
| 122 | return false; | |
| 123 | fi | |
| 124 | od | |
| 125 | ||
| 126 | return true; | |
| 127 | si | |
| 128 | ||
| 129 | // True when every parameter of `f` that the call does not | |
| 130 | // supply by name declares a default value, so the call may | |
| 131 | // legitimately omit it. | |
| 132 | _omitted_have_defaults( | |
| 133 | f: Symbols.Function, | |
| 134 | argument_names: Collections.List[Trees.Identifiers.Identifier] | |
| 135 | ) -> bool is | |
| 136 | for formal_index in 0..f.argument_names.count do | |
| 137 | let formal = f.argument_names[formal_index]; | |
| 138 | ||
| 139 | if !(argument_names |> any(supplied => supplied.name =~ formal)) then | |
| 140 | if formal_index >= f.argument_defaults.count \/ !f.argument_defaults[formal_index]? then | |
| 141 | return false; | |
| 142 | fi | |
| 143 | fi | |
| 144 | od | |
| 145 | ||
| 146 | return true; | |
| 147 | si | |
| 148 | ||
| 149 | _permutation_for( | |
| 150 | f: Symbols.Function, | |
| 151 | argument_names: Collections.List[Trees.Identifiers.Identifier] | |
| 152 | ) -> Collections.List[int] is | |
| 153 | let permutation = Collections.LIST[int](); | |
| 154 | ||
| 155 | for formal in f.argument_names do | |
| 156 | permutation.add(_find_supplied(argument_names, formal)); | |
| 157 | od | |
| 158 | ||
| 159 | return permutation; | |
| 160 | si | |
| 161 | ||
| 162 | _find_supplied( | |
| 163 | argument_names: Collections.List[Trees.Identifiers.Identifier], | |
| 164 | formal: string | |
| 165 | ) -> int is | |
| 166 | for source_index in 0..argument_names.count do | |
| 167 | if argument_names[source_index].name =~ formal then | |
| 168 | return source_index; | |
| 169 | fi | |
| 170 | od | |
| 171 | ||
| 172 | return -1; | |
| 173 | si | |
| 174 | ||
| 175 | _same_permutation(a: Collections.List[int], b: Collections.List[int]) -> bool is | |
| 176 | if a.count != b.count then | |
| 177 | return false; | |
| 178 | fi | |
| 179 | ||
| 180 | for index in 0..a.count do | |
| 181 | if a[index] != b[index] then | |
| 182 | return false; | |
| 183 | fi | |
| 184 | od | |
| 185 | ||
| 186 | return true; | |
| 187 | si | |
| 188 | si | |
| 189 | si |