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
context.write_line("ldc.i4 {_lo}");
39
context.write_line("ldc.i4 {_mid}");
40
context.write_line("ldc.i4 {_hi}");
41
context.write_line("ldc.i4 0");
42
context.write_line("ldc.i4 {_scale}");
43
context.write_line("newobj instance void valuetype ['System.Runtime']'System'.'Decimal'::'.ctor'(int32, int32, int32, bool, unsigned int8)");
44
si
45
46
// Parse `value` (already stripped of its `m`/`M` suffix and
47
// any unary sign) into the four-field form expected by
48
// Decimal's full-resolution constructor. The `error` slot
49
// is non-null when the literal exceeds Decimal's precision
50
// envelope:
51
// - scale (digits after the dot) must be 0-28
52
// - mantissa must fit in 96 bits
53
// Both failures are diagnoseable at parse time; the loop
54
// tracks the high-word carry-out so the caller can reject
55
// before any IL is emitted.
56
decompose(value: string) -> (lo: int, mid: int, hi: int, scale: int, error: string?) static is
57
let dot_index = value.index_of('.');
58
let scale_count: int mut = 0;
59
60
let digits_buffer = System.Text.StringBuilder();
61
62
if dot_index < 0 then
63
digits_buffer.append(value);
64
else
65
digits_buffer.append(value.substring(0, dot_index));
66
digits_buffer.append(value.substring(dot_index + 1));
67
scale_count = value.length - dot_index - 1;
68
fi
69
70
if scale_count > 28 then
71
return (0, 0, 0, 0, "decimal literal has more than 28 fractional digits");
72
fi
73
74
let digits = digits_buffer.to_string();
75
76
let lo: long mut = 0l;
77
let mid: long mut = 0l;
78
let hi: long mut = 0l;
79
let overflow: bool mut = false;
80
81
for i in 0..digits.length do
82
let c = digits.get_chars(i);
83
84
if c >= '0' /\ c <= '9' then
85
let d = cast long(c - '0');
86
87
let prod_lo = lo * 10l + d;
88
lo = prod_lo & 0xFFFFFFFFl;
89
let carry_lo = prod_lo >> 32l;
90
91
let prod_mid = mid * 10l + carry_lo;
92
mid = prod_mid & 0xFFFFFFFFl;
93
let carry_mid = prod_mid >> 32l;
94
95
let prod_hi = hi * 10l + carry_mid;
96
if prod_hi > 0xFFFFFFFFl then
97
overflow = true;
98
fi
99
hi = prod_hi & 0xFFFFFFFFl;
100
fi
101
od
102
103
if overflow then
104
return (0, 0, 0, 0, "decimal literal magnitude exceeds maximum");
105
fi
106
107
return (cast int(lo), cast int(mid), cast int(hi), scale_count, null);
108
si
109
110
to_string() -> string =>
111
"literal:[{type}]({value}m)";
112
si
113
si