#
#
# Nim's Runtime Library
# (c) Copyright 2012 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
# The generic ``repr`` procedure for the javascript backend.
proc reprInt(x: int64): string {.compilerproc.} = $x
proc reprFloat(x: float): string {.compilerproc.} = $x
proc reprPointer(p: pointer): string {.compilerproc.} =
# Do we need to generate the full 8bytes ? In js a pointer is an int anyway
var tmp: int
{.emit: "`tmp` = `p`_Idx || 0;".}
result = $tmp
proc reprBool(x: bool): string {.compilerRtl.} =
if x: result = "true"
else: result = "false"
proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
var tmp: bool
let item = typ.node.sons[e]
{.emit: "`tmp` = `item` !== undefined".}
if tmp:
result = makeNimstrLit(item.name)
else:
result = $e & " (invalid data!)"
proc reprChar(x: char): string {.compilerRtl.} =
result = "\'"
case x
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
of '\127'..'\255', '\0'..'\31': add(result, "\\" & reprInt(ord(x)))
else: add(result, x)
add(result, "\'")
proc reprStrAux(result: var string, s: cstring | string, len: int) =
add(result, "\"")
for i in 0 .. len-1:
let c = s[i]
case c
of '"': add(result, "\\\"")
of '\\': add(result, "\\\\")
#of '\10': add(result, "\\10\"\n\"")
of '\127'..'\255', '\0'..'\31':
add(result, "\\" & reprInt(ord(c)))
else:
add(result, c)
add(result, "\"")
proc reprStr(s: string): string {.compilerRtl.} =
reprStrAux(result, s, s.len)
proc addSetElem(result: var string, elem: int, typ: PNimType) =
# Dispatch each set element to the correct repr<Type> proc
case typ.kind:
of tyEnum: add(result, reprEnum(elem, typ))
of tyBool: add(result, reprBool(bool(elem)))
of tyChar: add(result, reprChar(chr(elem)))
of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type
of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
else: # data corrupt --> inform the user
add(result, " (invalid data!)")
iterator setKeys(s: int): int {.inline.} =
# The type of s is a lie, but it's expected to be a set.
# Iterate over the JS object representing a set
# and returns the keys as int.
var len: int
var yieldRes: int
var i: int = 0
{. emit: """
var setObjKeys = Object.getOwnPropertyNames(`s`);
`len` = setObjKeys.length;
""" .}
while i < len:
{. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .}
yield yieldRes
inc i
proc reprSetAux(result: var string, s: int, typ: PNimType) =
add(result, "{")
var first: bool = true
for el in setKeys(s):
if first:
first = false
else:
add(result, ", ")
addSetElem(result, el, typ.base)
add(result, "}")
proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} =
reprSetAux(result, e, typ)
type
ReprClosure {.final.} = object
recDepth: int # do not recurse endlessly
indent: int # indentation
proc initReprClosure(cl: var ReprClosure) =
cl.recDepth = -1 # default is to display everything!
cl.indent = 0
proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
proc reprArray(a: pointer, typ: PNimType,
cl: var ReprClosure): string {.compilerRtl.} =
# We prepend @ to seq, the C backend prepends the pointer to the seq.
result = if typ.kind == tySequence: "@[" else: "["
var len: int = 0
var i: int = 0
{. emit: "`len` = `a`.length;\n" .}
var dereffed: pointer = a
for i in 0 .. len-1:
if i > 0 :
add(result, ", ")
# advance pointer and point to element at index
{. emit: """
`dereffed`_Idx = `i`;
`dereffed` = `a`[`dereffed`_Idx];
""" .}
reprAux(result, dereffed, typ.base, cl)
add(result, "]")
proc isPointedToNil(p: pointer): bool =
{. emit: "if (`p` === null) {`result` = true};\n" .}
proc reprRef(result: var string, p: pointer, typ: PNimType,
cl: var ReprClosure) =
if p.isPointedToNil:
add(result, "nil")
return
add(result, "ref " & reprPointer(p))
add(result, " --> ")
if typ.base.kind != tyArray:
{. emit: """
if (`p` != null && `p`.length > 0) {
`p` = `p`[`p`_Idx];
}
""" .}
reprAux(result, p, typ.base, cl)
proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
add(result, "[")
var first = true
var val = o
if typ.node.len == 0:
# if the object has only one field, len is 0 and sons is nil, the field is in node
let key: cstring = typ.node.name
add(result, $key & " = ")
{. emit: "`val` = `o`[`key`];\n" .}
reprAux(result, val, typ.node.typ, cl)
else:
# if the object has more than one field, sons is not nil and contains the fields.
for i in 0 .. typ.node.len-1:
if first: first = false
else: add(result, ",\n")
let key: cstring = typ.node.sons[i].name
add(result, $key & " = ")
{. emit: "`val` = `o`[`key`];\n" .} # access the field by name
reprAux(result, val, typ.node.sons[i].typ, cl)
add(result, "]")
proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} =
reprRecordAux(result, o, typ, cl)
proc reprJsonStringify(p: int): string {.compilerRtl.} =
# As a last resort, use stringify
# We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented
var tmp: cstring
{. emit: "`tmp` = JSON.stringify(`p`);\n" .}
result = $tmp
proc reprAux(result: var string, p: pointer, typ: PNimType,
cl: var ReprClosure) =
if cl.recDepth == 0:
add(result, "...")
return
dec(cl.recDepth)
case typ.kind
of tyInt..tyInt64, tyUInt..tyUInt64:
add(result, reprInt(cast[int](p)))
of tyChar:
add(result, reprChar(cast[char](p)))
of tyBool:
add(result, reprBool(cast[bool](p)))
of tyFloat..tyFloat128:
add(result, reprFloat(cast[float](p)))
of tyString:
var fp: int
{. emit: "`fp` = `p`;\n" .}
add(result, reprStr(cast[string](p)))
of tyCString:
var fp: cstring
{. emit: "`fp` = `p`;\n" .}
if fp.isNil:
add(result, "nil")
else:
reprStrAux(result, fp, fp.len)
of tyEnum, tyOrdinal:
var fp: int
{. emit: "`fp` = `p`;\n" .}
add(result, reprEnum(fp, typ))
of tySet:
var fp: int
{. emit: "`fp` = `p`;\n" .}
add(result, reprSet(fp, typ))
of tyRange: reprAux(result, p, typ.base, cl)
of tyObject, tyTuple:
add(result, reprRecord(p, typ, cl))
of tyArray, tyArrayConstr, tySequence:
add(result, reprArray(p, typ, cl))
of tyPointer:
add(result, reprPointer(p))
of tyPtr, tyRef:
reprRef(result, p, typ, cl)
of tyProc:
if p.isPointedToNil:
add(result, "nil")
else:
add(result, reprPointer(p))
else:
add(result, "(invalid data!)" & reprJsonStringify(cast[int](p)))
inc(cl.recDepth)
proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} =
var cl: ReprClosure
initReprClosure(cl)
reprAux(result, p, typ, cl)
when defined(nimLegacyReprWithNewline): # see PR #16034
add result, "\n"