carats 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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