strutils.nim 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208
  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 system module defines several common functions for working with strings,
  10. ## such as:
  11. ## * ``$`` for converting other data-types to strings
  12. ## * ``&`` for string concatenation
  13. ## * ``add`` for adding a new character or a string to the existing one
  14. ## * ``in`` (alias for ``contains``) and ``notin`` for checking if a character
  15. ## is in a string
  16. ##
  17. ## This module builds upon that, providing additional functionality in form of
  18. ## procedures, iterators and templates for strings.
  19. ##
  20. ## .. code-block::
  21. ## import strutils
  22. ##
  23. ## let
  24. ## numbers = @[867, 5309]
  25. ## multiLineString = "first line\nsecond line\nthird line"
  26. ##
  27. ## let jenny = numbers.join("-")
  28. ## assert jenny == "867-5309"
  29. ##
  30. ## assert splitLines(multiLineString) ==
  31. ## @["first line", "second line", "third line"]
  32. ## assert split(multiLineString) == @["first", "line", "second",
  33. ## "line", "third", "line"]
  34. ## assert indent(multiLineString, 4) ==
  35. ## " first line\n second line\n third line"
  36. ## assert 'z'.repeat(5) == "zzzzz"
  37. ##
  38. ## The chaining of functions is possible thanks to the
  39. ## `method call syntax<manual.html#procedures-method-call-syntax>`_:
  40. ##
  41. ## .. code-block::
  42. ## import strutils
  43. ## from sequtils import map
  44. ##
  45. ## let jenny = "867-5309"
  46. ## assert jenny.split('-').map(parseInt) == @[867, 5309]
  47. ##
  48. ## assert "Beetlejuice".indent(1).repeat(3).strip ==
  49. ## "Beetlejuice Beetlejuice Beetlejuice"
  50. ##
  51. ## This module is available for the `JavaScript target
  52. ## <backends.html#backends-the-javascript-target>`_.
  53. ##
  54. ## ----
  55. ##
  56. ## **See also:**
  57. ## * `strformat module<strformat.html>`_ for string interpolation and formatting
  58. ## * `unicode module<unicode.html>`_ for Unicode UTF-8 handling
  59. ## * `sequtils module<sequtils.html>`_ for operations on container
  60. ## types (including strings)
  61. ## * `parseutils module<parseutils.html>`_ for lower-level parsing of tokens,
  62. ## numbers, identifiers, etc.
  63. ## * `parseopt module<parseopt.html>`_ for command-line parsing
  64. ## * `strtabs module<strtabs.html>`_ for efficient hash tables
  65. ## (dictionaries, in some programming languages) mapping from strings to strings
  66. ## * `pegs module<pegs.html>`_ for PEG (Parsing Expression Grammar) support
  67. ## * `ropes module<ropes.html>`_ for rope data type, which can represent very
  68. ## long strings efficiently
  69. ## * `re module<re.html>`_ for regular expression (regex) support
  70. ## * `strscans<strscans.html>`_ for ``scanf`` and ``scanp`` macros, which offer
  71. ## easier substring extraction than regular expressions
  72. import parseutils
  73. from math import pow, floor, log10
  74. from algorithm import reverse
  75. import macros # for `parseEnum`
  76. when defined(nimVmExportFixed):
  77. from unicode import toLower, toUpper
  78. export toLower, toUpper
  79. include "system/inclrtl"
  80. import std/private/since
  81. const
  82. Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'}
  83. ## All the characters that count as whitespace (space, tab, vertical tab,
  84. ## carriage return, new line, form feed)
  85. Letters* = {'A'..'Z', 'a'..'z'}
  86. ## the set of letters
  87. Digits* = {'0'..'9'}
  88. ## the set of digits
  89. HexDigits* = {'0'..'9', 'A'..'F', 'a'..'f'}
  90. ## the set of hexadecimal digits
  91. IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'}
  92. ## the set of characters an identifier can consist of
  93. IdentStartChars* = {'a'..'z', 'A'..'Z', '_'}
  94. ## the set of characters an identifier can start with
  95. Newlines* = {'\13', '\10'}
  96. ## the set of characters a newline terminator can start with (carriage
  97. ## return, line feed)
  98. AllChars* = {'\x00'..'\xFF'}
  99. ## A set with all the possible characters.
  100. ##
  101. ## Not very useful by its own, you can use it to create *inverted* sets to
  102. ## make the `find proc<#find,string,set[char],Natural,int>`_
  103. ## find **invalid** characters in strings. Example:
  104. ##
  105. ## .. code-block:: nim
  106. ## let invalid = AllChars - Digits
  107. ## doAssert "01234".find(invalid) == -1
  108. ## doAssert "01A34".find(invalid) == 2
  109. proc isAlphaAscii*(c: char): bool {.noSideEffect,
  110. rtl, extern: "nsuIsAlphaAsciiChar".} =
  111. ## Checks whether or not character `c` is alphabetical.
  112. ##
  113. ## This checks a-z, A-Z ASCII characters only.
  114. ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
  115. runnableExamples:
  116. doAssert isAlphaAscii('e') == true
  117. doAssert isAlphaAscii('E') == true
  118. doAssert isAlphaAscii('8') == false
  119. return c in Letters
  120. proc isAlphaNumeric*(c: char): bool {.noSideEffect,
  121. rtl, extern: "nsuIsAlphaNumericChar".} =
  122. ## Checks whether or not `c` is alphanumeric.
  123. ##
  124. ## This checks a-z, A-Z, 0-9 ASCII characters only.
  125. runnableExamples:
  126. doAssert isAlphaNumeric('n') == true
  127. doAssert isAlphaNumeric('8') == true
  128. doAssert isAlphaNumeric(' ') == false
  129. return c in Letters+Digits
  130. proc isDigit*(c: char): bool {.noSideEffect,
  131. rtl, extern: "nsuIsDigitChar".} =
  132. ## Checks whether or not `c` is a number.
  133. ##
  134. ## This checks 0-9 ASCII characters only.
  135. runnableExamples:
  136. doAssert isDigit('n') == false
  137. doAssert isDigit('8') == true
  138. return c in Digits
  139. proc isSpaceAscii*(c: char): bool {.noSideEffect,
  140. rtl, extern: "nsuIsSpaceAsciiChar".} =
  141. ## Checks whether or not `c` is a whitespace character.
  142. runnableExamples:
  143. doAssert isSpaceAscii('n') == false
  144. doAssert isSpaceAscii(' ') == true
  145. doAssert isSpaceAscii('\t') == true
  146. return c in Whitespace
  147. proc isLowerAscii*(c: char): bool {.noSideEffect,
  148. rtl, extern: "nsuIsLowerAsciiChar".} =
  149. ## Checks whether or not `c` is a lower case character.
  150. ##
  151. ## This checks ASCII characters only.
  152. ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
  153. ##
  154. ## See also:
  155. ## * `toLowerAscii proc<#toLowerAscii,char>`_
  156. runnableExamples:
  157. doAssert isLowerAscii('e') == true
  158. doAssert isLowerAscii('E') == false
  159. doAssert isLowerAscii('7') == false
  160. return c in {'a'..'z'}
  161. proc isUpperAscii*(c: char): bool {.noSideEffect,
  162. rtl, extern: "nsuIsUpperAsciiChar".} =
  163. ## Checks whether or not `c` is an upper case character.
  164. ##
  165. ## This checks ASCII characters only.
  166. ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
  167. ##
  168. ## See also:
  169. ## * `toUpperAscii proc<#toUpperAscii,char>`_
  170. runnableExamples:
  171. doAssert isUpperAscii('e') == false
  172. doAssert isUpperAscii('E') == true
  173. doAssert isUpperAscii('7') == false
  174. return c in {'A'..'Z'}
  175. proc toLowerAscii*(c: char): char {.noSideEffect,
  176. rtl, extern: "nsuToLowerAsciiChar".} =
  177. ## Returns the lower case version of character ``c``.
  178. ##
  179. ## This works only for the letters ``A-Z``. See `unicode.toLower
  180. ## <unicode.html#toLower,Rune>`_ for a version that works for any Unicode
  181. ## character.
  182. ##
  183. ## See also:
  184. ## * `isLowerAscii proc<#isLowerAscii,char>`_
  185. ## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string
  186. runnableExamples:
  187. doAssert toLowerAscii('A') == 'a'
  188. doAssert toLowerAscii('e') == 'e'
  189. if c in {'A'..'Z'}:
  190. result = chr(ord(c) + (ord('a') - ord('A')))
  191. else:
  192. result = c
  193. template toImpl(call) =
  194. result = newString(len(s))
  195. for i in 0..len(s) - 1:
  196. result[i] = call(s[i])
  197. proc toLowerAscii*(s: string): string {.noSideEffect,
  198. rtl, extern: "nsuToLowerAsciiStr".} =
  199. ## Converts string `s` into lower case.
  200. ##
  201. ## This works only for the letters ``A-Z``. See `unicode.toLower
  202. ## <unicode.html#toLower,string>`_ for a version that works for any Unicode
  203. ## character.
  204. ##
  205. ## See also:
  206. ## * `normalize proc<#normalize,string>`_
  207. runnableExamples:
  208. doAssert toLowerAscii("FooBar!") == "foobar!"
  209. toImpl toLowerAscii
  210. proc toUpperAscii*(c: char): char {.noSideEffect,
  211. rtl, extern: "nsuToUpperAsciiChar".} =
  212. ## Converts character `c` into upper case.
  213. ##
  214. ## This works only for the letters ``A-Z``. See `unicode.toUpper
  215. ## <unicode.html#toUpper,Rune>`_ for a version that works for any Unicode
  216. ## character.
  217. ##
  218. ## See also:
  219. ## * `isLowerAscii proc<#isLowerAscii,char>`_
  220. ## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string
  221. ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
  222. runnableExamples:
  223. doAssert toUpperAscii('a') == 'A'
  224. doAssert toUpperAscii('E') == 'E'
  225. if c in {'a'..'z'}:
  226. result = chr(ord(c) - (ord('a') - ord('A')))
  227. else:
  228. result = c
  229. proc toUpperAscii*(s: string): string {.noSideEffect,
  230. rtl, extern: "nsuToUpperAsciiStr".} =
  231. ## Converts string `s` into upper case.
  232. ##
  233. ## This works only for the letters ``A-Z``. See `unicode.toUpper
  234. ## <unicode.html#toUpper,string>`_ for a version that works for any Unicode
  235. ## character.
  236. ##
  237. ## See also:
  238. ## * `capitalizeAscii proc<#capitalizeAscii,string>`_
  239. runnableExamples:
  240. doAssert toUpperAscii("FooBar!") == "FOOBAR!"
  241. toImpl toUpperAscii
  242. proc capitalizeAscii*(s: string): string {.noSideEffect,
  243. rtl, extern: "nsuCapitalizeAscii".} =
  244. ## Converts the first character of string `s` into upper case.
  245. ##
  246. ## This works only for the letters ``A-Z``.
  247. ## Use `Unicode module<unicode.html>`_ for UTF-8 support.
  248. ##
  249. ## See also:
  250. ## * `toUpperAscii proc<#toUpperAscii,char>`_
  251. runnableExamples:
  252. doAssert capitalizeAscii("foo") == "Foo"
  253. doAssert capitalizeAscii("-bar") == "-bar"
  254. if s.len == 0: result = ""
  255. else: result = toUpperAscii(s[0]) & substr(s, 1)
  256. proc nimIdentNormalize*(s: string): string =
  257. ## Normalizes the string `s` as a Nim identifier.
  258. ##
  259. ## That means to convert to lower case and remove any '_' on all characters
  260. ## except first one.
  261. runnableExamples:
  262. doAssert nimIdentNormalize("Foo_bar") == "Foobar"
  263. result = newString(s.len)
  264. if s.len > 0:
  265. result[0] = s[0]
  266. var j = 1
  267. for i in 1..len(s) - 1:
  268. if s[i] in {'A'..'Z'}:
  269. result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
  270. inc j
  271. elif s[i] != '_':
  272. result[j] = s[i]
  273. inc j
  274. if j != s.len: setLen(result, j)
  275. proc normalize*(s: string): string {.noSideEffect,
  276. rtl, extern: "nsuNormalize".} =
  277. ## Normalizes the string `s`.
  278. ##
  279. ## That means to convert it to lower case and remove any '_'. This
  280. ## should NOT be used to normalize Nim identifier names.
  281. ##
  282. ## See also:
  283. ## * `toLowerAscii proc<#toLowerAscii,string>`_
  284. runnableExamples:
  285. doAssert normalize("Foo_bar") == "foobar"
  286. doAssert normalize("Foo Bar") == "foo bar"
  287. result = newString(s.len)
  288. var j = 0
  289. for i in 0..len(s) - 1:
  290. if s[i] in {'A'..'Z'}:
  291. result[j] = chr(ord(s[i]) + (ord('a') - ord('A')))
  292. inc j
  293. elif s[i] != '_':
  294. result[j] = s[i]
  295. inc j
  296. if j != s.len: setLen(result, j)
  297. proc cmpIgnoreCase*(a, b: string): int {.noSideEffect,
  298. rtl, extern: "nsuCmpIgnoreCase".} =
  299. ## Compares two strings in a case insensitive manner. Returns:
  300. ##
  301. ## | 0 if a == b
  302. ## | < 0 if a < b
  303. ## | > 0 if a > b
  304. runnableExamples:
  305. doAssert cmpIgnoreCase("FooBar", "foobar") == 0
  306. doAssert cmpIgnoreCase("bar", "Foo") < 0
  307. doAssert cmpIgnoreCase("Foo5", "foo4") > 0
  308. var i = 0
  309. var m = min(a.len, b.len)
  310. while i < m:
  311. result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i]))
  312. if result != 0: return
  313. inc(i)
  314. result = a.len - b.len
  315. {.push checks: off, line_trace: off.} # this is a hot-spot in the compiler!
  316. # thus we compile without checks here
  317. proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect,
  318. rtl, extern: "nsuCmpIgnoreStyle".} =
  319. ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It
  320. ## is just optimized to not allocate temporary strings. This should
  321. ## NOT be used to compare Nim identifier names.
  322. ## Use `macros.eqIdent<macros.html#eqIdent,string,string>`_ for that.
  323. ##
  324. ## Returns:
  325. ##
  326. ## | 0 if a == b
  327. ## | < 0 if a < b
  328. ## | > 0 if a > b
  329. runnableExamples:
  330. doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0
  331. doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0
  332. var i = 0
  333. var j = 0
  334. while true:
  335. while i < a.len and a[i] == '_': inc i
  336. while j < b.len and b[j] == '_': inc j
  337. var aa = if i < a.len: toLowerAscii(a[i]) else: '\0'
  338. var bb = if j < b.len: toLowerAscii(b[j]) else: '\0'
  339. result = ord(aa) - ord(bb)
  340. if result != 0: return result
  341. # the characters are identical:
  342. if i >= a.len:
  343. # both cursors at the end:
  344. if j >= b.len: return 0
  345. # not yet at the end of 'b':
  346. return -1
  347. elif j >= b.len:
  348. return 1
  349. inc i
  350. inc j
  351. {.pop.}
  352. # --------- Private templates for different split separators -----------
  353. proc substrEq(s: string, pos: int, substr: string): bool =
  354. var i = 0
  355. var length = substr.len
  356. while i < length and pos+i < s.len and s[pos+i] == substr[i]:
  357. inc i
  358. return i == length
  359. template stringHasSep(s: string, index: int, seps: set[char]): bool =
  360. s[index] in seps
  361. template stringHasSep(s: string, index: int, sep: char): bool =
  362. s[index] == sep
  363. template stringHasSep(s: string, index: int, sep: string): bool =
  364. s.substrEq(index, sep)
  365. template splitCommon(s, sep, maxsplit, sepLen) =
  366. ## Common code for split procs
  367. var last = 0
  368. var splits = maxsplit
  369. while last <= len(s):
  370. var first = last
  371. while last < len(s) and not stringHasSep(s, last, sep):
  372. inc(last)
  373. if splits == 0: last = len(s)
  374. yield substr(s, first, last-1)
  375. if splits == 0: break
  376. dec(splits)
  377. inc(last, sepLen)
  378. template oldSplit(s, seps, maxsplit) =
  379. var last = 0
  380. var splits = maxsplit
  381. assert(not ('\0' in seps))
  382. while last < len(s):
  383. while last < len(s) and s[last] in seps: inc(last)
  384. var first = last
  385. while last < len(s) and s[last] notin seps: inc(last)
  386. if first <= last-1:
  387. if splits == 0: last = len(s)
  388. yield substr(s, first, last-1)
  389. if splits == 0: break
  390. dec(splits)
  391. template accResult(iter: untyped) =
  392. result = @[]
  393. for x in iter: add(result, x)
  394. iterator split*(s: string, sep: char, maxsplit: int = -1): string =
  395. ## Splits the string `s` into substrings using a single separator.
  396. ##
  397. ## Substrings are separated by the character `sep`.
  398. ## The code:
  399. ##
  400. ## .. code-block:: nim
  401. ## for word in split(";;this;is;an;;example;;;", ';'):
  402. ## writeLine(stdout, word)
  403. ##
  404. ## Results in:
  405. ##
  406. ## .. code-block::
  407. ## ""
  408. ## ""
  409. ## "this"
  410. ## "is"
  411. ## "an"
  412. ## ""
  413. ## "example"
  414. ## ""
  415. ## ""
  416. ## ""
  417. ##
  418. ## See also:
  419. ## * `rsplit iterator<#rsplit.i,string,char,int>`_
  420. ## * `splitLines iterator<#splitLines.i,string>`_
  421. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  422. ## * `split proc<#split,string,char,int>`_
  423. splitCommon(s, sep, maxsplit, 1)
  424. iterator split*(s: string, seps: set[char] = Whitespace,
  425. maxsplit: int = -1): string =
  426. ## Splits the string `s` into substrings using a group of separators.
  427. ##
  428. ## Substrings are separated by a substring containing only `seps`.
  429. ##
  430. ## .. code-block:: nim
  431. ## for word in split("this\lis an\texample"):
  432. ## writeLine(stdout, word)
  433. ##
  434. ## ...generates this output:
  435. ##
  436. ## .. code-block::
  437. ## "this"
  438. ## "is"
  439. ## "an"
  440. ## "example"
  441. ##
  442. ## And the following code:
  443. ##
  444. ## .. code-block:: nim
  445. ## for word in split("this:is;an$example", {';', ':', '$'}):
  446. ## writeLine(stdout, word)
  447. ##
  448. ## ...produces the same output as the first example. The code:
  449. ##
  450. ## .. code-block:: nim
  451. ## let date = "2012-11-20T22:08:08.398990"
  452. ## let separators = {' ', '-', ':', 'T'}
  453. ## for number in split(date, separators):
  454. ## writeLine(stdout, number)
  455. ##
  456. ## ...results in:
  457. ##
  458. ## .. code-block::
  459. ## "2012"
  460. ## "11"
  461. ## "20"
  462. ## "22"
  463. ## "08"
  464. ## "08.398990"
  465. ##
  466. ## See also:
  467. ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_
  468. ## * `splitLines iterator<#splitLines.i,string>`_
  469. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  470. ## * `split proc<#split,string,set[char],int>`_
  471. splitCommon(s, seps, maxsplit, 1)
  472. iterator split*(s: string, sep: string, maxsplit: int = -1): string =
  473. ## Splits the string `s` into substrings using a string separator.
  474. ##
  475. ## Substrings are separated by the string `sep`.
  476. ## The code:
  477. ##
  478. ## .. code-block:: nim
  479. ## for word in split("thisDATAisDATAcorrupted", "DATA"):
  480. ## writeLine(stdout, word)
  481. ##
  482. ## Results in:
  483. ##
  484. ## .. code-block::
  485. ## "this"
  486. ## "is"
  487. ## "corrupted"
  488. ##
  489. ## See also:
  490. ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_
  491. ## * `splitLines iterator<#splitLines.i,string>`_
  492. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  493. ## * `split proc<#split,string,string,int>`_
  494. splitCommon(s, sep, maxsplit, sep.len)
  495. template rsplitCommon(s, sep, maxsplit, sepLen) =
  496. ## Common code for rsplit functions
  497. var
  498. last = s.len - 1
  499. first = last
  500. splits = maxsplit
  501. startPos = 0
  502. # go to -1 in order to get separators at the beginning
  503. while first >= -1:
  504. while first >= 0 and not stringHasSep(s, first, sep):
  505. dec(first)
  506. if splits == 0:
  507. # No more splits means set first to the beginning
  508. first = -1
  509. if first == -1:
  510. startPos = 0
  511. else:
  512. startPos = first + sepLen
  513. yield substr(s, startPos, last)
  514. if splits == 0: break
  515. dec(splits)
  516. dec(first)
  517. last = first
  518. iterator rsplit*(s: string, sep: char,
  519. maxsplit: int = -1): string =
  520. ## Splits the string `s` into substrings from the right using a
  521. ## string separator. Works exactly the same as `split iterator
  522. ## <#split.i,string,char,int>`_ except in reverse order.
  523. ##
  524. ## .. code-block:: nim
  525. ## for piece in "foo:bar".rsplit(':'):
  526. ## echo piece
  527. ##
  528. ## Results in:
  529. ##
  530. ## .. code-block:: nim
  531. ## "bar"
  532. ## "foo"
  533. ##
  534. ## Substrings are separated from the right by the char `sep`.
  535. ##
  536. ## See also:
  537. ## * `split iterator<#split.i,string,char,int>`_
  538. ## * `splitLines iterator<#splitLines.i,string>`_
  539. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  540. ## * `rsplit proc<#rsplit,string,char,int>`_
  541. rsplitCommon(s, sep, maxsplit, 1)
  542. iterator rsplit*(s: string, seps: set[char] = Whitespace,
  543. maxsplit: int = -1): string =
  544. ## Splits the string `s` into substrings from the right using a
  545. ## string separator. Works exactly the same as `split iterator
  546. ## <#split.i,string,char,int>`_ except in reverse order.
  547. ##
  548. ## .. code-block:: nim
  549. ## for piece in "foo bar".rsplit(WhiteSpace):
  550. ## echo piece
  551. ##
  552. ## Results in:
  553. ##
  554. ## .. code-block:: nim
  555. ## "bar"
  556. ## "foo"
  557. ##
  558. ## Substrings are separated from the right by the set of chars `seps`
  559. ##
  560. ## See also:
  561. ## * `split iterator<#split.i,string,set[char],int>`_
  562. ## * `splitLines iterator<#splitLines.i,string>`_
  563. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  564. ## * `rsplit proc<#rsplit,string,set[char],int>`_
  565. rsplitCommon(s, seps, maxsplit, 1)
  566. iterator rsplit*(s: string, sep: string, maxsplit: int = -1,
  567. keepSeparators: bool = false): string =
  568. ## Splits the string `s` into substrings from the right using a
  569. ## string separator. Works exactly the same as `split iterator
  570. ## <#split.i,string,string,int>`_ except in reverse order.
  571. ##
  572. ## .. code-block:: nim
  573. ## for piece in "foothebar".rsplit("the"):
  574. ## echo piece
  575. ##
  576. ## Results in:
  577. ##
  578. ## .. code-block:: nim
  579. ## "bar"
  580. ## "foo"
  581. ##
  582. ## Substrings are separated from the right by the string `sep`
  583. ##
  584. ## See also:
  585. ## * `split iterator<#split.i,string,string,int>`_
  586. ## * `splitLines iterator<#splitLines.i,string>`_
  587. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  588. ## * `rsplit proc<#rsplit,string,string,int>`_
  589. rsplitCommon(s, sep, maxsplit, sep.len)
  590. iterator splitLines*(s: string, keepEol = false): string =
  591. ## Splits the string `s` into its containing lines.
  592. ##
  593. ## Every `character literal <manual.html#lexical-analysis-character-literals>`_
  594. ## newline combination (CR, LF, CR-LF) is supported. The result strings
  595. ## contain no trailing end of line characters unless parameter ``keepEol``
  596. ## is set to ``true``.
  597. ##
  598. ## Example:
  599. ##
  600. ## .. code-block:: nim
  601. ## for line in splitLines("\nthis\nis\nan\n\nexample\n"):
  602. ## writeLine(stdout, line)
  603. ##
  604. ## Results in:
  605. ##
  606. ## .. code-block:: nim
  607. ## ""
  608. ## "this"
  609. ## "is"
  610. ## "an"
  611. ## ""
  612. ## "example"
  613. ## ""
  614. ##
  615. ## See also:
  616. ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_
  617. ## * `splitLines proc<#splitLines,string>`_
  618. var first = 0
  619. var last = 0
  620. var eolpos = 0
  621. while true:
  622. while last < s.len and s[last] notin {'\c', '\l'}: inc(last)
  623. eolpos = last
  624. if last < s.len:
  625. if s[last] == '\l': inc(last)
  626. elif s[last] == '\c':
  627. inc(last)
  628. if last < s.len and s[last] == '\l': inc(last)
  629. yield substr(s, first, if keepEol: last-1 else: eolpos-1)
  630. # no eol characters consumed means that the string is over
  631. if eolpos == last:
  632. break
  633. first = last
  634. iterator splitWhitespace*(s: string, maxsplit: int = -1): string =
  635. ## Splits the string ``s`` at whitespace stripping leading and trailing
  636. ## whitespace if necessary. If ``maxsplit`` is specified and is positive,
  637. ## no more than ``maxsplit`` splits is made.
  638. ##
  639. ## The following code:
  640. ##
  641. ## .. code-block:: nim
  642. ## let s = " foo \t bar baz "
  643. ## for ms in [-1, 1, 2, 3]:
  644. ## echo "------ maxsplit = ", ms, ":"
  645. ## for item in s.splitWhitespace(maxsplit=ms):
  646. ## echo '"', item, '"'
  647. ##
  648. ## ...results in:
  649. ##
  650. ## .. code-block::
  651. ## ------ maxsplit = -1:
  652. ## "foo"
  653. ## "bar"
  654. ## "baz"
  655. ## ------ maxsplit = 1:
  656. ## "foo"
  657. ## "bar baz "
  658. ## ------ maxsplit = 2:
  659. ## "foo"
  660. ## "bar"
  661. ## "baz "
  662. ## ------ maxsplit = 3:
  663. ## "foo"
  664. ## "bar"
  665. ## "baz"
  666. ##
  667. ## See also:
  668. ## * `splitLines iterator<#splitLines.i,string>`_
  669. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  670. oldSplit(s, Whitespace, maxsplit)
  671. proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect,
  672. rtl, extern: "nsuSplitChar".} =
  673. ## The same as the `split iterator <#split.i,string,char,int>`_ (see its
  674. ## documentation), but is a proc that returns a sequence of substrings.
  675. ##
  676. ## See also:
  677. ## * `split iterator <#split.i,string,char,int>`_
  678. ## * `rsplit proc<#rsplit,string,char,int>`_
  679. ## * `splitLines proc<#splitLines,string>`_
  680. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  681. runnableExamples:
  682. doAssert "a,b,c".split(',') == @["a", "b", "c"]
  683. doAssert "".split(' ') == @[""]
  684. accResult(split(s, sep, maxsplit))
  685. proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {.
  686. noSideEffect, rtl, extern: "nsuSplitCharSet".} =
  687. ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its
  688. ## documentation), but is a proc that returns a sequence of substrings.
  689. ##
  690. ## See also:
  691. ## * `split iterator <#split.i,string,set[char],int>`_
  692. ## * `rsplit proc<#rsplit,string,set[char],int>`_
  693. ## * `splitLines proc<#splitLines,string>`_
  694. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  695. runnableExamples:
  696. doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"]
  697. doAssert "".split({' '}) == @[""]
  698. accResult(split(s, seps, maxsplit))
  699. proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect,
  700. rtl, extern: "nsuSplitString".} =
  701. ## Splits the string `s` into substrings using a string separator.
  702. ##
  703. ## Substrings are separated by the string `sep`. This is a wrapper around the
  704. ## `split iterator <#split.i,string,string,int>`_.
  705. ##
  706. ## See also:
  707. ## * `split iterator <#split.i,string,string,int>`_
  708. ## * `rsplit proc<#rsplit,string,string,int>`_
  709. ## * `splitLines proc<#splitLines,string>`_
  710. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  711. runnableExamples:
  712. doAssert "a,b,c".split(",") == @["a", "b", "c"]
  713. doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"]
  714. doAssert "".split("Elon Musk") == @[""]
  715. doAssert "a largely spaced sentence".split(" ") == @["a", "", "largely",
  716. "", "", "", "spaced", "sentence"]
  717. doAssert "a largely spaced sentence".split(" ", maxsplit = 1) == @["a", " largely spaced sentence"]
  718. doAssert(sep.len > 0)
  719. accResult(split(s, sep, maxsplit))
  720. proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string]
  721. {.noSideEffect, rtl, extern: "nsuRSplitChar".} =
  722. ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc
  723. ## that returns a sequence of substrings.
  724. ##
  725. ## A possible common use case for `rsplit` is path manipulation,
  726. ## particularly on systems that don't use a common delimiter.
  727. ##
  728. ## For example, if a system had `#` as a delimiter, you could
  729. ## do the following to get the tail of the path:
  730. ##
  731. ## .. code-block:: nim
  732. ## var tailSplit = rsplit("Root#Object#Method#Index", '#', maxsplit=1)
  733. ##
  734. ## Results in `tailSplit` containing:
  735. ##
  736. ## .. code-block:: nim
  737. ## @["Root#Object#Method", "Index"]
  738. ##
  739. ## See also:
  740. ## * `rsplit iterator <#rsplit.i,string,char,int>`_
  741. ## * `split proc<#split,string,char,int>`_
  742. ## * `splitLines proc<#splitLines,string>`_
  743. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  744. accResult(rsplit(s, sep, maxsplit))
  745. result.reverse()
  746. proc rsplit*(s: string, seps: set[char] = Whitespace,
  747. maxsplit: int = -1): seq[string]
  748. {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} =
  749. ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a
  750. ## proc that returns a sequence of substrings.
  751. ##
  752. ## A possible common use case for `rsplit` is path manipulation,
  753. ## particularly on systems that don't use a common delimiter.
  754. ##
  755. ## For example, if a system had `#` as a delimiter, you could
  756. ## do the following to get the tail of the path:
  757. ##
  758. ## .. code-block:: nim
  759. ## var tailSplit = rsplit("Root#Object#Method#Index", {'#'}, maxsplit=1)
  760. ##
  761. ## Results in `tailSplit` containing:
  762. ##
  763. ## .. code-block:: nim
  764. ## @["Root#Object#Method", "Index"]
  765. ##
  766. ## See also:
  767. ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_
  768. ## * `split proc<#split,string,set[char],int>`_
  769. ## * `splitLines proc<#splitLines,string>`_
  770. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  771. accResult(rsplit(s, seps, maxsplit))
  772. result.reverse()
  773. proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string]
  774. {.noSideEffect, rtl, extern: "nsuRSplitString".} =
  775. ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a proc
  776. ## that returns a sequence of substrings.
  777. ##
  778. ## A possible common use case for `rsplit` is path manipulation,
  779. ## particularly on systems that don't use a common delimiter.
  780. ##
  781. ## For example, if a system had `#` as a delimiter, you could
  782. ## do the following to get the tail of the path:
  783. ##
  784. ## .. code-block:: nim
  785. ## var tailSplit = rsplit("Root#Object#Method#Index", "#", maxsplit=1)
  786. ##
  787. ## Results in `tailSplit` containing:
  788. ##
  789. ## .. code-block:: nim
  790. ## @["Root#Object#Method", "Index"]
  791. ##
  792. ## See also:
  793. ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_
  794. ## * `split proc<#split,string,string,int>`_
  795. ## * `splitLines proc<#splitLines,string>`_
  796. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  797. runnableExamples:
  798. doAssert "a largely spaced sentence".rsplit(" ", maxsplit = 1) == @[
  799. "a largely spaced", "sentence"]
  800. doAssert "a,b,c".rsplit(",") == @["a", "b", "c"]
  801. doAssert "a man a plan a canal panama".rsplit("a ") == @["", "man ",
  802. "plan ", "canal panama"]
  803. doAssert "".rsplit("Elon Musk") == @[""]
  804. doAssert "a largely spaced sentence".rsplit(" ") == @["a", "",
  805. "largely", "", "", "", "spaced", "sentence"]
  806. accResult(rsplit(s, sep, maxsplit))
  807. result.reverse()
  808. proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect,
  809. rtl, extern: "nsuSplitLines".} =
  810. ## The same as the `splitLines iterator<#splitLines.i,string>`_ (see its
  811. ## documentation), but is a proc that returns a sequence of substrings.
  812. ##
  813. ## See also:
  814. ## * `splitLines iterator<#splitLines.i,string>`_
  815. ## * `splitWhitespace proc<#splitWhitespace,string,int>`_
  816. ## * `countLines proc<#countLines,string>`_
  817. accResult(splitLines(s, keepEol = keepEol))
  818. proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect,
  819. rtl, extern: "nsuSplitWhitespace".} =
  820. ## The same as the `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
  821. ## (see its documentation), but is a proc that returns a sequence of substrings.
  822. ##
  823. ## See also:
  824. ## * `splitWhitespace iterator <#splitWhitespace.i,string,int>`_
  825. ## * `splitLines proc<#splitLines,string>`_
  826. accResult(splitWhitespace(s, maxsplit))
  827. proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect,
  828. rtl, extern: "nsuToBin".} =
  829. ## Converts `x` into its binary representation.
  830. ##
  831. ## The resulting string is always `len` characters long. No leading ``0b``
  832. ## prefix is generated.
  833. runnableExamples:
  834. let
  835. a = 29
  836. b = 257
  837. doAssert a.toBin(8) == "00011101"
  838. doAssert b.toBin(8) == "00000001"
  839. doAssert b.toBin(9) == "100000001"
  840. var
  841. mask = BiggestUInt 1
  842. shift = BiggestUInt 0
  843. assert(len > 0)
  844. result = newString(len)
  845. for j in countdown(len-1, 0):
  846. result[j] = chr(int((BiggestUInt(x) and mask) shr shift) + ord('0'))
  847. inc shift
  848. mask = mask shl BiggestUInt(1)
  849. proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect,
  850. rtl, extern: "nsuToOct".} =
  851. ## Converts `x` into its octal representation.
  852. ##
  853. ## The resulting string is always `len` characters long. No leading ``0o``
  854. ## prefix is generated.
  855. ##
  856. ## Do not confuse it with `toOctal proc<#toOctal,char>`_.
  857. runnableExamples:
  858. let
  859. a = 62
  860. b = 513
  861. doAssert a.toOct(3) == "076"
  862. doAssert b.toOct(3) == "001"
  863. doAssert b.toOct(5) == "01001"
  864. var
  865. mask = BiggestUInt 7
  866. shift = BiggestUInt 0
  867. assert(len > 0)
  868. result = newString(len)
  869. for j in countdown(len-1, 0):
  870. result[j] = chr(int((BiggestUInt(x) and mask) shr shift) + ord('0'))
  871. inc shift, 3
  872. mask = mask shl BiggestUInt(3)
  873. proc toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string {.noSideEffect.} =
  874. const
  875. HexChars = "0123456789ABCDEF"
  876. var n = x
  877. result = newString(len)
  878. for j in countdown(len-1, 0):
  879. result[j] = HexChars[int(n and 0xF)]
  880. n = n shr 4
  881. # handle negative overflow
  882. if n == 0 and handleNegative: n = not(BiggestUInt 0)
  883. proc toHex*(x: BiggestUInt, len: Positive): string {.noSideEffect.} =
  884. ## Converts `x` to its hexadecimal representation.
  885. ##
  886. ## The resulting string will be exactly `len` characters long. No prefix like
  887. ## ``0x`` is generated.
  888. runnableExamples:
  889. let
  890. a = 62'u64
  891. b = 4097'u64
  892. doAssert a.toHex(3) == "03E"
  893. doAssert b.toHex(3) == "001"
  894. doAssert b.toHex(4) == "1001"
  895. toHexImpl(x, len, false)
  896. proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect,
  897. rtl, extern: "nsuToHex".} =
  898. ## Converts `x` to its hexadecimal representation.
  899. ##
  900. ## The resulting string will be exactly `len` characters long. No prefix like
  901. ## ``0x`` is generated. `x` is treated as an unsigned value.
  902. runnableExamples:
  903. let
  904. a = 62
  905. b = 4097
  906. c = -8
  907. doAssert a.toHex(3) == "03E"
  908. doAssert b.toHex(3) == "001"
  909. doAssert b.toHex(4) == "1001"
  910. doAssert c.toHex(6) == "FFFFF8"
  911. toHexImpl(cast[BiggestUInt](x), len, x < 0)
  912. proc toHex*[T: SomeInteger](x: T): string {.noSideEffect.} =
  913. ## Shortcut for ``toHex(x, T.sizeof * 2)``
  914. runnableExamples:
  915. doAssert toHex(1984'i64) == "00000000000007C0"
  916. doAssert toHex(1984'i16) == "07C0"
  917. toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0)
  918. proc toHex*(s: string): string {.noSideEffect, rtl.} =
  919. ## Converts a bytes string to its hexadecimal representation.
  920. ##
  921. ## The output is twice the input long. No prefix like
  922. ## ``0x`` is generated.
  923. ##
  924. ## See also:
  925. ## * `parseHexStr proc<#parseHexStr,string>`_ for the reverse operation
  926. runnableExamples:
  927. let
  928. a = "1"
  929. b = "A"
  930. c = "\0\255"
  931. doAssert a.toHex() == "31"
  932. doAssert b.toHex() == "41"
  933. doAssert c.toHex() == "00FF"
  934. const HexChars = "0123456789ABCDEF"
  935. result = newString(s.len * 2)
  936. for pos, c in s:
  937. var n = ord(c)
  938. result[pos * 2 + 1] = HexChars[n and 0xF]
  939. n = n shr 4
  940. result[pos * 2] = HexChars[n]
  941. proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} =
  942. ## Converts a character `c` to its octal representation.
  943. ##
  944. ## The resulting string may not have a leading zero. Its length is always
  945. ## exactly 3.
  946. ##
  947. ## Do not confuse it with `toOct proc<#toOct,BiggestInt,Positive>`_.
  948. runnableExamples:
  949. doAssert toOctal('1') == "061"
  950. doAssert toOctal('A') == "101"
  951. doAssert toOctal('a') == "141"
  952. doAssert toOctal('!') == "041"
  953. result = newString(3)
  954. var val = ord(c)
  955. for i in countdown(2, 0):
  956. result[i] = chr(val mod 8 + ord('0'))
  957. val = val div 8
  958. proc fromBin*[T: SomeInteger](s: string): T =
  959. ## Parses a binary integer value from a string `s`.
  960. ##
  961. ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
  962. ## one of the following optional prefixes: `0b`, `0B`. Underscores within
  963. ## `s` are ignored.
  964. ##
  965. ## Does not check for overflow. If the value represented by `s`
  966. ## is too big to fit into a return type, only the value of the rightmost
  967. ## binary digits of `s` is returned without producing an error.
  968. runnableExamples:
  969. let s = "0b_0100_1000_1000_1000_1110_1110_1001_1001"
  970. doAssert fromBin[int](s) == 1216933529
  971. doAssert fromBin[int8](s) == 0b1001_1001'i8
  972. doAssert fromBin[int8](s) == -103'i8
  973. doAssert fromBin[uint8](s) == 153
  974. doAssert s.fromBin[:int16] == 0b1110_1110_1001_1001'i16
  975. doAssert s.fromBin[:uint64] == 1216933529'u64
  976. let p = parseutils.parseBin(s, result)
  977. if p != s.len or p == 0:
  978. raise newException(ValueError, "invalid binary integer: " & s)
  979. proc fromOct*[T: SomeInteger](s: string): T =
  980. ## Parses an octal integer value from a string `s`.
  981. ##
  982. ## If `s` is not a valid octal integer, `ValueError` is raised. `s` can have
  983. ## one of the following optional prefixes: `0o`, `0O`. Underscores within
  984. ## `s` are ignored.
  985. ##
  986. ## Does not check for overflow. If the value represented by `s`
  987. ## is too big to fit into a return type, only the value of the rightmost
  988. ## octal digits of `s` is returned without producing an error.
  989. runnableExamples:
  990. let s = "0o_123_456_777"
  991. doAssert fromOct[int](s) == 21913087
  992. doAssert fromOct[int8](s) == 0o377'i8
  993. doAssert fromOct[int8](s) == -1'i8
  994. doAssert fromOct[uint8](s) == 255'u8
  995. doAssert s.fromOct[:int16] == 24063'i16
  996. doAssert s.fromOct[:uint64] == 21913087'u64
  997. let p = parseutils.parseOct(s, result)
  998. if p != s.len or p == 0:
  999. raise newException(ValueError, "invalid oct integer: " & s)
  1000. proc fromHex*[T: SomeInteger](s: string): T =
  1001. ## Parses a hex integer value from a string `s`.
  1002. ##
  1003. ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have
  1004. ## one of the following optional prefixes: `0x`, `0X`, `#`. Underscores within
  1005. ## `s` are ignored.
  1006. ##
  1007. ## Does not check for overflow. If the value represented by `s`
  1008. ## is too big to fit into a return type, only the value of the rightmost
  1009. ## hex digits of `s` is returned without producing an error.
  1010. runnableExamples:
  1011. let s = "0x_1235_8df6"
  1012. doAssert fromHex[int](s) == 305499638
  1013. doAssert fromHex[int8](s) == 0xf6'i8
  1014. doAssert fromHex[int8](s) == -10'i8
  1015. doAssert fromHex[uint8](s) == 246'u8
  1016. doAssert s.fromHex[:int16] == -29194'i16
  1017. doAssert s.fromHex[:uint64] == 305499638'u64
  1018. let p = parseutils.parseHex(s, result)
  1019. if p != s.len or p == 0:
  1020. raise newException(ValueError, "invalid hex integer: " & s)
  1021. proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect,
  1022. rtl, extern: "nsuIntToStr".} =
  1023. ## Converts `x` to its decimal representation.
  1024. ##
  1025. ## The resulting string will be minimally `minchars` characters long. This is
  1026. ## achieved by adding leading zeros.
  1027. runnableExamples:
  1028. doAssert intToStr(1984) == "1984"
  1029. doAssert intToStr(1984, 6) == "001984"
  1030. result = $abs(x)
  1031. for i in 1 .. minchars - len(result):
  1032. result = '0' & result
  1033. if x < 0:
  1034. result = '-' & result
  1035. proc parseInt*(s: string): int {.noSideEffect,
  1036. rtl, extern: "nsuParseInt".} =
  1037. ## Parses a decimal integer value contained in `s`.
  1038. ##
  1039. ## If `s` is not a valid integer, `ValueError` is raised.
  1040. runnableExamples:
  1041. doAssert parseInt("-0042") == -42
  1042. result = 0
  1043. let L = parseutils.parseInt(s, result, 0)
  1044. if L != s.len or L == 0:
  1045. raise newException(ValueError, "invalid integer: " & s)
  1046. proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect,
  1047. rtl, extern: "nsuParseBiggestInt".} =
  1048. ## Parses a decimal integer value contained in `s`.
  1049. ##
  1050. ## If `s` is not a valid integer, `ValueError` is raised.
  1051. result = BiggestInt(0)
  1052. let L = parseutils.parseBiggestInt(s, result, 0)
  1053. if L != s.len or L == 0:
  1054. raise newException(ValueError, "invalid integer: " & s)
  1055. proc parseUInt*(s: string): uint {.noSideEffect,
  1056. rtl, extern: "nsuParseUInt".} =
  1057. ## Parses a decimal unsigned integer value contained in `s`.
  1058. ##
  1059. ## If `s` is not a valid integer, `ValueError` is raised.
  1060. result = uint(0)
  1061. let L = parseutils.parseUInt(s, result, 0)
  1062. if L != s.len or L == 0:
  1063. raise newException(ValueError, "invalid unsigned integer: " & s)
  1064. proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect,
  1065. rtl, extern: "nsuParseBiggestUInt".} =
  1066. ## Parses a decimal unsigned integer value contained in `s`.
  1067. ##
  1068. ## If `s` is not a valid integer, `ValueError` is raised.
  1069. result = BiggestUInt(0)
  1070. let L = parseutils.parseBiggestUInt(s, result, 0)
  1071. if L != s.len or L == 0:
  1072. raise newException(ValueError, "invalid unsigned integer: " & s)
  1073. proc parseFloat*(s: string): float {.noSideEffect,
  1074. rtl, extern: "nsuParseFloat".} =
  1075. ## Parses a decimal floating point value contained in `s`.
  1076. ##
  1077. ## If `s` is not a valid floating point number, `ValueError` is raised.
  1078. ##``NAN``, ``INF``, ``-INF`` are also supported (case insensitive comparison).
  1079. runnableExamples:
  1080. doAssert parseFloat("3.14") == 3.14
  1081. doAssert parseFloat("inf") == 1.0/0
  1082. result = 0.0
  1083. let L = parseutils.parseFloat(s, result, 0)
  1084. if L != s.len or L == 0:
  1085. raise newException(ValueError, "invalid float: " & s)
  1086. proc parseBinInt*(s: string): int {.noSideEffect,
  1087. rtl, extern: "nsuParseBinInt".} =
  1088. ## Parses a binary integer value contained in `s`.
  1089. ##
  1090. ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have
  1091. ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within
  1092. ## `s` are ignored.
  1093. runnableExamples:
  1094. let
  1095. a = "0b11_0101"
  1096. b = "111"
  1097. doAssert a.parseBinInt() == 53
  1098. doAssert b.parseBinInt() == 7
  1099. result = 0
  1100. let L = parseutils.parseBin(s, result, 0)
  1101. if L != s.len or L == 0:
  1102. raise newException(ValueError, "invalid binary integer: " & s)
  1103. proc parseOctInt*(s: string): int {.noSideEffect,
  1104. rtl, extern: "nsuParseOctInt".} =
  1105. ## Parses an octal integer value contained in `s`.
  1106. ##
  1107. ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one
  1108. ## of the following optional prefixes: ``0o``, ``0O``. Underscores within
  1109. ## `s` are ignored.
  1110. result = 0
  1111. let L = parseutils.parseOct(s, result, 0)
  1112. if L != s.len or L == 0:
  1113. raise newException(ValueError, "invalid oct integer: " & s)
  1114. proc parseHexInt*(s: string): int {.noSideEffect,
  1115. rtl, extern: "nsuParseHexInt".} =
  1116. ## Parses a hexadecimal integer value contained in `s`.
  1117. ##
  1118. ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one
  1119. ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores
  1120. ## within `s` are ignored.
  1121. result = 0
  1122. let L = parseutils.parseHex(s, result, 0)
  1123. if L != s.len or L == 0:
  1124. raise newException(ValueError, "invalid hex integer: " & s)
  1125. proc generateHexCharToValueMap(): string =
  1126. ## Generate a string to map a hex digit to uint value
  1127. result = ""
  1128. for inp in 0..255:
  1129. let ch = chr(inp)
  1130. let o =
  1131. case ch:
  1132. of '0'..'9': inp - ord('0')
  1133. of 'a'..'f': inp - ord('a') + 10
  1134. of 'A'..'F': inp - ord('A') + 10
  1135. else: 17 # indicates an invalid hex char
  1136. result.add chr(o)
  1137. const hexCharToValueMap = generateHexCharToValueMap()
  1138. proc parseHexStr*(s: string): string {.noSideEffect,
  1139. rtl, extern: "nsuParseHexStr".} =
  1140. ## Convert hex-encoded string to byte string, e.g.:
  1141. ##
  1142. ## Raises ``ValueError`` for an invalid hex values. The comparison is
  1143. ## case-insensitive.
  1144. ##
  1145. ## See also:
  1146. ## * `toHex proc<#toHex,string>`_ for the reverse operation
  1147. runnableExamples:
  1148. let
  1149. a = "41"
  1150. b = "3161"
  1151. c = "00ff"
  1152. doAssert parseHexStr(a) == "A"
  1153. doAssert parseHexStr(b) == "1a"
  1154. doAssert parseHexStr(c) == "\0\255"
  1155. if s.len mod 2 != 0:
  1156. raise newException(ValueError, "Incorrect hex string len")
  1157. result = newString(s.len div 2)
  1158. var buf = 0
  1159. for pos, c in s:
  1160. let val = hexCharToValueMap[ord(c)].ord
  1161. if val == 17:
  1162. raise newException(ValueError, "Invalid hex char `" &
  1163. c & "` (ord " & $c.ord & ")")
  1164. if pos mod 2 == 0:
  1165. buf = val
  1166. else:
  1167. result[pos div 2] = chr(val + buf shl 4)
  1168. proc parseBool*(s: string): bool =
  1169. ## Parses a value into a `bool`.
  1170. ##
  1171. ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then
  1172. ## returns `true`. If ``s`` is one of the following values: ``n, no, false,
  1173. ## 0, off``, then returns `false`. If ``s`` is something else a
  1174. ## ``ValueError`` exception is raised.
  1175. runnableExamples:
  1176. let a = "n"
  1177. doAssert parseBool(a) == false
  1178. case normalize(s)
  1179. of "y", "yes", "true", "1", "on": result = true
  1180. of "n", "no", "false", "0", "off": result = false
  1181. else: raise newException(ValueError, "cannot interpret as a bool: " & s)
  1182. proc addOfBranch(s: string, field, enumType: NimNode): NimNode =
  1183. result = nnkOfBranch.newTree(
  1184. newLit s,
  1185. nnkCall.newTree(enumType, field) # `T(<fieldValue>)`
  1186. )
  1187. macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped =
  1188. # generates a case stmt, which assigns the correct enum field given
  1189. # a normalized string comparison to the `argSym` input.
  1190. # NOTE: for an enum with fields Foo, Bar, ... we cannot generate
  1191. # `of "Foo".nimIdentNormalize: Foo`.
  1192. # This will fail, if the enum is not defined at top level (e.g. in a block).
  1193. # Thus we check for the field value of the (possible holed enum) and convert
  1194. # the integer value to the generic argument `typ`.
  1195. let typ = typ.getTypeInst[1]
  1196. let impl = typ.getImpl[2]
  1197. expectKind impl, nnkEnumTy
  1198. result = nnkCaseStmt.newTree(newCall(bindSym"nimIdentNormalize", argSym))
  1199. # stores all processed field strings to give error msg for ambiguous enums
  1200. var foundFields: seq[string] = @[]
  1201. var fStr = "" # string of current field
  1202. var fNum = BiggestInt(0) # int value of current field
  1203. for f in impl:
  1204. case f.kind
  1205. of nnkEmpty: continue # skip first node of `enumTy`
  1206. of nnkSym, nnkIdent: fStr = f.strVal
  1207. of nnkEnumFieldDef:
  1208. case f[1].kind
  1209. of nnkStrLit: fStr = f[1].strVal
  1210. of nnkTupleConstr:
  1211. fStr = f[1][1].strVal
  1212. fNum = f[1][0].intVal
  1213. of nnkIntLit:
  1214. fStr = f[0].strVal
  1215. fNum = f[1].intVal
  1216. else: error("Invalid tuple syntax!", f[1])
  1217. else: error("Invalid node for enum type!", f)
  1218. # add field if string not already added
  1219. fStr = nimIdentNormalize(fStr)
  1220. if fStr notin foundFields:
  1221. result.add addOfBranch(fStr, newLit fNum, typ)
  1222. foundFields.add fStr
  1223. else:
  1224. error("Ambiguous enums cannot be parsed, field " & $fStr &
  1225. " appears multiple times!", f)
  1226. inc fNum
  1227. # finally add else branch to raise or use default
  1228. if default == nil:
  1229. let raiseStmt = quote do:
  1230. raise newException(ValueError, "Invalid enum value: " & $`argSym`)
  1231. result.add nnkElse.newTree(raiseStmt)
  1232. else:
  1233. expectKind(default, nnkSym)
  1234. result.add nnkElse.newTree(default)
  1235. proc parseEnum*[T: enum](s: string): T =
  1236. ## Parses an enum ``T``. This errors at compile time, if the given enum
  1237. ## type contains multiple fields with the same string value.
  1238. ##
  1239. ## Raises ``ValueError`` for an invalid value in `s`. The comparison is
  1240. ## done in a style insensitive way.
  1241. runnableExamples:
  1242. type
  1243. MyEnum = enum
  1244. first = "1st",
  1245. second,
  1246. third = "3rd"
  1247. doAssert parseEnum[MyEnum]("1_st") == first
  1248. doAssert parseEnum[MyEnum]("second") == second
  1249. doAssertRaises(ValueError):
  1250. echo parseEnum[MyEnum]("third")
  1251. genEnumStmt(T, s, default = nil)
  1252. proc parseEnum*[T: enum](s: string, default: T): T =
  1253. ## Parses an enum ``T``. This errors at compile time, if the given enum
  1254. ## type contains multiple fields with the same string value.
  1255. ##
  1256. ## Uses `default` for an invalid value in `s`. The comparison is done in a
  1257. ## style insensitive way.
  1258. runnableExamples:
  1259. type
  1260. MyEnum = enum
  1261. first = "1st",
  1262. second,
  1263. third = "3rd"
  1264. doAssert parseEnum[MyEnum]("1_st") == first
  1265. doAssert parseEnum[MyEnum]("second") == second
  1266. doAssert parseEnum[MyEnum]("last", third) == third
  1267. genEnumStmt(T, s, default)
  1268. proc repeat*(c: char, count: Natural): string {.noSideEffect,
  1269. rtl, extern: "nsuRepeatChar".} =
  1270. ## Returns a string of length `count` consisting only of
  1271. ## the character `c`.
  1272. runnableExamples:
  1273. let a = 'z'
  1274. doAssert a.repeat(5) == "zzzzz"
  1275. result = newString(count)
  1276. for i in 0..count-1: result[i] = c
  1277. proc repeat*(s: string, n: Natural): string {.noSideEffect,
  1278. rtl, extern: "nsuRepeatStr".} =
  1279. ## Returns string `s` concatenated `n` times.
  1280. runnableExamples:
  1281. doAssert "+ foo +".repeat(3) == "+ foo ++ foo ++ foo +"
  1282. result = newStringOfCap(n * s.len)
  1283. for i in 1..n: result.add(s)
  1284. proc spaces*(n: Natural): string {.inline.} =
  1285. ## Returns a string with `n` space characters. You can use this proc
  1286. ## to left align strings.
  1287. ##
  1288. ## See also:
  1289. ## * `align proc<#align,string,Natural,char>`_
  1290. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1291. ## * `indent proc<#indent,string,Natural,string>`_
  1292. ## * `center proc<#center,string,int,char>`_
  1293. runnableExamples:
  1294. let
  1295. width = 15
  1296. text1 = "Hello user!"
  1297. text2 = "This is a very long string"
  1298. doAssert text1 & spaces(max(0, width - text1.len)) & "|" ==
  1299. "Hello user! |"
  1300. doAssert text2 & spaces(max(0, width - text2.len)) & "|" ==
  1301. "This is a very long string|"
  1302. repeat(' ', n)
  1303. proc align*(s: string, count: Natural, padding = ' '): string {.
  1304. noSideEffect, rtl, extern: "nsuAlignString".} =
  1305. ## Aligns a string `s` with `padding`, so that it is of length `count`.
  1306. ##
  1307. ## `padding` characters (by default spaces) are added before `s` resulting in
  1308. ## right alignment. If ``s.len >= count``, no spaces are added and `s` is
  1309. ## returned unchanged. If you need to left align a string use the `alignLeft
  1310. ## proc <#alignLeft,string,Natural,char>`_.
  1311. ##
  1312. ## See also:
  1313. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1314. ## * `spaces proc<#spaces,Natural>`_
  1315. ## * `indent proc<#indent,string,Natural,string>`_
  1316. ## * `center proc<#center,string,int,char>`_
  1317. runnableExamples:
  1318. assert align("abc", 4) == " abc"
  1319. assert align("a", 0) == "a"
  1320. assert align("1232", 6) == " 1232"
  1321. assert align("1232", 6, '#') == "##1232"
  1322. if s.len < count:
  1323. result = newString(count)
  1324. let spaces = count - s.len
  1325. for i in 0..spaces-1: result[i] = padding
  1326. for i in spaces..count-1: result[i] = s[i-spaces]
  1327. else:
  1328. result = s
  1329. proc alignLeft*(s: string, count: Natural, padding = ' '): string {.
  1330. noSideEffect.} =
  1331. ## Left-Aligns a string `s` with `padding`, so that it is of length `count`.
  1332. ##
  1333. ## `padding` characters (by default spaces) are added after `s` resulting in
  1334. ## left alignment. If ``s.len >= count``, no spaces are added and `s` is
  1335. ## returned unchanged. If you need to right align a string use the `align
  1336. ## proc <#align,string,Natural,char>`_.
  1337. ##
  1338. ## See also:
  1339. ## * `align proc<#align,string,Natural,char>`_
  1340. ## * `spaces proc<#spaces,Natural>`_
  1341. ## * `indent proc<#indent,string,Natural,string>`_
  1342. ## * `center proc<#center,string,int,char>`_
  1343. runnableExamples:
  1344. assert alignLeft("abc", 4) == "abc "
  1345. assert alignLeft("a", 0) == "a"
  1346. assert alignLeft("1232", 6) == "1232 "
  1347. assert alignLeft("1232", 6, '#') == "1232##"
  1348. if s.len < count:
  1349. result = newString(count)
  1350. if s.len > 0:
  1351. result[0 .. (s.len - 1)] = s
  1352. for i in s.len ..< count:
  1353. result[i] = padding
  1354. else:
  1355. result = s
  1356. proc center*(s: string, width: int, fillChar: char = ' '): string {.
  1357. noSideEffect, rtl, extern: "nsuCenterString".} =
  1358. ## Return the contents of `s` centered in a string `width` long using
  1359. ## `fillChar` (default: space) as padding.
  1360. ##
  1361. ## The original string is returned if `width` is less than or equal
  1362. ## to `s.len`.
  1363. ##
  1364. ## See also:
  1365. ## * `align proc<#align,string,Natural,char>`_
  1366. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1367. ## * `spaces proc<#spaces,Natural>`_
  1368. ## * `indent proc<#indent,string,Natural,string>`_
  1369. runnableExamples:
  1370. let a = "foo"
  1371. doAssert a.center(2) == "foo"
  1372. doAssert a.center(5) == " foo "
  1373. doAssert a.center(6) == " foo "
  1374. if width <= s.len: return s
  1375. result = newString(width)
  1376. # Left padding will be one fillChar
  1377. # smaller if there are an odd number
  1378. # of characters
  1379. let
  1380. charsLeft = (width - s.len)
  1381. leftPadding = charsLeft div 2
  1382. for i in 0 ..< width:
  1383. if i >= leftPadding and i < leftPadding + s.len:
  1384. # we are where the string should be located
  1385. result[i] = s[i-leftPadding]
  1386. else:
  1387. # we are either before or after where
  1388. # the string s should go
  1389. result[i] = fillChar
  1390. proc indent*(s: string, count: Natural, padding: string = " "): string
  1391. {.noSideEffect, rtl, extern: "nsuIndent".} =
  1392. ## Indents each line in ``s`` by ``count`` amount of ``padding``.
  1393. ##
  1394. ## **Note:** This does not preserve the new line characters used in ``s``.
  1395. ##
  1396. ## See also:
  1397. ## * `align proc<#align,string,Natural,char>`_
  1398. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1399. ## * `spaces proc<#spaces,Natural>`_
  1400. ## * `unindent proc<#unindent,string,Natural,string>`_
  1401. ## * `dedent proc<#dedent,string,Natural,string>`_
  1402. runnableExamples:
  1403. doAssert indent("First line\c\l and second line.", 2) ==
  1404. " First line\l and second line."
  1405. result = ""
  1406. var i = 0
  1407. for line in s.splitLines():
  1408. if i != 0:
  1409. result.add("\n")
  1410. for j in 1..count:
  1411. result.add(padding)
  1412. result.add(line)
  1413. i.inc
  1414. proc unindent*(s: string, count: Natural = int.high, padding: string = " "): string
  1415. {.noSideEffect, rtl, extern: "nsuUnindent".} =
  1416. ## Unindents each line in ``s`` by ``count`` amount of ``padding``.
  1417. ##
  1418. ## **Note:** This does not preserve the new line characters used in ``s``.
  1419. ##
  1420. ## See also:
  1421. ## * `dedent proc<#dedent,string,Natural,string>`
  1422. ## * `align proc<#align,string,Natural,char>`_
  1423. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1424. ## * `spaces proc<#spaces,Natural>`_
  1425. ## * `indent proc<#indent,string,Natural,string>`_
  1426. runnableExamples:
  1427. let x = """
  1428. Hello
  1429. There
  1430. """.unindent()
  1431. doAssert x == "Hello\nThere\n"
  1432. result = ""
  1433. var i = 0
  1434. for line in s.splitLines():
  1435. if i != 0:
  1436. result.add("\n")
  1437. var indentCount = 0
  1438. for j in 0..<count.int:
  1439. indentCount.inc
  1440. if j + padding.len-1 >= line.len or line[j .. j + padding.len-1] != padding:
  1441. indentCount = j
  1442. break
  1443. result.add(line[indentCount*padding.len .. ^1])
  1444. i.inc
  1445. proc indentation*(s: string): Natural {.since: (1, 3).} =
  1446. ## Returns the amount of indentation all lines of ``s`` have in common,
  1447. ## ignoring lines that consist only of whitespace.
  1448. result = int.high
  1449. for line in s.splitLines:
  1450. for i, c in line:
  1451. if i >= result: break
  1452. elif c != ' ':
  1453. result = i
  1454. break
  1455. if result == int.high:
  1456. result = 0
  1457. proc dedent*(s: string, count: Natural = indentation(s)): string
  1458. {.noSideEffect, rtl, extern: "nsuDedent", since: (1, 3).} =
  1459. ## Unindents each line in ``s`` by ``count`` amount of ``padding``.
  1460. ## The only difference between this and `unindent proc<#unindent,string,Natural,string>`
  1461. ## is that this by default only cuts off the amount of indentation that all
  1462. ## lines of ``s`` share as opposed to all indentation. It only supports spcaes as padding.
  1463. ##
  1464. ## **Note:** This does not preserve the new line characters used in ``s``.
  1465. ##
  1466. ## See also:
  1467. ## * `unindent proc<#unindent,string,Natural,string>`
  1468. ## * `align proc<#align,string,Natural,char>`_
  1469. ## * `alignLeft proc<#alignLeft,string,Natural,char>`_
  1470. ## * `spaces proc<#spaces,Natural>`_
  1471. ## * `indent proc<#indent,string,Natural,string>`_
  1472. runnableExamples:
  1473. let x = """
  1474. Hello
  1475. There
  1476. """.dedent()
  1477. doAssert x == "Hello\n There\n"
  1478. unindent(s, count, " ")
  1479. proc delete*(s: var string, first, last: int) {.noSideEffect,
  1480. rtl, extern: "nsuDelete".} =
  1481. ## Deletes in `s` (must be declared as ``var``) the characters at positions
  1482. ## ``first ..last`` (both ends included).
  1483. ##
  1484. ## This modifies `s` itself, it does not return a copy.
  1485. runnableExamples:
  1486. var a = "abracadabra"
  1487. a.delete(4, 5)
  1488. doAssert a == "abradabra"
  1489. a.delete(1, 6)
  1490. doAssert a == "ara"
  1491. a.delete(2, 999)
  1492. doAssert a == "ar"
  1493. var i = first
  1494. var j = min(len(s), last+1)
  1495. var newLen = len(s)-j+i
  1496. while i < newLen:
  1497. s[i] = s[j]
  1498. inc(i)
  1499. inc(j)
  1500. setLen(s, newLen)
  1501. proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} =
  1502. ## Returns true if ``s`` starts with character ``prefix``.
  1503. ##
  1504. ## See also:
  1505. ## * `endsWith proc<#endsWith,string,char>`_
  1506. ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
  1507. ## * `removePrefix proc<#removePrefix,string,char>`_
  1508. runnableExamples:
  1509. let a = "abracadabra"
  1510. doAssert a.startsWith('a') == true
  1511. doAssert a.startsWith('b') == false
  1512. result = s.len > 0 and s[0] == prefix
  1513. proc startsWith*(s, prefix: string): bool {.noSideEffect,
  1514. rtl, extern: "nsuStartsWith".} =
  1515. ## Returns true if ``s`` starts with string ``prefix``.
  1516. ##
  1517. ## If ``prefix == ""`` true is returned.
  1518. ##
  1519. ## See also:
  1520. ## * `endsWith proc<#endsWith,string,string>`_
  1521. ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
  1522. ## * `removePrefix proc<#removePrefix,string,string>`_
  1523. runnableExamples:
  1524. let a = "abracadabra"
  1525. doAssert a.startsWith("abra") == true
  1526. doAssert a.startsWith("bra") == false
  1527. var i = 0
  1528. while true:
  1529. if i >= prefix.len: return true
  1530. if i >= s.len or s[i] != prefix[i]: return false
  1531. inc(i)
  1532. proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} =
  1533. ## Returns true if ``s`` ends with ``suffix``.
  1534. ##
  1535. ## See also:
  1536. ## * `startsWith proc<#startsWith,string,char>`_
  1537. ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
  1538. ## * `removeSuffix proc<#removeSuffix,string,char>`_
  1539. runnableExamples:
  1540. let a = "abracadabra"
  1541. doAssert a.endsWith('a') == true
  1542. doAssert a.endsWith('b') == false
  1543. result = s.len > 0 and s[s.high] == suffix
  1544. proc endsWith*(s, suffix: string): bool {.noSideEffect,
  1545. rtl, extern: "nsuEndsWith".} =
  1546. ## Returns true if ``s`` ends with ``suffix``.
  1547. ##
  1548. ## If ``suffix == ""`` true is returned.
  1549. ##
  1550. ## See also:
  1551. ## * `startsWith proc<#startsWith,string,string>`_
  1552. ## * `continuesWith proc<#continuesWith,string,string,Natural>`_
  1553. ## * `removeSuffix proc<#removeSuffix,string,string>`_
  1554. runnableExamples:
  1555. let a = "abracadabra"
  1556. doAssert a.endsWith("abra") == true
  1557. doAssert a.endsWith("dab") == false
  1558. var i = 0
  1559. var j = len(s) - len(suffix)
  1560. while i+j >= 0 and i+j < s.len:
  1561. if s[i+j] != suffix[i]: return false
  1562. inc(i)
  1563. if i >= suffix.len: return true
  1564. proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect,
  1565. rtl, extern: "nsuContinuesWith".} =
  1566. ## Returns true if ``s`` continues with ``substr`` at position ``start``.
  1567. ##
  1568. ## If ``substr == ""`` true is returned.
  1569. ##
  1570. ## See also:
  1571. ## * `startsWith proc<#startsWith,string,string>`_
  1572. ## * `endsWith proc<#endsWith,string,string>`_
  1573. runnableExamples:
  1574. let a = "abracadabra"
  1575. doAssert a.continuesWith("ca", 4) == true
  1576. doAssert a.continuesWith("ca", 5) == false
  1577. doAssert a.continuesWith("dab", 6) == true
  1578. var i = 0
  1579. while true:
  1580. if i >= substr.len: return true
  1581. if i+start >= s.len or s[i+start] != substr[i]: return false
  1582. inc(i)
  1583. proc removePrefix*(s: var string, chars: set[char] = Newlines) {.
  1584. rtl, extern: "nsuRemovePrefixCharSet".} =
  1585. ## Removes all characters from `chars` from the start of the string `s`
  1586. ## (in-place).
  1587. ##
  1588. ## See also:
  1589. ## * `removeSuffix proc<#removeSuffix,string,set[char]>`_
  1590. runnableExamples:
  1591. var userInput = "\r\n*~Hello World!"
  1592. userInput.removePrefix
  1593. doAssert userInput == "*~Hello World!"
  1594. userInput.removePrefix({'~', '*'})
  1595. doAssert userInput == "Hello World!"
  1596. var otherInput = "?!?Hello!?!"
  1597. otherInput.removePrefix({'!', '?'})
  1598. doAssert otherInput == "Hello!?!"
  1599. var start = 0
  1600. while start < s.len and s[start] in chars: start += 1
  1601. if start > 0: s.delete(0, start - 1)
  1602. proc removePrefix*(s: var string, c: char) {.
  1603. rtl, extern: "nsuRemovePrefixChar".} =
  1604. ## Removes all occurrences of a single character (in-place) from the start
  1605. ## of a string.
  1606. ##
  1607. ## See also:
  1608. ## * `removeSuffix proc<#removeSuffix,string,char>`_
  1609. ## * `startsWith proc<#startsWith,string,char>`_
  1610. runnableExamples:
  1611. var ident = "pControl"
  1612. ident.removePrefix('p')
  1613. doAssert ident == "Control"
  1614. removePrefix(s, chars = {c})
  1615. proc removePrefix*(s: var string, prefix: string) {.
  1616. rtl, extern: "nsuRemovePrefixString".} =
  1617. ## Remove the first matching prefix (in-place) from a string.
  1618. ##
  1619. ## See also:
  1620. ## * `removeSuffix proc<#removeSuffix,string,string>`_
  1621. ## * `startsWith proc<#startsWith,string,string>`_
  1622. runnableExamples:
  1623. var answers = "yesyes"
  1624. answers.removePrefix("yes")
  1625. doAssert answers == "yes"
  1626. if s.startsWith(prefix):
  1627. s.delete(0, prefix.len - 1)
  1628. proc removeSuffix*(s: var string, chars: set[char] = Newlines) {.
  1629. rtl, extern: "nsuRemoveSuffixCharSet".} =
  1630. ## Removes all characters from `chars` from the end of the string `s`
  1631. ## (in-place).
  1632. ##
  1633. ## See also:
  1634. ## * `removePrefix proc<#removePrefix,string,set[char]>`_
  1635. runnableExamples:
  1636. var userInput = "Hello World!*~\r\n"
  1637. userInput.removeSuffix
  1638. doAssert userInput == "Hello World!*~"
  1639. userInput.removeSuffix({'~', '*'})
  1640. doAssert userInput == "Hello World!"
  1641. var otherInput = "Hello!?!"
  1642. otherInput.removeSuffix({'!', '?'})
  1643. doAssert otherInput == "Hello"
  1644. if s.len == 0: return
  1645. var last = s.high
  1646. while last > -1 and s[last] in chars: last -= 1
  1647. s.setLen(last + 1)
  1648. proc removeSuffix*(s: var string, c: char) {.
  1649. rtl, extern: "nsuRemoveSuffixChar".} =
  1650. ## Removes all occurrences of a single character (in-place) from the end
  1651. ## of a string.
  1652. ##
  1653. ## See also:
  1654. ## * `removePrefix proc<#removePrefix,string,char>`_
  1655. ## * `endsWith proc<#endsWith,string,char>`_
  1656. runnableExamples:
  1657. var table = "users"
  1658. table.removeSuffix('s')
  1659. doAssert table == "user"
  1660. var dots = "Trailing dots......."
  1661. dots.removeSuffix('.')
  1662. doAssert dots == "Trailing dots"
  1663. removeSuffix(s, chars = {c})
  1664. proc removeSuffix*(s: var string, suffix: string) {.
  1665. rtl, extern: "nsuRemoveSuffixString".} =
  1666. ## Remove the first matching suffix (in-place) from a string.
  1667. ##
  1668. ## See also:
  1669. ## * `removePrefix proc<#removePrefix,string,string>`_
  1670. ## * `endsWith proc<#endsWith,string,string>`_
  1671. runnableExamples:
  1672. var answers = "yeses"
  1673. answers.removeSuffix("es")
  1674. doAssert answers == "yes"
  1675. var newLen = s.len
  1676. if s.endsWith(suffix):
  1677. newLen -= len(suffix)
  1678. s.setLen(newLen)
  1679. proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0)
  1680. {.noSideEffect, inline.} =
  1681. ## Adds a separator to `dest` only if its length is bigger than `startLen`.
  1682. ##
  1683. ## A shorthand for:
  1684. ##
  1685. ## .. code-block:: nim
  1686. ## if dest.len > startLen: add(dest, sep)
  1687. ##
  1688. ## This is often useful for generating some code where the items need to
  1689. ## be *separated* by `sep`. `sep` is only added if `dest` is longer than
  1690. ## `startLen`. The following example creates a string describing
  1691. ## an array of integers.
  1692. runnableExamples:
  1693. var arr = "["
  1694. for x in items([2, 3, 5, 7, 11]):
  1695. addSep(arr, startLen = len("["))
  1696. add(arr, $x)
  1697. add(arr, "]")
  1698. doAssert arr == "[2, 3, 5, 7, 11]"
  1699. if dest.len > startLen: add(dest, sep)
  1700. proc allCharsInSet*(s: string, theSet: set[char]): bool =
  1701. ## Returns true if every character of `s` is in the set `theSet`.
  1702. runnableExamples:
  1703. doAssert allCharsInSet("aeea", {'a', 'e'}) == true
  1704. doAssert allCharsInSet("", {'a', 'e'}) == true
  1705. for c in items(s):
  1706. if c notin theSet: return false
  1707. return true
  1708. proc abbrev*(s: string, possibilities: openArray[string]): int =
  1709. ## Returns the index of the first item in ``possibilities`` which starts
  1710. ## with ``s``, if not ambiguous.
  1711. ##
  1712. ## Returns -1 if no item has been found and -2 if multiple items match.
  1713. runnableExamples:
  1714. doAssert abbrev("fac", ["college", "faculty", "industry"]) == 1
  1715. doAssert abbrev("foo", ["college", "faculty", "industry"]) == -1 # Not found
  1716. doAssert abbrev("fac", ["college", "faculty", "faculties"]) == -2 # Ambiguous
  1717. doAssert abbrev("college", ["college", "colleges", "industry"]) == 0
  1718. result = -1 # none found
  1719. for i in 0..possibilities.len-1:
  1720. if possibilities[i].startsWith(s):
  1721. if possibilities[i] == s:
  1722. # special case: exact match shouldn't be ambiguous
  1723. return i
  1724. if result >= 0: return -2 # ambiguous
  1725. result = i
  1726. # ---------------------------------------------------------------------------
  1727. proc join*(a: openArray[string], sep: string = ""): string {.
  1728. noSideEffect, rtl, extern: "nsuJoinSep".} =
  1729. ## Concatenates all strings in the container `a`, separating them with `sep`.
  1730. runnableExamples:
  1731. doAssert join(["A", "B", "Conclusion"], " -> ") == "A -> B -> Conclusion"
  1732. if len(a) > 0:
  1733. var L = sep.len * (a.len-1)
  1734. for i in 0..high(a): inc(L, a[i].len)
  1735. result = newStringOfCap(L)
  1736. add(result, a[0])
  1737. for i in 1..high(a):
  1738. add(result, sep)
  1739. add(result, a[i])
  1740. else:
  1741. result = ""
  1742. proc join*[T: not string](a: openArray[T], sep: string = ""): string {.
  1743. noSideEffect, rtl.} =
  1744. ## Converts all elements in the container `a` to strings using `$`,
  1745. ## and concatenates them with `sep`.
  1746. runnableExamples:
  1747. doAssert join([1, 2, 3], " -> ") == "1 -> 2 -> 3"
  1748. result = ""
  1749. for i, x in a:
  1750. if i > 0:
  1751. add(result, sep)
  1752. add(result, $x)
  1753. type
  1754. SkipTable* = array[char, int]
  1755. proc initSkipTable*(a: var SkipTable, sub: string)
  1756. {.noSideEffect, rtl, extern: "nsuInitSkipTable".} =
  1757. ## Preprocess table `a` for `sub`.
  1758. let m = len(sub)
  1759. var i = 0
  1760. while i <= 0xff-7:
  1761. a[chr(i + 0)] = m
  1762. a[chr(i + 1)] = m
  1763. a[chr(i + 2)] = m
  1764. a[chr(i + 3)] = m
  1765. a[chr(i + 4)] = m
  1766. a[chr(i + 5)] = m
  1767. a[chr(i + 6)] = m
  1768. a[chr(i + 7)] = m
  1769. i += 8
  1770. for i in 0 ..< m - 1:
  1771. a[sub[i]] = m - 1 - i
  1772. proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int
  1773. {.noSideEffect, rtl, extern: "nsuFindStrA".} =
  1774. ## Searches for `sub` in `s` inside range `start..last` using preprocessed
  1775. ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last
  1776. ## element).
  1777. ##
  1778. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
  1779. let
  1780. last = if last == 0: s.high else: last
  1781. subLast = sub.len - 1
  1782. if subLast == -1:
  1783. # this was an empty needle string,
  1784. # we count this as match in the first possible position:
  1785. return start
  1786. # This is an implementation of the Boyer-Moore Horspool algorithms
  1787. # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm
  1788. var skip = start
  1789. while last - skip >= subLast:
  1790. var i = subLast
  1791. while s[skip + i] == sub[i]:
  1792. if i == 0:
  1793. return skip
  1794. dec i
  1795. inc skip, a[s[skip + subLast]]
  1796. return -1
  1797. when not (defined(js) or defined(nimdoc) or defined(nimscript)):
  1798. proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {.
  1799. importc: "memchr", header: "<string.h>".}
  1800. const hasCStringBuiltin = true
  1801. else:
  1802. const hasCStringBuiltin = false
  1803. proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffect,
  1804. rtl, extern: "nsuFindChar".} =
  1805. ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
  1806. ## If `last` is unspecified, it defaults to `s.high` (the last element).
  1807. ##
  1808. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
  1809. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1810. ## Use `s[start..last].rfind` for a ``start``-origin index.
  1811. ##
  1812. ## See also:
  1813. ## * `rfind proc<#rfind,string,char,Natural,int>`_
  1814. ## * `replace proc<#replace,string,char,char>`_
  1815. let last = if last == 0: s.high else: last
  1816. when nimvm:
  1817. for i in int(start)..last:
  1818. if sub == s[i]: return i
  1819. else:
  1820. when hasCStringBuiltin:
  1821. let L = last-start+1
  1822. if L > 0:
  1823. let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L))
  1824. if not found.isNil:
  1825. return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring)
  1826. else:
  1827. for i in int(start)..last:
  1828. if sub == s[i]: return i
  1829. return -1
  1830. proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect,
  1831. rtl, extern: "nsuFindCharSet".} =
  1832. ## Searches for `chars` in `s` inside range ``start..last`` (both ends included).
  1833. ## If `last` is unspecified, it defaults to `s.high` (the last element).
  1834. ##
  1835. ## If `s` contains none of the characters in `chars`, -1 is returned.
  1836. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1837. ## Use `s[start..last].find` for a ``start``-origin index.
  1838. ##
  1839. ## See also:
  1840. ## * `rfind proc<#rfind,string,set[char],Natural,int>`_
  1841. ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
  1842. let last = if last == 0: s.high else: last
  1843. for i in int(start)..last:
  1844. if s[i] in chars: return i
  1845. return -1
  1846. proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect,
  1847. rtl, extern: "nsuFindStr".} =
  1848. ## Searches for `sub` in `s` inside range ``start..last`` (both ends included).
  1849. ## If `last` is unspecified, it defaults to `s.high` (the last element).
  1850. ##
  1851. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
  1852. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1853. ## Use `s[start..last].find` for a ``start``-origin index.
  1854. ##
  1855. ## See also:
  1856. ## * `rfind proc<#rfind,string,string,Natural,int>`_
  1857. ## * `replace proc<#replace,string,string,string>`_
  1858. if sub.len > s.len: return -1
  1859. if sub.len == 1: return find(s, sub[0], start, last)
  1860. var a {.noinit.}: SkipTable
  1861. initSkipTable(a, sub)
  1862. result = find(a, s, sub, start, last)
  1863. proc rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.noSideEffect,
  1864. rtl, extern: "nsuRFindChar".} =
  1865. ## Searches for `sub` in `s` inside range ``start..last`` (both ends included)
  1866. ## in reverse -- starting at high indexes and moving lower to the first
  1867. ## character or ``start``. If `last` is unspecified, it defaults to `s.high`
  1868. ## (the last element).
  1869. ##
  1870. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
  1871. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1872. ## Use `s[start..last].find` for a ``start``-origin index.
  1873. ##
  1874. ## See also:
  1875. ## * `find proc<#find,string,char,Natural,int>`_
  1876. let last = if last == -1: s.high else: last
  1877. for i in countdown(last, start):
  1878. if sub == s[i]: return i
  1879. return -1
  1880. proc rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.noSideEffect,
  1881. rtl, extern: "nsuRFindCharSet".} =
  1882. ## Searches for `chars` in `s` inside range ``start..last`` (both ends
  1883. ## included) in reverse -- starting at high indexes and moving lower to the
  1884. ## first character or ``start``. If `last` is unspecified, it defaults to
  1885. ## `s.high` (the last element).
  1886. ##
  1887. ## If `s` contains none of the characters in `chars`, -1 is returned.
  1888. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1889. ## Use `s[start..last].rfind` for a ``start``-origin index.
  1890. ##
  1891. ## See also:
  1892. ## * `find proc<#find,string,set[char],Natural,int>`_
  1893. let last = if last == -1: s.high else: last
  1894. for i in countdown(last, start):
  1895. if s[i] in chars: return i
  1896. return -1
  1897. proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect,
  1898. rtl, extern: "nsuRFindStr".} =
  1899. ## Searches for `sub` in `s` inside range ``start..last`` (both ends included)
  1900. ## included) in reverse -- starting at high indexes and moving lower to the
  1901. ## first character or ``start``. If `last` is unspecified, it defaults to
  1902. ## `s.high` (the last element).
  1903. ##
  1904. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned.
  1905. ## Otherwise the index returned is relative to ``s[0]``, not ``start``.
  1906. ## Use `s[start..last].rfind` for a ``start``-origin index.
  1907. ##
  1908. ## See also:
  1909. ## * `find proc<#find,string,string,Natural,int>`_
  1910. if sub.len == 0:
  1911. return -1
  1912. let last = if last == -1: s.high else: last
  1913. result = 0
  1914. for i in countdown(last - sub.len + 1, start):
  1915. for j in 0..sub.len-1:
  1916. result = i
  1917. if sub[j] != s[i+j]:
  1918. result = -1
  1919. break
  1920. if result != -1: return
  1921. return -1
  1922. proc count*(s: string, sub: char): int {.noSideEffect,
  1923. rtl, extern: "nsuCountChar".} =
  1924. ## Count the occurrences of the character `sub` in the string `s`.
  1925. ##
  1926. ## See also:
  1927. ## * `countLines proc<#countLines,string>`_
  1928. result = 0
  1929. for c in s:
  1930. if c == sub: inc result
  1931. proc count*(s: string, subs: set[char]): int {.noSideEffect,
  1932. rtl, extern: "nsuCountCharSet".} =
  1933. ## Count the occurrences of the group of character `subs` in the string `s`.
  1934. ##
  1935. ## See also:
  1936. ## * `countLines proc<#countLines,string>`_
  1937. doAssert card(subs) > 0
  1938. result = 0
  1939. for c in s:
  1940. if c in subs: inc result
  1941. proc count*(s: string, sub: string, overlapping: bool = false): int {.
  1942. noSideEffect, rtl, extern: "nsuCountString".} =
  1943. ## Count the occurrences of a substring `sub` in the string `s`.
  1944. ## Overlapping occurrences of `sub` only count when `overlapping`
  1945. ## is set to true (default: false).
  1946. ##
  1947. ## See also:
  1948. ## * `countLines proc<#countLines,string>`_
  1949. doAssert sub.len > 0
  1950. result = 0
  1951. var i = 0
  1952. while true:
  1953. i = s.find(sub, i)
  1954. if i < 0: break
  1955. if overlapping: inc i
  1956. else: i += sub.len
  1957. inc result
  1958. proc countLines*(s: string): int {.noSideEffect,
  1959. rtl, extern: "nsuCountLines".} =
  1960. ## Returns the number of lines in the string `s`.
  1961. ##
  1962. ## This is the same as ``len(splitLines(s))``, but much more efficient
  1963. ## because it doesn't modify the string creating temporal objects. Every
  1964. ## `character literal <manual.html#lexical-analysis-character-literals>`_
  1965. ## newline combination (CR, LF, CR-LF) is supported.
  1966. ##
  1967. ## In this context, a line is any string separated by a newline combination.
  1968. ## A line can be an empty string.
  1969. ##
  1970. ## See also:
  1971. ## * `splitLines proc<#splitLines,string>`_
  1972. runnableExamples:
  1973. doAssert countLines("First line\l and second line.") == 2
  1974. result = 1
  1975. var i = 0
  1976. while i < s.len:
  1977. case s[i]
  1978. of '\c':
  1979. if i+1 < s.len and s[i+1] == '\l': inc i
  1980. inc result
  1981. of '\l': inc result
  1982. else: discard
  1983. inc i
  1984. proc contains*(s, sub: string): bool {.noSideEffect.} =
  1985. ## Same as ``find(s, sub) >= 0``.
  1986. ##
  1987. ## See also:
  1988. ## * `find proc<#find,string,string,Natural,int>`_
  1989. return find(s, sub) >= 0
  1990. proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} =
  1991. ## Same as ``find(s, chars) >= 0``.
  1992. ##
  1993. ## See also:
  1994. ## * `find proc<#find,string,set[char],Natural,int>`_
  1995. return find(s, chars) >= 0
  1996. proc replace*(s, sub: string, by = ""): string {.noSideEffect,
  1997. rtl, extern: "nsuReplaceStr".} =
  1998. ## Replaces `sub` in `s` by the string `by`.
  1999. ##
  2000. ## See also:
  2001. ## * `find proc<#find,string,string,Natural,int>`_
  2002. ## * `replace proc<#replace,string,char,char>`_ for replacing
  2003. ## single characters
  2004. ## * `replaceWord proc<#replaceWord,string,string,string>`_
  2005. ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
  2006. result = ""
  2007. let subLen = sub.len
  2008. if subLen == 0:
  2009. result = s
  2010. elif subLen == 1:
  2011. # when the pattern is a single char, we use a faster
  2012. # char-based search that doesn't need a skip table:
  2013. let c = sub[0]
  2014. let last = s.high
  2015. var i = 0
  2016. while true:
  2017. let j = find(s, c, i, last)
  2018. if j < 0: break
  2019. add result, substr(s, i, j - 1)
  2020. add result, by
  2021. i = j + subLen
  2022. # copy the rest:
  2023. add result, substr(s, i)
  2024. else:
  2025. var a {.noinit.}: SkipTable
  2026. initSkipTable(a, sub)
  2027. let last = s.high
  2028. var i = 0
  2029. while true:
  2030. let j = find(a, s, sub, i, last)
  2031. if j < 0: break
  2032. add result, substr(s, i, j - 1)
  2033. add result, by
  2034. i = j + subLen
  2035. # copy the rest:
  2036. add result, substr(s, i)
  2037. proc replace*(s: string, sub, by: char): string {.noSideEffect,
  2038. rtl, extern: "nsuReplaceChar".} =
  2039. ## Replaces `sub` in `s` by the character `by`.
  2040. ##
  2041. ## Optimized version of `replace <#replace,string,string,string>`_ for
  2042. ## characters.
  2043. ##
  2044. ## See also:
  2045. ## * `find proc<#find,string,char,Natural,int>`_
  2046. ## * `replaceWord proc<#replaceWord,string,string,string>`_
  2047. ## * `multiReplace proc<#multiReplace,string,varargs[]>`_
  2048. result = newString(s.len)
  2049. var i = 0
  2050. while i < s.len:
  2051. if s[i] == sub: result[i] = by
  2052. else: result[i] = s[i]
  2053. inc(i)
  2054. proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect,
  2055. rtl, extern: "nsuReplaceWord".} =
  2056. ## Replaces `sub` in `s` by the string `by`.
  2057. ##
  2058. ## Each occurrence of `sub` has to be surrounded by word boundaries
  2059. ## (comparable to ``\b`` in regular expressions), otherwise it is not
  2060. ## replaced.
  2061. if sub.len == 0: return s
  2062. const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'}
  2063. var a {.noinit.}: SkipTable
  2064. result = ""
  2065. initSkipTable(a, sub)
  2066. var i = 0
  2067. let last = s.high
  2068. let sublen = sub.len
  2069. if sublen > 0:
  2070. while true:
  2071. var j = find(a, s, sub, i, last)
  2072. if j < 0: break
  2073. # word boundary?
  2074. if (j == 0 or s[j-1] notin wordChars) and
  2075. (j+sub.len >= s.len or s[j+sub.len] notin wordChars):
  2076. add result, substr(s, i, j - 1)
  2077. add result, by
  2078. i = j + sublen
  2079. else:
  2080. add result, substr(s, i, j)
  2081. i = j + 1
  2082. # copy the rest:
  2083. add result, substr(s, i)
  2084. proc multiReplace*(s: string, replacements: varargs[(string, string)]):
  2085. string {.noSideEffect.} =
  2086. ## Same as replace, but specialized for doing multiple replacements in a single
  2087. ## pass through the input string.
  2088. ##
  2089. ## `multiReplace` performs all replacements in a single pass, this means it
  2090. ## can be used to swap the occurrences of "a" and "b", for instance.
  2091. ##
  2092. ## If the resulting string is not longer than the original input string,
  2093. ## only a single memory allocation is required.
  2094. ##
  2095. ## The order of the replacements does matter. Earlier replacements are
  2096. ## preferred over later replacements in the argument list.
  2097. result = newStringOfCap(s.len)
  2098. var i = 0
  2099. var fastChk: set[char] = {}
  2100. for sub, by in replacements.items:
  2101. if sub.len > 0:
  2102. # Include first character of all replacements
  2103. fastChk.incl sub[0]
  2104. while i < s.len:
  2105. block sIteration:
  2106. # Assume most chars in s are not candidates for any replacement operation
  2107. if s[i] in fastChk:
  2108. for sub, by in replacements.items:
  2109. if sub.len > 0 and s.continuesWith(sub, i):
  2110. add result, by
  2111. inc(i, sub.len)
  2112. break sIteration
  2113. # No matching replacement found
  2114. # copy current character from s
  2115. add result, s[i]
  2116. inc(i)
  2117. proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect,
  2118. rtl, extern: "nsuInsertSep".} =
  2119. ## Inserts the separator `sep` after `digits` characters (default: 3)
  2120. ## from right to left.
  2121. ##
  2122. ## Even though the algorithm works with any string `s`, it is only useful
  2123. ## if `s` contains a number.
  2124. runnableExamples:
  2125. doAssert insertSep("1000000") == "1_000_000"
  2126. result = newStringOfCap(s.len)
  2127. let hasPrefix = isDigit(s[s.low]) == false
  2128. var idx:int
  2129. if hasPrefix:
  2130. result.add s[s.low]
  2131. for i in (s.low + 1)..s.high:
  2132. idx = i
  2133. if not isDigit(s[i]):
  2134. result.add s[i]
  2135. else:
  2136. break
  2137. let partsLen = s.len - idx
  2138. var L = (partsLen-1) div digits + partsLen
  2139. result.setLen(L + idx)
  2140. var j = 0
  2141. dec(L)
  2142. for i in countdown(partsLen-1,0):
  2143. if j == digits:
  2144. result[L + idx] = sep
  2145. dec(L)
  2146. j = 0
  2147. result[L + idx] = s[i + idx]
  2148. inc(j)
  2149. dec(L)
  2150. proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
  2151. rtl, extern: "nsuEscape".} =
  2152. ## Escapes a string `s`. See `system.addEscapedChar
  2153. ## <system.html#addEscapedChar,string,char>`_ for the escaping scheme.
  2154. ##
  2155. ## The resulting string is prefixed with `prefix` and suffixed with `suffix`.
  2156. ## Both may be empty strings.
  2157. ##
  2158. ## See also:
  2159. ## * `unescape proc<#unescape,string,string,string>`_ for the opposite
  2160. ## operation
  2161. result = newStringOfCap(s.len + s.len shr 2)
  2162. result.add(prefix)
  2163. for c in items(s):
  2164. case c
  2165. of '\0'..'\31', '\127'..'\255':
  2166. add(result, "\\x")
  2167. add(result, toHex(ord(c), 2))
  2168. of '\\': add(result, "\\\\")
  2169. of '\'': add(result, "\\'")
  2170. of '\"': add(result, "\\\"")
  2171. else: add(result, c)
  2172. add(result, suffix)
  2173. proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect,
  2174. rtl, extern: "nsuUnescape".} =
  2175. ## Unescapes a string `s`.
  2176. ##
  2177. ## This complements `escape proc<#escape,string,string,string>`_
  2178. ## as it performs the opposite operations.
  2179. ##
  2180. ## If `s` does not begin with ``prefix`` and end with ``suffix`` a
  2181. ## ValueError exception will be raised.
  2182. result = newStringOfCap(s.len)
  2183. var i = prefix.len
  2184. if not s.startsWith(prefix):
  2185. raise newException(ValueError,
  2186. "String does not start with: " & prefix)
  2187. while true:
  2188. if i >= s.len-suffix.len: break
  2189. if s[i] == '\\':
  2190. if i+1 >= s.len:
  2191. result.add('\\')
  2192. break
  2193. case s[i+1]:
  2194. of 'x':
  2195. inc i, 2
  2196. var c = 0
  2197. i += parseutils.parseHex(s, c, i, maxLen = 2)
  2198. result.add(chr(c))
  2199. dec i, 2
  2200. of '\\':
  2201. result.add('\\')
  2202. of '\'':
  2203. result.add('\'')
  2204. of '\"':
  2205. result.add('\"')
  2206. else:
  2207. result.add("\\" & s[i+1])
  2208. inc(i, 2)
  2209. else:
  2210. result.add(s[i])
  2211. inc(i)
  2212. if not s.endsWith(suffix):
  2213. raise newException(ValueError,
  2214. "String does not end in: " & suffix)
  2215. proc validIdentifier*(s: string): bool {.noSideEffect,
  2216. rtl, extern: "nsuValidIdentifier".} =
  2217. ## Returns true if `s` is a valid identifier.
  2218. ##
  2219. ## A valid identifier starts with a character of the set `IdentStartChars`
  2220. ## and is followed by any number of characters of the set `IdentChars`.
  2221. runnableExamples:
  2222. doAssert "abc_def08".validIdentifier
  2223. if s.len > 0 and s[0] in IdentStartChars:
  2224. for i in 1..s.len-1:
  2225. if s[i] notin IdentChars: return false
  2226. return true
  2227. # floating point formatting:
  2228. when not defined(js):
  2229. proc c_sprintf(buf, frmt: cstring): cint {.header: "<stdio.h>",
  2230. importc: "sprintf", varargs, noSideEffect.}
  2231. type
  2232. FloatFormatMode* = enum
  2233. ## the different modes of floating point formatting
  2234. ffDefault, ## use the shorter floating point notation
  2235. ffDecimal, ## use decimal floating point notation
  2236. ffScientific ## use scientific notation (using ``e`` character)
  2237. proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault,
  2238. precision: range[-1..32] = 16;
  2239. decimalSep = '.'): string {.
  2240. noSideEffect, rtl, extern: "nsu$1".} =
  2241. ## Converts a floating point value `f` to a string.
  2242. ##
  2243. ## If ``format == ffDecimal`` then precision is the number of digits to
  2244. ## be printed after the decimal point.
  2245. ## If ``format == ffScientific`` then precision is the maximum number
  2246. ## of significant digits to be printed.
  2247. ## `precision`'s default value is the maximum number of meaningful digits
  2248. ## after the decimal point for Nim's ``biggestFloat`` type.
  2249. ##
  2250. ## If ``precision == -1``, it tries to format it nicely.
  2251. runnableExamples:
  2252. let x = 123.456
  2253. doAssert x.formatBiggestFloat() == "123.4560000000000"
  2254. doAssert x.formatBiggestFloat(ffDecimal, 4) == "123.4560"
  2255. doAssert x.formatBiggestFloat(ffScientific, 2) == "1.23e+02"
  2256. when defined(js):
  2257. var precision = precision
  2258. if precision == -1:
  2259. # use the same default precision as c_sprintf
  2260. precision = 6
  2261. var res: cstring
  2262. case format
  2263. of ffDefault:
  2264. {.emit: "`res` = `f`.toString();".}
  2265. of ffDecimal:
  2266. {.emit: "`res` = `f`.toFixed(`precision`);".}
  2267. of ffScientific:
  2268. {.emit: "`res` = `f`.toExponential(`precision`);".}
  2269. result = $res
  2270. if 1.0 / f == -Inf:
  2271. # JavaScript removes the "-" from negative Zero, add it back here
  2272. result = "-" & $res
  2273. for i in 0 ..< result.len:
  2274. # Depending on the locale either dot or comma is produced,
  2275. # but nothing else is possible:
  2276. if result[i] in {'.', ','}: result[i] = decimalSep
  2277. else:
  2278. const floatFormatToChar: array[FloatFormatMode, char] = ['g', 'f', 'e']
  2279. var
  2280. frmtstr {.noinit.}: array[0..5, char]
  2281. buf {.noinit.}: array[0..2500, char]
  2282. L: cint
  2283. frmtstr[0] = '%'
  2284. if precision >= 0:
  2285. frmtstr[1] = '#'
  2286. frmtstr[2] = '.'
  2287. frmtstr[3] = '*'
  2288. frmtstr[4] = floatFormatToChar[format]
  2289. frmtstr[5] = '\0'
  2290. when defined(nimNoArrayToCstringConversion):
  2291. L = c_sprintf(addr buf, addr frmtstr, precision, f)
  2292. else:
  2293. L = c_sprintf(buf, frmtstr, precision, f)
  2294. else:
  2295. frmtstr[1] = floatFormatToChar[format]
  2296. frmtstr[2] = '\0'
  2297. when defined(nimNoArrayToCstringConversion):
  2298. L = c_sprintf(addr buf, addr frmtstr, f)
  2299. else:
  2300. L = c_sprintf(buf, frmtstr, f)
  2301. result = newString(L)
  2302. for i in 0 ..< L:
  2303. # Depending on the locale either dot or comma is produced,
  2304. # but nothing else is possible:
  2305. if buf[i] in {'.', ','}: result[i] = decimalSep
  2306. else: result[i] = buf[i]
  2307. when defined(windows):
  2308. # VS pre 2015 violates the C standard: "The exponent always contains at
  2309. # least two digits, and only as many more digits as necessary to
  2310. # represent the exponent." [C11 §7.21.6.1]
  2311. # The following post-processing fixes this behavior.
  2312. if result.len > 4 and result[^4] == '+' and result[^3] == '0':
  2313. result[^3] = result[^2]
  2314. result[^2] = result[^1]
  2315. result.setLen(result.len - 1)
  2316. proc formatFloat*(f: float, format: FloatFormatMode = ffDefault,
  2317. precision: range[-1..32] = 16; decimalSep = '.'): string {.
  2318. noSideEffect, rtl, extern: "nsu$1".} =
  2319. ## Converts a floating point value `f` to a string.
  2320. ##
  2321. ## If ``format == ffDecimal`` then precision is the number of digits to
  2322. ## be printed after the decimal point.
  2323. ## If ``format == ffScientific`` then precision is the maximum number
  2324. ## of significant digits to be printed.
  2325. ## `precision`'s default value is the maximum number of meaningful digits
  2326. ## after the decimal point for Nim's ``float`` type.
  2327. ##
  2328. ## If ``precision == -1``, it tries to format it nicely.
  2329. runnableExamples:
  2330. let x = 123.456
  2331. doAssert x.formatFloat() == "123.4560000000000"
  2332. doAssert x.formatFloat(ffDecimal, 4) == "123.4560"
  2333. doAssert x.formatFloat(ffScientific, 2) == "1.23e+02"
  2334. result = formatBiggestFloat(f, format, precision, decimalSep)
  2335. proc trimZeros*(x: var string; decimalSep = '.') {.noSideEffect.} =
  2336. ## Trim trailing zeros from a formatted floating point
  2337. ## value `x` (must be declared as ``var``).
  2338. ##
  2339. ## This modifies `x` itself, it does not return a copy.
  2340. runnableExamples:
  2341. var x = "123.456000000"
  2342. x.trimZeros()
  2343. doAssert x == "123.456"
  2344. let sPos = find(x, decimalSep)
  2345. if sPos >= 0:
  2346. var last = find(x, 'e', start = sPos)
  2347. last = if last >= 0: last - 1 else: high(x)
  2348. var pos = last
  2349. while pos >= 0 and x[pos] == '0': dec(pos)
  2350. if pos > sPos: inc(pos)
  2351. x.delete(pos, last)
  2352. type
  2353. BinaryPrefixMode* = enum ## the different names for binary prefixes
  2354. bpIEC, # use the IEC/ISO standard prefixes such as kibi
  2355. bpColloquial # use the colloquial kilo, mega etc
  2356. proc formatSize*(bytes: int64,
  2357. decimalSep = '.',
  2358. prefix = bpIEC,
  2359. includeSpace = false): string {.noSideEffect.} =
  2360. ## Rounds and formats `bytes`.
  2361. ##
  2362. ## By default, uses the IEC/ISO standard binary prefixes, so 1024 will be
  2363. ## formatted as 1KiB. Set prefix to `bpColloquial` to use the colloquial
  2364. ## names from the SI standard (e.g. k for 1000 being reused as 1024).
  2365. ##
  2366. ## `includeSpace` can be set to true to include the (SI preferred) space
  2367. ## between the number and the unit (e.g. 1 KiB).
  2368. ##
  2369. ## See also:
  2370. ## * `strformat module<strformat.html>`_ for string interpolation and formatting
  2371. runnableExamples:
  2372. doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB"
  2373. doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
  2374. doAssert formatSize(4096, includeSpace = true) == "4 KiB"
  2375. doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB"
  2376. doAssert formatSize(4096) == "4KiB"
  2377. doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB"
  2378. const iecPrefixes = ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"]
  2379. const collPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"]
  2380. var
  2381. xb: int64 = bytes
  2382. fbytes: float
  2383. lastXb: int64 = bytes
  2384. matchedIndex = 0
  2385. prefixes: array[9, string]
  2386. if prefix == bpColloquial:
  2387. prefixes = collPrefixes
  2388. else:
  2389. prefixes = iecPrefixes
  2390. # Iterate through prefixes seeing if value will be greater than
  2391. # 0 in each case
  2392. for index in 1..<prefixes.len:
  2393. lastXb = xb
  2394. xb = bytes div (1'i64 shl (index*10))
  2395. matchedIndex = index
  2396. if xb == 0:
  2397. xb = lastXb
  2398. matchedIndex = index - 1
  2399. break
  2400. # xb has the integer number for the latest value; index should be correct
  2401. fbytes = bytes.float / (1'i64 shl (matchedIndex*10)).float
  2402. result = formatFloat(fbytes, format = ffDecimal, precision = 3,
  2403. decimalSep = decimalSep)
  2404. result.trimZeros(decimalSep)
  2405. if includeSpace:
  2406. result &= " "
  2407. result &= prefixes[matchedIndex]
  2408. result &= "B"
  2409. proc formatEng*(f: BiggestFloat,
  2410. precision: range[0..32] = 10,
  2411. trim: bool = true,
  2412. siPrefix: bool = false,
  2413. unit: string = "",
  2414. decimalSep = '.',
  2415. useUnitSpace = false): string {.noSideEffect.} =
  2416. ## Converts a floating point value `f` to a string using engineering notation.
  2417. ##
  2418. ## Numbers in of the range -1000.0<f<1000.0 will be formatted without an
  2419. ## exponent. Numbers outside of this range will be formatted as a
  2420. ## significand in the range -1000.0<f<1000.0 and an exponent that will always
  2421. ## be an integer multiple of 3, corresponding with the SI prefix scale k, M,
  2422. ## G, T etc for numbers with an absolute value greater than 1 and m, μ, n, p
  2423. ## etc for numbers with an absolute value less than 1.
  2424. ##
  2425. ## The default configuration (`trim=true` and `precision=10`) shows the
  2426. ## **shortest** form that precisely (up to a maximum of 10 decimal places)
  2427. ## displays the value. For example, 4.100000 will be displayed as 4.1 (which
  2428. ## is mathematically identical) whereas 4.1000003 will be displayed as
  2429. ## 4.1000003.
  2430. ##
  2431. ## If `trim` is set to true, trailing zeros will be removed; if false, the
  2432. ## number of digits specified by `precision` will always be shown.
  2433. ##
  2434. ## `precision` can be used to set the number of digits to be shown after the
  2435. ## decimal point or (if `trim` is true) the maximum number of digits to be
  2436. ## shown.
  2437. ##
  2438. ## .. code-block:: nim
  2439. ##
  2440. ## formatEng(0, 2, trim=false) == "0.00"
  2441. ## formatEng(0, 2) == "0"
  2442. ## formatEng(0.053, 0) == "53e-3"
  2443. ## formatEng(52731234, 2) == "52.73e6"
  2444. ## formatEng(-52731234, 2) == "-52.73e6"
  2445. ##
  2446. ## If `siPrefix` is set to true, the number will be displayed with the SI
  2447. ## prefix corresponding to the exponent. For example 4100 will be displayed
  2448. ## as "4.1 k" instead of "4.1e3". Note that `u` is used for micro- in place
  2449. ## of the greek letter mu (μ) as per ISO 2955. Numbers with an absolute
  2450. ## value outside of the range 1e-18<f<1000e18 (1a<f<1000E) will be displayed
  2451. ## with an exponent rather than an SI prefix, regardless of whether
  2452. ## `siPrefix` is true.
  2453. ##
  2454. ## If `useUnitSpace` is true, the provided unit will be appended to the string
  2455. ## (with a space as required by the SI standard). This behaviour is slightly
  2456. ## different to appending the unit to the result as the location of the space
  2457. ## is altered depending on whether there is an exponent.
  2458. ##
  2459. ## .. code-block:: nim
  2460. ##
  2461. ## formatEng(4100, siPrefix=true, unit="V") == "4.1 kV"
  2462. ## formatEng(4.1, siPrefix=true, unit="V") == "4.1 V"
  2463. ## formatEng(4.1, siPrefix=true) == "4.1" # Note lack of space
  2464. ## formatEng(4100, siPrefix=true) == "4.1 k"
  2465. ## formatEng(4.1, siPrefix=true, unit="") == "4.1 " # Space with unit=""
  2466. ## formatEng(4100, siPrefix=true, unit="") == "4.1 k"
  2467. ## formatEng(4100) == "4.1e3"
  2468. ## formatEng(4100, unit="V") == "4.1e3 V"
  2469. ## formatEng(4100, unit="", useUnitSpace=true) == "4.1e3 " # Space with useUnitSpace=true
  2470. ##
  2471. ## `decimalSep` is used as the decimal separator.
  2472. ##
  2473. ## See also:
  2474. ## * `strformat module<strformat.html>`_ for string interpolation and formatting
  2475. var
  2476. absolute: BiggestFloat
  2477. significand: BiggestFloat
  2478. fexponent: BiggestFloat
  2479. exponent: int
  2480. splitResult: seq[string]
  2481. suffix: string = ""
  2482. proc getPrefix(exp: int): char =
  2483. ## Get the SI prefix for a given exponent
  2484. ##
  2485. ## Assumes exponent is a multiple of 3; returns ' ' if no prefix found
  2486. const siPrefixes = ['a', 'f', 'p', 'n', 'u', 'm', ' ', 'k', 'M', 'G', 'T',
  2487. 'P', 'E']
  2488. var index: int = (exp div 3) + 6
  2489. result = ' '
  2490. if index in low(siPrefixes)..high(siPrefixes):
  2491. result = siPrefixes[index]
  2492. # Most of the work is done with the sign ignored, so get the absolute value
  2493. absolute = abs(f)
  2494. significand = f
  2495. if absolute == 0.0:
  2496. # Simple case: just format it and force the exponent to 0
  2497. exponent = 0
  2498. result = significand.formatBiggestFloat(ffDecimal, precision,
  2499. decimalSep = '.')
  2500. else:
  2501. # Find the best exponent that's a multiple of 3
  2502. fexponent = floor(log10(absolute))
  2503. fexponent = 3.0 * floor(fexponent / 3.0)
  2504. # Adjust the significand for the new exponent
  2505. significand /= pow(10.0, fexponent)
  2506. # Adjust the significand and check whether it has affected
  2507. # the exponent
  2508. absolute = abs(significand)
  2509. if absolute >= 1000.0:
  2510. significand *= 0.001
  2511. fexponent += 3
  2512. # Components of the result:
  2513. result = significand.formatBiggestFloat(ffDecimal, precision,
  2514. decimalSep = '.')
  2515. exponent = fexponent.int()
  2516. splitResult = result.split('.')
  2517. result = splitResult[0]
  2518. # result should have at most one decimal character
  2519. if splitResult.len() > 1:
  2520. # If trim is set, we get rid of trailing zeros. Don't use trimZeros here as
  2521. # we can be a bit more efficient through knowledge that there will never be
  2522. # an exponent in this part.
  2523. if trim:
  2524. while splitResult[1].endsWith("0"):
  2525. # Trim last character
  2526. splitResult[1].setLen(splitResult[1].len-1)
  2527. if splitResult[1].len() > 0:
  2528. result &= decimalSep & splitResult[1]
  2529. else:
  2530. result &= decimalSep & splitResult[1]
  2531. # Combine the results accordingly
  2532. if siPrefix and exponent != 0:
  2533. var p = getPrefix(exponent)
  2534. if p != ' ':
  2535. suffix = " " & p
  2536. exponent = 0 # Exponent replaced by SI prefix
  2537. if suffix == "" and useUnitSpace:
  2538. suffix = " "
  2539. suffix &= unit
  2540. if exponent != 0:
  2541. result &= "e" & $exponent
  2542. result &= suffix
  2543. proc findNormalized(x: string, inArray: openArray[string]): int =
  2544. var i = 0
  2545. while i < high(inArray):
  2546. if cmpIgnoreStyle(x, inArray[i]) == 0: return i
  2547. inc(i, 2) # incrementing by 1 would probably lead to a
  2548. # security hole...
  2549. return -1
  2550. proc invalidFormatString() {.noinline.} =
  2551. raise newException(ValueError, "invalid format string")
  2552. proc addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.
  2553. noSideEffect, rtl, extern: "nsuAddf".} =
  2554. ## The same as ``add(s, formatstr % a)``, but more efficient.
  2555. const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'}
  2556. var i = 0
  2557. var num = 0
  2558. while i < len(formatstr):
  2559. if formatstr[i] == '$' and i+1 < len(formatstr):
  2560. case formatstr[i+1]
  2561. of '#':
  2562. if num > a.high: invalidFormatString()
  2563. add s, a[num]
  2564. inc i, 2
  2565. inc num
  2566. of '$':
  2567. add s, '$'
  2568. inc(i, 2)
  2569. of '1'..'9', '-':
  2570. var j = 0
  2571. inc(i) # skip $
  2572. var negative = formatstr[i] == '-'
  2573. if negative: inc i
  2574. while i < formatstr.len and formatstr[i] in Digits:
  2575. j = j * 10 + ord(formatstr[i]) - ord('0')
  2576. inc(i)
  2577. let idx = if not negative: j-1 else: a.len-j
  2578. if idx < 0 or idx > a.high: invalidFormatString()
  2579. add s, a[idx]
  2580. of '{':
  2581. var j = i+2
  2582. var k = 0
  2583. var negative = formatstr[j] == '-'
  2584. if negative: inc j
  2585. var isNumber = 0
  2586. while j < formatstr.len and formatstr[j] notin {'\0', '}'}:
  2587. if formatstr[j] in Digits:
  2588. k = k * 10 + ord(formatstr[j]) - ord('0')
  2589. if isNumber == 0: isNumber = 1
  2590. else:
  2591. isNumber = -1
  2592. inc(j)
  2593. if isNumber == 1:
  2594. let idx = if not negative: k-1 else: a.len-k
  2595. if idx < 0 or idx > a.high: invalidFormatString()
  2596. add s, a[idx]
  2597. else:
  2598. var x = findNormalized(substr(formatstr, i+2, j-1), a)
  2599. if x >= 0 and x < high(a): add s, a[x+1]
  2600. else: invalidFormatString()
  2601. i = j+1
  2602. of 'a'..'z', 'A'..'Z', '\128'..'\255', '_':
  2603. var j = i+1
  2604. while j < formatstr.len and formatstr[j] in PatternChars: inc(j)
  2605. var x = findNormalized(substr(formatstr, i+1, j-1), a)
  2606. if x >= 0 and x < high(a): add s, a[x+1]
  2607. else: invalidFormatString()
  2608. i = j
  2609. else:
  2610. invalidFormatString()
  2611. else:
  2612. add s, formatstr[i]
  2613. inc(i)
  2614. proc `%` *(formatstr: string, a: openArray[string]): string {.noSideEffect,
  2615. rtl, extern: "nsuFormatOpenArray".} =
  2616. ## Interpolates a format string with the values from `a`.
  2617. ##
  2618. ## The `substitution`:idx: operator performs string substitutions in
  2619. ## `formatstr` and returns a modified `formatstr`. This is often called
  2620. ## `string interpolation`:idx:.
  2621. ##
  2622. ## This is best explained by an example:
  2623. ##
  2624. ## .. code-block:: nim
  2625. ## "$1 eats $2." % ["The cat", "fish"]
  2626. ##
  2627. ## Results in:
  2628. ##
  2629. ## .. code-block:: nim
  2630. ## "The cat eats fish."
  2631. ##
  2632. ## The substitution variables (the thing after the ``$``) are enumerated
  2633. ## from 1 to ``a.len``.
  2634. ## To produce a verbatim ``$``, use ``$$``.
  2635. ## The notation ``$#`` can be used to refer to the next substitution
  2636. ## variable:
  2637. ##
  2638. ## .. code-block:: nim
  2639. ## "$# eats $#." % ["The cat", "fish"]
  2640. ##
  2641. ## Substitution variables can also be words (that is
  2642. ## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even
  2643. ## indices are keys and with odd indices are the corresponding values.
  2644. ## An example:
  2645. ##
  2646. ## .. code-block:: nim
  2647. ## "$animal eats $food." % ["animal", "The cat", "food", "fish"]
  2648. ##
  2649. ## Results in:
  2650. ##
  2651. ## .. code-block:: nim
  2652. ## "The cat eats fish."
  2653. ##
  2654. ## The variables are compared with `cmpIgnoreStyle`. `ValueError` is
  2655. ## raised if an ill-formed format string has been passed to the `%` operator.
  2656. ##
  2657. ## See also:
  2658. ## * `strformat module<strformat.html>`_ for string interpolation and formatting
  2659. result = newStringOfCap(formatstr.len + a.len shl 4)
  2660. addf(result, formatstr, a)
  2661. proc `%` *(formatstr, a: string): string {.noSideEffect,
  2662. rtl, extern: "nsuFormatSingleElem".} =
  2663. ## This is the same as ``formatstr % [a]`` (see
  2664. ## `% proc<#%25,string,openArray[string]>`_).
  2665. result = newStringOfCap(formatstr.len + a.len)
  2666. addf(result, formatstr, [a])
  2667. proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect,
  2668. rtl, extern: "nsuFormatVarargs".} =
  2669. ## This is the same as ``formatstr % a`` (see
  2670. ## `% proc<#%25,string,openArray[string]>`_) except that it supports
  2671. ## auto stringification.
  2672. ##
  2673. ## See also:
  2674. ## * `strformat module<strformat.html>`_ for string interpolation and formatting
  2675. result = newStringOfCap(formatstr.len + a.len)
  2676. addf(result, formatstr, a)
  2677. proc strip*(s: string, leading = true, trailing = true,
  2678. chars: set[char] = Whitespace): string
  2679. {.noSideEffect, rtl, extern: "nsuStrip".} =
  2680. ## Strips leading or trailing `chars` (default: whitespace characters)
  2681. ## from `s` and returns the resulting string.
  2682. ##
  2683. ## If `leading` is true (default), leading `chars` are stripped.
  2684. ## If `trailing` is true (default), trailing `chars` are stripped.
  2685. ## If both are false, the string is returned unchanged.
  2686. ##
  2687. ## See also:
  2688. ## * `stripLineEnd proc<#stripLineEnd,string>`_
  2689. runnableExamples:
  2690. let a = " vhellov "
  2691. let b = strip(a)
  2692. doAssert b == "vhellov"
  2693. doAssert a.strip(leading = false) == " vhellov"
  2694. doAssert a.strip(trailing = false) == "vhellov "
  2695. doAssert b.strip(chars = {'v'}) == "hello"
  2696. doAssert b.strip(leading = false, chars = {'v'}) == "vhello"
  2697. let c = "blaXbla"
  2698. doAssert c.strip(chars = {'b', 'a'}) == "laXbl"
  2699. doAssert c.strip(chars = {'b', 'a', 'l'}) == "X"
  2700. var
  2701. first = 0
  2702. last = len(s)-1
  2703. if leading:
  2704. while first <= last and s[first] in chars: inc(first)
  2705. if trailing:
  2706. while last >= 0 and s[last] in chars: dec(last)
  2707. result = substr(s, first, last)
  2708. proc stripLineEnd*(s: var string) =
  2709. ## Returns ``s`` stripped from one of these suffixes:
  2710. ## ``\r, \n, \r\n, \f, \v`` (at most once instance).
  2711. ## For example, can be useful in conjunction with ``osproc.execCmdEx``.
  2712. ## aka: `chomp`:idx:
  2713. runnableExamples:
  2714. var s = "foo\n\n"
  2715. s.stripLineEnd
  2716. doAssert s == "foo\n"
  2717. s = "foo\r\n"
  2718. s.stripLineEnd
  2719. doAssert s == "foo"
  2720. if s.len > 0:
  2721. case s[^1]
  2722. of '\n':
  2723. if s.len > 1 and s[^2] == '\r':
  2724. s.setLen s.len-2
  2725. else:
  2726. s.setLen s.len-1
  2727. of '\r', '\v', '\f':
  2728. s.setLen s.len-1
  2729. else:
  2730. discard
  2731. iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[
  2732. token: string, isSep: bool] =
  2733. ## Tokenizes the string `s` into substrings.
  2734. ##
  2735. ## Substrings are separated by a substring containing only `seps`.
  2736. ## Example:
  2737. ##
  2738. ## .. code-block:: nim
  2739. ## for word in tokenize(" this is an example "):
  2740. ## writeLine(stdout, word)
  2741. ##
  2742. ## Results in:
  2743. ##
  2744. ## .. code-block:: nim
  2745. ## (" ", true)
  2746. ## ("this", false)
  2747. ## (" ", true)
  2748. ## ("is", false)
  2749. ## (" ", true)
  2750. ## ("an", false)
  2751. ## (" ", true)
  2752. ## ("example", false)
  2753. ## (" ", true)
  2754. var i = 0
  2755. while true:
  2756. var j = i
  2757. var isSep = j < s.len and s[j] in seps
  2758. while j < s.len and (s[j] in seps) == isSep: inc(j)
  2759. if j > i:
  2760. yield (substr(s, i, j-1), isSep)
  2761. else:
  2762. break
  2763. i = j
  2764. proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl,
  2765. extern: "nsuIsEmptyOrWhitespace".} =
  2766. ## Checks if `s` is empty or consists entirely of whitespace characters.
  2767. result = s.allCharsInSet(Whitespace)
  2768. when isMainModule:
  2769. proc nonStaticTests =
  2770. doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000"
  2771. when not defined(js):
  2772. doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586
  2773. doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6"
  2774. doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001"
  2775. doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in
  2776. ["1,0e-11", "1,0e-011"]
  2777. # bug #6589
  2778. when not defined(js):
  2779. doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02"
  2780. doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c"
  2781. doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb"
  2782. block: # formatSize tests
  2783. when not defined(js):
  2784. doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231
  2785. doAssert formatSize((2.234*1024*1024).int) == "2.234MiB"
  2786. doAssert formatSize(4096) == "4KiB"
  2787. doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB"
  2788. doAssert formatSize(4096, includeSpace = true) == "4 KiB"
  2789. doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB"
  2790. block: # formatEng tests
  2791. doAssert formatEng(0, 2, trim = false) == "0.00"
  2792. doAssert formatEng(0, 2) == "0"
  2793. doAssert formatEng(53, 2, trim = false) == "53.00"
  2794. doAssert formatEng(0.053, 2, trim = false) == "53.00e-3"
  2795. doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3"
  2796. doAssert formatEng(0.053, 4, trim = true) == "53e-3"
  2797. doAssert formatEng(0.053, 0) == "53e-3"
  2798. doAssert formatEng(52731234) == "52.731234e6"
  2799. doAssert formatEng(-52731234) == "-52.731234e6"
  2800. doAssert formatEng(52731234, 1) == "52.7e6"
  2801. doAssert formatEng(-52731234, 1) == "-52.7e6"
  2802. doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6"
  2803. doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6"
  2804. doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV"
  2805. doAssert formatEng(4.1, siPrefix = true, unit = "V",
  2806. useUnitSpace = true) == "4.1 V"
  2807. doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space
  2808. doAssert formatEng(4100, siPrefix = true) == "4.1 k"
  2809. doAssert formatEng(4.1, siPrefix = true, unit = "",
  2810. useUnitSpace = true) == "4.1 " # Includes space
  2811. doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k"
  2812. doAssert formatEng(4100) == "4.1e3"
  2813. doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V"
  2814. doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 "
  2815. # Don't use SI prefix as number is too big
  2816. doAssert formatEng(3.1e22, siPrefix = true, unit = "a",
  2817. useUnitSpace = true) == "31e21 a"
  2818. # Don't use SI prefix as number is too small
  2819. doAssert formatEng(3.1e-25, siPrefix = true, unit = "A",
  2820. useUnitSpace = true) == "310e-27 A"
  2821. proc staticTests =
  2822. doAssert align("abc", 4) == " abc"
  2823. doAssert align("a", 0) == "a"
  2824. doAssert align("1232", 6) == " 1232"
  2825. doAssert align("1232", 6, '#') == "##1232"
  2826. doAssert alignLeft("abc", 4) == "abc "
  2827. doAssert alignLeft("a", 0) == "a"
  2828. doAssert alignLeft("1232", 6) == "1232 "
  2829. doAssert alignLeft("1232", 6, '#') == "1232##"
  2830. doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] ==
  2831. "The cat eats fish."
  2832. doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz "
  2833. doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc"
  2834. doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc"
  2835. doAssert "oo".replace("", "abc") == "oo"
  2836. type MyEnum = enum enA, enB, enC, enuD, enE
  2837. doAssert parseEnum[MyEnum]("enu_D") == enuD
  2838. doAssert parseEnum("invalid enum value", enC) == enC
  2839. doAssert center("foo", 13) == " foo "
  2840. doAssert center("foo", 0) == "foo"
  2841. doAssert center("foo", 3, fillChar = 'a') == "foo"
  2842. doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t"
  2843. doAssert count("foofoofoo", "foofoo") == 1
  2844. doAssert count("foofoofoo", "foofoo", overlapping = true) == 2
  2845. doAssert count("foofoofoo", 'f') == 3
  2846. doAssert count("foofoofoobar", {'f', 'b'}) == 4
  2847. doAssert strip(" foofoofoo ") == "foofoofoo"
  2848. doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo"
  2849. doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo"
  2850. doAssert strip("stripme but don't strip this stripme",
  2851. chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) ==
  2852. " but don't strip this "
  2853. doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo"
  2854. doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos"
  2855. doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar"
  2856. doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab"
  2857. doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.",
  2858. "PEOPLE!")) == "HELLO PEOPLE!"
  2859. doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa"
  2860. doAssert isAlphaAscii('r')
  2861. doAssert isAlphaAscii('A')
  2862. doAssert(not isAlphaAscii('$'))
  2863. doAssert isAlphaNumeric('3')
  2864. doAssert isAlphaNumeric('R')
  2865. doAssert(not isAlphaNumeric('!'))
  2866. doAssert isDigit('3')
  2867. doAssert(not isDigit('a'))
  2868. doAssert(not isDigit('%'))
  2869. doAssert isSpaceAscii('\t')
  2870. doAssert isSpaceAscii('\l')
  2871. doAssert(not isSpaceAscii('A'))
  2872. doAssert(isEmptyOrWhitespace(""))
  2873. doAssert(isEmptyOrWhitespace(" "))
  2874. doAssert(isEmptyOrWhitespace("\t\l \v\r\f"))
  2875. doAssert(not isEmptyOrWhitespace("ABc \td"))
  2876. doAssert isLowerAscii('a')
  2877. doAssert isLowerAscii('z')
  2878. doAssert(not isLowerAscii('A'))
  2879. doAssert(not isLowerAscii('5'))
  2880. doAssert(not isLowerAscii('&'))
  2881. doAssert(not isLowerAscii(' '))
  2882. doAssert isUpperAscii('A')
  2883. doAssert(not isUpperAscii('b'))
  2884. doAssert(not isUpperAscii('5'))
  2885. doAssert(not isUpperAscii('%'))
  2886. doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"]
  2887. doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"]
  2888. doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[
  2889. " foo bar", ""]
  2890. doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"]
  2891. doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"]
  2892. doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"]
  2893. doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"]
  2894. doAssert(unescape(r"\x013", "", "") == "\x013")
  2895. doAssert join(["foo", "bar", "baz"]) == "foobarbaz"
  2896. doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz"
  2897. doAssert join([1, 2, 3]) == "123"
  2898. doAssert join(@[1, 2, 3], ", ") == "1, 2, 3"
  2899. doAssert """~~!!foo
  2900. ~~!!bar
  2901. ~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz"
  2902. doAssert """~~!!foo
  2903. ~~!!bar
  2904. ~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz"
  2905. doAssert """~~foo
  2906. ~~ bar
  2907. ~~ baz""".unindent(4, "~") == "foo\n bar\n baz"
  2908. doAssert """foo
  2909. bar
  2910. baz
  2911. """.unindent(4) == "foo\nbar\nbaz\n"
  2912. doAssert """foo
  2913. bar
  2914. baz
  2915. """.unindent(2) == "foo\n bar\n baz\n"
  2916. doAssert """foo
  2917. bar
  2918. baz
  2919. """.unindent(100) == "foo\nbar\nbaz\n"
  2920. doAssert """foo
  2921. foo
  2922. bar
  2923. """.unindent() == "foo\nfoo\nbar\n"
  2924. let s = " this is an example "
  2925. let s2 = ":this;is;an:example;;"
  2926. doAssert s.split() == @["", "this", "is", "an", "example", "", ""]
  2927. doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example",
  2928. "", ""]
  2929. doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "]
  2930. doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "]
  2931. doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "]
  2932. doAssert s.splitWhitespace() == @["this", "is", "an", "example"]
  2933. doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "]
  2934. doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "]
  2935. doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "]
  2936. doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"]
  2937. block: # startsWith / endsWith char tests
  2938. var s = "abcdef"
  2939. doAssert s.startsWith('a')
  2940. doAssert s.startsWith('b') == false
  2941. doAssert s.endsWith('f')
  2942. doAssert s.endsWith('a') == false
  2943. doAssert s.endsWith('\0') == false
  2944. #echo("strutils tests passed")
  2945. nonStaticTests()
  2946. staticTests()
  2947. static: staticTests()