reprjs.nim 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # The generic ``repr`` procedure for the javascript backend.
  10. when defined(nimPreviewSlimSystem):
  11. import std/formatfloat
  12. proc reprInt(x: int64): string {.compilerproc.} = $x
  13. proc reprInt(x: uint64): string {.compilerproc.} = $x
  14. proc reprInt(x: int): string {.compilerproc.} = $x
  15. proc reprFloat(x: float): string {.compilerproc.} = $x
  16. proc reprPointer(p: pointer): string {.compilerproc.} =
  17. # Do we need to generate the full 8bytes ? In js a pointer is an int anyway
  18. var tmp: int
  19. {.emit: "`tmp` = `p`_Idx || 0;".}
  20. result = $tmp
  21. proc reprBool(x: bool): string {.compilerRtl.} =
  22. if x: result = "true"
  23. else: result = "false"
  24. proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
  25. var tmp: bool
  26. let item = typ.node.sons[e]
  27. {.emit: "`tmp` = `item` !== undefined;".}
  28. if tmp:
  29. result = makeNimstrLit(item.name)
  30. else:
  31. result = $e & " (invalid data!)"
  32. include system/repr_impl
  33. proc reprChar(x: char): string {.compilerRtl.} =
  34. result = "\'"
  35. case x
  36. of '"': add(result, "\\\"")
  37. of '\\': add(result, "\\\\")
  38. of '\127'..'\255', '\0'..'\31': add(result, "\\" & reprInt(ord(x)))
  39. else: add(result, x)
  40. add(result, "\'")
  41. proc reprStrAux(result: var string, s: cstring | string, len: int) =
  42. add(result, "\"")
  43. for i in 0 .. len-1:
  44. let c = s[i]
  45. case c
  46. of '"': add(result, "\\\"")
  47. of '\\': add(result, "\\\\")
  48. #of '\10': add(result, "\\10\"\n\"")
  49. of '\127'..'\255', '\0'..'\31':
  50. add(result, "\\" & reprInt(ord(c)))
  51. else:
  52. add(result, c)
  53. add(result, "\"")
  54. proc reprStr(s: string): string {.compilerRtl.} =
  55. reprStrAux(result, s, s.len)
  56. proc addSetElem(result: var string, elem: int, typ: PNimType) =
  57. # Dispatch each set element to the correct repr<Type> proc
  58. case typ.kind:
  59. of tyEnum: add(result, reprEnum(elem, typ))
  60. of tyBool: add(result, reprBool(bool(elem)))
  61. of tyChar: add(result, reprChar(chr(elem)))
  62. of tyRange: addSetElem(result, elem, typ.base) # Note the base to advance towards the element type
  63. of tyInt..tyInt64, tyUInt8, tyUInt16: add result, reprInt(elem)
  64. else: # data corrupt --> inform the user
  65. add(result, " (invalid data!)")
  66. iterator setKeys(s: int): int {.inline.} =
  67. # The type of s is a lie, but it's expected to be a set.
  68. # Iterate over the JS object representing a set
  69. # and returns the keys as int.
  70. var len: int
  71. var yieldRes: int
  72. var i: int = 0
  73. {. emit: """
  74. var setObjKeys = Object.getOwnPropertyNames(`s`);
  75. `len` = setObjKeys.length;
  76. """ .}
  77. while i < len:
  78. {. emit: "`yieldRes` = parseInt(setObjKeys[`i`],10);\n" .}
  79. yield yieldRes
  80. inc i
  81. proc reprSetAux(result: var string, s: int, typ: PNimType) =
  82. add(result, "{")
  83. var first: bool = true
  84. for el in setKeys(s):
  85. if first:
  86. first = false
  87. else:
  88. add(result, ", ")
  89. addSetElem(result, el, typ.base)
  90. add(result, "}")
  91. proc reprSet(e: int, typ: PNimType): string {.compilerRtl.} =
  92. reprSetAux(result, e, typ)
  93. type
  94. ReprClosure {.final.} = object
  95. recDepth: int # do not recurse endlessly
  96. indent: int # indentation
  97. proc initReprClosure(cl: var ReprClosure) =
  98. cl.recDepth = -1 # default is to display everything!
  99. cl.indent = 0
  100. proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var ReprClosure)
  101. proc reprArray(a: pointer, typ: PNimType,
  102. cl: var ReprClosure): string {.compilerRtl.} =
  103. # We prepend @ to seq, the C backend prepends the pointer to the seq.
  104. result = if typ.kind == tySequence: "@[" else: "["
  105. var len: int = 0
  106. {. emit: "`len` = `a`.length;\n" .}
  107. var dereffed: pointer = a
  108. for i in 0 .. len-1:
  109. if i > 0 :
  110. add(result, ", ")
  111. # advance pointer and point to element at index
  112. {. emit: """
  113. `dereffed`_Idx = `i`;
  114. `dereffed` = `a`[`dereffed`_Idx];
  115. """ .}
  116. reprAux(result, dereffed, typ.base, cl)
  117. add(result, "]")
  118. proc isPointedToNil(p: pointer): bool =
  119. {. emit: "if (`p` === null) {`result` = true;}\n" .}
  120. proc reprRef(result: var string, p: pointer, typ: PNimType,
  121. cl: var ReprClosure) =
  122. if p.isPointedToNil:
  123. add(result, "nil")
  124. return
  125. add(result, "ref " & reprPointer(p))
  126. add(result, " --> ")
  127. if typ.base.kind != tyArray:
  128. {. emit: """
  129. if (`p` != null && `p`.length > 0) {
  130. `p` = `p`[`p`_Idx];
  131. }
  132. """ .}
  133. reprAux(result, p, typ.base, cl)
  134. proc reprRecordAux(result: var string, o: pointer, typ: PNimType, cl: var ReprClosure) =
  135. add(result, "[")
  136. var first = true
  137. var val = o
  138. if typ.node.len == 0:
  139. # if the object has only one field, len is 0 and sons is nil, the field is in node
  140. let key: cstring = typ.node.name
  141. add(result, $key & " = ")
  142. {. emit: "`val` = `o`[`key`];\n" .}
  143. reprAux(result, val, typ.node.typ, cl)
  144. else:
  145. # if the object has more than one field, sons is not nil and contains the fields.
  146. for i in 0 .. typ.node.len-1:
  147. if first: first = false
  148. else: add(result, ",\n")
  149. let key: cstring = typ.node.sons[i].name
  150. add(result, $key & " = ")
  151. {. emit: "`val` = `o`[`key`];\n" .} # access the field by name
  152. reprAux(result, val, typ.node.sons[i].typ, cl)
  153. add(result, "]")
  154. proc reprRecord(o: pointer, typ: PNimType, cl: var ReprClosure): string {.compilerRtl.} =
  155. reprRecordAux(result, o, typ, cl)
  156. proc reprJsonStringify(p: int): string {.compilerRtl.} =
  157. # As a last resort, use stringify
  158. # We use this for tyOpenArray, tyVarargs while genTypeInfo is not implemented
  159. var tmp: cstring
  160. {. emit: "`tmp` = JSON.stringify(`p`);\n" .}
  161. result = $tmp
  162. proc reprAux(result: var string, p: pointer, typ: PNimType,
  163. cl: var ReprClosure) =
  164. if cl.recDepth == 0:
  165. add(result, "...")
  166. return
  167. dec(cl.recDepth)
  168. case typ.kind
  169. of tyInt..tyInt32, tyUInt..tyUInt32:
  170. add(result, reprInt(cast[int](p)))
  171. of tyInt64:
  172. add(result, reprInt(cast[int64](p)))
  173. of tyUInt64:
  174. add(result, reprInt(cast[uint64](p)))
  175. of tyChar:
  176. add(result, reprChar(cast[char](p)))
  177. of tyBool:
  178. add(result, reprBool(cast[bool](p)))
  179. of tyFloat..tyFloat128:
  180. add(result, reprFloat(cast[float](p)))
  181. of tyString:
  182. var fp: int
  183. {. emit: "`fp` = `p`;\n" .}
  184. add(result, reprStr(cast[string](p)))
  185. of tyCstring:
  186. var fp: cstring
  187. {. emit: "`fp` = `p`;\n" .}
  188. if fp.isNil:
  189. add(result, "nil")
  190. else:
  191. reprStrAux(result, fp, fp.len)
  192. of tyEnum, tyOrdinal:
  193. var fp: int
  194. {. emit: "`fp` = `p`;\n" .}
  195. add(result, reprEnum(fp, typ))
  196. of tySet:
  197. var fp: int
  198. {. emit: "`fp` = `p`;\n" .}
  199. add(result, reprSet(fp, typ))
  200. of tyRange: reprAux(result, p, typ.base, cl)
  201. of tyObject, tyTuple:
  202. add(result, reprRecord(p, typ, cl))
  203. of tyArray, tyArrayConstr, tySequence:
  204. add(result, reprArray(p, typ, cl))
  205. of tyPointer:
  206. add(result, reprPointer(p))
  207. of tyPtr, tyRef:
  208. reprRef(result, p, typ, cl)
  209. of tyProc:
  210. if p.isPointedToNil:
  211. add(result, "nil")
  212. else:
  213. add(result, reprPointer(p))
  214. else:
  215. add(result, "(invalid data!)" & reprJsonStringify(cast[int](p)))
  216. inc(cl.recDepth)
  217. proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} =
  218. var cl: ReprClosure
  219. initReprClosure(cl)
  220. reprAux(result, p, typ, cl)
  221. when defined(nimLegacyReprWithNewline): # see PR #16034
  222. add result, "\n"