sexp.nim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf, Dominik Picheta
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. import
  10. hashes, strutils, lexbase, streams, unicode, macros
  11. type
  12. SexpEventKind* = enum ## enumeration of all events that may occur when parsing
  13. sexpError, ## an error occurred during parsing
  14. sexpEof, ## end of file reached
  15. sexpString, ## a string literal
  16. sexpSymbol, ## a symbol
  17. sexpInt, ## an integer literal
  18. sexpFloat, ## a float literal
  19. sexpNil, ## the value ``nil``
  20. sexpDot, ## the dot to separate car/cdr
  21. sexpListStart, ## start of a list: the ``(`` token
  22. sexpListEnd, ## end of a list: the ``)`` token
  23. TTokKind = enum # must be synchronized with SexpEventKind!
  24. tkError,
  25. tkEof,
  26. tkString,
  27. tkSymbol,
  28. tkInt,
  29. tkFloat,
  30. tkNil,
  31. tkDot,
  32. tkParensLe,
  33. tkParensRi
  34. tkSpace
  35. SexpError* = enum ## enumeration that lists all errors that can occur
  36. errNone, ## no error
  37. errInvalidToken, ## invalid token
  38. errParensRiExpected, ## ``)`` expected
  39. errQuoteExpected, ## ``"`` expected
  40. errEofExpected, ## EOF expected
  41. SexpParser* = object of BaseLexer ## the parser object.
  42. a: string
  43. tok: TTokKind
  44. kind: SexpEventKind
  45. err: SexpError
  46. const
  47. errorMessages: array[SexpError, string] = [
  48. "no error",
  49. "invalid token",
  50. "')' expected",
  51. "'\"' or \"'\" expected",
  52. "EOF expected",
  53. ]
  54. tokToStr: array[TTokKind, string] = [
  55. "invalid token",
  56. "EOF",
  57. "string literal",
  58. "symbol",
  59. "int literal",
  60. "float literal",
  61. "nil",
  62. ".",
  63. "(", ")", "space"
  64. ]
  65. proc close*(my: var SexpParser) {.inline.} =
  66. ## closes the parser `my` and its associated input stream.
  67. lexbase.close(my)
  68. proc str*(my: SexpParser): string {.inline.} =
  69. ## returns the character data for the events: ``sexpInt``, ``sexpFloat``,
  70. ## ``sexpString``
  71. assert(my.kind in {sexpInt, sexpFloat, sexpString})
  72. result = my.a
  73. proc getInt*(my: SexpParser): BiggestInt {.inline.} =
  74. ## returns the number for the event: ``sexpInt``
  75. assert(my.kind == sexpInt)
  76. result = parseBiggestInt(my.a)
  77. proc getFloat*(my: SexpParser): float {.inline.} =
  78. ## returns the number for the event: ``sexpFloat``
  79. assert(my.kind == sexpFloat)
  80. result = parseFloat(my.a)
  81. proc kind*(my: SexpParser): SexpEventKind {.inline.} =
  82. ## returns the current event type for the SEXP parser
  83. result = my.kind
  84. proc getColumn*(my: SexpParser): int {.inline.} =
  85. ## get the current column the parser has arrived at.
  86. result = getColNumber(my, my.bufpos)
  87. proc getLine*(my: SexpParser): int {.inline.} =
  88. ## get the current line the parser has arrived at.
  89. result = my.lineNumber
  90. proc errorMsg*(my: SexpParser): string =
  91. ## returns a helpful error message for the event ``sexpError``
  92. assert(my.kind == sexpError)
  93. result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), errorMessages[my.err]]
  94. proc errorMsgExpected*(my: SexpParser, e: string): string =
  95. ## returns an error message "`e` expected" in the same format as the
  96. ## other error messages
  97. result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"]
  98. proc handleHexChar(c: char, x: var int): bool =
  99. result = true # Success
  100. case c
  101. of '0'..'9': x = (x shl 4) or (ord(c) - ord('0'))
  102. of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10)
  103. of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10)
  104. else: result = false # error
  105. proc parseString(my: var SexpParser): TTokKind =
  106. result = tkString
  107. var pos = my.bufpos + 1
  108. var buf = my.buf
  109. while true:
  110. case buf[pos]
  111. of '\0':
  112. my.err = errQuoteExpected
  113. result = tkError
  114. break
  115. of '"':
  116. inc(pos)
  117. break
  118. of '\\':
  119. case buf[pos+1]
  120. of '\\', '"', '\'', '/':
  121. add(my.a, buf[pos+1])
  122. inc(pos, 2)
  123. of 'b':
  124. add(my.a, '\b')
  125. inc(pos, 2)
  126. of 'f':
  127. add(my.a, '\f')
  128. inc(pos, 2)
  129. of 'n':
  130. add(my.a, '\L')
  131. inc(pos, 2)
  132. of 'r':
  133. add(my.a, '\C')
  134. inc(pos, 2)
  135. of 't':
  136. add(my.a, '\t')
  137. inc(pos, 2)
  138. of 'u':
  139. inc(pos, 2)
  140. var r: int
  141. if handleHexChar(buf[pos], r): inc(pos)
  142. if handleHexChar(buf[pos], r): inc(pos)
  143. if handleHexChar(buf[pos], r): inc(pos)
  144. if handleHexChar(buf[pos], r): inc(pos)
  145. add(my.a, toUTF8(Rune(r)))
  146. else:
  147. # don't bother with the error
  148. add(my.a, buf[pos])
  149. inc(pos)
  150. of '\c':
  151. pos = lexbase.handleCR(my, pos)
  152. buf = my.buf
  153. add(my.a, '\c')
  154. of '\L':
  155. pos = lexbase.handleLF(my, pos)
  156. buf = my.buf
  157. add(my.a, '\L')
  158. else:
  159. add(my.a, buf[pos])
  160. inc(pos)
  161. my.bufpos = pos # store back
  162. proc parseNumber(my: var SexpParser) =
  163. var pos = my.bufpos
  164. var buf = my.buf
  165. if buf[pos] == '-':
  166. add(my.a, '-')
  167. inc(pos)
  168. if buf[pos] == '.':
  169. add(my.a, "0.")
  170. inc(pos)
  171. else:
  172. while buf[pos] in Digits:
  173. add(my.a, buf[pos])
  174. inc(pos)
  175. if buf[pos] == '.':
  176. add(my.a, '.')
  177. inc(pos)
  178. # digits after the dot:
  179. while buf[pos] in Digits:
  180. add(my.a, buf[pos])
  181. inc(pos)
  182. if buf[pos] in {'E', 'e'}:
  183. add(my.a, buf[pos])
  184. inc(pos)
  185. if buf[pos] in {'+', '-'}:
  186. add(my.a, buf[pos])
  187. inc(pos)
  188. while buf[pos] in Digits:
  189. add(my.a, buf[pos])
  190. inc(pos)
  191. my.bufpos = pos
  192. proc parseSymbol(my: var SexpParser) =
  193. var pos = my.bufpos
  194. var buf = my.buf
  195. if buf[pos] in IdentStartChars:
  196. while buf[pos] in IdentChars:
  197. add(my.a, buf[pos])
  198. inc(pos)
  199. my.bufpos = pos
  200. proc getTok(my: var SexpParser): TTokKind =
  201. setLen(my.a, 0)
  202. case my.buf[my.bufpos]
  203. of '-', '0'..'9': # numbers that start with a . are not parsed
  204. # correctly.
  205. parseNumber(my)
  206. if {'.', 'e', 'E'} in my.a:
  207. result = tkFloat
  208. else:
  209. result = tkInt
  210. of '"': #" # gotta fix nim-mode
  211. result = parseString(my)
  212. of '(':
  213. inc(my.bufpos)
  214. result = tkParensLe
  215. of ')':
  216. inc(my.bufpos)
  217. result = tkParensRi
  218. of '\0':
  219. result = tkEof
  220. of 'a'..'z', 'A'..'Z', '_':
  221. parseSymbol(my)
  222. if my.a == "nil":
  223. result = tkNil
  224. else:
  225. result = tkSymbol
  226. of ' ':
  227. result = tkSpace
  228. inc(my.bufpos)
  229. of '.':
  230. result = tkDot
  231. inc(my.bufpos)
  232. else:
  233. inc(my.bufpos)
  234. result = tkError
  235. my.tok = result
  236. # ------------- higher level interface ---------------------------------------
  237. type
  238. SexpNodeKind* = enum ## possible SEXP node types
  239. SNil,
  240. SInt,
  241. SFloat,
  242. SString,
  243. SSymbol,
  244. SList,
  245. SCons
  246. SexpNode* = ref SexpNodeObj ## SEXP node
  247. SexpNodeObj* {.acyclic.} = object
  248. case kind*: SexpNodeKind
  249. of SString:
  250. str*: string
  251. of SSymbol:
  252. symbol*: string
  253. of SInt:
  254. num*: BiggestInt
  255. of SFloat:
  256. fnum*: float
  257. of SList:
  258. elems*: seq[SexpNode]
  259. of SCons:
  260. car: SexpNode
  261. cdr: SexpNode
  262. of SNil:
  263. discard
  264. Cons = tuple[car: SexpNode, cdr: SexpNode]
  265. SexpParsingError* = object of ValueError ## is raised for a SEXP error
  266. proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} =
  267. ## raises an `ESexpParsingError` exception.
  268. raise newException(SexpParsingError, errorMsgExpected(p, msg))
  269. proc newSString*(s: string): SexpNode {.procvar.}=
  270. ## Creates a new `SString SexpNode`.
  271. new(result)
  272. result.kind = SString
  273. result.str = s
  274. proc newSStringMove(s: string): SexpNode =
  275. new(result)
  276. result.kind = SString
  277. shallowCopy(result.str, s)
  278. proc newSInt*(n: BiggestInt): SexpNode {.procvar.} =
  279. ## Creates a new `SInt SexpNode`.
  280. new(result)
  281. result.kind = SInt
  282. result.num = n
  283. proc newSFloat*(n: float): SexpNode {.procvar.} =
  284. ## Creates a new `SFloat SexpNode`.
  285. new(result)
  286. result.kind = SFloat
  287. result.fnum = n
  288. proc newSNil*(): SexpNode {.procvar.} =
  289. ## Creates a new `SNil SexpNode`.
  290. new(result)
  291. proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} =
  292. ## Creates a new `SCons SexpNode`
  293. new(result)
  294. result.kind = SCons
  295. result.car = car
  296. result.cdr = cdr
  297. proc newSList*(): SexpNode {.procvar.} =
  298. ## Creates a new `SList SexpNode`
  299. new(result)
  300. result.kind = SList
  301. result.elems = @[]
  302. proc newSSymbol*(s: string): SexpNode {.procvar.} =
  303. new(result)
  304. result.kind = SSymbol
  305. result.symbol = s
  306. proc newSSymbolMove(s: string): SexpNode =
  307. new(result)
  308. result.kind = SSymbol
  309. shallowCopy(result.symbol, s)
  310. proc getStr*(n: SexpNode, default: string = ""): string =
  311. ## Retrieves the string value of a `SString SexpNode`.
  312. ##
  313. ## Returns ``default`` if ``n`` is not a ``SString``.
  314. if n.kind != SString: return default
  315. else: return n.str
  316. proc getNum*(n: SexpNode, default: BiggestInt = 0): BiggestInt =
  317. ## Retrieves the int value of a `SInt SexpNode`.
  318. ##
  319. ## Returns ``default`` if ``n`` is not a ``SInt``.
  320. if n.kind != SInt: return default
  321. else: return n.num
  322. proc getFNum*(n: SexpNode, default: float = 0.0): float =
  323. ## Retrieves the float value of a `SFloat SexpNode`.
  324. ##
  325. ## Returns ``default`` if ``n`` is not a ``SFloat``.
  326. if n.kind != SFloat: return default
  327. else: return n.fnum
  328. proc getSymbol*(n: SexpNode, default: string = ""): string =
  329. ## Retrieves the int value of a `SList SexpNode`.
  330. ##
  331. ## Returns ``default`` if ``n`` is not a ``SList``.
  332. if n.kind != SSymbol: return default
  333. else: return n.symbol
  334. proc getElems*(n: SexpNode, default: seq[SexpNode] = @[]): seq[SexpNode] =
  335. ## Retrieves the int value of a `SList SexpNode`.
  336. ##
  337. ## Returns ``default`` if ``n`` is not a ``SList``.
  338. if n.kind == SNil: return @[]
  339. elif n.kind != SList: return default
  340. else: return n.elems
  341. proc getCons*(n: SexpNode, defaults: Cons = (newSNil(), newSNil())): Cons =
  342. ## Retrieves the cons value of a `SList SexpNode`.
  343. ##
  344. ## Returns ``default`` if ``n`` is not a ``SList``.
  345. if n.kind == SCons: return (n.car, n.cdr)
  346. elif n.kind == SList: return (n.elems[0], n.elems[1])
  347. else: return defaults
  348. proc sexp*(s: string): SexpNode =
  349. ## Generic constructor for SEXP data. Creates a new `SString SexpNode`.
  350. new(result)
  351. result.kind = SString
  352. result.str = s
  353. proc sexp*(n: BiggestInt): SexpNode =
  354. ## Generic constructor for SEXP data. Creates a new `SInt SexpNode`.
  355. new(result)
  356. result.kind = SInt
  357. result.num = n
  358. proc sexp*(n: float): SexpNode =
  359. ## Generic constructor for SEXP data. Creates a new `SFloat SexpNode`.
  360. new(result)
  361. result.kind = SFloat
  362. result.fnum = n
  363. proc sexp*(b: bool): SexpNode =
  364. ## Generic constructor for SEXP data. Creates a new `SSymbol
  365. ## SexpNode` with value t or `SNil SexpNode`.
  366. new(result)
  367. if b:
  368. result.kind = SSymbol
  369. result.symbol = "t"
  370. else:
  371. result.kind = SNil
  372. proc sexp*(elements: openArray[SexpNode]): SexpNode =
  373. ## Generic constructor for SEXP data. Creates a new `SList SexpNode`
  374. new(result)
  375. result.kind = SList
  376. newSeq(result.elems, elements.len)
  377. for i, p in pairs(elements): result.elems[i] = p
  378. proc sexp*(s: SexpNode): SexpNode =
  379. result = s
  380. proc toSexp(x: NimNode): NimNode {.compiletime.} =
  381. case x.kind
  382. of nnkBracket:
  383. result = newNimNode(nnkBracket)
  384. for i in 0 ..< x.len:
  385. result.add(toSexp(x[i]))
  386. else:
  387. result = x
  388. result = prefix(result, "sexp")
  389. macro convertSexp*(x: untyped): untyped =
  390. ## Convert an expression to a SexpNode directly, without having to specify
  391. ## `%` for every element.
  392. result = toSexp(x)
  393. proc `==`* (a,b: SexpNode): bool =
  394. ## Check two nodes for equality
  395. if a.isNil:
  396. if b.isNil: return true
  397. return false
  398. elif b.isNil or a.kind != b.kind:
  399. return false
  400. else:
  401. return case a.kind
  402. of SString:
  403. a.str == b.str
  404. of SInt:
  405. a.num == b.num
  406. of SFloat:
  407. a.fnum == b.fnum
  408. of SNil:
  409. true
  410. of SList:
  411. a.elems == b.elems
  412. of SSymbol:
  413. a.symbol == b.symbol
  414. of SCons:
  415. a.car == b.car and a.cdr == b.cdr
  416. proc hash* (n:SexpNode): Hash =
  417. ## Compute the hash for a SEXP node
  418. case n.kind
  419. of SList:
  420. result = hash(n.elems)
  421. of SInt:
  422. result = hash(n.num)
  423. of SFloat:
  424. result = hash(n.fnum)
  425. of SString:
  426. result = hash(n.str)
  427. of SNil:
  428. result = hash(0)
  429. of SSymbol:
  430. result = hash(n.symbol)
  431. of SCons:
  432. result = hash(n.car) !& hash(n.cdr)
  433. proc len*(n: SexpNode): int =
  434. ## If `n` is a `SList`, it returns the number of elements.
  435. ## If `n` is a `JObject`, it returns the number of pairs.
  436. ## Else it returns 0.
  437. case n.kind
  438. of SList: result = n.elems.len
  439. else: discard
  440. proc `[]`*(node: SexpNode, index: int): SexpNode =
  441. ## Gets the node at `index` in a List. Result is undefined if `index`
  442. ## is out of bounds
  443. assert(not isNil(node))
  444. assert(node.kind == SList)
  445. return node.elems[index]
  446. proc add*(father, child: SexpNode) =
  447. ## Adds `child` to a SList node `father`.
  448. assert father.kind == SList
  449. father.elems.add(child)
  450. # ------------- pretty printing ----------------------------------------------
  451. proc indent(s: var string, i: int) =
  452. s.add(spaces(i))
  453. proc newIndent(curr, indent: int, ml: bool): int =
  454. if ml: return curr + indent
  455. else: return indent
  456. proc nl(s: var string, ml: bool) =
  457. if ml: s.add("\n")
  458. proc escapeJson*(s: string): string =
  459. ## Converts a string `s` to its JSON representation.
  460. result = newStringOfCap(s.len + s.len shr 3)
  461. result.add("\"")
  462. for x in runes(s):
  463. var r = int(x)
  464. if r >= 32 and r <= 127:
  465. var c = chr(r)
  466. case c
  467. of '"': result.add("\\\"") #" # gotta fix nim-mode
  468. of '\\': result.add("\\\\")
  469. else: result.add(c)
  470. else:
  471. result.add("\\u")
  472. result.add(toHex(r, 4))
  473. result.add("\"")
  474. proc copy*(p: SexpNode): SexpNode =
  475. ## Performs a deep copy of `a`.
  476. case p.kind
  477. of SString:
  478. result = newSString(p.str)
  479. of SInt:
  480. result = newSInt(p.num)
  481. of SFloat:
  482. result = newSFloat(p.fnum)
  483. of SNil:
  484. result = newSNil()
  485. of SSymbol:
  486. result = newSSymbol(p.symbol)
  487. of SList:
  488. result = newSList()
  489. for i in items(p.elems):
  490. result.elems.add(copy(i))
  491. of SCons:
  492. result = newSCons(copy(p.car), copy(p.cdr))
  493. proc toPretty(result: var string, node: SexpNode, indent = 2, ml = true,
  494. lstArr = false, currIndent = 0) =
  495. case node.kind
  496. of SString:
  497. if lstArr: result.indent(currIndent)
  498. result.add(escapeJson(node.str))
  499. of SInt:
  500. if lstArr: result.indent(currIndent)
  501. result.add(node.num)
  502. of SFloat:
  503. if lstArr: result.indent(currIndent)
  504. result.add(node.fnum)
  505. of SNil:
  506. if lstArr: result.indent(currIndent)
  507. result.add("nil")
  508. of SSymbol:
  509. if lstArr: result.indent(currIndent)
  510. result.add(node.symbol)
  511. of SList:
  512. if lstArr: result.indent(currIndent)
  513. if len(node.elems) != 0:
  514. result.add("(")
  515. result.nl(ml)
  516. for i in 0..len(node.elems)-1:
  517. if i > 0:
  518. result.add(" ")
  519. result.nl(ml) # New Line
  520. toPretty(result, node.elems[i], indent, ml,
  521. true, newIndent(currIndent, indent, ml))
  522. result.nl(ml)
  523. result.indent(currIndent)
  524. result.add(")")
  525. else: result.add("nil")
  526. of SCons:
  527. if lstArr: result.indent(currIndent)
  528. result.add("(")
  529. toPretty(result, node.car, indent, ml,
  530. true, newIndent(currIndent, indent, ml))
  531. result.add(" . ")
  532. toPretty(result, node.cdr, indent, ml,
  533. true, newIndent(currIndent, indent, ml))
  534. result.add(")")
  535. proc pretty*(node: SexpNode, indent = 2): string =
  536. ## Converts `node` to its Sexp Representation, with indentation and
  537. ## on multiple lines.
  538. result = ""
  539. toPretty(result, node, indent)
  540. proc `$`*(node: SexpNode): string =
  541. ## Converts `node` to its SEXP Representation on one line.
  542. result = ""
  543. toPretty(result, node, 0, false)
  544. iterator items*(node: SexpNode): SexpNode =
  545. ## Iterator for the items of `node`. `node` has to be a SList.
  546. assert node.kind == SList
  547. for i in items(node.elems):
  548. yield i
  549. iterator mitems*(node: var SexpNode): var SexpNode =
  550. ## Iterator for the items of `node`. `node` has to be a SList. Items can be
  551. ## modified.
  552. assert node.kind == SList
  553. for i in mitems(node.elems):
  554. yield i
  555. proc eat(p: var SexpParser, tok: TTokKind) =
  556. if p.tok == tok: discard getTok(p)
  557. else: raiseParseErr(p, tokToStr[tok])
  558. proc parseSexp(p: var SexpParser): SexpNode =
  559. ## Parses SEXP from a SEXP Parser `p`.
  560. case p.tok
  561. of tkString:
  562. # we capture 'p.a' here, so we need to give it a fresh buffer afterwards:
  563. result = newSStringMove(p.a)
  564. p.a = ""
  565. discard getTok(p)
  566. of tkInt:
  567. result = newSInt(parseBiggestInt(p.a))
  568. discard getTok(p)
  569. of tkFloat:
  570. result = newSFloat(parseFloat(p.a))
  571. discard getTok(p)
  572. of tkNil:
  573. result = newSNil()
  574. discard getTok(p)
  575. of tkSymbol:
  576. result = newSSymbolMove(p.a)
  577. p.a = ""
  578. discard getTok(p)
  579. of tkParensLe:
  580. result = newSList()
  581. discard getTok(p)
  582. while p.tok notin {tkParensRi, tkDot}:
  583. result.add(parseSexp(p))
  584. if p.tok != tkSpace: break
  585. discard getTok(p)
  586. if p.tok == tkDot:
  587. eat(p, tkDot)
  588. eat(p, tkSpace)
  589. result.add(parseSexp(p))
  590. result = newSCons(result[0], result[1])
  591. eat(p, tkParensRi)
  592. of tkSpace, tkDot, tkError, tkParensRi, tkEof:
  593. raiseParseErr(p, "(")
  594. proc open*(my: var SexpParser, input: Stream) =
  595. ## initializes the parser with an input stream.
  596. lexbase.open(my, input)
  597. my.kind = sexpError
  598. my.a = ""
  599. proc parseSexp*(s: Stream): SexpNode =
  600. ## Parses from a buffer `s` into a `SexpNode`.
  601. var p: SexpParser
  602. p.open(s)
  603. discard getTok(p) # read first token
  604. result = p.parseSexp()
  605. p.close()
  606. proc parseSexp*(buffer: string): SexpNode =
  607. ## Parses Sexp from `buffer`.
  608. result = parseSexp(newStringStream(buffer))
  609. when isMainModule:
  610. let testSexp = parseSexp("""(1 (98 2) nil (2) foobar "foo" 9.234)""")
  611. assert(testSexp[0].getNum == 1)
  612. assert(testSexp[1][0].getNum == 98)
  613. assert(testSexp[2].getElems == @[])
  614. assert(testSexp[4].getSymbol == "foobar")
  615. assert(testSexp[5].getStr == "foo")
  616. let alist = parseSexp("""((1 . 2) (2 . "foo"))""")
  617. assert(alist[0].getCons.car.getNum == 1)
  618. assert(alist[0].getCons.cdr.getNum == 2)
  619. assert(alist[1].getCons.cdr.getStr == "foo")
  620. # Generator:
  621. var j = convertSexp([true, false, "foobar", [1, 2, "baz"]])
  622. assert($j == """(t nil "foobar" (1 2 "baz"))""")