Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use Semantic.Types.Type | |
| 3 | ||
| 4 | use IR.Values | |
| 5 | ||
| 6 | // Translates a value reflected out of assembly metadata into an IR | |
| 7 | // value of the type it is wanted at. Metadata carries such a value | |
| 8 | // as invariant-culture text — symbol_factory stores it that way for | |
| 9 | // both a parameter's default and a constant's value — so reading it | |
| 10 | // back is a per-type dispatch on the type the position expects. | |
| 11 | // | |
| 12 | // Two entries, differing only over what an absent value looks like. | |
| 13 | // `build` serves an argument default, where absence is spelled: ghūl | |
| 14 | // source declares only `= _`, which is stored as the word "default", | |
| 15 | // and a null reflected default is stored the same way. `build_value` | |
| 16 | // serves a caller whose text is a value and nothing else — a | |
| 17 | // constant's value is whatever a library author wrote, the same word | |
| 18 | // included — and so reserves no spelling. | |
| 19 | // | |
| 20 | // Consumed by an omitted call argument (COMPILE_CALLS), an omitted | |
| 21 | // attribute constructor argument (ATTRIBUTE_RESOLVER), and a | |
| 22 | // constant read (SYMBOL_LOADER.load_constant_field). | |
| 23 | // | |
| 24 | // An enum-typed value is delegated to Symbols.ENUM_CONSTANT, which | |
| 25 | // reads it at the width the enum was declared over rather than | |
| 26 | // always at int32. | |
| 27 | class DEFAULT_ARGUMENT_VALUES is | |
| 28 | build( | |
| 29 | stored: string?, | |
| 30 | formal_type: Type, | |
| 31 | innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup | |
| 32 | ) -> Value static is | |
| 33 | if !stored? \/ stored =~ "default" then | |
| 34 | return IR.Values.DEFAULT(formal_type) | |
| 35 | fi | |
| 36 | ||
| 37 | return build_value(stored, formal_type, innate_symbol_lookup) | |
| 38 | si | |
| 39 | ||
| 40 | // The same conversion without the absent-value sentinel above. | |
| 41 | // A caller that already knows its text is a value comes in here: | |
| 42 | // a constant's value is whatever a library author wrote, the | |
| 43 | // word the sentinel uses included, so a channel carrying one | |
| 44 | // cannot also reserve a spelling to mean "no value". | |
| 45 | build_value( | |
| 46 | stored: string, | |
| 47 | formal_type: Type, | |
| 48 | innate_symbol_lookup: Semantic.Lookups.InnateSymbolLookup | |
| 49 | ) -> Value static is | |
| 50 | if formal_type.matches(innate_symbol_lookup.get_string_type()) then | |
| 51 | return IR.Values.Literal.STRING(stored, formal_type) | |
| 52 | fi | |
| 53 | ||
| 54 | if formal_type.matches(innate_symbol_lookup.get_bool_type()) then | |
| 55 | let v = if stored =~ "True" then 1 else 0 fi | |
| 56 | return IR.Values.Literal.NUMBER(v, formal_type) | |
| 57 | fi | |
| 58 | ||
| 59 | if formal_type.matches(innate_symbol_lookup.get_char_type()) then | |
| 60 | let v = | |
| 61 | if stored.length > 0 then | |
| 62 | cast int(stored[0]) | |
| 63 | else | |
| 64 | 0 | |
| 65 | fi | |
| 66 | return IR.Values.Literal.NUMBER(v, formal_type) | |
| 67 | fi | |
| 68 | ||
| 69 | let is_enum_type = | |
| 70 | formal_type.symbol.symbol_kind == Semantic.Symbols.SymbolKind.ENUM | |
| 71 | ||
| 72 | // An enum reads its values at the width it was declared | |
| 73 | // over, which is the enum's own decision to make - see | |
| 74 | // Symbols.ENUM_CONSTANT. | |
| 75 | if is_enum_type then | |
| 76 | let (ok, value) = _stored_integer(stored) | |
| 77 | ||
| 78 | return | |
| 79 | if ok then | |
| 80 | Semantic.Symbols.ENUM_CONSTANT.from_pattern(formal_type, value, innate_symbol_lookup) | |
| 81 | else | |
| 82 | IR.Values.DEFAULT(formal_type) | |
| 83 | fi | |
| 84 | fi | |
| 85 | ||
| 86 | if formal_type.matches(innate_symbol_lookup.get_long_type()) \/ | |
| 87 | formal_type.matches(innate_symbol_lookup.get_ulong_type()) | |
| 88 | then | |
| 89 | let (ok, value) = _stored_integer(stored) | |
| 90 | ||
| 91 | return | |
| 92 | if ok then | |
| 93 | IR.Values.Literal.NUMBER(IR.Values.Literal.CONSTANT.I8(cast long(value)), formal_type) | |
| 94 | else | |
| 95 | IR.Values.DEFAULT(formal_type) | |
| 96 | fi | |
| 97 | fi | |
| 98 | ||
| 99 | if formal_type.matches(innate_symbol_lookup.get_single_type()) then | |
| 100 | let (ok, value) = _stored_real(stored) | |
| 101 | ||
| 102 | return | |
| 103 | if ok then | |
| 104 | IR.Values.Literal.NUMBER(cast single(value), formal_type) | |
| 105 | else | |
| 106 | IR.Values.DEFAULT(formal_type) | |
| 107 | fi | |
| 108 | fi | |
| 109 | ||
| 110 | if formal_type.matches(innate_symbol_lookup.get_double_type()) then | |
| 111 | let (ok, value) = _stored_real(stored) | |
| 112 | ||
| 113 | return | |
| 114 | if ok then | |
| 115 | IR.Values.Literal.NUMBER(value, formal_type) | |
| 116 | else | |
| 117 | IR.Values.DEFAULT(formal_type) | |
| 118 | fi | |
| 119 | fi | |
| 120 | ||
| 121 | if formal_type.matches(innate_symbol_lookup.get_byte_type()) \/ | |
| 122 | formal_type.matches(innate_symbol_lookup.get_ubyte_type()) \/ | |
| 123 | formal_type.matches(innate_symbol_lookup.get_short_type()) \/ | |
| 124 | formal_type.matches(innate_symbol_lookup.get_ushort_type()) \/ | |
| 125 | formal_type.matches(innate_symbol_lookup.get_int_type()) \/ | |
| 126 | formal_type.matches(innate_symbol_lookup.get_uint_type()) | |
| 127 | then | |
| 128 | let (ok, value) = _stored_integer(stored) | |
| 129 | ||
| 130 | return | |
| 131 | if ok then | |
| 132 | IR.Values.Literal.NUMBER( | |
| 133 | IR.Values.Literal.CONSTANT.I4(cast int(cast uint(value))), | |
| 134 | formal_type) | |
| 135 | else | |
| 136 | IR.Values.DEFAULT(formal_type) | |
| 137 | fi | |
| 138 | fi | |
| 139 | ||
| 140 | return IR.Values.DEFAULT(formal_type) | |
| 141 | si | |
| 142 | ||
| 143 | // A value arrives from assembly metadata as its rendered form, so | |
| 144 | // it is read back here - the one place a parse is unavoidable, | |
| 145 | // because the rendering is all the metadata carries. | |
| 146 | // | |
| 147 | // Read in the culture it was written in, which is the invariant | |
| 148 | // one. Reading it in whatever culture the process happens to be | |
| 149 | // running under would not merely fail: `1.7976931348623157E+308` | |
| 150 | // read where `.` groups digits rather than separating the | |
| 151 | // fraction parses successfully, to infinity. | |
| 152 | // | |
| 153 | // Both signed and unsigned are tried, because one parse cannot | |
| 154 | // cover the range: a negative value is out of range for `ulong` | |
| 155 | // and a `ulong` above `long`'s maximum is out of range for | |
| 156 | // `long`, and both are ordinary .NET defaults. The result is | |
| 157 | // the bit pattern either way. | |
| 158 | _stored_integer(stored: string) -> (ok: bool, value: ulong) static is | |
| 159 | let signed: long mut = _ | |
| 160 | ||
| 161 | if | |
| 162 | long.try_parse( | |
| 163 | stored, | |
| 164 | System.Globalization.NumberStyles.INTEGER, | |
| 165 | System.Globalization.CultureInfo.invariant_culture, | |
| 166 | signed ref | |
| 167 | ) | |
| 168 | then | |
| 169 | return (true, cast ulong(signed)) | |
| 170 | fi | |
| 171 | ||
| 172 | let unsigned: ulong mut = _ | |
| 173 | ||
| 174 | if | |
| 175 | ulong.try_parse( | |
| 176 | stored, | |
| 177 | System.Globalization.NumberStyles.INTEGER, | |
| 178 | System.Globalization.CultureInfo.invariant_culture, | |
| 179 | unsigned ref | |
| 180 | ) | |
| 181 | then | |
| 182 | return (true, unsigned) | |
| 183 | fi | |
| 184 | ||
| 185 | return (false, 0UL) | |
| 186 | si | |
| 187 | ||
| 188 | _stored_real(stored: string) -> (ok: bool, value: double) static is | |
| 189 | let value: double mut = _ | |
| 190 | ||
| 191 | if | |
| 192 | double.try_parse( | |
| 193 | stored, | |
| 194 | System.Globalization.NumberStyles.FLOAT, | |
| 195 | System.Globalization.CultureInfo.invariant_culture, | |
| 196 | value ref | |
| 197 | ) | |
| 198 | then | |
| 199 | return (true, value) | |
| 200 | fi | |
| 201 | ||
| 202 | return (false, 0.0D) | |
| 203 | si | |
| 204 | si | |
| 205 | si |