Appearance
| 1 | namespace Semantic is | |
| 2 | use Semantic.Types.Type | |
| 3 | ||
| 4 | // How an interpolated value becomes text. | |
| 5 | enum InterpolatedValueRendering is | |
| 6 | // Formatted as .NET formats it: a string, a number, an enum, a | |
| 7 | // type declaring its own to_string, or any value given a format | |
| 8 | // or an alignment. | |
| 9 | FORMATTED, | |
| 10 | ||
| 11 | // `true` or `false`, as ghūl spells them. | |
| 12 | BOOL, | |
| 13 | ||
| 14 | // The value it holds, rendered by the rules for its non-optional | |
| 15 | // type, or `null` when it holds none. | |
| 16 | OPTIONAL, | |
| 17 | ||
| 18 | // Rendered by the runtime's `$`: a value whose static type gives | |
| 19 | // it no text of its own, which .NET would print as its type name. | |
| 20 | DISPLAYED | |
| 21 | si | |
| 22 | ||
| 23 | class INTERPOLATED_VALUE_CLASSIFIER is | |
| 24 | _lookup: Lookups.InnateSymbolLookup | |
| 25 | ||
| 26 | init(lookup: Lookups.InnateSymbolLookup) is | |
| 27 | _lookup = lookup | |
| 28 | si | |
| 29 | ||
| 30 | classify(type: Type, is_formatted: bool) -> InterpolatedValueRendering is | |
| 31 | if is_formatted then | |
| 32 | return InterpolatedValueRendering.FORMATTED | |
| 33 | fi | |
| 34 | ||
| 35 | if type.is_optional /\ type.optional_inner_type? then | |
| 36 | return InterpolatedValueRendering.OPTIONAL | |
| 37 | fi | |
| 38 | ||
| 39 | if _is(type, _lookup.get_bool_type()) then | |
| 40 | return InterpolatedValueRendering.BOOL | |
| 41 | fi | |
| 42 | ||
| 43 | // A function value has no text worth reading beyond its type, | |
| 44 | // and rendering its delegate's members would expose the | |
| 45 | // compiler's closure machinery. | |
| 46 | if type.is_function \/ type.is_action then | |
| 47 | return InterpolatedValueRendering.FORMATTED | |
| 48 | fi | |
| 49 | ||
| 50 | if | |
| 51 | type.is_value_tuple \/ | |
| 52 | isa Types.TUPLE(type) \/ | |
| 53 | type.is_object \/ | |
| 54 | type.is_type_variable \/ | |
| 55 | type.is_classy_generic_argument \/ | |
| 56 | type.is_function_generic_argument | |
| 57 | then | |
| 58 | return InterpolatedValueRendering.DISPLAYED | |
| 59 | fi | |
| 60 | ||
| 61 | if _is(type, _lookup.get_string_type()) \/ has_own_to_string(type) then | |
| 62 | return InterpolatedValueRendering.FORMATTED | |
| 63 | fi | |
| 64 | ||
| 65 | if isa Types.ARRAY(type) \/ type.is_trait \/ type.find_ancestor(_lookup.get_unspecialized_iterable_type())? then | |
| 66 | return InterpolatedValueRendering.DISPLAYED | |
| 67 | fi | |
| 68 | ||
| 69 | // An imported type that says nothing of itself keeps its .NET | |
| 70 | // type name: rendering it by its members would run whatever | |
| 71 | // its property getters do. | |
| 72 | if type.symbol.is_reflected then | |
| 73 | return InterpolatedValueRendering.FORMATTED | |
| 74 | fi | |
| 75 | ||
| 76 | return InterpolatedValueRendering.DISPLAYED | |
| 77 | si | |
| 78 | ||
| 79 | // Whether a type says what it is in its own words, through a | |
| 80 | // to_string declared somewhere other than the roots every type | |
| 81 | // inherits one from. An enum counts: System.Enum's to_string gives | |
| 82 | // the member's name. | |
| 83 | has_own_to_string(type: Type) -> bool is | |
| 84 | let to_string = zero_argument_to_string(type.find_member("to_string")) | |
| 85 | ||
| 86 | if !to_string? then | |
| 87 | return false | |
| 88 | fi | |
| 89 | ||
| 90 | let owner = to_string.owner | |
| 91 | ||
| 92 | return | |
| 93 | owner? /\ | |
| 94 | owner != _lookup.get_object_type().symbol /\ | |
| 95 | owner != _lookup.get_value_type().symbol | |
| 96 | si | |
| 97 | ||
| 98 | // The overload of the runtime's `$` that renders any value, or | |
| 99 | // null where the runtime referenced has no `$`. | |
| 100 | display_function: Symbols.Function? is | |
| 101 | if let functions = _lookup.get_display_functions() then | |
| 102 | for function in functions.functions do | |
| 103 | if !function.is_generic /\ function.arguments.count == 1 then | |
| 104 | let parameter = function.arguments[0].type! | |
| 105 | ||
| 106 | if (parameter.optional_inner_type ?? parameter).is_object then | |
| 107 | return function | |
| 108 | fi | |
| 109 | fi | |
| 110 | od | |
| 111 | fi | |
| 112 | ||
| 113 | return null | |
| 114 | si | |
| 115 | ||
| 116 | _is(type: Type, other: Type) -> bool => | |
| 117 | type.compare(other) == Types.MATCH.SAME | |
| 118 | ||
| 119 | zero_argument_to_string(member: Symbols.Symbol?) -> Symbols.Function? static is | |
| 120 | if let group: Symbols.FUNCTION_GROUP = member then | |
| 121 | for function in group.functions do | |
| 122 | if !function.are_arguments_declared \/ function.arguments.count == 0 then | |
| 123 | return function | |
| 124 | fi | |
| 125 | od | |
| 126 | fi | |
| 127 | ||
| 128 | return null | |
| 129 | si | |
| 130 | si | |
| 131 | si |