sexp.nim 18 KB

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