nim-ffi/ffi/codegen/consts.nim

70 lines
1.9 KiB
Nim

## Literal rendering for `{.ffiConst.}` values, shared by the C/C++/Rust generators.
## The registry stores Nim's `$value`; each backend re-quotes it for its syntax.
import std/strutils
import ./types_ir
func cByteEscape(ch: char): string =
## 3-digit octal: C caps an octal escape at 3 digits, so a following digit
## can't be swallowed into it the way it can with `\x`.
return "\\" & toOct(ord(ch), 3)
func rustByteEscape(ch: char): string =
return "\\x" & toHex(ord(ch), 2)
func escapeLit(
s: string, byteEscape: proc(ch: char): string {.noSideEffect, nimcall.}
): string =
var escaped = ""
for ch in s:
case ch
of '"':
escaped.add("\\\"")
of '\\':
escaped.add("\\\\")
of '\n':
escaped.add("\\n")
of '\r':
escaped.add("\\r")
of '\t':
escaped.add("\\t")
else:
if ch < ' ' or ch == '\x7F':
escaped.add(byteEscape(ch))
else:
escaped.add(ch)
return escaped
func cEscapeStringLit*(s: string): string =
return escapeLit(s, cByteEscape)
func rustEscapeStringLit*(s: string): string =
return escapeLit(s, rustByteEscape)
func cConstValue*(t: FFIType, value: string): string =
## C/C++ literal. Every emission site is a typed declaration, so the declared
## type already fixes the width; only the two cases the type can't rescue get
## a suffix — `ULL` because a decimal above `INT64_MAX` fits no signed type,
## and `f` because a bare `1.5` is a double and narrowing it warns.
case t.kind
of ftStr:
return "\"" & cEscapeStringLit(value) & "\""
of ftScalar:
case t.scalar
of skU64:
return value & "ULL"
of skF32:
return value & "f"
else:
return value
else:
return value
func rustConstValue*(t: FFIType, value: string): string =
## Rust literal; the declared type annotation carries the width, so no suffix.
case t.kind
of ftStr:
return "\"" & rustEscapeStringLit(value) & "\""
else:
return value