nandi/rustnimpublic Fork 0
8ac32afd8fe6eb71bb6de56905986a93ee2cde00
Commits
Clone
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

prelude.nim · 142 lines · 4.9 KBNim Blame HistoryRaw
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 19h ago1## 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
9import std/[unicode, strutils]
10
11type
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
24proc rsPanic*(msg: string) {.noreturn.} =
25 raise newException(RustPanic, msg)
26
27proc rsSome*[T](v: T): Option[T] = Option[T](has: true, val: v)
28proc rsNone*[T](): Option[T] = Option[T](has: false)
29proc rsOk*[T, E](v: T): Result[T, E] = Result[T, E](ok: true, val: v)
30proc rsErr*[T, E](e: E): Result[T, E] = Result[T, E](ok: false, err: e)
31
32proc unwrap*[T](o: Option[T]): T =
33 if not o.has: rsPanic("called `Option::unwrap()` on a `None` value")
34 o.val
35proc 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
47proc rsDisplay*(x: SomeInteger): string = $x
48proc rsDebug*(x: SomeInteger): string = $x
49proc rsDisplay*(x: bool): string = $x
50proc rsDebug*(x: bool): string = $x
51
52proc 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
63proc rsDisplay*(x: float32 | float64): string = rsFloatStr(float64(x), false)
64proc rsDebug*(x: float32 | float64): string = rsFloatStr(float64(x), true)
65
66proc rsDisplay*(x: string): string = x
67proc 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
79proc rsDisplay*(x: Rune): string = $x
80proc 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
89proc 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
96proc rsDebug*[T](o: Option[T]): string =
97 if o.has: "Some(" & rsDebug(o.val) & ")" else: "None"
98proc 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
107proc 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
124proc 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
134proc 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
140proc newSeqWith*[T](n: int, v: T): seq[T] =
141 result = newSeq[T](n)
142 for i in 0 ..< n: result[i] = v