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