parseutils.nim 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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. ## This module contains helpers for parsing tokens, numbers, integers, floats,
  10. ## identifiers, etc.
  11. ##
  12. ## To unpack raw bytes look at the `streams <streams.html>`_ module.
  13. ##
  14. ##
  15. ## .. code-block::
  16. ## import parseutils
  17. ##
  18. ## let logs = @["2019-01-10: OK_", "2019-01-11: FAIL_", "2019-01: aaaa"]
  19. ##
  20. ## for log in logs:
  21. ## var res: string
  22. ## if parseUntil(log, res, ':') == 10: # YYYY-MM-DD == 10
  23. ## echo res & " - " & captureBetween(log, ' ', '_')
  24. ## # => 2019-01-10 - OK
  25. ##
  26. ##
  27. ## .. code-block::
  28. ## import parseutils
  29. ## from strutils import Digits, parseInt
  30. ##
  31. ## let userInput1 = "2019 school start"
  32. ## let userInput2 = "3 years back"
  33. ##
  34. ## let startYear = input1[0..skipWhile(input1, Digits)-1] # 2019
  35. ## let yearsBack = input2[0..skipWhile(input2, Digits)-1] # 3
  36. ##
  37. ## echo "Examination is in " & $(parseInt(startYear) + parseInt(yearsBack))
  38. ##
  39. ##
  40. ## **See also:**
  41. ## * `strutils module<strutils.html>`_ for combined and identical parsing proc's
  42. ## * `json module<json.html>`_ for a JSON parser
  43. ## * `parsecfg module<parsecfg.html>`_ for a configuration file parser
  44. ## * `parsecsv module<parsecsv.html>`_ for a simple CSV (comma separated value) parser
  45. ## * `parseopt module<parseopt.html>`_ for a command line parser
  46. ## * `parsexml module<parsexml.html>`_ for a XML / HTML parser
  47. ## * `other parsers<lib.html#pure-libraries-parsers>`_ for other parsers
  48. {.deadCodeElim: on.} # dce option deprecated
  49. {.push debugger: off.} # the user does not want to trace a part
  50. # of the standard library!
  51. include "system/inclrtl"
  52. const
  53. Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
  54. IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
  55. IdentStartChars = {'a'..'z', 'A'..'Z', '_'}
  56. ## copied from strutils
  57. proc toLower(c: char): char {.inline.} =
  58. result = if c in {'A'..'Z'}: chr(ord(c)-ord('A')+ord('a')) else: c
  59. proc parseBin*[T: SomeInteger](s: string, number: var T, start = 0,
  60. maxLen = 0): int {.noSideEffect.} =
  61. ## Parses a binary number and stores its value in ``number``.
  62. ##
  63. ## Returns the number of the parsed characters or 0 in case of an error.
  64. ## If error, the value of ``number`` is not changed.
  65. ##
  66. ## If ``maxLen == 0``, the parsing continues until the first non-bin character
  67. ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
  68. ## are parsed starting from the ``start`` position.
  69. ##
  70. ## It does not check for overflow. If the value represented by the string is
  71. ## too big to fit into ``number``, only the value of last fitting characters
  72. ## will be stored in ``number`` without producing an error.
  73. runnableExamples:
  74. var num: int
  75. doAssert parseBin("0100_1110_0110_1001_1110_1101", num) == 29
  76. doAssert num == 5138925
  77. doAssert parseBin("3", num) == 0
  78. var num8: int8
  79. doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8) == 32
  80. doAssert num8 == 0b1110_1101'i8
  81. doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8, 3, 9) == 9
  82. doAssert num8 == 0b0100_1110'i8
  83. var num8u: uint8
  84. doAssert parseBin("0b_0100_1110_0110_1001_1110_1101", num8u) == 32
  85. doAssert num8u == 237
  86. var num64: int64
  87. doAssert parseBin("0100111001101001111011010100111001101001", num64) == 40
  88. doAssert num64 == 336784608873
  89. var i = start
  90. var output = T(0)
  91. var foundDigit = false
  92. let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
  93. if i + 1 < last and s[i] == '0' and (s[i+1] in {'b', 'B'}): inc(i, 2)
  94. while i < last:
  95. case s[i]
  96. of '_': discard
  97. of '0'..'1':
  98. output = output shl 1 or T(ord(s[i]) - ord('0'))
  99. foundDigit = true
  100. else: break
  101. inc(i)
  102. if foundDigit:
  103. number = output
  104. result = i - start
  105. proc parseOct*[T: SomeInteger](s: string, number: var T, start = 0,
  106. maxLen = 0): int {.noSideEffect.} =
  107. ## Parses an octal number and stores its value in ``number``.
  108. ##
  109. ## Returns the number of the parsed characters or 0 in case of an error.
  110. ## If error, the value of ``number`` is not changed.
  111. ##
  112. ## If ``maxLen == 0``, the parsing continues until the first non-oct character
  113. ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
  114. ## are parsed starting from the ``start`` position.
  115. ##
  116. ## It does not check for overflow. If the value represented by the string is
  117. ## too big to fit into ``number``, only the value of last fitting characters
  118. ## will be stored in ``number`` without producing an error.
  119. runnableExamples:
  120. var num: int
  121. doAssert parseOct("0o23464755", num) == 10
  122. doAssert num == 5138925
  123. doAssert parseOct("8", num) == 0
  124. var num8: int8
  125. doAssert parseOct("0o_1464_755", num8) == 11
  126. doAssert num8 == -19
  127. doAssert parseOct("0o_1464_755", num8, 3, 3) == 3
  128. doAssert num8 == 102
  129. var num8u: uint8
  130. doAssert parseOct("1464755", num8u) == 7
  131. doAssert num8u == 237
  132. var num64: int64
  133. doAssert parseOct("2346475523464755", num64) == 16
  134. doAssert num64 == 86216859871725
  135. var i = start
  136. var output = T(0)
  137. var foundDigit = false
  138. let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
  139. if i + 1 < last and s[i] == '0' and (s[i+1] in {'o', 'O'}): inc(i, 2)
  140. while i < last:
  141. case s[i]
  142. of '_': discard
  143. of '0'..'7':
  144. output = output shl 3 or T(ord(s[i]) - ord('0'))
  145. foundDigit = true
  146. else: break
  147. inc(i)
  148. if foundDigit:
  149. number = output
  150. result = i - start
  151. proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0,
  152. maxLen = 0): int {.noSideEffect.} =
  153. ## Parses a hexadecimal number and stores its value in ``number``.
  154. ##
  155. ## Returns the number of the parsed characters or 0 in case of an error.
  156. ## If error, the value of ``number`` is not changed.
  157. ##
  158. ## If ``maxLen == 0``, the parsing continues until the first non-hex character
  159. ## or to the end of the string. Otherwise, no more than ``maxLen`` characters
  160. ## are parsed starting from the ``start`` position.
  161. ##
  162. ## It does not check for overflow. If the value represented by the string is
  163. ## too big to fit into ``number``, only the value of last fitting characters
  164. ## will be stored in ``number`` without producing an error.
  165. runnableExamples:
  166. var num: int
  167. doAssert parseHex("4E_69_ED", num) == 8
  168. doAssert num == 5138925
  169. doAssert parseHex("X", num) == 0
  170. doAssert parseHex("#ABC", num) == 4
  171. var num8: int8
  172. doAssert parseHex("0x_4E_69_ED", num8) == 11
  173. doAssert num8 == 0xED'i8
  174. doAssert parseHex("0x_4E_69_ED", num8, 3, 2) == 2
  175. doAssert num8 == 0x4E'i8
  176. var num8u: uint8
  177. doAssert parseHex("0x_4E_69_ED", num8u) == 11
  178. doAssert num8u == 237
  179. var num64: int64
  180. doAssert parseHex("4E69ED4E69ED", num64) == 12
  181. doAssert num64 == 86216859871725
  182. var i = start
  183. var output = T(0)
  184. var foundDigit = false
  185. let last = min(s.len, if maxLen == 0: s.len else: i + maxLen)
  186. if i + 1 < last and s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
  187. elif i < last and s[i] == '#': inc(i)
  188. while i < last:
  189. case s[i]
  190. of '_': discard
  191. of '0'..'9':
  192. output = output shl 4 or T(ord(s[i]) - ord('0'))
  193. foundDigit = true
  194. of 'a'..'f':
  195. output = output shl 4 or T(ord(s[i]) - ord('a') + 10)
  196. foundDigit = true
  197. of 'A'..'F':
  198. output = output shl 4 or T(ord(s[i]) - ord('A') + 10)
  199. foundDigit = true
  200. else: break
  201. inc(i)
  202. if foundDigit:
  203. number = output
  204. result = i - start
  205. proc parseIdent*(s: string, ident: var string, start = 0): int =
  206. ## Parses an identifier and stores it in ``ident``. Returns
  207. ## the number of the parsed characters or 0 in case of an error.
  208. runnableExamples:
  209. var res: string
  210. doAssert parseIdent("Hello World", res, 0) == 5
  211. doAssert res == "Hello"
  212. doAssert parseIdent("Hello World", res, 1) == 4
  213. doAssert res == "ello"
  214. doAssert parseIdent("Hello World", res, 6) == 5
  215. doAssert res == "World"
  216. var i = start
  217. if i < s.len and s[i] in IdentStartChars:
  218. inc(i)
  219. while i < s.len and s[i] in IdentChars: inc(i)
  220. ident = substr(s, start, i-1)
  221. result = i-start
  222. proc parseIdent*(s: string, start = 0): string =
  223. ## Parses an identifier and returns it or an empty string in
  224. ## case of an error.
  225. runnableExamples:
  226. doAssert parseIdent("Hello World", 0) == "Hello"
  227. doAssert parseIdent("Hello World", 1) == "ello"
  228. doAssert parseIdent("Hello World", 5) == ""
  229. doAssert parseIdent("Hello World", 6) == "World"
  230. result = ""
  231. var i = start
  232. if i < s.len and s[i] in IdentStartChars:
  233. inc(i)
  234. while i < s.len and s[i] in IdentChars: inc(i)
  235. result = substr(s, start, i-1)
  236. proc skipWhitespace*(s: string, start = 0): int {.inline.} =
  237. ## Skips the whitespace starting at ``s[start]``. Returns the number of
  238. ## skipped characters.
  239. runnableExamples:
  240. doAssert skipWhitespace("Hello World", 0) == 0
  241. doAssert skipWhitespace(" Hello World", 0) == 1
  242. doAssert skipWhitespace("Hello World", 5) == 1
  243. doAssert skipWhitespace("Hello World", 5) == 2
  244. while start+result < s.len and s[start+result] in Whitespace: inc(result)
  245. proc skip*(s, token: string, start = 0): int {.inline.} =
  246. ## Skips the `token` starting at ``s[start]``. Returns the length of `token`
  247. ## or 0 if there was no `token` at ``s[start]``.
  248. runnableExamples:
  249. doAssert skip("2019-01-22", "2019", 0) == 4
  250. doAssert skip("2019-01-22", "19", 0) == 0
  251. doAssert skip("2019-01-22", "19", 2) == 2
  252. doAssert skip("CAPlow", "CAP", 0) == 3
  253. doAssert skip("CAPlow", "cap", 0) == 0
  254. while start+result < s.len and result < token.len and
  255. s[result+start] == token[result]:
  256. inc(result)
  257. if result != token.len: result = 0
  258. proc skipIgnoreCase*(s, token: string, start = 0): int =
  259. ## Same as `skip` but case is ignored for token matching.
  260. runnableExamples:
  261. doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3
  262. doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3
  263. while start+result < s.len and result < token.len and
  264. toLower(s[result+start]) == toLower(token[result]): inc(result)
  265. if result != token.len: result = 0
  266. proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} =
  267. ## Skips all characters until one char from the set `until` is found
  268. ## or the end is reached.
  269. ## Returns number of characters skipped.
  270. runnableExamples:
  271. doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1
  272. doAssert skipUntil("Hello World", {'W'}, 0) == 6
  273. doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6
  274. while start+result < s.len and s[result+start] notin until: inc(result)
  275. proc skipUntil*(s: string, until: char, start = 0): int {.inline.} =
  276. ## Skips all characters until the char `until` is found
  277. ## or the end is reached.
  278. ## Returns number of characters skipped.
  279. runnableExamples:
  280. doAssert skipUntil("Hello World", 'o', 0) == 4
  281. doAssert skipUntil("Hello World", 'o', 4) == 0
  282. doAssert skipUntil("Hello World", 'W', 0) == 6
  283. doAssert skipUntil("Hello World", 'w', 0) == 11
  284. while start+result < s.len and s[result+start] != until: inc(result)
  285. proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} =
  286. ## Skips all characters while one char from the set `token` is found.
  287. ## Returns number of characters skipped.
  288. runnableExamples:
  289. doAssert skipWhile("Hello World", {'H', 'e'}) == 2
  290. doAssert skipWhile("Hello World", {'e'}) == 0
  291. doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3
  292. while start+result < s.len and s[result+start] in toSkip: inc(result)
  293. proc parseUntil*(s: string, token: var string, until: set[char],
  294. start = 0): int {.inline.} =
  295. ## Parses a token and stores it in ``token``. Returns
  296. ## the number of the parsed characters or 0 in case of an error. A token
  297. ## consists of the characters notin `until`.
  298. runnableExamples:
  299. var myToken: string
  300. doAssert parseUntil("Hello World", myToken, {'W', 'o', 'r'}) == 4
  301. doAssert myToken == "Hell"
  302. doAssert parseUntil("Hello World", myToken, {'W', 'r'}) == 6
  303. doAssert myToken == "Hello "
  304. doAssert parseUntil("Hello World", myToken, {'W', 'r'}, 3) == 3
  305. doAssert myToken == "lo "
  306. var i = start
  307. while i < s.len and s[i] notin until: inc(i)
  308. result = i-start
  309. token = substr(s, start, i-1)
  310. proc parseUntil*(s: string, token: var string, until: char,
  311. start = 0): int {.inline.} =
  312. ## Parses a token and stores it in ``token``. Returns
  313. ## the number of the parsed characters or 0 in case of an error. A token
  314. ## consists of any character that is not the `until` character.
  315. runnableExamples:
  316. var myToken: string
  317. doAssert parseUntil("Hello World", myToken, 'W') == 6
  318. doAssert myToken == "Hello "
  319. doAssert parseUntil("Hello World", myToken, 'o') == 4
  320. doAssert myToken == "Hell"
  321. doAssert parseUntil("Hello World", myToken, 'o', 2) == 2
  322. doAssert myToken == "ll"
  323. var i = start
  324. while i < s.len and s[i] != until: inc(i)
  325. result = i-start
  326. token = substr(s, start, i-1)
  327. proc parseUntil*(s: string, token: var string, until: string,
  328. start = 0): int {.inline.} =
  329. ## Parses a token and stores it in ``token``. Returns
  330. ## the number of the parsed characters or 0 in case of an error. A token
  331. ## consists of any character that comes before the `until` token.
  332. runnableExamples:
  333. var myToken: string
  334. doAssert parseUntil("Hello World", myToken, "Wor") == 6
  335. doAssert myToken == "Hello "
  336. doAssert parseUntil("Hello World", myToken, "Wor", 2) == 4
  337. doAssert myToken == "llo "
  338. if until.len == 0:
  339. token.setLen(0)
  340. return 0
  341. var i = start
  342. while i < s.len:
  343. if s[i] == until[0]:
  344. var u = 1
  345. while i+u < s.len and u < until.len and s[i+u] == until[u]:
  346. inc u
  347. if u >= until.len: break
  348. inc(i)
  349. result = i-start
  350. token = substr(s, start, i-1)
  351. proc parseWhile*(s: string, token: var string, validChars: set[char],
  352. start = 0): int {.inline.} =
  353. ## Parses a token and stores it in ``token``. Returns
  354. ## the number of the parsed characters or 0 in case of an error. A token
  355. ## consists of the characters in `validChars`.
  356. runnableExamples:
  357. var myToken: string
  358. doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 0) == 0
  359. doAssert myToken.len() == 0
  360. doAssert parseWhile("Hello World", myToken, {'W', 'o', 'r'}, 6) == 3
  361. doAssert myToken == "Wor"
  362. var i = start
  363. while i < s.len and s[i] in validChars: inc(i)
  364. result = i-start
  365. token = substr(s, start, i-1)
  366. proc captureBetween*(s: string, first: char, second = '\0', start = 0): string =
  367. ## Finds the first occurrence of ``first``, then returns everything from there
  368. ## up to ``second`` (if ``second`` is '\0', then ``first`` is used).
  369. runnableExamples:
  370. doAssert captureBetween("Hello World", 'e') == "llo World"
  371. doAssert captureBetween("Hello World", 'e', 'r') == "llo Wo"
  372. doAssert captureBetween("Hello World", 'l', start = 6) == "d"
  373. var i = skipUntil(s, first, start)+1+start
  374. result = ""
  375. discard s.parseUntil(result, if second == '\0': first else: second, i)
  376. proc integerOutOfRangeError() {.noinline.} =
  377. raise newException(ValueError, "Parsed integer outside of valid range")
  378. # See #6752
  379. when defined(js):
  380. {.push overflowChecks: off.}
  381. proc rawParseInt(s: string, b: var BiggestInt, start = 0): int =
  382. var
  383. sign: BiggestInt = -1
  384. i = start
  385. if i < s.len:
  386. if s[i] == '+': inc(i)
  387. elif s[i] == '-':
  388. inc(i)
  389. sign = 1
  390. if i < s.len and s[i] in {'0'..'9'}:
  391. b = 0
  392. while i < s.len and s[i] in {'0'..'9'}:
  393. let c = ord(s[i]) - ord('0')
  394. if b >= (low(BiggestInt) + c) div 10:
  395. b = b * 10 - c
  396. else:
  397. integerOutOfRangeError()
  398. inc(i)
  399. while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
  400. if sign == -1 and b == low(BiggestInt):
  401. integerOutOfRangeError()
  402. else:
  403. b = b * sign
  404. result = i - start
  405. when defined(js):
  406. {.pop.} # overflowChecks: off
  407. proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {.
  408. rtl, extern: "npuParseBiggestInt", noSideEffect, raises: [ValueError].} =
  409. ## Parses an integer starting at `start` and stores the value into `number`.
  410. ## Result is the number of processed chars or 0 if there is no integer.
  411. ## `ValueError` is raised if the parsed integer is out of the valid range.
  412. runnableExamples:
  413. var res: BiggestInt
  414. doAssert parseBiggestInt("9223372036854775807", res, 0) == 19
  415. doAssert res == 9223372036854775807
  416. var res: BiggestInt
  417. # use 'res' for exception safety (don't write to 'number' in case of an
  418. # overflow exception):
  419. result = rawParseInt(s, res, start)
  420. if result != 0:
  421. number = res
  422. proc parseInt*(s: string, number: var int, start = 0): int {.
  423. rtl, extern: "npuParseInt", noSideEffect, raises: [ValueError].} =
  424. ## Parses an integer starting at `start` and stores the value into `number`.
  425. ## Result is the number of processed chars or 0 if there is no integer.
  426. ## `ValueError` is raised if the parsed integer is out of the valid range.
  427. runnableExamples:
  428. var res: int
  429. doAssert parseInt("2019", res, 0) == 4
  430. doAssert res == 2019
  431. doAssert parseInt("2019", res, 2) == 2
  432. doAssert res == 19
  433. var res: BiggestInt
  434. result = parseBiggestInt(s, res, start)
  435. when sizeof(int) <= 4:
  436. if res < low(int) or res > high(int):
  437. integerOutOfRangeError()
  438. if result != 0:
  439. number = int(res)
  440. proc parseSaturatedNatural*(s: string, b: var int, start = 0): int {.
  441. raises: [].} =
  442. ## Parses a natural number into ``b``. This cannot raise an overflow
  443. ## error. ``high(int)`` is returned for an overflow.
  444. ## The number of processed character is returned.
  445. ## This is usually what you really want to use instead of `parseInt`:idx:.
  446. runnableExamples:
  447. var res = 0
  448. discard parseSaturatedNatural("848", res)
  449. doAssert res == 848
  450. var i = start
  451. if i < s.len and s[i] == '+': inc(i)
  452. if i < s.len and s[i] in {'0'..'9'}:
  453. b = 0
  454. while i < s.len and s[i] in {'0'..'9'}:
  455. let c = ord(s[i]) - ord('0')
  456. if b <= (high(int) - c) div 10:
  457. b = b * 10 + c
  458. else:
  459. b = high(int)
  460. inc(i)
  461. while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
  462. result = i - start
  463. proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int =
  464. var
  465. res = 0.BiggestUInt
  466. prev = 0.BiggestUInt
  467. i = start
  468. if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}:
  469. integerOutOfRangeError()
  470. if i < s.len and s[i] == '+': inc(i) # Allow
  471. if i < s.len and s[i] in {'0'..'9'}:
  472. b = 0
  473. while i < s.len and s[i] in {'0'..'9'}:
  474. prev = res
  475. res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt
  476. if prev > res:
  477. integerOutOfRangeError()
  478. inc(i)
  479. while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored
  480. b = res
  481. result = i - start
  482. proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {.
  483. rtl, extern: "npuParseBiggestUInt", noSideEffect, raises: [ValueError].} =
  484. ## Parses an unsigned integer starting at `start` and stores the value
  485. ## into `number`.
  486. ## `ValueError` is raised if the parsed integer is out of the valid range.
  487. runnableExamples:
  488. var res: BiggestUInt
  489. doAssert parseBiggestUInt("12", res, 0) == 2
  490. doAssert res == 12
  491. doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19
  492. doAssert res == 1111111111111111111'u64
  493. var res: BiggestUInt
  494. # use 'res' for exception safety (don't write to 'number' in case of an
  495. # overflow exception):
  496. result = rawParseUInt(s, res, start)
  497. if result != 0:
  498. number = res
  499. proc parseUInt*(s: string, number: var uint, start = 0): int {.
  500. rtl, extern: "npuParseUInt", noSideEffect, raises: [ValueError].} =
  501. ## Parses an unsigned integer starting at `start` and stores the value
  502. ## into `number`.
  503. ## `ValueError` is raised if the parsed integer is out of the valid range.
  504. runnableExamples:
  505. var res: uint
  506. doAssert parseUInt("3450", res) == 4
  507. doAssert res == 3450
  508. doAssert parseUInt("3450", res, 2) == 2
  509. doAssert res == 50
  510. var res: BiggestUInt
  511. result = parseBiggestUInt(s, res, start)
  512. when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4:
  513. if res > 0xFFFF_FFFF'u64:
  514. integerOutOfRangeError()
  515. if result != 0:
  516. number = uint(res)
  517. proc parseBiggestFloat*(s: string, number: var BiggestFloat, start = 0): int {.
  518. magic: "ParseBiggestFloat", importc: "nimParseBiggestFloat", noSideEffect.}
  519. ## Parses a float starting at `start` and stores the value into `number`.
  520. ## Result is the number of processed chars or 0 if a parsing error
  521. ## occurred.
  522. proc parseFloat*(s: string, number: var float, start = 0): int {.
  523. rtl, extern: "npuParseFloat", noSideEffect.} =
  524. ## Parses a float starting at `start` and stores the value into `number`.
  525. ## Result is the number of processed chars or 0 if there occurred a parsing
  526. ## error.
  527. runnableExamples:
  528. var res: float
  529. doAssert parseFloat("32", res, 0) == 2
  530. doAssert res == 32.0
  531. doAssert parseFloat("32.57", res, 0) == 5
  532. doAssert res == 32.57
  533. doAssert parseFloat("32.57", res, 3) == 2
  534. doAssert res == 57.00
  535. var bf: BiggestFloat
  536. result = parseBiggestFloat(s, bf, start)
  537. if result != 0:
  538. number = bf
  539. type
  540. InterpolatedKind* = enum ## Describes for `interpolatedFragments`
  541. ## which part of the interpolated string is
  542. ## yielded; for example in "str$$$var${expr}"
  543. ikStr, ## ``str`` part of the interpolated string
  544. ikDollar, ## escaped ``$`` part of the interpolated string
  545. ikVar, ## ``var`` part of the interpolated string
  546. ikExpr ## ``expr`` part of the interpolated string
  547. iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind,
  548. value: string] =
  549. ## Tokenizes the string `s` into substrings for interpolation purposes.
  550. ##
  551. ## Example:
  552. ##
  553. ## .. code-block:: nim
  554. ## for k, v in interpolatedFragments(" $this is ${an example} $$"):
  555. ## echo "(", k, ", \"", v, "\")"
  556. ##
  557. ## Results in:
  558. ##
  559. ## .. code-block:: nim
  560. ## (ikString, " ")
  561. ## (ikExpr, "this")
  562. ## (ikString, " is ")
  563. ## (ikExpr, "an example")
  564. ## (ikString, " ")
  565. ## (ikDollar, "$")
  566. var i = 0
  567. var kind: InterpolatedKind
  568. while true:
  569. var j = i
  570. if j < s.len and s[j] == '$':
  571. if j+1 < s.len and s[j+1] == '{':
  572. inc j, 2
  573. var nesting = 0
  574. block curlies:
  575. while j < s.len:
  576. case s[j]
  577. of '{': inc nesting
  578. of '}':
  579. if nesting == 0:
  580. inc j
  581. break curlies
  582. dec nesting
  583. else: discard
  584. inc j
  585. raise newException(ValueError,
  586. "Expected closing '}': " & substr(s, i, s.high))
  587. inc i, 2 # skip ${
  588. kind = ikExpr
  589. elif j+1 < s.len and s[j+1] in IdentStartChars:
  590. inc j, 2
  591. while j < s.len and s[j] in IdentChars: inc(j)
  592. inc i # skip $
  593. kind = ikVar
  594. elif j+1 < s.len and s[j+1] == '$':
  595. inc j, 2
  596. inc i # skip $
  597. kind = ikDollar
  598. else:
  599. raise newException(ValueError,
  600. "Unable to parse a variable name at " & substr(s, i, s.high))
  601. else:
  602. while j < s.len and s[j] != '$': inc j
  603. kind = ikStr
  604. if j > i:
  605. # do not copy the trailing } for ikExpr:
  606. yield (kind, substr(s, i, j-1-ord(kind == ikExpr)))
  607. else:
  608. break
  609. i = j
  610. when isMainModule:
  611. import sequtils
  612. let input = "$test{} $this is ${an{ example}} "
  613. let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"),
  614. (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")]
  615. doAssert toSeq(interpolatedFragments(input)) == expected
  616. var value = 0
  617. discard parseHex("0x38", value)
  618. doAssert value == 56
  619. value = -1
  620. doAssert(parseSaturatedNatural("848", value) == 3)
  621. doAssert value == 848
  622. value = -1
  623. discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value)
  624. doAssert value == high(int)
  625. value = -1
  626. discard parseSaturatedNatural("9223372036854775808", value)
  627. doAssert value == high(int)
  628. value = -1
  629. discard parseSaturatedNatural("9223372036854775807", value)
  630. doAssert value == high(int)
  631. value = -1
  632. discard parseSaturatedNatural("18446744073709551616", value)
  633. doAssert value == high(int)
  634. value = -1
  635. discard parseSaturatedNatural("18446744073709551615", value)
  636. doAssert value == high(int)
  637. value = -1
  638. doAssert(parseSaturatedNatural("1_000_000", value) == 9)
  639. doAssert value == 1_000_000
  640. var i64Value: int64
  641. discard parseBiggestInt("9223372036854775807", i64Value)
  642. doAssert i64Value == 9223372036854775807
  643. {.pop.}