reprjs.nim 7.0 KB

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