reprjs.nim 7.1 KB

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