carats 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (422) hide show
  1. data/ChangeLog +2 -0
  2. data/LICENSE +5 -0
  3. data/README +67 -0
  4. data/Reapfile +72 -0
  5. data/ToDo +24 -0
  6. data/bench/b_harray.rb +49 -0
  7. data/bench/b_range.rb +64 -0
  8. data/demo/ansicolor/cdiff.rb +20 -0
  9. data/demo/ansicolor/example.rb +82 -0
  10. data/demo/io-reactor/chatserver.rb +347 -0
  11. data/doc/classes/Array.html +172 -0
  12. data/doc/classes/BBCode.html +638 -0
  13. data/doc/classes/BinaryReader.html +675 -0
  14. data/doc/classes/Binding.html +223 -0
  15. data/doc/classes/ByteOrder.html +284 -0
  16. data/doc/classes/Class.html +189 -0
  17. data/doc/classes/Constant.html +785 -0
  18. data/doc/classes/Constant/CGS.html +609 -0
  19. data/doc/classes/Continuation.html +113 -0
  20. data/doc/classes/Coroutine.html +387 -0
  21. data/doc/classes/CountingSemaphore.html +337 -0
  22. data/doc/classes/CrossCase.html +561 -0
  23. data/doc/classes/DynamicMixin.html +185 -0
  24. data/doc/classes/EnumerableWithArgs.html +550 -0
  25. data/doc/classes/Expect.html +428 -0
  26. data/doc/classes/Expect/DefaultChain.html +113 -0
  27. data/doc/classes/Expect/SeparatedPattern.html +223 -0
  28. data/doc/classes/Expect/Timeout.html +167 -0
  29. data/doc/classes/Expect/UnmatchedMessage.html +160 -0
  30. data/doc/classes/FileList.html +833 -0
  31. data/doc/classes/FloatString.html +534 -0
  32. data/doc/classes/Functor.html +211 -0
  33. data/doc/classes/HArray.html +1872 -0
  34. data/doc/classes/HTML.html +328 -0
  35. data/doc/classes/Heap.html +683 -0
  36. data/doc/classes/Heap/EmptyHeapException.html +111 -0
  37. data/doc/classes/Heap/Max.html +176 -0
  38. data/doc/classes/Heap/Min.html +176 -0
  39. data/doc/classes/IO.html +108 -0
  40. data/doc/classes/IO/Reactor.html +790 -0
  41. data/doc/classes/IO/Redirect.html +289 -0
  42. data/doc/classes/Infinity.html +320 -0
  43. data/doc/classes/Interval.html +1014 -0
  44. data/doc/classes/InvalidNackError.html +161 -0
  45. data/doc/classes/Kernel.html +204 -0
  46. data/doc/classes/Lisp.html +1132 -0
  47. data/doc/classes/Lisp/DottedPair.html +379 -0
  48. data/doc/classes/Lisp/Format.html +178 -0
  49. data/doc/classes/Lisp/Format/ArgumentError.html +126 -0
  50. data/doc/classes/Lisp/Format/Directives.html +155 -0
  51. data/doc/classes/Lisp/Format/Directives/ArgJump.html +178 -0
  52. data/doc/classes/Lisp/Format/Directives/Ascii.html +119 -0
  53. data/doc/classes/Lisp/Format/Directives/BeginCaseConversion.html +185 -0
  54. data/doc/classes/Lisp/Format/Directives/BeginConditional.html +307 -0
  55. data/doc/classes/Lisp/Format/Directives/BeginIteration.html +338 -0
  56. data/doc/classes/Lisp/Format/Directives/Binary.html +157 -0
  57. data/doc/classes/Lisp/Format/Directives/Character.html +228 -0
  58. data/doc/classes/Lisp/Format/Directives/CharacterDirective.html +203 -0
  59. data/doc/classes/Lisp/Format/Directives/ClauseSeparator.html +166 -0
  60. data/doc/classes/Lisp/Format/Directives/Decimal.html +119 -0
  61. data/doc/classes/Lisp/Format/Directives/Directive.html +397 -0
  62. data/doc/classes/Lisp/Format/Directives/DollarFP.html +205 -0
  63. data/doc/classes/Lisp/Format/Directives/EndCaseConversion.html +174 -0
  64. data/doc/classes/Lisp/Format/Directives/EndConditional.html +159 -0
  65. data/doc/classes/Lisp/Format/Directives/EndIteration.html +163 -0
  66. data/doc/classes/Lisp/Format/Directives/ExpFP.html +252 -0
  67. data/doc/classes/Lisp/Format/Directives/FFFP.html +224 -0
  68. data/doc/classes/Lisp/Format/Directives/Factory.html +180 -0
  69. data/doc/classes/Lisp/Format/Directives/FreshLine.html +175 -0
  70. data/doc/classes/Lisp/Format/Directives/GeneralFP.html +265 -0
  71. data/doc/classes/Lisp/Format/Directives/Hexadecimal.html +157 -0
  72. data/doc/classes/Lisp/Format/Directives/Indirection.html +183 -0
  73. data/doc/classes/Lisp/Format/Directives/Literal.html +187 -0
  74. data/doc/classes/Lisp/Format/Directives/NewLine.html +158 -0
  75. data/doc/classes/Lisp/Format/Directives/NewPage.html +158 -0
  76. data/doc/classes/Lisp/Format/Directives/Number.html +243 -0
  77. data/doc/classes/Lisp/Format/Directives/Octal.html +157 -0
  78. data/doc/classes/Lisp/Format/Directives/Plural.html +177 -0
  79. data/doc/classes/Lisp/Format/Directives/Print.html +237 -0
  80. data/doc/classes/Lisp/Format/Directives/Radix.html +432 -0
  81. data/doc/classes/Lisp/Format/Directives/SExpression.html +157 -0
  82. data/doc/classes/Lisp/Format/Directives/SkipWhitespace.html +165 -0
  83. data/doc/classes/Lisp/Format/Directives/Tabulate.html +198 -0
  84. data/doc/classes/Lisp/Format/Directives/Tilde.html +158 -0
  85. data/doc/classes/Lisp/Format/Directives/UnknownDirectiveError.html +117 -0
  86. data/doc/classes/Lisp/Format/Formatter.html +193 -0
  87. data/doc/classes/Lisp/Format/IncompleteParameterError.html +119 -0
  88. data/doc/classes/Lisp/Format/IndexError.html +126 -0
  89. data/doc/classes/Lisp/Format/Lexer.html +479 -0
  90. data/doc/classes/Lisp/Format/MalformedError.html +119 -0
  91. data/doc/classes/Lisp/Format/MissingParameterError.html +119 -0
  92. data/doc/classes/Lisp/Format/ModifierError.html +119 -0
  93. data/doc/classes/Lisp/Format/Output.html +239 -0
  94. data/doc/classes/Lisp/Format/ParameterError.html +126 -0
  95. data/doc/classes/Lisp/Format/Parameters.html +123 -0
  96. data/doc/classes/Lisp/Format/Parameters/Argument.html +172 -0
  97. data/doc/classes/Lisp/Format/Parameters/ArgumentCount.html +159 -0
  98. data/doc/classes/Lisp/Format/Parameters/Character.html +119 -0
  99. data/doc/classes/Lisp/Format/Parameters/Default.html +120 -0
  100. data/doc/classes/Lisp/Format/Parameters/Integer.html +119 -0
  101. data/doc/classes/Lisp/Format/Parameters/Parameter.html +206 -0
  102. data/doc/classes/Lisp/Format/Parser.html +212 -0
  103. data/doc/classes/Lisp/Format/Positioned.html +166 -0
  104. data/doc/classes/Lisp/Format/State.html +455 -0
  105. data/doc/classes/Lisp/Format/SyntaxError.html +126 -0
  106. data/doc/classes/Method.html +274 -0
  107. data/doc/classes/Method/DuckHunter.html +281 -0
  108. data/doc/classes/Module.html +709 -0
  109. data/doc/classes/Multiton.html +284 -0
  110. data/doc/classes/Multiton/New.html +164 -0
  111. data/doc/classes/Multiton/Semi.html +155 -0
  112. data/doc/classes/NackClass.html +322 -0
  113. data/doc/classes/NilClass.html +231 -0
  114. data/doc/classes/NilComparable.html +179 -0
  115. data/doc/classes/NotCopyable.html +273 -0
  116. data/doc/classes/Object.html +261 -0
  117. data/doc/classes/OpenStructable.html +346 -0
  118. data/doc/classes/OrderedHash.html +939 -0
  119. data/doc/classes/Parser.html +515 -0
  120. data/doc/classes/Parser/Main.html +178 -0
  121. data/doc/classes/Parser/Marker.html +475 -0
  122. data/doc/classes/Parser/Registry.html +354 -0
  123. data/doc/classes/Parser/Token.html +399 -0
  124. data/doc/classes/Parser/UnitToken.html +222 -0
  125. data/doc/classes/Ref.html +230 -0
  126. data/doc/classes/SimpleStringIO.html +183 -0
  127. data/doc/classes/StaticHash.html +202 -0
  128. data/doc/classes/String.html +172 -0
  129. data/doc/classes/Struct.html +181 -0
  130. data/doc/classes/SuperStruct.html +382 -0
  131. data/doc/classes/Term.html +156 -0
  132. data/doc/classes/Term/ANSIColor.html +185 -0
  133. data/doc/classes/Text.html +138 -0
  134. data/doc/classes/Text/Soundex.html +222 -0
  135. data/doc/classes/TextTagIterator.html +911 -0
  136. data/doc/classes/Time.html +119 -0
  137. data/doc/classes/Time/Limit.html +303 -0
  138. data/doc/classes/Time/Limit/Dummy.html +181 -0
  139. data/doc/classes/Timer.html +351 -0
  140. data/doc/classes/TracePoint.html +532 -0
  141. data/doc/classes/Tuple.html +178 -0
  142. data/doc/classes/Uninheritable.html +213 -0
  143. data/doc/classes/Version.html +783 -0
  144. data/doc/created.rid +1 -0
  145. data/doc/files/LICENSE.html +113 -0
  146. data/doc/files/README.html +201 -0
  147. data/doc/files/lib/carat/1st_rb.html +107 -0
  148. data/doc/files/lib/carat/ansicolor_rb.html +146 -0
  149. data/doc/files/lib/carat/attr_rb.html +101 -0
  150. data/doc/files/lib/carat/bbcode_rb.html +152 -0
  151. data/doc/files/lib/carat/binaryreader_rb.html +114 -0
  152. data/doc/files/lib/carat/binding-of-caller_rb.html +108 -0
  153. data/doc/files/lib/carat/byteorder_rb.html +107 -0
  154. data/doc/files/lib/carat/constant_rb.html +167 -0
  155. data/doc/files/lib/carat/coroutine_rb.html +156 -0
  156. data/doc/files/lib/carat/crosscase_rb.html +224 -0
  157. data/doc/files/lib/carat/duckhunter_rb.html +165 -0
  158. data/doc/files/lib/carat/dynamic-mixin_rb.html +101 -0
  159. data/doc/files/lib/carat/enumerable-args_rb.html +140 -0
  160. data/doc/files/lib/carat/expect_rb.html +137 -0
  161. data/doc/files/lib/carat/filelist_rb.html +130 -0
  162. data/doc/files/lib/carat/floatstring_rb.html +126 -0
  163. data/doc/files/lib/carat/functor_rb.html +140 -0
  164. data/doc/files/lib/carat/harray_rb.html +128 -0
  165. data/doc/files/lib/carat/heap_rb.html +154 -0
  166. data/doc/files/lib/carat/html-helpers_rb.html +116 -0
  167. data/doc/files/lib/carat/infinity_rb.html +142 -0
  168. data/doc/files/lib/carat/init_rb.html +127 -0
  169. data/doc/files/lib/carat/interval_rb.html +162 -0
  170. data/doc/files/lib/carat/io-reactor_rb.html +211 -0
  171. data/doc/files/lib/carat/io-redirect_rb.html +132 -0
  172. data/doc/files/lib/carat/lisp-format_rb.html +187 -0
  173. data/doc/files/lib/carat/lisp_rb.html +152 -0
  174. data/doc/files/lib/carat/macro_rb.html +129 -0
  175. data/doc/files/lib/carat/multiton_rb.html +178 -0
  176. data/doc/files/lib/carat/nack_rb.html +193 -0
  177. data/doc/files/lib/carat/nil-comparable_rb.html +126 -0
  178. data/doc/files/lib/carat/nil-missing_rb.html +101 -0
  179. data/doc/files/lib/carat/notcopyable_rb.html +166 -0
  180. data/doc/files/lib/carat/orderedhash_rb.html +107 -0
  181. data/doc/files/lib/carat/ostructable_rb.html +157 -0
  182. data/doc/files/lib/carat/parser-old_rb.html +239 -0
  183. data/doc/files/lib/carat/parser_rb.html +244 -0
  184. data/doc/files/lib/carat/ref_rb.html +107 -0
  185. data/doc/files/lib/carat/semaphore_rb.html +132 -0
  186. data/doc/files/lib/carat/snapshot_rb.html +152 -0
  187. data/doc/files/lib/carat/soundex_rb.html +128 -0
  188. data/doc/files/lib/carat/sstruct_rb.html +142 -0
  189. data/doc/files/lib/carat/statichash_rb.html +150 -0
  190. data/doc/files/lib/carat/tagiter_rb.html +236 -0
  191. data/doc/files/lib/carat/timelimit_rb.html +136 -0
  192. data/doc/files/lib/carat/timer_rb.html +153 -0
  193. data/doc/files/lib/carat/tracepoint_rb.html +160 -0
  194. data/doc/files/lib/carat/tuple_rb.html +123 -0
  195. data/doc/files/lib/carat/uninheritable_rb.html +101 -0
  196. data/doc/files/lib/carat/version_rb.html +141 -0
  197. data/doc/fr_class_index.html +159 -0
  198. data/doc/fr_file_index.html +78 -0
  199. data/doc/fr_method_index.html +663 -0
  200. data/doc/index.html +24 -0
  201. data/doc/rdoc-style.css +208 -0
  202. data/lib/carat-dev/README +11 -0
  203. data/lib/carat-dev/access-blocks/access_blocks.rb +42 -0
  204. data/lib/carat-dev/association/association.rb +70 -0
  205. data/lib/carat-dev/blankslate/blankslate.rb +51 -0
  206. data/lib/carat-dev/breakpoint.rb +518 -0
  207. data/lib/carat-dev/class-constructor/constructor_methods.rb +78 -0
  208. data/lib/carat-dev/class-constructor/tc_constructor.rb +35 -0
  209. data/lib/carat-dev/closecall/closecall-test.rb +39 -0
  210. data/lib/carat-dev/closecall/closecall.rb +101 -0
  211. data/lib/carat-dev/commandrunner/commandrunner.rb +100 -0
  212. data/lib/carat-dev/conditional/conditionals.rb +106 -0
  213. data/lib/carat-dev/daemon/daemon.rb +70 -0
  214. data/lib/carat-dev/detach/detach.rb +363 -0
  215. data/lib/carat-dev/detach/tc_detach.rb +57 -0
  216. data/lib/carat-dev/import-module/import-module-0.81.tar.gz +0 -0
  217. data/lib/carat-dev/import-module/import-module-0.81/LICENSE +1 -0
  218. data/lib/carat-dev/import-module/import-module-0.81/Makefile +11 -0
  219. data/lib/carat-dev/import-module/import-module-0.81/README +4 -0
  220. data/lib/carat-dev/import-module/import-module-0.81/dev-lib/import-module-hash.rb +49 -0
  221. data/lib/carat-dev/import-module/import-module-0.81/dev-lib/import-module-pip.rb +71 -0
  222. data/lib/carat-dev/import-module/import-module-0.81/dev-lib/import-module-unbound-method.rb +36 -0
  223. data/lib/carat-dev/import-module/import-module-0.81/doc/README-ja.html +293 -0
  224. data/lib/carat-dev/import-module/import-module-0.81/doc/README-ja.rd +306 -0
  225. data/lib/carat-dev/import-module/import-module-0.81/doc/README.html +290 -0
  226. data/lib/carat-dev/import-module/import-module-0.81/doc/README.rd +303 -0
  227. data/lib/carat-dev/import-module/import-module-0.81/doc/style.css +85 -0
  228. data/lib/carat-dev/import-module/import-module-0.81/install.rb +99 -0
  229. data/lib/carat-dev/import-module/import-module-0.81/lib/import-module-single-thread.rb +84 -0
  230. data/lib/carat-dev/import-module/import-module-0.81/lib/import-module.rb +493 -0
  231. data/lib/carat-dev/import-module/import-module-0.81/test/Makefile +2 -0
  232. data/lib/carat-dev/import-module/import-module-0.81/test/test +91 -0
  233. data/lib/carat-dev/import-module/import-module-0.81/test/test-import-module.rb +936 -0
  234. data/lib/carat-dev/import-module/import-module-0.81/test/test-import_scope.rb +33 -0
  235. data/lib/carat-dev/import-module/import-module-0.81/test/test-scope_import.rb +33 -0
  236. data/lib/carat-dev/import-module/import-module-0.81/test/test-time.rb +184 -0
  237. data/lib/carat-dev/import-module/import-module-0.81/test/test.sh +91 -0
  238. data/lib/carat-dev/import-module/import-module-0.81/test/time.rb +135 -0
  239. data/lib/carat-dev/import-module/import-module-0.81/test/times-do.sh +10 -0
  240. data/lib/carat-dev/interface_work/SCRAP/i-contracts.rb +37 -0
  241. data/lib/carat-dev/interface_work/SCRAP/interface.rb +105 -0
  242. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0.zip +0 -0
  243. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/CHANGES +3 -0
  244. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/MANIFEST +15 -0
  245. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/README +32 -0
  246. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/doc/interface.rd +73 -0
  247. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/doc/interface.txt +94 -0
  248. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/install.rb +16 -0
  249. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/lib/interface.rb +50 -0
  250. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/test/tc_interface.rb +51 -0
  251. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/test/test.rb +29 -0
  252. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/test/test_instance.rb +24 -0
  253. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/test/test_sub.rb +29 -0
  254. data/lib/carat-dev/interface_work/SCRAP/j-interface/interface-0.1.0/test/test_unrequire.rb +28 -0
  255. data/lib/carat-dev/interface_work/contracts/contract.zip +0 -0
  256. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.html +376 -0
  257. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000001.html +23 -0
  258. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000002.html +18 -0
  259. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000003.html +19 -0
  260. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000004.html +29 -0
  261. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000005.html +18 -0
  262. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract.src/M000006.html +18 -0
  263. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract/ContractError.html +142 -0
  264. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract/ContractException.html +153 -0
  265. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Contract/ContractMismatch.html +125 -0
  266. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Module.html +157 -0
  267. data/lib/carat-dev/interface_work/contracts/contract/doc/classes/Module.src/M000007.html +112 -0
  268. data/lib/carat-dev/interface_work/contracts/contract/doc/created.rid +1 -0
  269. data/lib/carat-dev/interface_work/contracts/contract/doc/files/lib/contract/assertions_rb.html +109 -0
  270. data/lib/carat-dev/interface_work/contracts/contract/doc/files/lib/contract/exception_rb.html +108 -0
  271. data/lib/carat-dev/interface_work/contracts/contract/doc/files/lib/contract/integration_rb.html +108 -0
  272. data/lib/carat-dev/interface_work/contracts/contract/doc/files/lib/contract/overrides_rb.html +109 -0
  273. data/lib/carat-dev/interface_work/contracts/contract/doc/files/lib/contract_rb.html +114 -0
  274. data/lib/carat-dev/interface_work/contracts/contract/doc/fr_class_index.html +31 -0
  275. data/lib/carat-dev/interface_work/contracts/contract/doc/fr_file_index.html +31 -0
  276. data/lib/carat-dev/interface_work/contracts/contract/doc/fr_method_index.html +33 -0
  277. data/lib/carat-dev/interface_work/contracts/contract/doc/index.html +24 -0
  278. data/lib/carat-dev/interface_work/contracts/contract/doc/rdoc-style.css +208 -0
  279. data/lib/carat-dev/interface_work/contracts/contract/lib/contract.rb +88 -0
  280. data/lib/carat-dev/interface_work/contracts/contract/lib/contract/assertions.rb +37 -0
  281. data/lib/carat-dev/interface_work/contracts/contract/lib/contract/exception.rb +92 -0
  282. data/lib/carat-dev/interface_work/contracts/contract/lib/contract/integration.rb +132 -0
  283. data/lib/carat-dev/interface_work/contracts/contract/lib/contract/overrides.rb +32 -0
  284. data/lib/carat-dev/interface_work/must.rb +37 -0
  285. data/lib/carat-dev/interface_work/type.rb +232 -0
  286. data/lib/carat-dev/main/tc_toplevel.rb +23 -0
  287. data/lib/carat-dev/main/toplevel.rb +33 -0
  288. data/lib/carat-dev/main/toplevel_test.rb +24 -0
  289. data/lib/carat-dev/misc/1.rb +40 -0
  290. data/lib/carat-dev/misc/misc.rb +44 -0
  291. data/lib/carat-dev/misc/sys.rb +186 -0
  292. data/lib/carat-dev/misc/temp.rb +1 -0
  293. data/lib/carat-dev/misc/utils.rb +518 -0
  294. data/lib/carat-dev/predicate/predicate.rb +109 -0
  295. data/lib/carat-dev/predicate/predicate_test.rb +67 -0
  296. data/lib/carat-dev/premshree.rb +40 -0
  297. data/lib/carat-dev/priority-queue/pqueue.rb +183 -0
  298. data/lib/carat-dev/priority-queue/priorityqueue.rb +84 -0
  299. data/lib/carat-dev/quaternion/quaternion.rb +529 -0
  300. data/lib/carat-dev/range.rb +259 -0
  301. data/lib/carat-dev/system/rubyinfo.rb +75 -0
  302. data/lib/carat-dev/system/system.rb +105 -0
  303. data/lib/carat-dev/tc_range.rb +118 -0
  304. data/lib/carat-dev/timer/timer-doc/classes/Timer.html +291 -0
  305. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000001.html +26 -0
  306. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000002.html +19 -0
  307. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000003.html +18 -0
  308. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000004.html +18 -0
  309. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000005.html +21 -0
  310. data/lib/carat-dev/timer/timer-doc/classes/Timer.src/M000006.html +23 -0
  311. data/lib/carat-dev/timer/timer-doc/created.rid +1 -0
  312. data/lib/carat-dev/timer/timer-doc/files/timer_rb.html +144 -0
  313. data/lib/carat-dev/timer/timer-doc/fr_class_index.html +27 -0
  314. data/lib/carat-dev/timer/timer-doc/fr_file_index.html +27 -0
  315. data/lib/carat-dev/timer/timer-doc/fr_method_index.html +32 -0
  316. data/lib/carat-dev/timer/timer-doc/index.html +24 -0
  317. data/lib/carat-dev/timer/timer-doc/rdoc-style.css +208 -0
  318. data/lib/carat-dev/timer/timer.rb +160 -0
  319. data/lib/carat-dev/vars/vars.rb +62 -0
  320. data/lib/carat/1st.rb +58 -0
  321. data/lib/carat/ansicolor.rb +122 -0
  322. data/lib/carat/attr.rb +342 -0
  323. data/lib/carat/bbcode.rb +352 -0
  324. data/lib/carat/binaryreader.rb +170 -0
  325. data/lib/carat/binding-of-caller.rb +83 -0
  326. data/lib/carat/byteorder.rb +34 -0
  327. data/lib/carat/constant.rb +287 -0
  328. data/lib/carat/coroutine.rb +102 -0
  329. data/lib/carat/crosscase.rb +309 -0
  330. data/lib/carat/duckhunter.rb +158 -0
  331. data/lib/carat/dynamic-mixin.rb +71 -0
  332. data/lib/carat/enumerable-args.rb +125 -0
  333. data/lib/carat/expect.rb +192 -0
  334. data/lib/carat/filelist.rb +277 -0
  335. data/lib/carat/floatstring.rb +153 -0
  336. data/lib/carat/functor.rb +41 -0
  337. data/lib/carat/harray.rb +507 -0
  338. data/lib/carat/heap.rb +193 -0
  339. data/lib/carat/html-helpers.rb +82 -0
  340. data/lib/carat/infinity.rb +61 -0
  341. data/lib/carat/init.rb +33 -0
  342. data/lib/carat/interval.rb +248 -0
  343. data/lib/carat/io-reactor.rb +657 -0
  344. data/lib/carat/io-redirect.rb +99 -0
  345. data/lib/carat/lisp-format.rb +1832 -0
  346. data/lib/carat/lisp.rb +336 -0
  347. data/lib/carat/macro.rb +68 -0
  348. data/lib/carat/multiton.rb +153 -0
  349. data/lib/carat/nack.rb +115 -0
  350. data/lib/carat/nil-comparable.rb +67 -0
  351. data/lib/carat/nil-missing.rb +12 -0
  352. data/lib/carat/notcopyable.rb +68 -0
  353. data/lib/carat/orderedhash.rb +249 -0
  354. data/lib/carat/ostructable.rb +138 -0
  355. data/lib/carat/parser-old.rb +350 -0
  356. data/lib/carat/parser.rb +393 -0
  357. data/lib/carat/ref.rb +31 -0
  358. data/lib/carat/semaphore.rb +69 -0
  359. data/lib/carat/snapshot.rb +94 -0
  360. data/lib/carat/soundex.rb +104 -0
  361. data/lib/carat/sstruct.rb +223 -0
  362. data/lib/carat/statichash.rb +49 -0
  363. data/lib/carat/tagiter.rb +345 -0
  364. data/lib/carat/timelimit.rb +114 -0
  365. data/lib/carat/timer.rb +99 -0
  366. data/lib/carat/tracepoint.rb +154 -0
  367. data/lib/carat/tuple.rb +31 -0
  368. data/lib/carat/uninheritable.rb +68 -0
  369. data/lib/carat/version.rb +147 -0
  370. data/pub/doc/sstruct.txt +204 -0
  371. data/pub/doc/tagiter.html +145 -0
  372. data/setup.rb +1361 -0
  373. data/test/fixtures/filelist/testfile.txt +1 -0
  374. data/test/fixtures/filelist/testfile2.txt +1 -0
  375. data/test/tc_1st.rb +16 -0
  376. data/test/tc_ansicolor.rb +23 -0
  377. data/test/tc_attr.rb +46 -0
  378. data/test/tc_bbcode.rb +20 -0
  379. data/test/tc_binaryreader.rb +14 -0
  380. data/test/tc_binding_of_caller.rb +2 -0
  381. data/test/tc_byteorder.rb +41 -0
  382. data/test/tc_constant.rb +12 -0
  383. data/test/tc_coroutine.rb +50 -0
  384. data/test/tc_crosscase.rb +33 -0
  385. data/test/tc_duckhunter.rb +37 -0
  386. data/test/tc_dynamic-mixin.rb +66 -0
  387. data/test/tc_enumerable-args.rb +66 -0
  388. data/test/tc_expect.rb +83 -0
  389. data/test/tc_filelist.rb +17 -0
  390. data/test/tc_floatstring.rb +22 -0
  391. data/test/tc_functor.rb +18 -0
  392. data/test/tc_harray.rb +280 -0
  393. data/test/tc_heap.rb +6 -0
  394. data/test/tc_infinity.rb +23 -0
  395. data/test/tc_init.rb +29 -0
  396. data/test/tc_interval.rb +153 -0
  397. data/test/tc_io-reactor.rb +5 -0
  398. data/test/tc_io-redirect.rb +5 -0
  399. data/test/tc_lisp-format.rb +24 -0
  400. data/test/tc_lisp.rb +32 -0
  401. data/test/tc_macro.rb +48 -0
  402. data/test/tc_multiton.rb +188 -0
  403. data/test/tc_nack.rb +31 -0
  404. data/test/tc_nil-as-emptiness.rb +19 -0
  405. data/test/tc_nil-comparable.rb +29 -0
  406. data/test/tc_nil-missing.rb +0 -0
  407. data/test/tc_notcopyable.rb +64 -0
  408. data/test/tc_orderedhash.rb +36 -0
  409. data/test/tc_ostructable.rb +31 -0
  410. data/test/tc_semaphore.rb +5 -0
  411. data/test/tc_snapshot.rb +20 -0
  412. data/test/tc_soundex.rb +19 -0
  413. data/test/tc_sstruct.rb +313 -0
  414. data/test/tc_statichash.rb +24 -0
  415. data/test/tc_tagiter.rb +80 -0
  416. data/test/tc_timelimit.rb +47 -0
  417. data/test/tc_timer.rb +35 -0
  418. data/test/tc_tracepoint.rb +10 -0
  419. data/test/tc_tuple.rb +25 -0
  420. data/test/tc_uninheritable.rb +33 -0
  421. data/test/tc_version.rb +90 -0
  422. metadata +531 -0
