nandi/rustnimpublic Fork 0
0a375d8ec0bfffe1baadd340864ab1ffa92fdb98
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 · 179 lines · 6.5 KBNim Blame HistoryRaw
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 10h 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
Add trait impls and slice iterators; base16ct's decoder now goes through ae9f986 nandithebull 9h ago9# Rust's `&[T]` is a borrowed view, not a copy. Nim's view types model exactly
10# that, including returning one from a proc: writing through the returned view
11# is visible in the original buffer. Probed against Nim 2.2.4 before relying on
12# it, because copying instead would silently change aliasing.
13{.experimental: "views".}
14
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 10h ago15import std/[unicode, strutils]
16
17type
18 RustPanic* = object of CatchableError
19
Add trait impls and slice iterators; base16ct's decoder now goes through ae9f986 nandithebull 9h ago20 ## `core::fmt`'s own error and sink, kept distinct from any user type that
21 ## happens to be called `Error`. A formatting impl is lowered to a proc that
22 ## returns the formatted string, so these appear only in signatures.
23 FmtError* = object
24 FmtResult* = object
25 ok*: bool
26
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 10h ago27 Option*[T] = object
28 case has*: bool
29 of true: val*: T
30 of false: discard
31
32 Result*[T, E] = object
33 case ok*: bool
34 of true: val*: T
35 of false: err*: E
36
37proc rsPanic*(msg: string) {.noreturn.} =
38 raise newException(RustPanic, msg)
39
40proc rsSome*[T](v: T): Option[T] = Option[T](has: true, val: v)
41proc rsNone*[T](): Option[T] = Option[T](has: false)
42proc rsOk*[T, E](v: T): Result[T, E] = Result[T, E](ok: true, val: v)
43proc rsErr*[T, E](e: E): Result[T, E] = Result[T, E](ok: false, err: e)
44
Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 nandithebull 9h ago45proc rsOkOr*[T, E](o: Option[T], e: E): Result[T, E] =
46 if o.has: Result[T, E](ok: true, val: o.val) else: Result[T, E](ok: false, err: e)
47
48proc unwrapOr*[T](o: Option[T], d: T): T =
49 if o.has: o.val else: d
50proc unwrapOr*[T, E](r: Result[T, E], d: T): T =
51 if r.ok: r.val else: d
52
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 10h ago53proc unwrap*[T](o: Option[T]): T =
54 if not o.has: rsPanic("called `Option::unwrap()` on a `None` value")
55 o.val
56proc unwrap*[T, E](r: Result[T, E]): T =
57 if not r.ok: rsPanic("called `Result::unwrap()` on an `Err` value")
58 r.val
59
60# ---------------------------------------------------------------------------
61# Display / Debug.
62#
63# Rust's `{}` and `{:?}` are two distinct formats and they differ for floats,
64# strings, chars and sequences. Nim's `$` matches neither consistently, so both
65# are implemented here rather than approximated with `$`.
66# ---------------------------------------------------------------------------
67
68proc rsDisplay*(x: SomeInteger): string = $x
69proc rsDebug*(x: SomeInteger): string = $x
70proc rsDisplay*(x: bool): string = $x
71proc rsDebug*(x: bool): string = $x
72
73proc rsFloatStr(x: float64, debug: bool): string =
74 ## Nim and Rust both print the shortest round-tripping decimal, but they
75 ## spell the result differently in three places.
76 if x != x: return "NaN"
77 if x == Inf: return "inf"
78 if x == -Inf: return "-inf"
79 result = $x
80 result = result.replace("e+", "e") # Nim `1e+21`, Rust `1e21`
81 if not debug and result.endsWith(".0"): # Rust Display drops a bare `.0`
82 result.setLen(result.len - 2)
83
84proc rsDisplay*(x: float32 | float64): string = rsFloatStr(float64(x), false)
85proc rsDebug*(x: float32 | float64): string = rsFloatStr(float64(x), true)
86
87proc rsDisplay*(x: string): string = x
88proc rsDebug*(x: string): string =
89 result = "\""
90 for c in x:
91 case c
92 of '"': result.add("\\\"")
93 of '\\': result.add("\\\\")
94 of '\n': result.add("\\n")
95 of '\t': result.add("\\t")
96 of '\r': result.add("\\r")
97 else: result.add(c)
98 result.add("\"")
99
100proc rsDisplay*(x: Rune): string = $x
101proc rsDebug*(x: Rune): string =
102 case $x
103 of "'": "'\\''"
104 of "\\": "'\\\\'"
105 of "\n": "'\\n'"
106 of "\t": "'\\t'"
107 of "\r": "'\\r'"
108 else: "'" & $x & "'"
109
110proc rsDebug*[T](x: seq[T] | openArray[T]): string =
111 result = "["
112 for i in 0 ..< x.len:
113 if i > 0: result.add(", ")
114 result.add(rsDebug(x[i]))
115 result.add("]")
116
117proc rsDebug*[T](o: Option[T]): string =
118 if o.has: "Some(" & rsDebug(o.val) & ")" else: "None"
119proc rsDebug*[T, E](r: Result[T, E]): string =
120 if r.ok: "Ok(" & rsDebug(r.val) & ")" else: "Err(" & rsDebug(r.err) & ")"
121
122# ---------------------------------------------------------------------------
123# Radix formats: `{:x}`, `{:X}`, `{:b}`, `{:o}`, with Rust's width/zero-fill.
124# Rust formats the *two's-complement bit pattern*, so a negative i8 prints as
125# `ff`, not `-1`. `toHex` on the unsigned view of the same width reproduces it.
126# ---------------------------------------------------------------------------
127
128proc rsRadix*[T: SomeInteger](x: T, base: int, upper: bool): string =
129 var v: uint64 =
130 when T is SomeSignedInt:
131 # Sign-extend then mask to the type's own width, so the printed bit
132 # pattern is the Rust one for this exact integer type.
133 cast[uint64](int64(x)) and (if sizeof(T) == 8: high(uint64) else: (1'u64 shl (sizeof(T) * 8)) - 1)
134 else:
135 uint64(x)
136 if v == 0: return "0"
137 const digits = "0123456789abcdef"
138 while v > 0'u64:
139 result.add(digits[int(v mod uint64(base))])
140 v = v div uint64(base)
141 for i in 0 ..< result.len div 2:
142 swap(result[i], result[result.len - 1 - i])
143 if upper: result = result.toUpperAscii()
144
145proc rsPad*(s: string, width: int, zero: bool): string =
146 if s.len >= width: return s
147 let fill = width - s.len
148 if zero and s.len > 0 and s[0] == '-':
149 "-" & repeat('0', fill) & s[1 .. ^1]
150 elif zero:
151 repeat('0', fill) & s
152 else:
153 repeat(' ', fill) & s
154
Add closures and unsafe; base16ct's lower.rs and upper.rs go through 0a375d8 nandithebull 8h ago155proc rsStrView*(b: openArray[uint8]): openArray[char] =
156 ## Rust's `str::from_utf8_unchecked` reinterprets a byte slice as a string
157 ## slice without copying or validating. Nim's `char` and `uint8` are both one
158 ## byte, so the same view is handed back over the same memory -- writes
159 ## through the original are visible here, as they are in Rust.
160 if b.len == 0:
161 result = toOpenArray(cast[ptr UncheckedArray[char]](nil), 0, -1)
162 else:
163 result = toOpenArray(cast[ptr UncheckedArray[char]](unsafeAddr b[0]), 0, b.len - 1)
164
165proc rsDisplay*(x: openArray[char]): string =
166 result = newStringOfCap(x.len)
167 for c in x: result.add(c)
168
169proc rsDebug*(x: openArray[char]): string = rsDebug(rsDisplay(x))
170
Add the differential test runner, and a lowering to measure with it 8ac32af nandithebull 10h ago171proc rsBytes*(s: string): seq[uint8] =
172 ## Rust's `str::as_bytes` is a view of the UTF-8 encoding; Nim's `string` is
173 ## already those bytes, so this is a reinterpretation, not a conversion.
174 result = newSeq[uint8](s.len)
175 for i in 0 ..< s.len: result[i] = uint8(s[i])
176
177proc newSeqWith*[T](n: int, v: T): seq[T] =
178 result = newSeq[T](n)
179 for i in 0 ..< n: result[i] = v