Appearance
| 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 |