| Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 19h ago | 1 | ## rustnim prelude — emitted at the top of every generated module. |
| 2 | ## |
| 3 | ## Everything here exists to make Nim's observable behaviour match Rust's |
| 4 | ## exactly. Where the two languages already agree (signed `shr` is arithmetic |
| 5 | ## in both; fixed-width unsigned arithmetic wraps in both; integer `div`/`mod` |
| 6 | ## truncate toward zero in both) there is deliberately nothing here: the |
| 7 | ## operator is mapped directly and no helper is involved. |
| 8 | |
| 9 | import std/[unicode, strutils] |
| 10 | |
| 11 | type |
| 12 | RustPanic* = object of CatchableError |
| 13 | |
| 14 | Option*[T] = object |
| 15 | case has*: bool |
| 16 | of true: val*: T |
| 17 | of false: discard |
| 18 | |
| 19 | Result*[T, E] = object |
| 20 | case ok*: bool |
| 21 | of true: val*: T |
| 22 | of false: err*: E |
| 23 | |
| 24 | proc rsPanic*(msg: string) {.noreturn.} = |
| 25 | raise newException(RustPanic, msg) |
| 26 | |
| 27 | proc rsSome*[T](v: T): Option[T] = Option[T](has: true, val: v) |
| 28 | proc rsNone*[T](): Option[T] = Option[T](has: false) |
| 29 | proc rsOk*[T, E](v: T): Result[T, E] = Result[T, E](ok: true, val: v) |
| 30 | proc rsErr*[T, E](e: E): Result[T, E] = Result[T, E](ok: false, err: e) |
| 31 | |
| 32 | proc unwrap*[T](o: Option[T]): T = |
| 33 | if not o.has: rsPanic("called `Option::unwrap()` on a `None` value") |
| 34 | o.val |
| 35 | proc unwrap*[T, E](r: Result[T, E]): T = |
| 36 | if not r.ok: rsPanic("called `Result::unwrap()` on an `Err` value") |
| 37 | r.val |
| 38 | |
| 39 | # --------------------------------------------------------------------------- |
| 40 | # Display / Debug. |
| 41 | # |
| 42 | # Rust's `{}` and `{:?}` are two distinct formats and they differ for floats, |
| 43 | # strings, chars and sequences. Nim's `$` matches neither consistently, so both |
| 44 | # are implemented here rather than approximated with `$`. |
| 45 | # --------------------------------------------------------------------------- |
| 46 | |
| 47 | proc rsDisplay*(x: SomeInteger): string = $x |
| 48 | proc rsDebug*(x: SomeInteger): string = $x |
| 49 | proc rsDisplay*(x: bool): string = $x |
| 50 | proc rsDebug*(x: bool): string = $x |
| 51 | |
| 52 | proc rsFloatStr(x: float64, debug: bool): string = |
| 53 | ## Nim and Rust both print the shortest round-tripping decimal, but they |
| 54 | ## spell the result differently in three places. |
| 55 | if x != x: return "NaN" |
| 56 | if x == Inf: return "inf" |
| 57 | if x == -Inf: return "-inf" |
| 58 | result = $x |
| 59 | result = result.replace("e+", "e") # Nim `1e+21`, Rust `1e21` |
| 60 | if not debug and result.endsWith(".0"): # Rust Display drops a bare `.0` |
| 61 | result.setLen(result.len - 2) |
| 62 | |
| 63 | proc rsDisplay*(x: float32 | float64): string = rsFloatStr(float64(x), false) |
| 64 | proc rsDebug*(x: float32 | float64): string = rsFloatStr(float64(x), true) |
| 65 | |
| 66 | proc rsDisplay*(x: string): string = x |
| 67 | proc rsDebug*(x: string): string = |
| 68 | result = "\"" |
| 69 | for c in x: |
| 70 | case c |
| 71 | of '"': result.add("\\\"") |
| 72 | of '\\': result.add("\\\\") |
| 73 | of '\n': result.add("\\n") |
| 74 | of '\t': result.add("\\t") |
| 75 | of '\r': result.add("\\r") |
| 76 | else: result.add(c) |
| 77 | result.add("\"") |
| 78 | |
| 79 | proc rsDisplay*(x: Rune): string = $x |
| 80 | proc rsDebug*(x: Rune): string = |
| 81 | case $x |
| 82 | of "'": "'\\''" |
| 83 | of "\\": "'\\\\'" |
| 84 | of "\n": "'\\n'" |
| 85 | of "\t": "'\\t'" |
| 86 | of "\r": "'\\r'" |
| 87 | else: "'" & $x & "'" |
| 88 | |
| 89 | proc rsDebug*[T](x: seq[T] | openArray[T]): string = |
| 90 | result = "[" |
| 91 | for i in 0 ..< x.len: |
| 92 | if i > 0: result.add(", ") |
| 93 | result.add(rsDebug(x[i])) |
| 94 | result.add("]") |
| 95 | |
| 96 | proc rsDebug*[T](o: Option[T]): string = |
| 97 | if o.has: "Some(" & rsDebug(o.val) & ")" else: "None" |
| 98 | proc rsDebug*[T, E](r: Result[T, E]): string = |
| 99 | if r.ok: "Ok(" & rsDebug(r.val) & ")" else: "Err(" & rsDebug(r.err) & ")" |
| 100 | |
| 101 | # --------------------------------------------------------------------------- |
| 102 | # Radix formats: `{:x}`, `{:X}`, `{:b}`, `{:o}`, with Rust's width/zero-fill. |
| 103 | # Rust formats the *two's-complement bit pattern*, so a negative i8 prints as |
| 104 | # `ff`, not `-1`. `toHex` on the unsigned view of the same width reproduces it. |
| 105 | # --------------------------------------------------------------------------- |
| 106 | |
| 107 | proc rsRadix*[T: SomeInteger](x: T, base: int, upper: bool): string = |
| 108 | var v: uint64 = |
| 109 | when T is SomeSignedInt: |
| 110 | # Sign-extend then mask to the type's own width, so the printed bit |
| 111 | # pattern is the Rust one for this exact integer type. |
| 112 | cast[uint64](int64(x)) and (if sizeof(T) == 8: high(uint64) else: (1'u64 shl (sizeof(T) * 8)) - 1) |
| 113 | else: |
| 114 | uint64(x) |
| 115 | if v == 0: return "0" |
| 116 | const digits = "0123456789abcdef" |
| 117 | while v > 0'u64: |
| 118 | result.add(digits[int(v mod uint64(base))]) |
| 119 | v = v div uint64(base) |
| 120 | for i in 0 ..< result.len div 2: |
| 121 | swap(result[i], result[result.len - 1 - i]) |
| 122 | if upper: result = result.toUpperAscii() |
| 123 | |
| 124 | proc rsPad*(s: string, width: int, zero: bool): string = |
| 125 | if s.len >= width: return s |
| 126 | let fill = width - s.len |
| 127 | if zero and s.len > 0 and s[0] == '-': |
| 128 | "-" & repeat('0', fill) & s[1 .. ^1] |
| 129 | elif zero: |
| 130 | repeat('0', fill) & s |
| 131 | else: |
| 132 | repeat(' ', fill) & s |
| 133 | |
| 134 | proc rsBytes*(s: string): seq[uint8] = |
| 135 | ## Rust's `str::as_bytes` is a view of the UTF-8 encoding; Nim's `string` is |
| 136 | ## already those bytes, so this is a reinterpretation, not a conversion. |
| 137 | result = newSeq[uint8](s.len) |
| 138 | for i in 0 ..< s.len: result[i] = uint8(s[i]) |
| 139 | |
| 140 | proc newSeqWith*[T](n: int, v: T): seq[T] = |
| 141 | result = newSeq[T](n) |
| 142 | for i in 0 ..< n: result[i] = v |