@@ -0,0 +1,99 @@
1
+ =begin rdoc
2
+
3
+ = IO::Redirect
4
+
5
+ A class to redirect $stdout, or other IO object, to a StringIO object,
6
+ or any other object with a write() method.
7
+
8
+ == Synopsis
9
+
10
+ require 'raspberry/new/io-redirect'
11
+
12
+ s = StringIO.new
13
+ r = Redirector.redirect($stdout, s) do
14
+ $stdout.puts "this is a test"
15
+ end
16
+
17
+ == Legal
18
+
19
+ Ported from Ruby treasures
20
+ Copyright (C) 2002 Paul Brannan <paul@atdesk.com>
21
+
22
+ You may distribute this software under the same terms as Ruby (see the file
23
+ COPYING that was distributed with this library).
24
+
25
+ == Version
26
+
27
+ $id: io-redirect.rb 2004/10/27 10:00:01 transami Exp $
28
+
29
+ =end
30
+
31
+ class IO::Redirect
32
+
33
+ # Start redirection from one IO object to any other object with a
34
+ # write() method. +from+ is the IO object to redirect from,
35
+ # and +to+ is the object to redirect to.
36
+ def initialize(from, to)
37
+ @from = from
38
+ @to = to
39
+ start()
40
+ end
41
+
42
+ # Start redirection, if it has not already been started.
43
+ def start
44
+ raise "Redirection already in progress" if @t
45
+ tmp = @from.dup
46
+ r, w = IO.pipe
47
+ @from.reopen(w)
48
+ @t = Thread.new do
49
+ begin
50
+ loop do
51
+ s = r.read(1) # TODO: can I make this buffered?
52
+ @to.write(s)
53
+ end
54
+ ensure
55
+ @from.reopen(tmp)
56
+ @t = nil
57
+ end
58
+ end
59
+ end
60
+
61
+ # Stop redirection, if it is occurring
62
+ def stop
63
+ raise "Redirection already stopped" if not @t
64
+ @t.kill
65
+ end
66
+
67
+ # An exception-safe class method for redirection
68
+ def self.redirect(from, to)
69
+ s = self.new(from, to)
70
+ begin
71
+ yield
72
+ ensure
73
+ s.stop
74
+ end
75
+ end
76
+ end
77
+
78
+
79
+ # --- test ---
80
+
81
+ if __FILE__ == $0 then
82
+
83
+ class SimpleStringIO
84
+ attr_reader :str
85
+ def initialize; @str = ''; end
86
+ def write(str); @str << str; end
87
+ end
88
+
89
+ Thread.abort_on_exception = true
90
+ s = SimpleStringIO.new
91
+ r = Redirector.redirect($stdout, s) do
92
+ $stdout.puts "this is a test"
93
+ $stdout.puts "of the StringIO redirection system"
94
+ end
95
+ puts "Done redirecting."
96
+ puts "Result:\n#{s.str}"
97
+
98
+ end
99
+
@@ -0,0 +1,1832 @@
1
+ =begin rdoc
2
+
3
+ = Format Lisp module
4
+
5
+ "So what is *ruby-lisp*?", you ask. Well, it is a module for implementing nice
6
+ Lisp features for use in Ruby. Currently the only sub-module that exists is
7
+ Lisp::Format, which implements Lisp's (format) function and its formatting
8
+ language. For people not familiar with this language, it is a very expressive
9
+ string formatting language, much like sprintf()'s, but much more powerful. It
10
+ allows for iteration, case conversion, conditionals, and much more. If you
11
+ have ever felt stupid while creating a string iteratively, to print an array or
12
+ some such, this is definitely for you!
13
+
14
+ This module contains various Lisp facilities that are of general usefulness.
15
+ Lisp's rich string formatting language is implemented in the Format module.
16
+ More modules may be added later.
17
+
18
+ This has been (very simply) integrated into the namespace of Lsip from lisp.rb.
19
+
20
+ == Author(s)
21
+
22
+ * Nikolai Weibull <source@pcppopper.org>
23
+
24
+ == More Information
25
+
26
+ The project website for ruby-lisp is currently located at
27
+ http://ned.rubyforge.org/.
28
+
29
+ == Legal
30
+
31
+ Copyright (C) 2003 Nikolai Weibull <source@pcppopper.org>.
32
+
33
+ This library is free software; you can redistribute it and/or
34
+ modify it under the terms of the GNU Lesser General Public
35
+ License as published by the Free Software Foundation; either
36
+ version 2.1 of the License, or (at your option) any later version.
37
+
38
+ This library is distributed in the hope that it will be useful,
39
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
40
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41
+ Lesser General Public License for more details.
42
+
43
+ You should have received a copy of the GNU Lesser General Public
44
+ License along with this library; if not, write to the Free Software
45
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
46
+
47
+ == TODO
48
+
49
+ * the floating point functions are rather horendous
50
+
51
+ == History
52
+
53
+ * 2004-11-16
54
+ ** Ported en-masse as a "sub-space" of Lisp.
55
+ * 2003-12-08 Nikolai Weibull <source@pcppopper.org>
56
+ ** AUTHORS,ChangeLog,COPYING,README,setup.rb: New files.
57
+ ** lib/: Created directory.
58
+ ** format.rb,lib/format.rb: Moved file.
59
+ ** Releasing version 0.2.
60
+
61
+ =end
62
+
63
+ # This module contains various Lisp facilities that are of general usefulness.
64
+ # Lisp's rich string formatting language is implemented in the Format module.
65
+ # More modules may be added later.
66
+ #
67
+ # TODO: the floating point functions are rather horendous
68
+ module Lisp
69
+ # This module implements the Common Lisp (format) function and it's language.
70
+ module Format
71
+ # An error, positioned somewhere in the input format. This is used to be
72
+ # able to show the user exactly where something failed in the input format
73
+ # string.
74
+ module Positioned
75
+ # Create at a given position.
76
+ def initialize(pos = -1)
77
+ @pos = pos
78
+ end
79
+
80
+ # The position at which the error occured.
81
+ attr :pos, true
82
+ end
83
+
84
+ # An argument was of the wrong type, or there aren't enough arguments.
85
+ class ArgumentError < ArgumentError; include Positioned; end
86
+
87
+ # An index was not valid. Used for argument jumping errors.
88
+ class IndexError < IndexError; include Positioned; end
89
+
90
+ # A parameter was wrong in some sense.
91
+ class ParameterError < ArgumentError; include Positioned; end
92
+
93
+ # The format string contained a syntax error.
94
+ class SyntaxError < SyntaxError; include Positioned; end
95
+
96
+ # The format string was ended before a parameter could be read.
97
+ class MissingParameterError < SyntaxError; end
98
+
99
+ # The format string was ended before a parameter could be completed.
100
+ class IncompleteParameterError < SyntaxError; end
101
+
102
+ # The format string was malformed somehow.
103
+ class MalformedError < SyntaxError; end
104
+
105
+ # A modifier was wrongly given, or there were too many of the same.
106
+ class ModifierError < SyntaxError; end
107
+
108
+ # Represents output for a State object.
109
+ class Output
110
+ # Create a new Output object.
111
+ def initialize(str = '', col = 0)
112
+ @output, @col = str, col
113
+ end
114
+
115
+ # Add +str+ to the output.
116
+ def <<(str)
117
+ if str.is_a? Fixnum
118
+ @output << str.chr
119
+ # How should tabs be handled here? They should perhaps be
120
+ # interpreted as eight (8) characters wide? This of course depends
121
+ # on where @col is already at (8 - @col % 8).
122
+ @col = str == ?\n ? 0 : @col + 1
123
+ else
124
+ @output << str
125
+ @col += str.length - (str.rindex(?\n) or 0)
126
+ end
127
+ end
128
+
129
+ # Convert this object to its string representation, which is the output
130
+ # gathered so far.
131
+ # TODO: this is rather silly
132
+ def to_s
133
+ @output
134
+ end
135
+
136
+ # The current output column.
137
+ attr :col, true
138
+ end
139
+
140
+ # This class represents the state of a given Formatter. It keeps track of
141
+ # output gathered and arguments left to be processed.
142
+ class State
143
+ # Create a state from arguments and a destination output
144
+ def initialize(args, output)
145
+ @args = args
146
+ @arg_pos = 0
147
+ @outputs = [output]
148
+ @case_conv = nil
149
+ end
150
+
151
+ # Push a new Output buffer to collect output in.
152
+ def push_output
153
+ @outputs << Output.new('', @outputs.last.col)
154
+ end
155
+
156
+ # Pop and return the latest output buffer.
157
+ def pop_output
158
+ @outputs.pop
159
+ end
160
+
161
+ # Delegates output to the top-most output buffer.
162
+ def output(str)
163
+ @outputs.last << str
164
+ end
165
+
166
+ # Retrieve the current output column.
167
+ def col
168
+ @outputs.last.col
169
+ end
170
+
171
+ # Retrieve the latest output buffer.
172
+ def latest_output
173
+ @outputs.last
174
+ end
175
+
176
+ # Move +n+ steps forward or backward depending on sign amongst the
177
+ # arguments. Movement is relative or absolute depending on the boolean
178
+ # value of +relative+.
179
+ def args_move(n = 1, relative = true)
180
+ @arg_pos = relative ? @arg_pos + n : n
181
+ raise IndexError.new,
182
+ 'too few arguments' if @arg_pos > @args.size
183
+ raise IndexError.new,
184
+ 'cannot move past first argument' if @arg_pos < 0
185
+ end
186
+
187
+ # Get the current argument and move forward one argument.
188
+ def next_arg
189
+ args_move(1, true)
190
+ @args[@arg_pos - 1]
191
+ end
192
+
193
+ # Get the previously returned argument, without moving.
194
+ def previous_arg
195
+ args_move(-1, relative)
196
+ next_arg
197
+ end
198
+
199
+ # Push back the previously returned argument.
200
+ def push_back_arg
201
+ args_move(-1, true)
202
+ end
203
+
204
+ # Get the number of arguments left to process
205
+ def args_left
206
+ @args.size - @arg_pos
207
+ end
208
+
209
+ attr :case_conv, true
210
+ end
211
+
212
+ # Given a format string, this class lexes it and returns directives it
213
+ # finds. This includes gathering parameters and modifiers for the coming
214
+ # directives. The necessary information to create a directive is gathered
215
+ # and then passed to Directives::Factory#build. Error checking
216
+ # is performed, so that incomplete parameters are caught and malformed
217
+ # modifiers are supressed.
218
+ class Lexer
219
+ @@errorstates = {
220
+ :START => [MissingParameterError,
221
+ 'format string ended before parameter was found'],
222
+ :CHAR => [IncompleteParameterError,
223
+ 'format string ended before character parameter could be read'],
224
+ :INTEGER => [IncompleteParameterError,
225
+ 'format string ended before integer parameter could be read'],
226
+ }
227
+
228
+ # Create a new Lexer using the given format string +format+.
229
+ def initialize(format)
230
+ @format = format
231
+ @pos = 0
232
+ end
233
+
234
+ # Read the next token, given the previous one in +previous+, and return
235
+ # it. This may either be a 'real' directive created by
236
+ # Directives::Factory#build, or a Literal created in-line. If no more
237
+ # tokens remain, +nil+ is returned.
238
+ def next_token(previous)
239
+ if have_more_input
240
+ ch = next_char
241
+ if ch == ?~
242
+ return Directives::Factory.build(params, modifiers, directive,
243
+ previous, @pos)
244
+ else
245
+ literal = Directives::Literal.new(ch.chr)
246
+ while have_more_input and (ch = peek_char) != ?~
247
+ literal << next_char.chr
248
+ end
249
+ literal << ch.chr if not have_more_input and ch == ?~
250
+ return [literal, 0]
251
+ end
252
+ else
253
+ return nil
254
+ end
255
+ end
256
+
257
+ private
258
+
259
+ # Returns +true+ if more input exists.
260
+ def have_more_input
261
+ @pos < @format.length
262
+ end
263
+
264
+ # Reads next character in input.
265
+ def next_char
266
+ @pos += 1
267
+ @format[@pos - 1]
268
+ end
269
+
270
+ # View the next character in input.
271
+ def peek_char
272
+ @format[@pos]
273
+ end
274
+
275
+ # Unread previously read character in input.
276
+ def unget_char
277
+ @pos -= 1
278
+ end
279
+
280
+ # Read parameters to the coming directive. Raises various errors
281
+ # sub-classed from PositionedError when given incomplete
282
+ # parameters, e.g. when it runs out of input.
283
+ def params
284
+ params = []
285
+ sign = 1
286
+ value = 0
287
+ state = :START
288
+ while have_more_input
289
+ ch = next_char
290
+ case state
291
+ when :START
292
+ case ch
293
+ when ?'
294
+ state = :CHAR
295
+ when ?V, ?v
296
+ params << Parameters::Argument.new(@pos)
297
+ state = :DONE
298
+ when ?#
299
+ params << Parameters::ArgumentCount.new(@pos)
300
+ state = :DONE
301
+ when ?,
302
+ params << Parameters::Default.new(@pos)
303
+ state = :START
304
+ when ?+, ?-, ?0, ?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9
305
+ sign = (ch == ?-) ? -1 : 1
306
+ value = (ch == ?+ or ch == ?-) ? 0 : (ch - ?0)
307
+ state = :INTEGER
308
+ else
309
+ unget_char
310
+ state = :DONE
311
+ end
312
+ when :INTEGER
313
+ if (?0..?9).to_a.include? ch
314
+ value = value * 10 + (ch - ?0)
315
+ else
316
+ unget_char
317
+ value *= sign
318
+ params << Parameters::Integer.new(@pos, value)
319
+ state = :DONE
320
+ end
321
+ when :CHAR
322
+ params << Parameters::Character.new(@pos, ch)
323
+ state = :DONE
324
+ when :DONE
325
+ if ch == ?,
326
+ state = :START
327
+ else
328
+ unget_char
329
+ break
330
+ end
331
+ end
332
+ end
333
+ unless state == :DONE
334
+ raise @@errorstates[state][0].new(@pos), @@errorstates[state][1]
335
+ end
336
+ return params
337
+ end
338
+
339
+ # Read modifiers to the coming directive. Raises ModifierError if
340
+ # a specific modifier is given more than once.
341
+ def modifiers
342
+ modifiers = []
343
+ while have_more_input
344
+ ch = next_char
345
+ if ch == ?: or ch == ?@
346
+ unless modifiers.include? ch
347
+ modifiers << ch
348
+ else
349
+ raise ModifierError.new(@pos), "duplicate #{ch.chr} modifier"
350
+ end
351
+ else
352
+ unget_char
353
+ break
354
+ end
355
+ end
356
+ return modifiers
357
+ end
358
+
359
+ # Reads the directive. Raises MalformedFormatError if no input
360
+ # remains to read the directive from.
361
+ def directive
362
+ unless have_more_input
363
+ raise MalformedFormatError.new(@pos),
364
+ 'format string ended before directive was found'
365
+ end
366
+ next_char
367
+ end
368
+ end
369
+
370
+ # Given a lexer, parses the tokens it constructs.
371
+ # It works, quite simply, by maintaining a stack of nesting levels and
372
+ # directives/tokens are added to the current stack level. When a directive
373
+ # removes a level of nesting the previous level's last directive gets
374
+ # connected, using Directive#connect.
375
+ class Parser
376
+ # Create a Parser, given a Lexer, +lexer+.
377
+ def initialize(lexer)
378
+ @lexer = lexer
379
+ end
380
+
381
+ # Do the actual parsing of the tokens. Returns a list of the
382
+ # top-level tokens to run.
383
+ def parse
384
+ stacks = [[]]
385
+ level = 0
386
+ until (token = @lexer.next_token(stacks[level>0?level-1:0].last)).nil?
387
+ directive = token[0]
388
+ nesting = token[1]
389
+ ntop = stacks[level].last.nil? ?
390
+ directive : stacks[level].last.join(directive)
391
+ stacks[level] << directive
392
+ if nesting > 0
393
+ stacks.push []
394
+ level += 1
395
+ elsif nesting < 0
396
+ directives = stacks.pop
397
+ level -= 1
398
+ stacks[level].last.connect directives
399
+ end
400
+ end
401
+ if stacks.size > 1
402
+ raise SyntaxError.new(stacks[-2].last.pos), 'unterminated directive'
403
+ end
404
+ return stacks[0]
405
+ end
406
+ end
407
+
408
+ # Engine for the format Lexer and Parser. Given an input format string and
409
+ # an initial State, it lets the Parser create the directives and then
410
+ # simply iterates over them, Directive#execute:ing them each.
411
+ class Formatter
412
+ # Create a Formatter from a String +format+ and a State +state+.
413
+ def initialize(format, state)
414
+ @parser, @state = Parser.new(Lexer.new(format)), state
415
+ end
416
+
417
+ # Parse and run the result.
418
+ def run
419
+ Format.execute_directives(@state, @parser.parse)
420
+ return @state.latest_output.to_s
421
+ end
422
+ end
423
+
424
+ # This module includes format parameter representation classes. These are
425
+ # passed to directives when they are created.
426
+ module Parameters
427
+ # Base class from which the sub-classes get their behavior. Every
428
+ # parameter should sub-class from this class.
429
+ class Parameter
430
+ # Create a parameter, found at +pos+ in the input format, and with
431
+ # +value+ as its value - if it has one.
432
+ def initialize(pos, value = nil)
433
+ @pos, @value = pos, value
434
+ end
435
+
436
+ # Retrieve this parameters value in the given State, using +default+ if
437
+ # it is not defined.
438
+ def value(state, default)
439
+ @value or default
440
+ end
441
+
442
+ # Position in the input format that this parameter was found.
443
+ attr :pos
444
+ end
445
+
446
+ # Represents the +V+ and +v+ parameters, which retrieve their value from
447
+ # the current argument.
448
+ class Argument < Parameter
449
+ # Retrieve this parameters value by getting the value of the current
450
+ # arguments integral value.
451
+ def value(state, default)
452
+ begin
453
+ arg = state.next_arg
454
+ rescue => e
455
+ e.pos = @pos if e.respond_to?(:pos) and e.pos == -1
456
+ raise
457
+ end
458
+ if arg.nil?
459
+ default
460
+ elsif arg.respond_to? :to_i
461
+ arg.to_i
462
+ else
463
+ raise ArgumentError.new(@pos),
464
+ 'argument not a number or a number string'
465
+ end
466
+ end
467
+ end
468
+
469
+ # Represents the <tt>#</tt> parameter, which retrieves the number of
470
+ # arguments left to process.
471
+ class ArgumentCount < Parameter
472
+ # Retrieve this parameters value by getting the number of arguments
473
+ # left to process.
474
+ def value(state, default)
475
+ state.args_left
476
+ end
477
+ end
478
+
479
+ # Parameter used when the parameter was not specified, so use its default
480
+ # value.
481
+ class Default < Parameter; end
482
+
483
+ # Represents integral parameters, specified directly in the input format.
484
+ class Integer < Parameter; end
485
+
486
+ # Represents character parameters, specified directly in the input
487
+ # format.
488
+ class Character < Parameter; end
489
+ end
490
+
491
+ # This module contains all the formatting directives that are defined in
492
+ # the Lisp formatting embedded language. The base class for all directives
493
+ # is aptly named Directive, and all directive classes should sub-class from
494
+ # it.
495
+ module Directives
496
+ # An unknown directive was found in the format string.
497
+ class UnknownDirectiveError < SyntaxError; end
498
+
499
+ # Represents a string literal in the input format. This is used for
500
+ # combining running lengths of characters with a single pseudo-directive.
501
+ class Literal < String
502
+ # Outputs the string literal this instance represents to the output
503
+ # stream.
504
+ def execute(state)
505
+ state.output self
506
+ end
507
+
508
+ # If the following directive is also a Literal, its contents is simply
509
+ # added to this ones, and returns self. Otherwise the other directive
510
+ # is returned.
511
+ def join(other)
512
+ other.is_a?(Literal) ? self << other : other
513
+ end
514
+ end
515
+
516
+ # Base class for formatting directives. This class makes it easy for
517
+ # sub-classes to access the parameters and modifiers that were given to
518
+ # it when created.
519
+ class Directive
520
+ # Given a set of parameters, modifiers, an owning directive if
521
+ # available, and a position in the output string, create a new
522
+ # Directive.
523
+ def initialize(params, modifiers, top, pos)
524
+ @params, @modifiers, @pos = params, modifiers, pos
525
+ end
526
+
527
+ # This method should be overridden in sub-classes. It gets called when
528
+ # the directive is to be executed. It does nothing by default.
529
+ def execute(state); end
530
+
531
+ # Join this directive with the following one, given in +other+. The
532
+ # default is to simply return +other+. The only known use for this is
533
+ # in SkipWhitespace, where some processing of other is done if it is a
534
+ # Literal.
535
+ def join(other)
536
+ other
537
+ end
538
+
539
+ # The position at which this directive occurs in the input string.
540
+ attr :pos
541
+
542
+ protected
543
+
544
+ # Read the +param+:th parameter in the Lisp::Format::State +state+,
545
+ # using +default+ as a default value if the parameter was not given.
546
+ def param(param, state, default)
547
+ param < @params.size ? @params[param].value(state, default) : default
548
+ end
549
+
550
+ # Check if the colon (<tt>:</tt>) modifier was given.
551
+ def colon_mod?
552
+ modifier? ?:
553
+ end
554
+
555
+ # Check if the at (<tt>@</tt>) modifier was given.
556
+ def at_mod?
557
+ modifier? ?@
558
+ end
559
+
560
+ # Check if the given +modifier+ was given. This method should not
561
+ # generally be called, as #colon_mod? and #at_mod? are easier and
562
+ # better to use.
563
+ def modifier?(modifier)
564
+ @modifiers.include? modifier
565
+ end
566
+
567
+ def arg_error(message)
568
+ raise ArgumentError.new(@pos), message
569
+ end
570
+
571
+ def param_error(pnum, message)
572
+ raise ParameterError.new(@params[pnum].pos), message
573
+ end
574
+ end
575
+
576
+ # Super-class for 'printing' directives, namely ~A (Ascii) and ~S
577
+ # (SExpression). These directives print, in some sense, their argument
578
+ # in a Ruby friendly manner. This means that their argument is either
579
+ # converted to a string using Object#to_s or Object#inspect.
580
+ class Print < Directive
581
+ # All parameters except +inspect+ are simply passed on to
582
+ # Directive#initialize. If +inspect+ is true, string arguments are
583
+ # inspected as well as all other objects.
584
+ def initialize(params, modifiers, top, pos, inspect = false)
585
+ super params, modifiers, top, pos
586
+ @inspect = inspect
587
+ end
588
+
589
+ # Output the given argument as it generally prints in Ruby. The full
590
+ # form is:
591
+ # ~mincol,colinc,minpad,padchar:@[AS]
592
+ # with the following interpretations
593
+ # [+mincol+ (0)]
594
+ # minimum number of columns to output,
595
+ # [+colinc+ (1)]
596
+ # number of columns to increase by, until +mincol+ is reached,
597
+ # [+minpad+ (0)]
598
+ # minimum amount of padding to add (added before +mincol+ is
599
+ # checked),
600
+ # [+padchar+ (?\s)]
601
+ # character to pad with,
602
+ # [:]
603
+ # +nil+ is output as +nil+ (In Lisp, this outputs +nil+ as
604
+ # <tt>()</tt>, which isn't generally useful in Ruby. TODO: come up
605
+ # with better use for this modifier),
606
+ # [@]
607
+ # padding is done on the right.
608
+ def execute(state)
609
+ padmethod = at_mod? ? :rjust : :ljust
610
+ mincol = param(0, state, 0)
611
+ colinc = param(1, state, 1)
612
+ minpad = param(2, state, 0)
613
+ padchar = param(3, state, ?\s).chr
614
+ arg = state.next_arg
615
+ # XXX: this needs checking use .to_s here?
616
+ str = (arg.is_a? String and not @inspect) ? arg.to_s : arg.inspect
617
+ str = str.send(padmethod, str.length + minpad, padchar)
618
+ k = ((mincol - str.length) / colinc.to_f).ceil
619
+ if k > 0
620
+ str = str.send(padmethod, str.length + colinc * k, padchar)
621
+ end
622
+ state.output str
623
+ end
624
+ end
625
+
626
+ # Represents the ~A (Ascii) directive.
627
+ class Ascii < Print; end
628
+
629
+ # Represents the ~S (S-Expression) directive.
630
+ class SExpression < Print
631
+ # Sets +inspect+ to +true+ in the argument list.
632
+ def initialize(*args)
633
+ super(*args << true)
634
+ end
635
+ end
636
+
637
+ # Super-class for number printing directives, namely ~D (Decimal, ~B
638
+ # (Binary), ~O (Octal), and ~X (Hexadecimal). The only difference
639
+ # between these directives is the output radix, which defaults to ten
640
+ # (10), which is decimal.
641
+ class Number < Directive
642
+ # Create a Number directive, using the given output radix +radix+.
643
+ def initialize(params, modifiers, top, pos, radix = 10)
644
+ super params, modifiers, top, pos
645
+ @radix = radix
646
+ end
647
+
648
+ # Output the given argument using its integral representation. The
649
+ # argument must respond to the :to_int message, or else it isn't
650
+ # considered an integer. This is the normal way in which Ruby
651
+ # operates. The full form is
652
+ # ~mincol,padchar,commachar,commainterval:@[DBOX]
653
+ # with the following interpretations
654
+ # [+mincol+ (0)]
655
+ # minimum number of columns to output,
656
+ # [+padchar+ (?\s)]
657
+ # character to pad with,
658
+ # [+commachar+ (,)]
659
+ # character to use for number grouping (see : below),
660
+ # [+commainterval+ (3)]
661
+ # how often to output +commachar+ above,
662
+ # [:]
663
+ # the number is output, with numbers grouped into +commainterval+
664
+ # sized groups of numbers, using +commachar+ as separator,
665
+ # [@]
666
+ # numbers are always output with sign prepended.
667
+ #
668
+ # An ArgumentError is raised if the argument does not respond to the
669
+ # #to_int message.
670
+ def execute(state)
671
+ mincol = param(0, state, 0)
672
+ padchar = param(1, state, ?\s).chr
673
+ commachar = param(2, state, ?,).chr
674
+ interval = param(3, state, 3)
675
+ arg = state.next_arg
676
+ if arg.respond_to? :to_int
677
+ num = arg.to_int
678
+ str = num.to_s(@radix)
679
+ if colon_mod?
680
+ str.gsub!(/(\d)(?=(\d{#{interval}})+(?!\d))/, "\\1#{commachar}")
681
+ end
682
+ str = '+' + str if at_mod? and num >= 0
683
+ # puts(str.rjust(mincol, '-'))
684
+ state.output str.rjust(mincol, padchar)
685
+ else
686
+ arg_error 'argument is not an integer'
687
+ end
688
+ end
689
+ end
690
+
691
+ # Represents the ~D (Decimal) directive.
692
+ class Decimal < Number; end
693
+
694
+ # Represents the ~B (Binary) directive.
695
+ class Binary < Number
696
+ # Set +radix+ to 2.
697
+ def initialize(args)
698
+ super(*args << 2)
699
+ end
700
+ end
701
+
702
+ # Represents the ~O (Octal) directive.
703
+ class Octal < Number
704
+ # Set +radix+ to 8.
705
+ def initialize(args)
706
+ super(*args << 8)
707
+ end
708
+ end
709
+
710
+ # Represents the ~X (Hexadecimal) directive.
711
+ class Hexadecimal < Number
712
+ # Set +radix+ to 16.
713
+ def initialize(args)
714
+ super(*args << 16)
715
+ end
716
+ end
717
+
718
+ # Represents the ~R (Radix) directive. This outputs numbers in a given
719
+ # radix, or using alternative forms, such as Roman numerals or
720
+ # cardinal/ordinal English numbers.
721
+ class Radix < Directive
722
+ @@names = {
723
+ 1 => 'one', 2 => 'two', 3 => 'three',
724
+ 4 => 'four', 5 => 'five', 6 => 'six',
725
+ 7 => 'seven', 8 => 'eight', 9 => 'nine',
726
+ 10 => 'ten', 11 => 'eleven', 12 => 'twelve',
727
+ 13 => 'thirteen', 14 => 'fourteen', 15 => 'fifteen',
728
+ 16 => 'sixteen', 17 => 'seventeen', 18 => 'eighteen',
729
+ 19 => 'nineteen', 20 => 'twenty', 30 => 'thirty',
730
+ 40 => 'forty', 50 => 'fifty', 60 => 'sixty',
731
+ 70 => 'seventy', 80 => 'eighty', 90 => 'ninety'
732
+ }
733
+ @@illions = %w[ \
734
+ thousand million billion trillion
735
+ quadrillion quintillion sextillion septillion
736
+ octillion nonillion decillion undecillion
737
+ duodecillion tredecillion quattuordecillion quindecillion
738
+ sexdecillion septendecillion octodecillion novemdecillion
739
+ vigintillion
740
+ ]
741
+ @@ordinal_ones = %w[
742
+ \ first second third fourth
743
+ fifth sixth seventh eight ninth
744
+ tenth eleventh twelfth thirteenth fourteenth
745
+ fifteenth sixteenth seventeenth eighteenth nineteenth
746
+ ]
747
+ @@ordinal_tens = %w[
748
+ \ \ twentieth thirtieth fortieth
749
+ fiftieth sixtieth seventieth eightieth ninetieth
750
+ ]
751
+ @@romans = [
752
+ [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'], [100, 'C'],
753
+ [90, 'XC'], [50, 'L'], [40, 'XL'], [10, 'X'], [9, 'IX'],
754
+ [5, 'V'], [4, 'IV'], [1, 'I']
755
+ ]
756
+ @@old_romans = [
757
+ [1000, 'M'], [900, 'DCCCC'], [500, 'D'], [400, 'CCCC'],
758
+ [100, 'C'], [90, 'LXXXX'], [50, 'L'], [40, 'XXXX'],
759
+ [10, 'X'], [9, 'VIIII'], [5, 'V'], [4, 'IIII'],
760
+ [1, 'I']
761
+ ]
762
+
763
+ # Output the given argument using one of a variety of methods. Either
764
+ # the argument is output using a specific radix, using cardinal or
765
+ # ordinal English numbers, or Roman numerals. The full form is
766
+ # ~radix,mincol,padchar,commachar,commainterval:@R
767
+ # with the same interpretation as for ~D (Decimal / Number), but using
768
+ # the given +radix+ instead. An ArgumentError is raised if the
769
+ # argument is not an integer, or, in the case of Roman numerals, if the
770
+ # argument is not a Fixnum value.
771
+ #
772
+ # If none of the parameters are given the output instead depends on the
773
+ # combination of modifiers:
774
+ # [<em>no modifiers</em>]
775
+ # the number is output as a cardinal English number,
776
+ # [:]
777
+ # the number is output as an ordinal English number,
778
+ # [@]
779
+ # the number is output as a Roman numeral,
780
+ # [:@]
781
+ # the number is output as an old Roman numeral.
782
+ #
783
+ # An ArgumentError is raised if the argument is not an integer.
784
+ def execute(state)
785
+ if @params.size > 0
786
+ # XXX: may be ugly to modify these
787
+ n = @params.shift.value
788
+ Factory.build(@params, @modifiers, nil, @pos, n).execute(state)
789
+ else
790
+ arg = state.next_arg
791
+ if arg.respond_to? :to_int
792
+ conversion = :cardinal
793
+ conversion = :ordinal if colon_mod?
794
+ conversion = :roman if at_mod?
795
+ conversion = :old_roman if colon_mod? and at_mod?
796
+ state.output self.send(conversion, arg.to_int)
797
+ else
798
+ arg_error 'argument is not an integer'
799
+ end
800
+ end
801
+ end
802
+
803
+ private
804
+
805
+ # Convert the given number to a cardinal English number string.
806
+ def cardinal(n)
807
+ return 'zero' if n.zero?
808
+ out = []
809
+ negative = n < 0
810
+ n = n.abs
811
+ power = 0
812
+ while n > 0
813
+ before, after = n.divmod(1000)
814
+ if after > 0
815
+ block = cardinal_100s(after) + (power.zero? ? '' : ' ')
816
+ if power < @@illions.size
817
+ block << @@illions[power]
818
+ else
819
+ block << 'times ten to the ' + (power * 3).to_s + ' power'
820
+ end
821
+ out << block
822
+ end
823
+ n = before
824
+ power += 1
825
+ end
826
+ negative and out << 'minus'
827
+ out.reverse.join(', ')
828
+ end
829
+
830
+ # Deal with hundreds for cardinals.
831
+ def cardinal_100s(n)
832
+ out = []
833
+ q, r = n.divmod(100)
834
+ out << @@names[q] + ' hundred' if q > 0
835
+ if r >= 20
836
+ out << @@names[r / 10 * 10] + (r%10>0 ? '-' + @@names[r % 10] : '')
837
+ elsif r > 0
838
+ out << @@names[r]
839
+ end
840
+ out.join(' ')
841
+ end
842
+
843
+ # Convert the given number to an ordinal English number string.
844
+ def ordinal(n)
845
+ return 'zeroth' if n.zero?
846
+ out = []
847
+ if n < 0
848
+ out << 'minus'
849
+ n = n.abs
850
+ end
851
+ hundreds, tens_ones = n.divmod(100)
852
+ if hundreds > 0
853
+ out << cardinal(hundreds * 100) + (tens_ones.zero? ? 'th' : '')
854
+ end
855
+ if tens_ones >= 20
856
+ tens, ones = tens_ones.divmod(10)
857
+ if ones.zero?
858
+ out << @@ordinal_tens[tens]
859
+ else
860
+ out << @@names[tens * 10] + '-' + @@ordinal_ones[ones]
861
+ end
862
+ elsif tens_ones > 0
863
+ out << @@ordinal_ones[tens_ones]
864
+ end
865
+ out.join(' ')
866
+ end
867
+
868
+ # Helper method for roman numerals.
869
+ def roman_helper(n, table)
870
+ unless n.is_a? Fixnum
871
+ arg_error 'only Fixnum values can be converted to roman numerals'
872
+ end
873
+ out = []
874
+ table.each do |decimal, roman|
875
+ q, r = n.divmod(decimal)
876
+ if q > 0
877
+ out << roman * q
878
+ n = r
879
+ end
880
+ end
881
+ out.join('')
882
+ end
883
+
884
+ # Convert the given number to a Roman numeral.
885
+ def roman(n)
886
+ roman_helper(n, @@romans)
887
+ end
888
+
889
+ # Convert the given number to an old Roman numeral.
890
+ def old_roman(n)
891
+ roman_helper(n, @@old_romans)
892
+ end
893
+ end
894
+
895
+ # Represents the ~R (Plural) directive. It outputs English plural
896
+ # suffixes depending on the given argument.
897
+ class Plural < Directive
898
+ # Outputs the given argument as an English plural suffix depending on
899
+ # the arguments value. If arg is not #eql? to 1, a lowercase +s+ is
900
+ # output, else nothing is output. The meaning of modifiers are:
901
+ # [:]
902
+ # backs up one argument before checking,
903
+ # [@]
904
+ # outputs +y+ or +ies+ instead of +s+ and _nothing_,
905
+ # [:@]
906
+ # a combination of the two above.
907
+ def execute(state)
908
+ arg = colon_mod? ? state.previous_arg : state.next_arg
909
+ if at_mod?
910
+ state.output((arg.eql? 1) ? 'y' : 'ies')
911
+ else
912
+ state.output(?s) unless arg.eql? 1
913
+ end
914
+ end
915
+ end
916
+
917
+ # Represents the ~C (Character) directive. It outputs a character
918
+ # argument in one of several different representations depending on given
919
+ # modifiers.
920
+ # TODO: UNICODE this shit.
921
+ class Character < Directive
922
+ # Outputs the given argument in one of the ways described in the table
923
+ # below depending on given modifiers:
924
+ # [<tt>no modifiers</tt>]
925
+ # simply output the character as a normal character,
926
+ # [:]
927
+ # spells out control-bits on the input character, such as
928
+ # Control-Meta-X,
929
+ # [@]
930
+ # outputs the character in such as way that it can be read in by the
931
+ # Ruby interpreter again as input, using the ?_char_ syntax,
932
+ # [:@]
933
+ # has the same effect as : only. The CLTL2 suggests that this
934
+ # outputs unusual shift keys in a manner to make it easy to locate
935
+ # them on a keyboard, but since no such standard has arisen, this is
936
+ # not done.
937
+ def execute(state)
938
+ arg = state.next_arg
939
+ arg_error 'argument not a Fixnum' unless arg.is_a? Fixnum
940
+ ch = arg & 0xff
941
+ if colon_mod?
942
+ state.output('Control-') if (ch & 0x7f) < 0x1f
943
+ state.output('Meta-') if ch >= 0x7f
944
+ state.output(ch & 0x7f | 0x60)
945
+ elsif at_mod?
946
+ state.output('?' + convert_char(ch))
947
+ else
948
+ state.output ch
949
+ end
950
+ end
951
+
952
+ private
953
+
954
+ # Convert a character to a escaped character string if possible.
955
+ # XXX: This should perhaps only use \x or \NNN syntax
956
+ def convert_char(ch)
957
+ if [?\\, ?\n, ?\t, ?\r, ?\f, ?\v, ?\a, ?\e, ?\b, ?\s].include? ch
958
+ ch.chr.inspect[1..-2]
959
+ else
960
+ if (ch & 0x7f) < 0x1f
961
+ "\\C-" + convert_char(ch | 0x60)
962
+ elsif ch >= 0x7f
963
+ "\\M-" + convert_char(ch & 0x7f | 0x60)
964
+ else
965
+ ch
966
+ end
967
+ end
968
+ end
969
+ end
970
+
971
+ # Represents the ~F (Fixed-format floating-point) directive. This
972
+ # outputs floating point values with various kinds of padding and such.
973
+ class FFFP < Directive
974
+ # Outputs the given argument using a floating-point representation.
975
+ # The full form is
976
+ # ~w,d,k,overflowchar,padchar@F
977
+ # with the following interpretations
978
+ # [+w+ (+nil+)]
979
+ # if non-+nil+, the output will be exactly +w+ characters long,
980
+ # [+d+ (+nil+)]
981
+ # if non-+nil+, this is the number of digits output after the decimal
982
+ # point (<tt>.</tt>),
983
+ # [+k+ (0)]
984
+ # scaling factor - the number is first scaled using this value,
985
+ # [+overflowchar+ (+nil+)]
986
+ # if non-+nil+, this character is used when this directive would
987
+ # produce output longer than that specified with the +w+ directive,
988
+ # [+padchar+ (?\s)]
989
+ # character to pad with if +w+ is non-nil and output isn't wide
990
+ # enough yet,
991
+ # [@]
992
+ # numbers are always output with sign prepended.
993
+ #
994
+ # An ArgumentError is raised if the argument is not a number or a
995
+ # string that can be converted to a number.
996
+ def execute(state)
997
+ width = param(0, state, nil)
998
+ digits = param(1, state, nil)
999
+ scale = param(2, state, 0)
1000
+ overflowchar = param(3, state, nil)
1001
+ padchar = param(4, state, ?\s)
1002
+ arg = state.next_arg
1003
+ if arg.respond_to? :to_f
1004
+ num = arg.to_f * (10 ** scale)
1005
+ str = (at_mod? and num >= 0) ? '+' : ''
1006
+ str = sign + (digits.nil? ? num.to_s : sprintf("%.#{digits}f",num))
1007
+ str = sign + sprintf("%.#{$1}f", num) if str =~ /e-([0-9]+)$/
1008
+ unless width.nil?
1009
+ if not digits.nil? and width == digits + 1
1010
+ str.sub!(/^([+-]?)0\./, '\1.')
1011
+ end
1012
+ str = str.rjust(width, padchar.chr) if str.length < width
1013
+ if str.length > width and digits.nil?
1014
+ prec = width - (str.index(/\./) + 1)
1015
+ str = sign + sprintf("%#.#{prec}f", num)
1016
+ end
1017
+ str.sub!(/\.$/, '') if str.length > width and digits.nil?
1018
+ if str.length > width and not overflowchar.nil?
1019
+ str = overflowchar.chr * width
1020
+ end
1021
+ end
1022
+ state.output str
1023
+ elsif arg.respond_to? :to_int
1024
+ state.push_back_arg
1025
+ parameters = @params[0].nil? ? [] : [@params[0]]
1026
+ Factory.build(parameters, [], ?D, nil, @pos).execute(state)
1027
+ else
1028
+ arg_error 'argument is not a number or a number string'
1029
+ end
1030
+ end
1031
+ end
1032
+
1033
+ # Represents the ~E (Exponential floating-point) directive. This outputs
1034
+ # floating point values in what is known as exponential or scientific
1035
+ # floating point format, such as 1.0e+3 for 1000.
1036
+ class ExpFP < Directive
1037
+
1038
+ # Outputs the argument using exponential floating-point format. The
1039
+ # full form is
1040
+ # ~w,d,e,k,overflowchar,padchar,exponentchar@E
1041
+ # with the following interpretations
1042
+ # [+w+ (+nil+)]
1043
+ # if non-+nil+, the output will be exactly +w+ characters long,
1044
+ # [+d+ (+nil+)]
1045
+ # if non-+nil+, this is the number of digits output after the decimal
1046
+ # point (<tt>.</tt>),
1047
+ # [+e+ (+nil+)]
1048
+ # if non-+nil+, the exponent part of the output will be exactly +e+
1049
+ # characters long,
1050
+ # [+k+ (1)]
1051
+ # scaling factor - the number is first scaled using this value,
1052
+ # [+overflowchar+ (+nil+)]
1053
+ # if non-+nil+, this character is used when this directive would
1054
+ # produce output longer than that specified with the +w+ directive,
1055
+ # [+padchar+ (?\s)]
1056
+ # character to pad with if +w+ is non-nil and output isn't wide
1057
+ # enough yet,
1058
+ # [+exponentchar+ (?e)]
1059
+ # character to use for exponent divider,
1060
+ # [@]
1061
+ # numbers are always output with sign prepended.
1062
+ #
1063
+ # An ArgumentError is raised if the argument is not a number or a
1064
+ # string that can be converted to a number.
1065
+ def execute(state)
1066
+ width = param(0, state, nil)
1067
+ digits = param(1, state, nil)
1068
+ edigits = param(2, state, nil)
1069
+ scale = param(3, state, 1)
1070
+ overflowchar = param(4, state, nil)
1071
+ padchar = param(5, state, ?\s).chr
1072
+ exponentchar = param(6, state, ?e).chr
1073
+ arg = state.next_arg
1074
+ if arg.respond_to? :to_f
1075
+ num = arg.to_f
1076
+ sign = (num >= 0 and at_mod?) ? '+' : ''
1077
+ exp = Math.log10(num.abs).floor - (scale - 1)
1078
+ exp_str = exponentchar + (exp >= 0 ? '+' : '-') +
1079
+ (edigits.nil? ? exp.abs.to_s : exp.abs.to_s.rjust(edigits, '0'))
1080
+ if digits.nil? and width.nil? and edigits.nil?
1081
+ str = sign + (num * (10 ** -exp)).to_s + exp_str
1082
+ else
1083
+ if digits.nil?
1084
+ prec = width - sign.length -
1085
+ ((num * (10 ** -exp)).to_s.index(/\./) + 1) - exp_str.length
1086
+ str = sign + sprintf("%#.#{prec}f", num) + exp_str
1087
+ else
1088
+ if scale > 0
1089
+ if scale < digits + 2
1090
+ prec = digits - scale + 1
1091
+ else
1092
+ param_error 3, 'scale must be < digits + 2'
1093
+ end
1094
+ else
1095
+ prec = -scale + (digits + scale)
1096
+ end
1097
+ str = sign + sprintf("%#.#{prec}f", num * (10**-exp)) + exp_str
1098
+ end
1099
+ unless width.nil?
1100
+ if scale <= 0 and str.length > width
1101
+ str.sub!(/^([+-]?)0\./, '\1.')
1102
+ end
1103
+ str = str.rjust(width, padchar) if str.length < width
1104
+ end
1105
+ unless width.nil? and overflowchar.nil?
1106
+ if not edigits.nil? and (exp_str.length - 2) > edigits
1107
+ str = overflowchar.chr * width
1108
+ end
1109
+ end
1110
+ end
1111
+ state.output str
1112
+ elsif arg.respond_to? :to_int
1113
+ state.push_back_arg
1114
+ parameters = @params[0].nil? ? [] : [@params[0]]
1115
+ Factory.build(parameters, [], ?D, nil, @pos).execute(state)
1116
+ else
1117
+ arg_error 'argument is not a number or a number string'
1118
+ end
1119
+ end
1120
+ end
1121
+
1122
+ # Represents the ~G (General floating-point) directive. This outputs its
1123
+ # argument using either a format like ~F or ~E depending upon given
1124
+ # parameters and the magnitude of the argument.
1125
+ class GeneralFP < Directive
1126
+ # Outputs the argument using exponential floating-point format. The
1127
+ # algorithm to decide what format to use looks something like
1128
+ # let
1129
+ # 10^n-1 <= arg < 10^n, or n = 0 if arg = 0,
1130
+ # ee = e + 2, or ee = 4 if e undefined,
1131
+ # ww = w - ee, or ww = nil if w undefined,
1132
+ # dd = d - n, or dd = max(q, min(n, 7)) where q = number of
1133
+ # characters necessary to print arg without loss of information
1134
+ # in
1135
+ # 0 <= dd <= d, and print using
1136
+ # ~ww,dd,,overflowchar,padcharF~ee@T
1137
+ # or print using
1138
+ # ~w,d,e,k,overflowchar,padchar,exponentcharG
1139
+ # Use of the @ modifier in the directives above depends on if it was
1140
+ # passed to ~G in the first place.
1141
+ #
1142
+ # The full form is
1143
+ # ~w,d,e,k,overflowchar,padchar,exponentchar@G
1144
+ # with the following interpretations
1145
+ # [+w+ (+nil+)]
1146
+ # if non-+nil+, the output will be exactly +w+ characters long,
1147
+ # [+d+ (+nil+)]
1148
+ # if non-+nil+, this is the number of digits output after the decimal
1149
+ # point (<tt>.</tt>),
1150
+ # [+e+ (+nil+)]
1151
+ # if non-+nil+, the exponent part of the output will be exactly +e+
1152
+ # characters long,
1153
+ # [+k+ (1)]
1154
+ # scaling factor - the number is first scaled using this value,
1155
+ # [+overflowchar+ (+nil+)]
1156
+ # if non-+nil+, this character is used when this directive would
1157
+ # produce output longer than that specified with the +w+ directive,
1158
+ # [+padchar+ (?\s)]
1159
+ # character to pad with if +w+ is non-nil and output isn't wide
1160
+ # enough yet,
1161
+ # [+exponentchar+ (?e)]
1162
+ # character to use for exponent divider,
1163
+ # [@]
1164
+ # numbers are always output with sign prepended.
1165
+ #
1166
+ # An ArgumentError is raised if the argument is not a number or a
1167
+ # string that can be converted to a number.
1168
+ def execute(state)
1169
+ width = param(0, state, nil)
1170
+ digits = param(1, state, nil)
1171
+ edigits = param(2, state, nil)
1172
+ scale = param(3, state, 1)
1173
+ overflowchar = param(4, state, nil)
1174
+ padchar = param(5, state, ?\s)
1175
+ exponentchar = param(6, state, nil)
1176
+ arg = state.next_arg
1177
+ if arg.respond_to? :to_f
1178
+ num = arg.to_f
1179
+ n = num == 0.0 ? 0 : Math.log10(num.abs).floor + 1
1180
+ ee = edigits.nil? ? 4 : edigits + 2
1181
+ ww = w.nil? ? nil : w - ee
1182
+ if d.nil?
1183
+ q = num.to_s.length
1184
+ d = Math.max(q, Math.min(n, 7))
1185
+ end
1186
+ dd = d - n
1187
+ if 0 <= dd and dd <= d
1188
+ state.push_back_arg
1189
+ parameters = [
1190
+ Parameters::Integer.new(@pos, ww),
1191
+ Parameters::Integer.new(@pos, dd),
1192
+ Parameters::Default.new(@pos),
1193
+ overflowchar.nil? ?
1194
+ Parameters::Default.new(@pos) :
1195
+ Parameters::Character.new(@pos, overflowchar),
1196
+ padchar.nil? ?
1197
+ Parameters::Default.new(@pos) :
1198
+ Parameters::Character.new(@pos, padchar),
1199
+ ]
1200
+ Factory.build(parameters, @modifiers, ?F, nil,
1201
+ @pos).execute(state)
1202
+ Factory.build([Parameters::Integere.new(@pos, ee)], @modifiers,
1203
+ ?T, nil, @pos).execute(state)
1204
+ else
1205
+ state.push_back_arg
1206
+ Factory.build(@params, @modifiers, ?E, nil, @pos).execute(state)
1207
+ end
1208
+ elsif arg.respond_to? :to_int
1209
+ state.push_back_arg
1210
+ parameters = @params[0].nil? ? [] : [@params[0]]
1211
+ Factory.build(parameters, [], ?D, nil, @pos).execute(state)
1212
+ else
1213
+ arg_error 'argument is not a number or a number string'
1214
+ end
1215
+ end
1216
+ end
1217
+
1218
+ # Represents the ~$ (Dollars floating-point) directive. This directive
1219
+ # outputs a floating point argument
1220
+ class DollarFP < Directive
1221
+ # Outputs the argument using a floating point format that suits dollar
1222
+ # values. The full form is
1223
+ # ~d,n,w,padchar:@$
1224
+ # with the following interpretations
1225
+ # [+d+ (2)]
1226
+ # number of digits to print after the decimal point (<tt>.</tt>),
1227
+ # [+n+ (1)]
1228
+ # number of digits to print before the decimal point (<tt>.</tt>),
1229
+ # [+w+ (0)]
1230
+ # minimum width of the field,
1231
+ # [+padchar+ (?\s)]
1232
+ # character used to produce right-adjusting padding with,
1233
+ # [:]
1234
+ # the sign of the value is output before any padding,
1235
+ # [@]
1236
+ # numbers are always output with sign prepended.
1237
+ def execute(state)
1238
+ digits = param(0, state, 2)
1239
+ idigits = param(1, state, 1)
1240
+ width = param(2, state, 0)
1241
+ padchar = param(3, state, ?\s)
1242
+ arg = state.next_arg
1243
+ if arg.respond_to :to_int
1244
+ sign = (arg >= 0 ? (at_mod? ? '+' : '') : '-')
1245
+ str = sprintf("%0#{idigits + digits + 1}.#{digits}f", arg.abs)
1246
+ if colon_mod?
1247
+ str = sign + str.rjust(width, padchar.chr)
1248
+ else
1249
+ str = (sign + str).rjust(width, padchar.chr)
1250
+ end
1251
+ state.output str
1252
+ elsif arg.respond_to? :to_i
1253
+ state.push_back_arg
1254
+ parameters = @params[2].nil? ? [] : [@params[2]]
1255
+ Factory.build(?D, parameters, [], nil, @pos).execute(state)
1256
+ else
1257
+ arg_error 'argument is not a number or a number string'
1258
+ end
1259
+ end
1260
+ end
1261
+
1262
+ # Parent class for character outputting directives, such as ~% (NewLine),
1263
+ # ~| (NewPage), and ~~ (Tilde), which output the same character a given
1264
+ # number of times.
1265
+ class CharacterDirective < Directive
1266
+ # Create and set the character to use to +ch+.
1267
+ def initialize(params, modifiers, top, pos, ch)
1268
+ super params, modifiers, top, pos
1269
+ @ch = ch
1270
+ end
1271
+
1272
+ # Outputs the specific character (depending on the
1273
+ # sub-classing directive) a given number of times. The full form is
1274
+ # ~n[%|~]
1275
+ # with the following interpretations
1276
+ # [+n+ (1)]
1277
+ # number of times to output the specific character.
1278
+ def execute(state)
1279
+ n = param(0, state, 1)
1280
+ state.output(@ch.chr * n)
1281
+ end
1282
+ end
1283
+
1284
+ # Represents the ~% (Newline) directive. This outputs a new-line (?\n)
1285
+ # character a given number of times.
1286
+ class NewLine < CharacterDirective
1287
+ # Set the output character to a new-line (?\n).
1288
+ def initialize(*args)
1289
+ super(*args << ?\n)
1290
+ end
1291
+ end
1292
+
1293
+ # Represents the ~% (Freshline) directive. This outputs a new-line (?\n)
1294
+ # character a given number of times, depending on if it is already at the
1295
+ # first output column or not.
1296
+ class FreshLine < Directive
1297
+ # Outputs a new-line character a given number of times depending on if
1298
+ # it is already at the first output column or not. If it is it outputs
1299
+ # it the given number minus one (1). The full form is
1300
+ # ~n&
1301
+ # with the following interpretations
1302
+ # [+n+ (1)]
1303
+ # number of times to output the specific character (maybe minus one).
1304
+ def execute(state)
1305
+ n = param(0, state, 1)
1306
+ return if n.zero?
1307
+ state.output(?\n) if state._col > 0
1308
+ (n - 1).times do state.output(?\n) end
1309
+ end
1310
+ end
1311
+
1312
+ # Represents the ~| (Newpage) directive. This outputs a new-page (?\f)
1313
+ # character a given number of times.
1314
+ class NewPage < CharacterDirective
1315
+ # Set the output character to a new-page (?\f).
1316
+ def initialize(*args)
1317
+ super(*args << ?\f)
1318
+ end
1319
+ end
1320
+
1321
+ # Represents the ~~ (Tilde) directive. This outputs a tilde (~)
1322
+ # character a given number of times.
1323
+ class Tilde < CharacterDirective
1324
+ # Set the output character to a tilde (~).
1325
+ def initialize(*args)
1326
+ super(*args << ?~)
1327
+ end
1328
+ end
1329
+
1330
+ # Represents the ~?\n (Real new-line) directive. This is used to skip
1331
+ # the new-line and any following white-space characters in the input
1332
+ # format. This is useful in long format strings, where the string has to
1333
+ # be split into multiple lines without ruining indentation.
1334
+ class SkipWhitespace < Directive
1335
+ # Join this directive with the following. This removes any new-line
1336
+ # and/or following white-space from the directive that follows, if it
1337
+ # is a string Literal.
1338
+ def join(other)
1339
+ return other if not other.is_a?(FormatLiteral) or colon_mod?
1340
+ other.sub!(/^[\s\t]+/, '')
1341
+ other[0,0] = '\n' if at_mod?
1342
+ other
1343
+ end
1344
+ end
1345
+
1346
+ # Represents the ~T (Tabulate) directive. This tabulates to a given
1347
+ # position in the output using white-space.
1348
+ class Tabulate < Directive
1349
+ # The output is spaced over to a given position, depending on where it
1350
+ # already is and parameters given to this directive.
1351
+ # ~colnum,colinc:@T
1352
+ # with the following interpretations
1353
+ # [+colnum+ (1)]
1354
+ # column to move to,
1355
+ # [+colinc+ (1)]
1356
+ # number of columns to space over by if already at or beyond
1357
+ # +colnum+,
1358
+ # [@]
1359
+ # performs relative tabulation. +colnum+ is treated as the column to
1360
+ # begin from (spacing over to it if necessary), and then moves over
1361
+ # to a column that is the smallest multiple of +colinc+.
1362
+ #
1363
+ # If output is already at or beyond +colnum+, then output is spaced
1364
+ # over to column <em>colnum + k * colinc</em>, for the smallest _k_
1365
+ # possible. An example of the @ modifiers effect is for the instance
1366
+ # of the ~T directive ~3,8@T, which moves over three columns, and then
1367
+ # to the first eight-sized tab-stop.
1368
+ def execute(state)
1369
+ colnum = param(0, state, 1)
1370
+ colinc = param(1, state, 1)
1371
+ padchar = ?\s.chr
1372
+ if at_mod?
1373
+ state.output(padchar * colnum)
1374
+ state.output(padchar * (colinc - state.col % colinc))
1375
+ else
1376
+ if state.col < colnum
1377
+ state.output(padchar * (colnum - state.col))
1378
+ elsif colinc > 0
1379
+ k = 1 + (state.col - colnum) / colinc
1380
+ state.output(padchar * ((colnum + k * colinc) - state.col))
1381
+ end
1382
+ end
1383
+ end
1384
+ end
1385
+
1386
+ # Represents the ~* (Argument jumping) directive. This moves arbitrarily
1387
+ # amongst the arguments passed to the formatting engine.
1388
+ class ArgJump < Directive
1389
+ # Moves backwards or forwards, relative or absolute, among the
1390
+ # formatting arguments. The full form is
1391
+ # ~n:@*
1392
+ # with the following interpretations
1393
+ # [+n+ (1)]
1394
+ # the amount of arguments to move or the argument to move to if
1395
+ # moving to an absolute position, indexed from zero (0),
1396
+ # [:]
1397
+ # move backwards +n+ arguments
1398
+ # [@]
1399
+ # move to the +n+:th argument, using zero-based indexing (absolute).
1400
+ def execute(state)
1401
+ n = param(0, state, 1)
1402
+ state.args_move(colon_mod? ? -n : n, !at_mod?)
1403
+ end
1404
+ end
1405
+
1406
+ # Represents the ~? (Indirection) directive. This takes the two
1407
+ # following arguments and formats them, using the first as the formatting
1408
+ # string, and the second as its arguments and inserts it into the output.
1409
+ class Indirection < Directive
1410
+ # Uses the next argument as a formatting string and then, depending on
1411
+ # modifiers specified, uses one of a set of different arguments as
1412
+ # input to this formatting. The full form is
1413
+ # ~@?
1414
+ # with the following interpretations
1415
+ # [@]
1416
+ # the formatting string is, much like a macro, inserted into the
1417
+ # formatting stream, so to speak, and consumes arguments and so on
1418
+ # from the current state, instead, as per default, consuming a second
1419
+ # argument and reading arguments from it.
1420
+ def execute(state)
1421
+ if at_mod?
1422
+ formatter = Formatter.new(state.next_arg, state)
1423
+ else
1424
+ format = state.next_arg
1425
+ state = State.new(state.next_arg, state.latest_output)
1426
+ formatter = Formatter.new(format, state)
1427
+ end
1428
+ formatter.run
1429
+ end
1430
+ end
1431
+
1432
+ # Represents the ~( (Begin case-conversion) directive. Everything
1433
+ # contained within it and its matching pair ~) (End case-conversion)
1434
+ # directive is subject to case conversion, such as upcasing or
1435
+ # capitalization.
1436
+ class BeginCaseConversion < Directive
1437
+ # Outputs the contained output case converted using some method that
1438
+ # depends on the combination of modifiers given to this directive. The
1439
+ # full form is
1440
+ # ~:@(
1441
+ # with the following interpretations
1442
+ # [<em>no modifiers</em>]
1443
+ # output is downcased,
1444
+ # [:]
1445
+ # every word is capitalized in the output,
1446
+ # [@]
1447
+ # the first word is capitalized in the output,
1448
+ # [:@]
1449
+ # output is upcased.
1450
+ def execute(state)
1451
+ conv = :DOWN
1452
+ conv = :CAP if colon_mod?
1453
+ conv = :CAP_FIRST if at_mod?
1454
+ conv = :UP if colon_mod? and at_mod?
1455
+ state.case_conv = conv
1456
+ state.push_output
1457
+ end
1458
+ end
1459
+
1460
+ # Represents the ~) (End case-conversion) directive. Everything
1461
+ # contained within it and its matching pair ~( (Begin case-conversion)
1462
+ # directive is subject to case conversion, such as upcasing or
1463
+ # capitalization.
1464
+ class EndCaseConversion < Directive
1465
+ # This does the actual work for the ~( directive, in that it collects
1466
+ # all the output between its matching directive (~() and outputs it
1467
+ # using one of the conversions set up by the ~( (BeginCaseConversion)
1468
+ # directive.
1469
+ def execute(state)
1470
+ output = state.pop_output.to_s
1471
+ state.output(
1472
+ case state.case_conv
1473
+ when :DOWN
1474
+ output.downcase
1475
+ when :CAP
1476
+ output.gsub(/\w+/) do |w| w.capitalize end
1477
+ when :CAP_FIRST
1478
+ output.capitalize
1479
+ when :UP
1480
+ output.upcase
1481
+ end
1482
+ )
1483
+ end
1484
+ end
1485
+
1486
+ # Represents the ~[ (Begin conditional expression) directive. This is
1487
+ # useful to choose among a set of directives depending on arguments and
1488
+ # numbers.
1489
+ class BeginConditional < Directive
1490
+ # Set up some variables and basically pass on to super.
1491
+ def initialize(*args)
1492
+ super(*args)
1493
+ if (colon_mod? or at_mod?) and @params.size > 0
1494
+ param_error 0, 'no parameters allowed together with' +
1495
+ ': and/or @ modifiers'
1496
+ end
1497
+ @clauses = []
1498
+ @default = -1
1499
+ end
1500
+
1501
+ # Process one of the given clauses, depending on the numeric value of
1502
+ # the given argument, or a specified parameter. The full form is
1503
+ # ~n[clause0~;clause1~:;clause2~]
1504
+ # or
1505
+ # ~:[clause0~;clause1~]
1506
+ # or
1507
+ # ~@[clause0~]
1508
+ # with the following interpretations
1509
+ # [+n+ (nil)]
1510
+ # if given, this value will be used to choose a clause instead of
1511
+ # reading an arguments value. This is only useful if +n+ is in fact
1512
+ # the arguments left parameter (<tt>#</tt>). An error is raised if
1513
+ # this argument does not #respond_to? :to_int,
1514
+ # [:]
1515
+ # instead of choosing a clause by number, the argument is tested as a
1516
+ # boolean value, and if false, then first clause is executed, else
1517
+ # the second one is,
1518
+ # [@]
1519
+ # instead of choosing a clause by number, the argument is tested as a
1520
+ # boolean value, and if true, the single clause is executed.
1521
+ def execute(state)
1522
+ test = state.next_arg if colon_mod? or at_mod?
1523
+ if colon_mod?
1524
+ c = test ? 1 : 0
1525
+ elsif at_mod?
1526
+ if test
1527
+ state.push_back_arg
1528
+ c = 0
1529
+ end
1530
+ else
1531
+ n = param(0, state, nil)
1532
+ if n.nil?
1533
+ arg = state.next_arg
1534
+ if arg.respond_to? :to_int
1535
+ n = arg.to_int
1536
+ else
1537
+ arg_error 'argument is not an integral value'
1538
+ end
1539
+ end
1540
+ if n < @clauses.size
1541
+ c = n
1542
+ elsif @default != -1
1543
+ c = @default
1544
+ end
1545
+ end
1546
+ Format.execute_directives(state, @clauses[c]) unless c.nil?
1547
+ end
1548
+
1549
+ # Connect a set of directives to this conditional directive.
1550
+ # A SyntaxError is raised if multiple clauses have been marked as
1551
+ # 'default', or if too many clauses have been specified for a given
1552
+ # set of modifiers.
1553
+ def connect(directives)
1554
+ @clauses = []
1555
+ clause = []
1556
+ directives.each do |d|
1557
+ if d.is_a? ClauseSeparator
1558
+ @clauses << clause
1559
+ clause = []
1560
+ if d.colon_mod?
1561
+ if @default == -1
1562
+ @default = @clauses.size
1563
+ else
1564
+ raise SyntaxError.new(d.pos),
1565
+ 'default clause has already been set'
1566
+ end
1567
+ end
1568
+ elsif d.is_a? EndConditional
1569
+ @clauses << clause
1570
+ break
1571
+ else
1572
+ clause << d
1573
+ end
1574
+ end
1575
+ must = 'two' if colon_mod? and @clauses.size != 2
1576
+ must = 'one' if at_mod? and @clauses.size != 1
1577
+ unless must.nil?
1578
+ raise SyntaxError.new(@pos), "must specify exactly #{must} clauses"
1579
+ end
1580
+ end
1581
+ end
1582
+
1583
+ # Represents the ~; (Clause separator) directive. It separates clauses
1584
+ # inside conditional and justifying directives (~[...~] and ~<...~>). It
1585
+ # may not appear anywhere else.
1586
+ class ClauseSeparator < Directive
1587
+ # Check that +top+ is either a BeginConditional or a
1588
+ # BeginJustification. If not, a SyntaxError is raised.
1589
+ def initialize(params, modifiers, top, pos)
1590
+ super params, modifiers, top, pos
1591
+ unless top.is_a? BeginConditional
1592
+ raise SyntaxError.new(@pos),
1593
+ '~; directive must be contained within a conditional (~[...~])' +
1594
+ ' or a justification (~<...~>)'
1595
+ end
1596
+ end
1597
+ end
1598
+
1599
+ # Represents the ~] (End conditional expression) directive. It ends a
1600
+ # conditional expression, and is an error if it appears without a
1601
+ # matching opening conditional expression.
1602
+ class EndConditional < Directive
1603
+ def initialize(params, modifiers, top, pos)
1604
+ super params, modifiers, top, pos
1605
+ unless top.is_a? BeginConditional
1606
+ raise SyntaxError.new(@pos), '~) without matching ~('
1607
+ end
1608
+ end
1609
+ end
1610
+
1611
+ # Represents the ~{ (Begin iteration) directive. A given set of
1612
+ # directives is used iteratively over a set of arguments (depends on the
1613
+ # modifiers specified) a given number of times, or until it runs out of
1614
+ # arguments.
1615
+ class BeginIteration < Directive
1616
+ # Create and set up some private variables
1617
+ def initialize(params, modifiers, top, pos)
1618
+ super params, modifiers, top, pos
1619
+ @directives = []
1620
+ end
1621
+
1622
+ # Iteratively run the contained directives using sets of arguments
1623
+ # depending upon what modifiers where given. The full form is
1624
+ # ~n:@{...~}
1625
+ # with the following interpretations
1626
+ # [n]
1627
+ # maximum number of times the iteration should be performed,
1628
+ # [<em>no modifiers</em>]
1629
+ # the iteration reads an argument, which must be an Array, and uses
1630
+ # it as the arguments to the contained directives,
1631
+ # [:]
1632
+ # the iteration reads an argument, which must be an Array
1633
+ # containing sub-arrays, and uses the sub-arrays as the arguments to
1634
+ # the contained directives, moving to the next one on each iteration,
1635
+ # [@]
1636
+ # the iteration uses the rest of the arguments as arguments to the
1637
+ # contained directives,
1638
+ # [:@]
1639
+ # the iteration uses the rest of the arguments, which must be Arrays,
1640
+ # using each Array as the set of arguments to the contained
1641
+ # directives.
1642
+ def execute(state)
1643
+ n = param(0, state, nil)
1644
+ return if not n.nil? and n == 0
1645
+ if colon_mod? and at_mod?
1646
+ execute_sets(n, state, false)
1647
+ elsif at_mod?
1648
+ execute_args(n, nil, state, false)
1649
+ elsif colon_mod?
1650
+ execute_sets(n, state, true)
1651
+ else
1652
+ execute_args(n, state.next_arg, state, true)
1653
+ end
1654
+ end
1655
+
1656
+ # Connect a set of directives to this iteration directive.
1657
+ def connect(directives)
1658
+ directives.pop
1659
+ @directives = directives
1660
+ end
1661
+
1662
+ private
1663
+
1664
+ # Execute the iteration using sub-lists either from the following
1665
+ # argument or from the remaining arguments.
1666
+ def execute_sets(n, state, use_next_arg)
1667
+ if use_next_arg
1668
+ sets = state.next_arg
1669
+ arg_error 'argument not an array' unless sets.is_a? Array
1670
+ sets = sets[0...n] unless n.nil?
1671
+ else
1672
+ m = n
1673
+ sets = []
1674
+ while state.args_left > 0 and (m.nil? or m > 0)
1675
+ sets << state.next_arg
1676
+ m -= 1 unless m.nil?
1677
+ end
1678
+ end
1679
+ sets.each do |set|
1680
+ execute_args(nil, set, state, true)
1681
+ end
1682
+ end
1683
+
1684
+ # Execute the iteration while there are arguments left and we haven't
1685
+ # reached our limit of iterations.
1686
+ def execute_args(n, args, state, use_new_state)
1687
+ if use_new_state
1688
+ arg_error 'argument not an array' unless args.is_a? Array
1689
+ state = State.new(args, state.latest_output)
1690
+ end
1691
+ while state.args_left > 0 and (n.nil? or n > 0)
1692
+ Format.execute_directives(state, @directives)
1693
+ n -= 1 unless n.nil?
1694
+ end
1695
+ end
1696
+ end
1697
+
1698
+ # Represents the ~} (End iteration) directive. See BeginIteration for an
1699
+ # explanation of how these directives work together.
1700
+ class EndIteration < Directive
1701
+ # Raise a SyntaxError if +top+ is not a BeginIteration.
1702
+ def initialize(params, modifiers, top, pos)
1703
+ super params, modifiers, top, pos
1704
+ unless top.is_a? BeginIteration
1705
+ raise SynaxError, '~} without matching ~{'
1706
+ end
1707
+ end
1708
+ end
1709
+
1710
+ # Factory for directives. Formatting directives should be created using
1711
+ # singleton methods in this class.
1712
+ class Factory
1713
+ @@directives = {
1714
+ ?A => [Ascii, 4, [], 0],
1715
+ ?S => [SExpression, 4, [], 0],
1716
+ ?D => [Decimal, 4, [], 0],
1717
+ ?B => [Binary, 4, [], 0],
1718
+ ?O => [Octal, 4, [], 0],
1719
+ ?X => [Hexadecimal, 4, [], 0],
1720
+ ?R => [Radix, 5, [], 0],
1721
+ ?P => [Plural, 0, [], 0],
1722
+ ?C => [Character, 0, [], 0],
1723
+ ?F => [FFFP, 5, [?:], 0],
1724
+ ?E => [ExpFP, 7, [?:], 0],
1725
+ ?G => [GeneralFP, 7, [?:], 0],
1726
+ ?$ => [DollarFP, 4, [], 0],
1727
+ ?% => [NewLine, 1, [?:, ?@], 0],
1728
+ ?& => [FreshLine, 1, [?:, ?@], 0],
1729
+ ?| => [NewPage, 1, [?:, ?@], 0],
1730
+ ?~ => [Tilde, 0, [?:, ?@], 0],
1731
+ ?\n => [SkipWhitespace, 0, [[?:, ?@]], 0],
1732
+ ?T => [Tabulate, 2, [], 0],
1733
+ ?* => [ArgJump, 1, [[?:, ?@]], 0],
1734
+ ?? => [Indirection, 0, [?:], 0],
1735
+ ?( => [BeginCaseConversion, 0, [], 0],
1736
+ ?) => [EndCaseConversion, 0, [?:, ?@], 0],
1737
+ ?[ => [BeginConditional, 1, [[?:, ?@]], 1],
1738
+ ?; => [ClauseSeparator, 0, [?@], 0],
1739
+ ?] => [EndConditional, 0, [?@], -1],
1740
+ ?{ => [BeginIteration, 1, [], 1],
1741
+ ?} => [EndIteration, 0, [?:, ?@], -1],
1742
+ }
1743
+
1744
+ # Create a directive given a set of paramaters, modifiers, the
1745
+ # character representing the directive, possible owning directive, and
1746
+ # position in the format string.
1747
+ def self.build(params, modifiers, directive, top, pos)
1748
+ @@directives.include? directive or
1749
+ raise UnknownDirectiveError.new(pos), 'unknown format directive'
1750
+ idx = directive.chr.upcase[0]
1751
+ params.size <= @@directives[idx][1] or
1752
+ raise ParameterError.new(pos),
1753
+ 'too many parameters given, expected no more than ' +
1754
+ @@directives[idx][1].to_s
1755
+ @@directives[idx][2].each do |illegal|
1756
+ if illegal.is_a? Array and (modifiers == illegal or
1757
+ modifiers.reverse == illegal)
1758
+ raise ModifierError.new(pos),
1759
+ 'cannot specify both : and @ modifiers'
1760
+ elsif modifiers.include? illegal
1761
+ raise ModifierError.new(pos),
1762
+ 'cannot specify the ' + mod.chr + 'modifier'
1763
+ end
1764
+ end
1765
+ return [
1766
+ @@directives[idx][0].new(params, modifiers, top, pos),
1767
+ @@directives[idx][3]
1768
+ ]
1769
+ end
1770
+ end
1771
+ end
1772
+
1773
+ private
1774
+
1775
+ # Execute a set of directives in a given state.
1776
+ def self.execute_directives(state, directives)
1777
+ directives.each do |directive|
1778
+ begin
1779
+ directive.execute state
1780
+ rescue => e
1781
+ e.pos = d.pos if e.respond_to?(:pos) and e.pos == -1
1782
+ raise
1783
+ end
1784
+ end
1785
+ end
1786
+ end
1787
+
1788
+ def Lisp.format(format, *args)
1789
+ begin
1790
+ state = Format::State.new(args, Format::Output.new)
1791
+ formatter = Format::Formatter.new(format, state)
1792
+ formatter.run
1793
+ rescue => e
1794
+ puts 'Format error: ' + e.message
1795
+ puts format
1796
+ puts ' ' * (e.pos - 1) + '^' if e.respond_to? :pos
1797
+ raise
1798
+ end
1799
+ end
1800
+ end
1801
+
1802
+
1803
+ =begin
1804
+ # --- development testing ---
1805
+
1806
+ if $0 == __FILE__
1807
+
1808
+ require 'test/unit'
1809
+ require 'test/unit/ui/console/testrunner'
1810
+
1811
+ class FormatTest < Test::Unit::TestCase
1812
+
1813
+ def test_decimal
1814
+ assert(Lisp.format("~D", 1) == "1")
1815
+ end
1816
+
1817
+ def test_decimal_params
1818
+ assert(Lisp.format("~5,'-D~6D", 1, 2) == "----1 2")
1819
+ end
1820
+
1821
+ def test_decimal_modifiers
1822
+ assert(Lisp.format("~5,'-,'.:@D~@D", 1013, 2) == "+1.013+2")
1823
+ assert(Lisp.format("~7,,,2:@D~@D", 1013, 2) == " +10,13+2")
1824
+ end
1825
+
1826
+ # Tests for ~v, ~V, ~#
1827
+ end
1828
+
1829
+ Test::Unit::UI::Console::TestRunner.run(FormatTest)
1830
+
1831
+ end
1832
+ =end