format.bmx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. ' Copyright (c) 2007-2020 Bruce A Henderson
  2. '
  3. ' Permission is hereby granted, free of charge, to any person obtaining a copy
  4. ' of this software and associated documentation files (the "Software"), to deal
  5. ' in the Software without restriction, including without limitation the rights
  6. ' to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. ' copies of the Software, and to permit persons to whom the Software is
  8. ' furnished to do so, subject to the following conditions:
  9. '
  10. ' The above copyright notice and this permission notice shall be included in
  11. ' all copies or substantial portions of the Software.
  12. '
  13. ' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. ' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. ' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. ' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. ' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. ' OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. ' THE SOFTWARE.
  20. '
  21. SuperStrict
  22. Rem
  23. bbdoc: String Formatter
  24. End Rem
  25. Module Text.Format
  26. ModuleInfo "Version: 1.04"
  27. ModuleInfo "License: MIT"
  28. ModuleInfo "Copyright: 2007-2020 Bruce A Henderson"
  29. ModuleInfo "Modserver: BRL"
  30. ModuleInfo "History: 1.04"
  31. ModuleInfo "History: TStringBuilder usage improvements."
  32. ModuleInfo "History: 1.03"
  33. ModuleInfo "History: Updated for inclusion into NG/BRL."
  34. ModuleInfo "History: 1.02"
  35. ModuleInfo "History: Fixed offset problem."
  36. ModuleInfo "History: Use StringBuilder/Buffer for string concat."
  37. ModuleInfo "History: 1.01"
  38. ModuleInfo "History: Rewritten to use native snfprintf."
  39. ModuleInfo "History: License change to MIT."
  40. ModuleInfo "History: 1.00"
  41. ModuleInfo "History: Initial Release."
  42. Import Text.RegEx
  43. Import BRL.LinkedList
  44. Import BRL.StringBuilder
  45. Import "glue.c"
  46. Private
  47. Extern
  48. Function bmx_sprintf_string(format:Byte Ptr, value:String, sb:Byte Ptr)
  49. Function bmx_sprintf_float(format:Byte Ptr, value:Float, sb:Byte Ptr)
  50. Function bmx_sprintf_int(format:Byte Ptr, value:Int, sb:Byte Ptr)
  51. Function bmx_sprintf_uint(format:Byte Ptr, value:UInt, sb:Byte Ptr)
  52. Function bmx_sprintf_double(format:Byte Ptr, value:Double, sb:Byte Ptr)
  53. Function bmx_sprintf_long(format:Byte Ptr, value:Long, sb:Byte Ptr)
  54. Function bmx_sprintf_ulong(format:Byte Ptr, value:ULong, sb:Byte Ptr)
  55. Function bmx_sprintf_sizet(format:Byte Ptr, value:Size_T, sb:Byte Ptr)
  56. Function bmx_sprintf_ptr(format:Byte Ptr, value:Byte Ptr, sb:Byte Ptr)
  57. End Extern
  58. Public
  59. Rem
  60. bbdoc: The string formatter.
  61. about: Processes printf-style format strings.
  62. End Rem
  63. Type TFormatter
  64. Private
  65. Global regex:TRegEx = TRegEx.Create("%(\d+\$)?([-#+ 0,l(\<]*)?(\d+)?(\.\d+)?([a-zA-Z%])")
  66. Field Text:String
  67. Field formatParts:TStringFormatPart[]
  68. Field args:TArg[]
  69. Field argCount:Int
  70. Public
  71. Rem
  72. bbdoc: Creates a new #TFormatter object.
  73. about: Parameters:
  74. <ul>
  75. <li><b>text</b> : The text containing formatting instructions</li>
  76. </ul>
  77. End Rem
  78. Function Create:TFormatter(Text:String)
  79. Local this:TFormatter = New TFormatter
  80. this.Text = Text
  81. If Text Then
  82. this.parse()
  83. End If
  84. Return this
  85. End Function
  86. Private
  87. Method parse()
  88. Local parts:TList = New TList
  89. Local match:TRegExMatch = regex.Find(Text)
  90. Local i:Int = 0
  91. While i < Text.length
  92. If match Then
  93. ' is there text before the formatting text ?
  94. If i <> match.SubStart(0) Then
  95. parts.addLast(TPlainText.Create(Text[i..match.SubStart(0)]))
  96. End If
  97. ' this is the formatting part
  98. Local sections:String[] = New String[5]
  99. For Local n:Int = 1 Until 6
  100. sections[n-1] = match.SubExp(n)
  101. Next
  102. parts.addLast(TFormattingText.Create(sections))
  103. Else
  104. ' is there text at the end of all the formatting parts?
  105. parts.addLast(TPlainText.Create(Text[i..]))
  106. Exit
  107. End If
  108. i = match.SubEnd(0) + 1
  109. match = regex.find()
  110. Wend
  111. formatParts = New TStringFormatPart[parts.count()]
  112. i = 0
  113. For Local part:TStringFormatPart = EachIn parts
  114. formatParts[i] = part
  115. i:+ 1
  116. Next
  117. End Method
  118. Method addArg(arg:TArg)
  119. If argCount >= args.length
  120. args = args[0..argCount + 4]
  121. End If
  122. args[argCount] = arg
  123. argCount :+ 1
  124. End Method
  125. Public
  126. Rem
  127. bbdoc: Appends a #Byte argument to the formatter.
  128. End Rem
  129. Method Arg:TFormatter(value:Byte)
  130. Local arg:TByteArg = New TByteArg
  131. arg.value = value
  132. addArg(arg)
  133. Return Self
  134. End Method
  135. Rem
  136. bbdoc: Appends a #Short argument to the formatter.
  137. End Rem
  138. Method Arg:TFormatter(value:Short)
  139. Local arg:TShortArg = New TShortArg
  140. arg.value = value
  141. addArg(arg)
  142. Return Self
  143. End Method
  144. Rem
  145. bbdoc: Appends an #Int argument to the formatter.
  146. End Rem
  147. Method Arg:TFormatter(value:Int)
  148. Local arg:TIntArg = New TIntArg
  149. arg.value = value
  150. addArg(arg)
  151. Return Self
  152. End Method
  153. Rem
  154. bbdoc: Appends a #UInt argument to the formatter.
  155. End Rem
  156. Method Arg:TFormatter(value:UInt)
  157. Local arg:TUIntArg = New TUIntArg
  158. arg.value = value
  159. addArg(arg)
  160. Return Self
  161. End Method
  162. Rem
  163. bbdoc: Appends a #Long argument to the formatter.
  164. End Rem
  165. Method Arg:TFormatter(value:Long)
  166. Local arg:TLongArg = New TLongArg
  167. arg.value = value
  168. addArg(arg)
  169. Return Self
  170. End Method
  171. Rem
  172. bbdoc: Appends a #ULong argument to the formatter.
  173. End Rem
  174. Method Arg:TFormatter(value:ULong)
  175. Local arg:TULongArg = New TULongArg
  176. arg.value = value
  177. addArg(arg)
  178. Return Self
  179. End Method
  180. Rem
  181. bbdoc: Appends a #Float argument to the formatter.
  182. End Rem
  183. Method Arg:TFormatter(value:Float)
  184. Local arg:TFloatArg = New TFloatArg
  185. arg.value = value
  186. addArg(arg)
  187. Return Self
  188. End Method
  189. Rem
  190. bbdoc: Appends a #Double argument to the formatter.
  191. End Rem
  192. Method Arg:TFormatter(value:Double)
  193. Local arg:TDoubleArg = New TDoubleArg
  194. arg.value = value
  195. addArg(arg)
  196. Return Self
  197. End Method
  198. Rem
  199. bbdoc: Appends a #Size_T argument to the formatter.
  200. End Rem
  201. Method Arg:TFormatter(value:Size_T)
  202. Local arg:TSizeTArg = New TSizeTArg
  203. arg.value = value
  204. addArg(arg)
  205. Return Self
  206. End Method
  207. Rem
  208. bbdoc: Appends a Byte Ptr argument to the formatter.
  209. End Rem
  210. Method Arg:TFormatter(value:Byte Ptr)
  211. Local arg:TPtrArg = New TPtrArg
  212. arg.value = value
  213. addArg(arg)
  214. Return Self
  215. End Method
  216. Rem
  217. bbdoc: Appends a #String argument to the formatter.
  218. End Rem
  219. Method Arg:TFormatter(value:String)
  220. Local arg:TStringArg = New TStringArg
  221. arg.value = value
  222. addArg(arg)
  223. Return Self
  224. End Method
  225. Rem
  226. bbdoc: Processes and returns the formatted string.
  227. returns: The formatted String.
  228. End Rem
  229. Method Format:String(sb:TStringBuilder = Null)
  230. If Not sb Then
  231. sb = New TStringBuilder
  232. End If
  233. Local arg:Int = 0
  234. If Text Then
  235. For Local i:Int = 0 Until formatParts.length
  236. Local part:TStringFormatPart = formatParts[i]
  237. If TPlainText(part) Then
  238. sb.Append(part.ToString())
  239. Else
  240. Local fpart:TFormattingText = TFormattingText(part)
  241. If fpart.formatType = TFormattingText.FTYPE_PERCENT Then
  242. sb.Append("%")
  243. Else
  244. If (Not fpart.invalid) And (args And arg < argCount) Then
  245. fpart.processArg(args[arg], sb)
  246. ' next arg only if this was a "real" arg format
  247. If fpart.formatType <> TFormattingText.FTYPE_LINEBREAK Then
  248. arg:+ 1
  249. End If
  250. Else
  251. If Not fpart.invalid Then
  252. fpart.processArg(New TNullArg, sb)
  253. Else
  254. sb.Append(part.ToString())
  255. End If
  256. End If
  257. End If
  258. End If
  259. Next
  260. End If
  261. Return sb.ToString()
  262. End Method
  263. Rem
  264. bbdoc: Clears the formatter argument list, ready for new arguments.
  265. End Rem
  266. Method Clear:TFormatter()
  267. For Local i:Int = 0 Until argCount
  268. args[i] = Null
  269. Next
  270. argCount = 0
  271. Return Self
  272. End Method
  273. End Type
  274. Private
  275. Type TStringFormatPart
  276. Method ToString:String() Abstract
  277. End Type
  278. Type TPlainText Extends TStringFormatPart
  279. Field Text:String
  280. Function Create:TPlainText(Text:String)
  281. Local this:TPlainText = New TPlainText
  282. this.Text = Text
  283. Return this
  284. End Function
  285. Method ToString:String()
  286. Return Text
  287. End Method
  288. End Type
  289. Type TFormattingText Extends TStringFormatPart
  290. Const FTYPE_LINEBREAK:Int = 110 ' n
  291. Const FTYPE_PERCENT:Int = 37 ' %
  292. Field sections:String[]
  293. Field formatType:Int = 0
  294. Field formatText:String
  295. Field formatPtr:Byte Ptr
  296. Field invalid:Int = False
  297. Function Create:TFormattingText(sections:String[])
  298. Local this:TFormattingText = New TFormattingText
  299. this.sections = sections
  300. this.configure()
  301. Return this
  302. End Function
  303. Method configure()
  304. formatText = "%"
  305. For Local i:Int = 0 Until sections.length
  306. formatText:+ sections[i]
  307. Next
  308. If formatText = "%%" Then
  309. formatType = FTYPE_PERCENT
  310. End If
  311. formatPtr = formatText.ToCString()
  312. End Method
  313. Method ProcessArg(arg:TArg, sb:TStringBuilder)
  314. Select arg.ArgType()
  315. Case TArg.ARG_STRING
  316. bmx_sprintf_string(formatText, TStringArg(arg).value, sb.buffer)
  317. Case TArg.ARG_INT
  318. bmx_sprintf_int(formatText, TIntArg(arg).value, sb.buffer)
  319. Case TArg.ARG_UINT
  320. bmx_sprintf_uint(formatText, TUIntArg(arg).value, sb.buffer)
  321. Case TArg.ARG_FLOAT
  322. bmx_sprintf_float(formatText, TFloatArg(arg).value, sb.buffer)
  323. Case TArg.ARG_DOUBLE
  324. bmx_sprintf_double(formatText, TDoubleArg(arg).value, sb.buffer)
  325. Case TArg.ARG_LONG
  326. bmx_sprintf_long(formatText, TLongArg(arg).value, sb.buffer)
  327. Case TArg.ARG_ULONG
  328. bmx_sprintf_ulong(formatText, TULongArg(arg).value, sb.buffer)
  329. Case TArg.ARG_SIZET
  330. bmx_sprintf_sizet(formatText, TSizeTArg(arg).value, sb.buffer)
  331. Case TArg.ARG_SHORT
  332. bmx_sprintf_int(formatText, Int(TShortArg(arg).value), sb.buffer)
  333. Case TArg.ARG_BYTE
  334. bmx_sprintf_int(formatText, Int(TByteArg(arg).value), sb.buffer)
  335. Case TArg.ARG_PTR
  336. bmx_sprintf_ptr(formatText, TPtrArg(arg).value, sb.buffer)
  337. End Select
  338. End Method
  339. Method ToString:String()
  340. Return "-"
  341. End Method
  342. Method Delete()
  343. If formatPtr Then
  344. MemFree(formatPtr)
  345. formatPtr = Null
  346. End If
  347. End Method
  348. End Type
  349. Type TArg
  350. Const ARG_NULL:Int = 0
  351. Const ARG_BYTE:Int = 1
  352. Const ARG_SHORT:Int = 2
  353. Const ARG_INT:Int = 3
  354. Const ARG_UINT:Int = 4
  355. Const ARG_LONG:Int = 5
  356. Const ARG_ULONG:Int = 6
  357. Const ARG_FLOAT:Int = 7
  358. Const ARG_DOUBLE:Int = 8
  359. Const ARG_SIZET:Int = 9
  360. Const ARG_STRING:Int = 10
  361. Const ARG_PTR:Int = 11
  362. Method ToString:String() Abstract
  363. Method ArgType:Int() Abstract
  364. End Type
  365. Type TNullArg Extends TArg
  366. Method ToString:String()
  367. Return Null
  368. End Method
  369. Method ArgType:Int()
  370. Return ARG_NULL
  371. End Method
  372. End Type
  373. Type TByteArg Extends TArg
  374. Field value:Byte
  375. Method ToString:String()
  376. Return String.fromInt(Int(value))
  377. End Method
  378. Method ArgType:Int()
  379. Return ARG_BYTE
  380. End Method
  381. End Type
  382. Type TShortArg Extends TArg
  383. Field value:Short
  384. Method ToString:String()
  385. Return String.fromInt(Int(value))
  386. End Method
  387. Method ArgType:Int()
  388. Return ARG_SHORT
  389. End Method
  390. End Type
  391. Type TIntArg Extends TArg
  392. Field value:Int
  393. Method ToString:String()
  394. Return String.fromInt(value)
  395. End Method
  396. Method ArgType:Int()
  397. Return ARG_INT
  398. End Method
  399. End Type
  400. Type TUIntArg Extends TArg
  401. Field value:UInt
  402. Method ToString:String()
  403. Return String.fromUInt(value)
  404. End Method
  405. Method ArgType:Int()
  406. Return ARG_UINT
  407. End Method
  408. End Type
  409. Type TFloatArg Extends TArg
  410. Field value:Float
  411. Method ToString:String()
  412. Return String.fromFloat(value)
  413. End Method
  414. Method ArgType:Int()
  415. Return ARG_FLOAT
  416. End Method
  417. End Type
  418. Type TDoubleArg Extends TArg
  419. Field value:Double
  420. Method ToString:String()
  421. Return String.fromDouble(value)
  422. End Method
  423. Method ArgType:Int()
  424. Return ARG_DOUBLE
  425. End Method
  426. End Type
  427. Type TStringArg Extends TArg
  428. Field value:String
  429. Method ToString:String()
  430. Return value
  431. End Method
  432. Method ArgType:Int()
  433. Return ARG_STRING
  434. End Method
  435. End Type
  436. Type TLongArg Extends TArg
  437. Field value:Long
  438. Method ToString:String()
  439. Return String.fromLong(value)
  440. End Method
  441. Method ArgType:Int()
  442. Return ARG_LONG
  443. End Method
  444. End Type
  445. Type TULongArg Extends TArg
  446. Field value:ULong
  447. Method ToString:String()
  448. Return String.fromULong(value)
  449. End Method
  450. Method ArgType:Int()
  451. Return ARG_ULONG
  452. End Method
  453. End Type
  454. Type TSizeTArg Extends TArg
  455. Field value:Size_T
  456. Method ToString:String()
  457. Return String.fromSizeT(value)
  458. End Method
  459. Method ArgType:Int()
  460. Return ARG_SIZET
  461. End Method
  462. End Type
  463. Type TPtrArg Extends TArg
  464. Field value:Byte Ptr
  465. Method ToString:String()
  466. Return String.fromSizeT((Size_T Ptr(value))[0])
  467. End Method
  468. Method ArgType:Int()
  469. Return ARG_PTR
  470. End Method
  471. End Type