Skip to content
← Back

src/syntax/process/compile-expressions/default_argument_values.ghul

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