nandi/rustnimpublic Fork 0
b0ccd80a849033974bea0c5eccff995ecb632c3e
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 · 150 lines · 5.2 KBNim Blame HistoryRaw
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 18h 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
Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 18h ago32proc rsOkOr*[T, E](o: Option[T], e: E): Result[T, E] =
33 if o.has: Result[T, E](ok: true, val: o.val) else: Result[T, E](ok: false, err: e)
34
35proc unwrapOr*[T](o: Option[T], d: T): T =
36 if o.has: o.val else: d
37proc unwrapOr*[T, E](r: Result[T, E], d: T): T =
38 if r.ok: r.val else: d
39
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 18h ago40proc unwrap*[T](o: Option[T]): T =
41 if not o.has: rsPanic("called `Option::unwrap()` on a `None` value")
42 o.val
43proc unwrap*[T, E](r: Result[T, E]): T =
44 if not r.ok: rsPanic("called `Result::unwrap()` on an `Err` value")
45 r.val
46
47# ---------------------------------------------------------------------------
48# Display / Debug.
49#
50# Rust's `{}` and `{:?}` are two distinct formats and they differ for floats,
51# strings, chars and sequences. Nim's `$` matches neither consistently, so both
52# are implemented here rather than approximated with `$`.
53# ---------------------------------------------------------------------------
54
55proc rsDisplay*(x: SomeInteger): string = $x
56proc rsDebug*(x: SomeInteger): string = $x
57proc rsDisplay*(x: bool): string = $x
58proc rsDebug*(x: bool): string = $x
59
60proc rsFloatStr(x: float64, debug: bool): string =
61 ## Nim and Rust both print the shortest round-tripping decimal, but they
62 ## spell the result differently in three places.
63 if x != x: return "NaN"
64 if x == Inf: return "inf"
65 if x == -Inf: return "-inf"
66 result = $x
67 result = result.replace("e+", "e") # Nim `1e+21`, Rust `1e21`
68 if not debug and result.endsWith(".0"): # Rust Display drops a bare `.0`
69 result.setLen(result.len - 2)
70
71proc rsDisplay*(x: float32 | float64): string = rsFloatStr(float64(x), false)
72proc rsDebug*(x: float32 | float64): string = rsFloatStr(float64(x), true)
73
74proc rsDisplay*(x: string): string = x
75proc rsDebug*(x: string): string =
76 result = "\""
77 for c in x:
78 case c
79 of '"': result.add("\\\"")
80 of '\\': result.add("\\\\")
81 of '\n': result.add("\\n")
82 of '\t': result.add("\\t")
83 of '\r': result.add("\\r")
84 else: result.add(c)
85 result.add("\"")
86
87proc rsDisplay*(x: Rune): string = $x
88proc rsDebug*(x: Rune): string =
89 case $x
90 of "'": "'\\''"
91 of "\\": "'\\\\'"
92 of "\n": "'\\n'"
93 of "\t": "'\\t'"
94 of "\r": "'\\r'"
95 else: "'" & $x & "'"
96
97proc rsDebug*[T](x: seq[T] | openArray[T]): string =
98 result = "["
99 for i in 0 ..< x.len:
100 if i > 0: result.add(", ")
101 result.add(rsDebug(x[i]))
102 result.add("]")
103
104proc rsDebug*[T](o: Option[T]): string =
105 if o.has: "Some(" & rsDebug(o.val) & ")" else: "None"
106proc rsDebug*[T, E](r: Result[T, E]): string =
107 if r.ok: "Ok(" & rsDebug(r.val) & ")" else: "Err(" & rsDebug(r.err) & ")"
108
109# ---------------------------------------------------------------------------
110# Radix formats: `{:x}`, `{:X}`, `{:b}`, `{:o}`, with Rust's width/zero-fill.
111# Rust formats the *two's-complement bit pattern*, so a negative i8 prints as
112# `ff`, not `-1`. `toHex` on the unsigned view of the same width reproduces it.
113# ---------------------------------------------------------------------------
114
115proc rsRadix*[T: SomeInteger](x: T, base: int, upper: bool): string =
116 var v: uint64 =
117 when T is SomeSignedInt:
118 # Sign-extend then mask to the type's own width, so the printed bit
119 # pattern is the Rust one for this exact integer type.
120 cast[uint64](int64(x)) and (if sizeof(T) == 8: high(uint64) else: (1'u64 shl (sizeof(T) * 8)) - 1)
121 else:
122 uint64(x)
123 if v == 0: return "0"
124 const digits = "0123456789abcdef"
125 while v > 0'u64:
126 result.add(digits[int(v mod uint64(base))])
127 v = v div uint64(base)
128 for i in 0 ..< result.len div 2:
129 swap(result[i], result[result.len - 1 - i])
130 if upper: result = result.toUpperAscii()
131
132proc rsPad*(s: string, width: int, zero: bool): string =
133 if s.len >= width: return s
134 let fill = width - s.len
135 if zero and s.len > 0 and s[0] == '-':
136 "-" & repeat('0', fill) & s[1 .. ^1]
137 elif zero:
138 repeat('0', fill) & s
139 else:
140 repeat(' ', fill) & s
141
142proc rsBytes*(s: string): seq[uint8] =
143 ## Rust's `str::as_bytes` is a view of the UTF-8 encoding; Nim's `string` is
144 ## already those bytes, so this is a reinterpretation, not a conversion.
145 result = newSeq[uint8](s.len)
146 for i in 0 ..< s.len: result[i] = uint8(s[i])
147
148proc newSeqWith*[T](n: int, v: T): seq[T] =
149 result = newSeq[T](n)
150 for i in 0 ..< n: result[i] = v