Skip to content
← Back

src/ir/values/literal/decimal.ghul

1
namespace IR.Values.Literal is
2
use TypeTyped = Semantic.Types.Typed
3
use Semantic.Types.Type
4
5
// System.Decimal has no `ldc.dec` opcode. The literal text is
6
// decomposed at compile time into the 96-bit mantissa + scale
7
// + sign form and re-emitted as a call to the five-arg
8
// Decimal::.ctor(int32 lo, int32 mid, int32 hi, bool isNegative,
9
// uint8 scale). Sign comes from a wrapping unary `-`, so the
10
// literal itself is always non-negative.
11
//
12
// Decomposition (`decompose`) lives separately so the caller —
13
// compile_literals — can surface a clean compile error when the
14
// literal overflows 96 bits or its scale exceeds Decimal's 0-28
15
// range, instead of silently truncating or deferring to a
16
// runtime ArgumentOutOfRangeException.
17
class DECIMAL: Value, TypeTyped is
18
value: string
19
type: Type
20
_lo: int
21
_mid: int
22
_hi: int
23
_scale: int
24
is_lightweight_pure: bool => true
25
26
init(value: string, type: Type, lo: int, mid: int, hi: int, scale: int) is
27
super.init()
28
29
self.value = value
30
self.type = type
31
_lo = lo
32
_mid = mid
33
_hi = hi
34
_scale = scale
35
si
36
37
gen(context: IR.CONTEXT) is
38
let body = context.current_srm_body_emitter!
39
body.ldc_i4(_lo)
40
body.ldc_i4(_mid)
41
body.ldc_i4(_hi)
42
body.ldc_i4(0)
43
body.ldc_i4(_scale)
44
body.new_object(context.resolve_decimal_constructor())
45
si
46
47
// Parse `value` (already stripped of its `m`/`M` suffix and
48
// any unary sign) into the four-field form expected by
49
// Decimal's full-resolution constructor. The `error` slot
50
// is non-null when the literal exceeds Decimal's precision
51
// envelope:
52
// - scale (digits after the dot) must be 0-28
53
// - mantissa must fit in 96 bits
54
// Both failures are diagnoseable at parse time; the loop
55
// tracks the high-word carry-out so the caller can reject
56
// before any IL is emitted.
57
decompose(value: string) -> (lo: int, mid: int, hi: int, scale: int, error: string?) static is
58
let dot_index = value.index_of('.')
59
let scale_count: int mut = 0
60
61
let digits_buffer = System.Text.StringBuilder()
62
63
if dot_index < 0 then
64
digits_buffer.append(value)
65
else
66
digits_buffer.append(value.substring(0, dot_index))
67
digits_buffer.append(value.substring(dot_index + 1))
68
scale_count = value.length - dot_index - 1
69
fi
70
71
if scale_count > 28 then
72
return (0, 0, 0, 0, "decimal literal has more than 28 fractional digits")
73
fi
74
75
let digits = digits_buffer.to_string()
76
77
let lo: long mut = 0l
78
let mid: long mut = 0l
79
let hi: long mut = 0l
80
let overflow: bool mut = false
81
82
for i in 0..digits.length do
83
let c = digits[i]
84
85
if c >= '0' /\ c <= '9' then
86
let d = cast long(c - '0')
87
88
let prod_lo = lo * 10l + d
89
lo = prod_lo & 0xFFFFFFFFl
90
let carry_lo = prod_lo >> 32
91
92
let prod_mid = mid * 10l + carry_lo
93
mid = prod_mid & 0xFFFFFFFFl
94
let carry_mid = prod_mid >> 32
95
96
let prod_hi = hi * 10l + carry_mid
97
if prod_hi > 0xFFFFFFFFl then
98
overflow = true
99
fi
100
hi = prod_hi & 0xFFFFFFFFl
101
fi
102
od
103
104
if overflow then
105
return (0, 0, 0, 0, "decimal literal magnitude exceeds maximum")
106
fi
107
108
return (cast int(lo), cast int(mid), cast int(hi), scale_count, null)
109
si
110
111
to_string() -> string =>
112
"literal:[{type}]({value}m)"
113
si
114
si