stupidedi 1.1.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 (500) hide show
  1. data/README.md +215 -0
  2. data/Rakefile +108 -0
  3. data/bin/edi-ed +71 -0
  4. data/bin/edi-pp +81 -0
  5. data/doc/Defining.md +0 -0
  6. data/doc/Generating.md +321 -0
  7. data/doc/LICENSE.md +0 -0
  8. data/doc/Navigating.md +645 -0
  9. data/doc/Parsing.md +0 -0
  10. data/doc/Serializing.md +7 -0
  11. data/doc/Tokenizing.md +0 -0
  12. data/doc/Validating.md +0 -0
  13. data/doc/design/Parser.md +0 -0
  14. data/doc/design/Reader.md +0 -0
  15. data/lib/ruby/array.rb +164 -0
  16. data/lib/ruby/blank.rb +67 -0
  17. data/lib/ruby/enumerable.rb +35 -0
  18. data/lib/ruby/exception.rb +33 -0
  19. data/lib/ruby/hash.rb +4 -0
  20. data/lib/ruby/instance_exec.rb +26 -0
  21. data/lib/ruby/module.rb +79 -0
  22. data/lib/ruby/object.rb +63 -0
  23. data/lib/ruby/string.rb +73 -0
  24. data/lib/ruby/symbol.rb +24 -0
  25. data/lib/ruby/to_d.rb +81 -0
  26. data/lib/ruby/to_date.rb +33 -0
  27. data/lib/ruby/to_time.rb +24 -0
  28. data/lib/ruby/try.rb +43 -0
  29. data/lib/stupidedi/blank_slate.rb +11 -0
  30. data/lib/stupidedi/builder/builder_dsl.rb +281 -0
  31. data/lib/stupidedi/builder/constraint_table.rb +418 -0
  32. data/lib/stupidedi/builder/generation.rb +112 -0
  33. data/lib/stupidedi/builder/instruction.rb +102 -0
  34. data/lib/stupidedi/builder/instruction_table.rb +204 -0
  35. data/lib/stupidedi/builder/navigation.rb +655 -0
  36. data/lib/stupidedi/builder/state_machine.rb +55 -0
  37. data/lib/stupidedi/builder/states/abstract_state.rb +332 -0
  38. data/lib/stupidedi/builder/states/failure_state.rb +69 -0
  39. data/lib/stupidedi/builder/states/functional_group_state.rb +97 -0
  40. data/lib/stupidedi/builder/states/initial_state.rb +63 -0
  41. data/lib/stupidedi/builder/states/interchange_state.rb +94 -0
  42. data/lib/stupidedi/builder/states/loop_state.rb +79 -0
  43. data/lib/stupidedi/builder/states/table_state.rb +96 -0
  44. data/lib/stupidedi/builder/states/transaction_set_state.rb +112 -0
  45. data/lib/stupidedi/builder/states/transmission_state.rb +59 -0
  46. data/lib/stupidedi/builder/tokenization.rb +196 -0
  47. data/lib/stupidedi/builder.rb +23 -0
  48. data/lib/stupidedi/color.rb +93 -0
  49. data/lib/stupidedi/config/code_list_config.rb +42 -0
  50. data/lib/stupidedi/config/editor_config.rb +51 -0
  51. data/lib/stupidedi/config/functional_group_config.rb +62 -0
  52. data/lib/stupidedi/config/interchange_config.rb +79 -0
  53. data/lib/stupidedi/config/transaction_set_config.rb +91 -0
  54. data/lib/stupidedi/config.rb +101 -0
  55. data/lib/stupidedi/editor/00501.rb +341 -0
  56. data/lib/stupidedi/editor/005010/N2.rb +0 -0
  57. data/lib/stupidedi/editor/005010/N3.rb +0 -0
  58. data/lib/stupidedi/editor/005010/N4.rb +63 -0
  59. data/lib/stupidedi/editor/005010/NM1.rb +0 -0
  60. data/lib/stupidedi/editor/005010.rb +469 -0
  61. data/lib/stupidedi/editor/X222-HC837.rb +195 -0
  62. data/lib/stupidedi/editor/abstract_ed.rb +36 -0
  63. data/lib/stupidedi/editor/claim_ack.rb +9 -0
  64. data/lib/stupidedi/editor/implementation_ack.rb +213 -0
  65. data/lib/stupidedi/editor/interchange_ack.rb +9 -0
  66. data/lib/stupidedi/editor/result.rb +100 -0
  67. data/lib/stupidedi/editor/result_set.rb +69 -0
  68. data/lib/stupidedi/editor/transaction_set_ed.rb +275 -0
  69. data/lib/stupidedi/editor/transmission_ed.rb +90 -0
  70. data/lib/stupidedi/editor.rb +37 -0
  71. data/lib/stupidedi/either.rb +287 -0
  72. data/lib/stupidedi/exceptions/invalid_element_error.rb +8 -0
  73. data/lib/stupidedi/exceptions/invalid_schema_error.rb +8 -0
  74. data/lib/stupidedi/exceptions/output_error.rb +8 -0
  75. data/lib/stupidedi/exceptions/parse_error.rb +8 -0
  76. data/lib/stupidedi/exceptions/stupidedi_error.rb +8 -0
  77. data/lib/stupidedi/exceptions/tokenize_error.rb +8 -0
  78. data/lib/stupidedi/exceptions/zipper_error.rb +8 -0
  79. data/lib/stupidedi/exceptions.rb +11 -0
  80. data/lib/stupidedi/guides/005010/X214-HN277.rb +409 -0
  81. data/lib/stupidedi/guides/005010/X221-HP835.rb +613 -0
  82. data/lib/stupidedi/guides/005010/X221A1-HP835.rb +613 -0
  83. data/lib/stupidedi/guides/005010/X222-HC837P.rb +2291 -0
  84. data/lib/stupidedi/guides/005010/X222A1-HC837P.rb +2297 -0
  85. data/lib/stupidedi/guides/005010/X231-FA999.rb +123 -0
  86. data/lib/stupidedi/guides/005010/X231A1-FA999.rb +119 -0
  87. data/lib/stupidedi/guides/005010/element_reqs.rb +38 -0
  88. data/lib/stupidedi/guides/005010/guide_builder.rb +180 -0
  89. data/lib/stupidedi/guides/005010/segment_reqs.rb +32 -0
  90. data/lib/stupidedi/guides/005010.rb +64 -0
  91. data/lib/stupidedi/guides.rb +5 -0
  92. data/lib/stupidedi/inspect.rb +26 -0
  93. data/lib/stupidedi/reader/input/abstract_input.rb +133 -0
  94. data/lib/stupidedi/reader/input/delegated_input.rb +111 -0
  95. data/lib/stupidedi/reader/input/file_input.rb +155 -0
  96. data/lib/stupidedi/reader/input.rb +30 -0
  97. data/lib/stupidedi/reader/position.rb +69 -0
  98. data/lib/stupidedi/reader/result.rb +168 -0
  99. data/lib/stupidedi/reader/segment_dict.rb +175 -0
  100. data/lib/stupidedi/reader/separators.rb +85 -0
  101. data/lib/stupidedi/reader/stream_reader.rb +172 -0
  102. data/lib/stupidedi/reader/token_reader.rb +466 -0
  103. data/lib/stupidedi/reader/tokens/component_element_tok.rb +56 -0
  104. data/lib/stupidedi/reader/tokens/composite_element_tok.rb +64 -0
  105. data/lib/stupidedi/reader/tokens/repeated_element_tok.rb +64 -0
  106. data/lib/stupidedi/reader/tokens/segment_tok.rb +51 -0
  107. data/lib/stupidedi/reader/tokens/simple_element_tok.rb +63 -0
  108. data/lib/stupidedi/reader.rb +121 -0
  109. data/lib/stupidedi/schema/abstract_def.rb +74 -0
  110. data/lib/stupidedi/schema/abstract_use.rb +73 -0
  111. data/lib/stupidedi/schema/code_list.rb +94 -0
  112. data/lib/stupidedi/schema/element_def.rb +173 -0
  113. data/lib/stupidedi/schema/element_req.rb +56 -0
  114. data/lib/stupidedi/schema/element_use.rb +251 -0
  115. data/lib/stupidedi/schema/functional_group_def.rb +114 -0
  116. data/lib/stupidedi/schema/interchange_def.rb +93 -0
  117. data/lib/stupidedi/schema/loop_def.rb +152 -0
  118. data/lib/stupidedi/schema/repeat_count.rb +85 -0
  119. data/lib/stupidedi/schema/segment_def.rb +108 -0
  120. data/lib/stupidedi/schema/segment_req.rb +43 -0
  121. data/lib/stupidedi/schema/segment_use.rb +98 -0
  122. data/lib/stupidedi/schema/syntax_note.rb +63 -0
  123. data/lib/stupidedi/schema/table_def.rb +139 -0
  124. data/lib/stupidedi/schema/transaction_set_def.rb +88 -0
  125. data/lib/stupidedi/schema.rb +28 -0
  126. data/lib/stupidedi/sets/absolute_set.rb +297 -0
  127. data/lib/stupidedi/sets/abstract_set.rb +174 -0
  128. data/lib/stupidedi/sets/null_set.rb +125 -0
  129. data/lib/stupidedi/sets/relative_complement.rb +137 -0
  130. data/lib/stupidedi/sets/relative_set.rb +269 -0
  131. data/lib/stupidedi/sets/universal_set.rb +104 -0
  132. data/lib/stupidedi/sets.rb +57 -0
  133. data/lib/stupidedi/tail_call.rb +109 -0
  134. data/lib/stupidedi/thread_local.rb +174 -0
  135. data/lib/stupidedi/values/abstract_element_val.rb +19 -0
  136. data/lib/stupidedi/values/abstract_val.rb +130 -0
  137. data/lib/stupidedi/values/composite_element_val.rb +95 -0
  138. data/lib/stupidedi/values/functional_group_val.rb +102 -0
  139. data/lib/stupidedi/values/interchange_val.rb +86 -0
  140. data/lib/stupidedi/values/invalid_envelope_val.rb +61 -0
  141. data/lib/stupidedi/values/invalid_segment_val.rb +78 -0
  142. data/lib/stupidedi/values/loop_val.rb +70 -0
  143. data/lib/stupidedi/values/repeated_element_val.rb +105 -0
  144. data/lib/stupidedi/values/segment_val.rb +104 -0
  145. data/lib/stupidedi/values/segment_val_group.rb +20 -0
  146. data/lib/stupidedi/values/simple_element_val.rb +80 -0
  147. data/lib/stupidedi/values/table_val.rb +66 -0
  148. data/lib/stupidedi/values/transaction_set_val.rb +66 -0
  149. data/lib/stupidedi/values/transmission_val.rb +52 -0
  150. data/lib/stupidedi/values.rb +21 -0
  151. data/lib/stupidedi/version.rb +3 -0
  152. data/lib/stupidedi/versions/functional_groups/004010/element_defs.rb +54 -0
  153. data/lib/stupidedi/versions/functional_groups/004010/element_reqs.rb +18 -0
  154. data/lib/stupidedi/versions/functional_groups/004010/element_types/date_val.rb +527 -0
  155. data/lib/stupidedi/versions/functional_groups/004010/element_types/fixnum_val.rb +335 -0
  156. data/lib/stupidedi/versions/functional_groups/004010/element_types/float_val.rb +299 -0
  157. data/lib/stupidedi/versions/functional_groups/004010/element_types/identifier_val.rb +287 -0
  158. data/lib/stupidedi/versions/functional_groups/004010/element_types/string_val.rb +338 -0
  159. data/lib/stupidedi/versions/functional_groups/004010/element_types/time_val.rb +309 -0
  160. data/lib/stupidedi/versions/functional_groups/004010/element_types.rb +124 -0
  161. data/lib/stupidedi/versions/functional_groups/004010/functional_group_def.rb +30 -0
  162. data/lib/stupidedi/versions/functional_groups/004010/segment_defs/GE.rb +20 -0
  163. data/lib/stupidedi/versions/functional_groups/004010/segment_defs/GS.rb +27 -0
  164. data/lib/stupidedi/versions/functional_groups/004010/segment_defs/SE.rb +20 -0
  165. data/lib/stupidedi/versions/functional_groups/004010/segment_defs/ST.rb +20 -0
  166. data/lib/stupidedi/versions/functional_groups/004010/segment_defs.rb +23 -0
  167. data/lib/stupidedi/versions/functional_groups/004010/segment_reqs.rb +18 -0
  168. data/lib/stupidedi/versions/functional_groups/004010/syntax_notes.rb +174 -0
  169. data/lib/stupidedi/versions/functional_groups/004010.rb +38 -0
  170. data/lib/stupidedi/versions/functional_groups/005010/element_defs.rb +1405 -0
  171. data/lib/stupidedi/versions/functional_groups/005010/element_reqs.rb +18 -0
  172. data/lib/stupidedi/versions/functional_groups/005010/element_types/date_val.rb +577 -0
  173. data/lib/stupidedi/versions/functional_groups/005010/element_types/fixnum_val.rb +322 -0
  174. data/lib/stupidedi/versions/functional_groups/005010/element_types/float_val.rb +354 -0
  175. data/lib/stupidedi/versions/functional_groups/005010/element_types/identifier_val.rb +368 -0
  176. data/lib/stupidedi/versions/functional_groups/005010/element_types/operators.rb +117 -0
  177. data/lib/stupidedi/versions/functional_groups/005010/element_types/string_val.rb +398 -0
  178. data/lib/stupidedi/versions/functional_groups/005010/element_types/time_val.rb +327 -0
  179. data/lib/stupidedi/versions/functional_groups/005010/element_types.rb +132 -0
  180. data/lib/stupidedi/versions/functional_groups/005010/functional_group_def.rb +30 -0
  181. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/AK1.rb +21 -0
  182. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/AK2.rb +21 -0
  183. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/AK9.rb +28 -0
  184. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/AMT.rb +21 -0
  185. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/BHT.rb +24 -0
  186. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/BPR.rb +49 -0
  187. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CAS.rb +56 -0
  188. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CL1.rb +22 -0
  189. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CLM.rb +41 -0
  190. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CLP.rb +34 -0
  191. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CN1.rb +24 -0
  192. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CR1.rb +32 -0
  193. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CR2.rb +35 -0
  194. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CR3.rb +25 -0
  195. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CRC.rb +26 -0
  196. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CTP.rb +36 -0
  197. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CTX.rb +24 -0
  198. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/CUR.rb +57 -0
  199. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/DMG.rb +34 -0
  200. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/DN1.rb +22 -0
  201. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/DN2.rb +24 -0
  202. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/DTM.rb +24 -0
  203. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/DTP.rb +21 -0
  204. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/FRM.rb +25 -0
  205. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/GE.rb +20 -0
  206. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/GS.rb +27 -0
  207. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/HCP.rb +39 -0
  208. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/HI.rb +31 -0
  209. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/HL.rb +22 -0
  210. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/IK3.rb +22 -0
  211. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/IK4.rb +22 -0
  212. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/IK5.rb +24 -0
  213. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/K3.rb +21 -0
  214. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/LIN.rb +69 -0
  215. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/LQ.rb +22 -0
  216. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/LX.rb +19 -0
  217. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/MEA.rb +39 -0
  218. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/MIA.rb +45 -0
  219. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/MOA.rb +28 -0
  220. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/N1.rb +24 -0
  221. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/N2.rb +20 -0
  222. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/N3.rb +20 -0
  223. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/N4.rb +30 -0
  224. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/NM1.rb +35 -0
  225. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/NTE.rb +20 -0
  226. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/OI.rb +24 -0
  227. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PAT.rb +31 -0
  228. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PER.rb +32 -0
  229. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PLB.rb +40 -0
  230. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PRV.rb +26 -0
  231. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PS1.rb +21 -0
  232. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/PWK.rb +30 -0
  233. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/QTY.rb +25 -0
  234. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/RDM.rb +23 -0
  235. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/REF.rb +23 -0
  236. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SBR.rb +28 -0
  237. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SE.rb +20 -0
  238. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/ST.rb +21 -0
  239. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/STC.rb +30 -0
  240. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SV1.rb +44 -0
  241. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SV2.rb +29 -0
  242. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SV3.rb +30 -0
  243. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SV5.rb +29 -0
  244. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SVC.rb +26 -0
  245. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/SVD.rb +24 -0
  246. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/TOO.rb +21 -0
  247. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/TRN.rb +22 -0
  248. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/TS2.rb +40 -0
  249. data/lib/stupidedi/versions/functional_groups/005010/segment_defs/TS3.rb +45 -0
  250. data/lib/stupidedi/versions/functional_groups/005010/segment_defs.rb +227 -0
  251. data/lib/stupidedi/versions/functional_groups/005010/segment_reqs.rb +18 -0
  252. data/lib/stupidedi/versions/functional_groups/005010/syntax_notes.rb +165 -0
  253. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/FA999.rb +38 -0
  254. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HB271.rb +85 -0
  255. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HC837.rb +163 -0
  256. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HI278.rb +64 -0
  257. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HN277.rb +74 -0
  258. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HP835.rb +68 -0
  259. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HR276.rb +57 -0
  260. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/HS270.rb +53 -0
  261. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs/RA820.rb +240 -0
  262. data/lib/stupidedi/versions/functional_groups/005010/transaction_set_defs.rb +68 -0
  263. data/lib/stupidedi/versions/functional_groups/005010.rb +38 -0
  264. data/lib/stupidedi/versions/functional_groups.rb +8 -0
  265. data/lib/stupidedi/versions/interchanges/00401/element_defs.rb +224 -0
  266. data/lib/stupidedi/versions/interchanges/00401/interchange_def.rb +45 -0
  267. data/lib/stupidedi/versions/interchanges/00401/segment_defs/IEA.rb +20 -0
  268. data/lib/stupidedi/versions/interchanges/00401/segment_defs/ISA.rb +34 -0
  269. data/lib/stupidedi/versions/interchanges/00401/segment_defs/TA1.rb +23 -0
  270. data/lib/stupidedi/versions/interchanges/00401/segment_defs.rb +28 -0
  271. data/lib/stupidedi/versions/interchanges/00401.rb +23 -0
  272. data/lib/stupidedi/versions/interchanges/00501/element_defs.rb +269 -0
  273. data/lib/stupidedi/versions/interchanges/00501/interchange_def.rb +47 -0
  274. data/lib/stupidedi/versions/interchanges/00501/segment_defs/IEA.rb +20 -0
  275. data/lib/stupidedi/versions/interchanges/00501/segment_defs/ISA.rb +34 -0
  276. data/lib/stupidedi/versions/interchanges/00501/segment_defs/ISB.rb +18 -0
  277. data/lib/stupidedi/versions/interchanges/00501/segment_defs/ISE.rb +18 -0
  278. data/lib/stupidedi/versions/interchanges/00501/segment_defs/TA1.rb +23 -0
  279. data/lib/stupidedi/versions/interchanges/00501/segment_defs/TA3.rb +18 -0
  280. data/lib/stupidedi/versions/interchanges/00501/segment_defs.rb +37 -0
  281. data/lib/stupidedi/versions/interchanges/00501.rb +23 -0
  282. data/lib/stupidedi/versions/interchanges.rb +8 -0
  283. data/lib/stupidedi/versions.rb +6 -0
  284. data/lib/stupidedi/writer/claredi.rb +142 -0
  285. data/lib/stupidedi/writer/default.rb +124 -0
  286. data/lib/stupidedi/writer.rb +6 -0
  287. data/lib/stupidedi/zipper/abstract_cursor.rb +351 -0
  288. data/lib/stupidedi/zipper/dangling_cursor.rb +103 -0
  289. data/lib/stupidedi/zipper/edited_cursor.rb +157 -0
  290. data/lib/stupidedi/zipper/memoized_cursor.rb +131 -0
  291. data/lib/stupidedi/zipper/path.rb +124 -0
  292. data/lib/stupidedi/zipper/root_cursor.rb +120 -0
  293. data/lib/stupidedi/zipper.rb +25 -0
  294. data/lib/stupidedi.rb +66 -0
  295. data/spec/examples/integration/generating.example +551 -0
  296. data/spec/examples/integration/navigating.example +214 -0
  297. data/spec/examples/integration/parsing.example +445 -0
  298. data/spec/examples/ruby/array.example +476 -0
  299. data/spec/examples/ruby/blank.example +62 -0
  300. data/spec/examples/ruby/count.example +68 -0
  301. data/spec/examples/ruby/object.example +99 -0
  302. data/spec/examples/ruby/string.example +111 -0
  303. data/spec/examples/ruby/symbol.example +117 -0
  304. data/spec/examples/ruby/to_d.example +90 -0
  305. data/spec/examples/ruby/try.example +50 -0
  306. data/spec/examples/stupidedi/either.example +375 -0
  307. data/spec/examples/stupidedi/reader/failure.example +68 -0
  308. data/spec/examples/stupidedi/reader/input/delegated_input.example +292 -0
  309. data/spec/examples/stupidedi/reader/separators.example +73 -0
  310. data/spec/examples/stupidedi/reader/stream_reader.example +48 -0
  311. data/spec/examples/stupidedi/reader/success.example +34 -0
  312. data/spec/examples/stupidedi/reader/token_reader.example +775 -0
  313. data/spec/examples/stupidedi/reader.example +168 -0
  314. data/spec/examples/stupidedi/sets/absolute_set.example +1577 -0
  315. data/spec/examples/stupidedi/sets/null_set.example +2 -0
  316. data/spec/examples/stupidedi/sets/relative_set.example +2 -0
  317. data/spec/examples/stupidedi/sets/universal_set.example +1 -0
  318. data/spec/examples/stupidedi/versions/005010/element_types/an.example +201 -0
  319. data/spec/examples/stupidedi/versions/005010/element_types/dt.example +258 -0
  320. data/spec/examples/stupidedi/versions/005010/element_types/id.example +192 -0
  321. data/spec/examples/stupidedi/versions/005010/element_types/nn.example +177 -0
  322. data/spec/examples/stupidedi/versions/005010/element_types/r.example +178 -0
  323. data/spec/examples/stupidedi/versions/005010/element_types/tm.example +2 -0
  324. data/spec/examples/stupidedi/zipper/abstract_cursor.example +417 -0
  325. data/spec/examples/stupidedi/zipper.example +9 -0
  326. data/spec/fixtures/X186-AG824/1-bad.txt +21 -0
  327. data/spec/fixtures/X186-AG824/1-good.txt +17 -0
  328. data/spec/fixtures/X186-AG824/2-bad.txt +26 -0
  329. data/spec/fixtures/X186-AG824/2-good.txt +21 -0
  330. data/spec/fixtures/X186-AG824/3-bad.txt +87 -0
  331. data/spec/fixtures/X186-AG824/3-good.txt +61 -0
  332. data/spec/fixtures/X212-HN277/1-bad.txt +54 -0
  333. data/spec/fixtures/X212-HN277/1-good.txt +46 -0
  334. data/spec/fixtures/X212-HN277/2-bad.txt +37 -0
  335. data/spec/fixtures/X212-HN277/2-good.txt +29 -0
  336. data/spec/fixtures/X212-HN277/3-bad.txt +22 -0
  337. data/spec/fixtures/X212-HN277/3-good.txt +17 -0
  338. data/spec/fixtures/X212-HN277/4-bad.txt +30 -0
  339. data/spec/fixtures/X212-HN277/4-good.txt +24 -0
  340. data/spec/fixtures/X212-HR276/1-bad.txt +53 -0
  341. data/spec/fixtures/X212-HR276/1-good.txt +46 -0
  342. data/spec/fixtures/X212-HR276/2-bad.txt +45 -0
  343. data/spec/fixtures/X212-HR276/2-good.txt +38 -0
  344. data/spec/fixtures/X212-HR276/3-bad.txt +32 -0
  345. data/spec/fixtures/X212-HR276/3-good.txt +26 -0
  346. data/spec/fixtures/X212-HR276/4-bad.txt +32 -0
  347. data/spec/fixtures/X212-HR276/4-good.txt +26 -0
  348. data/spec/fixtures/X214-HN277/1-bad.txt +58 -0
  349. data/spec/fixtures/X214-HN277/1-good.txt +47 -0
  350. data/spec/fixtures/X214-HN277/2-bad.txt +34 -0
  351. data/spec/fixtures/X214-HN277/2-good.txt +22 -0
  352. data/spec/fixtures/X214-HN277/3-bad.txt +64 -0
  353. data/spec/fixtures/X214-HN277/3-good.txt +54 -0
  354. data/spec/fixtures/X214-HN277/4-bad.txt +77 -0
  355. data/spec/fixtures/X214-HN277/4-good.txt +63 -0
  356. data/spec/fixtures/X216-HI278/1-bad.txt +42 -0
  357. data/spec/fixtures/X216-HI278/1-good.txt +27 -0
  358. data/spec/fixtures/X216-HI278/2-bad.txt +43 -0
  359. data/spec/fixtures/X216-HI278/2-good.txt +29 -0
  360. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-1.txt +24 -0
  361. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-1_Clean.txt +20 -0
  362. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-2.txt +46 -0
  363. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-2_Clean.txt +30 -0
  364. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-3.txt +38 -0
  365. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-3_Clean.txt +24 -0
  366. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-4a.txt +39 -0
  367. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-4a_Clean.txt +24 -0
  368. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-4b.txt +43 -0
  369. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-4b_Clean.txt +25 -0
  370. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-5.txt +63 -0
  371. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-5_Clean.txt +41 -0
  372. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-6.txt +36 -0
  373. data/spec/fixtures/X217-HI278/Sample_278_Request_5010X217-6_Clean.txt +20 -0
  374. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-1r.txt +34 -0
  375. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-1r_Clean.txt +23 -0
  376. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-2r.txt +46 -0
  377. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-2r_Clean.txt +31 -0
  378. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-3r.txt +44 -0
  379. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-3r_Clean.txt +26 -0
  380. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-4ar_Clean.txt +28 -0
  381. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-4br_Clean.txt +35 -0
  382. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-5r.txt +60 -0
  383. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-5r_Clean.txt +42 -0
  384. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-6r.txt +40 -0
  385. data/spec/fixtures/X217-HI278/Sample_278_Response_5010X217-6r_Clean.txt +24 -0
  386. data/spec/fixtures/X218-RA820/1-bad.txt +31 -0
  387. data/spec/fixtures/X218-RA820/1-good.txt +25 -0
  388. data/spec/fixtures/X218-RA820/2-bad.txt +37 -0
  389. data/spec/fixtures/X218-RA820/2-good.txt +29 -0
  390. data/spec/fixtures/X218-RA820/3-bad.txt +29 -0
  391. data/spec/fixtures/X218-RA820/3-good.txt +23 -0
  392. data/spec/fixtures/X218-RA820/4-bad.txt +36 -0
  393. data/spec/fixtures/X218-RA820/4-good.txt +27 -0
  394. data/spec/fixtures/X220-BE834/1-bad.txt +35 -0
  395. data/spec/fixtures/X220-BE834/1-good.txt +29 -0
  396. data/spec/fixtures/X220-BE834/10-bad.txt +26 -0
  397. data/spec/fixtures/X220-BE834/10-good.txt +19 -0
  398. data/spec/fixtures/X220-BE834/2-bad.txt +31 -0
  399. data/spec/fixtures/X220-BE834/2-good.txt +23 -0
  400. data/spec/fixtures/X220-BE834/3-bad.txt +36 -0
  401. data/spec/fixtures/X220-BE834/3-good.txt +26 -0
  402. data/spec/fixtures/X220-BE834/4-bad.txt +27 -0
  403. data/spec/fixtures/X220-BE834/4-good.txt +21 -0
  404. data/spec/fixtures/X220-BE834/5-bad.txt +26 -0
  405. data/spec/fixtures/X220-BE834/5-good.txt +17 -0
  406. data/spec/fixtures/X220-BE834/6-bad.txt +25 -0
  407. data/spec/fixtures/X220-BE834/6-good.txt +20 -0
  408. data/spec/fixtures/X220-BE834/7-bad.txt +25 -0
  409. data/spec/fixtures/X220-BE834/7-good.txt +20 -0
  410. data/spec/fixtures/X220-BE834/8-bad.txt +25 -0
  411. data/spec/fixtures/X220-BE834/8-good.txt +20 -0
  412. data/spec/fixtures/X220-BE834/9-bad.txt +27 -0
  413. data/spec/fixtures/X220-BE834/9-good.txt +21 -0
  414. data/spec/fixtures/X221-HP835/1-bad.txt +58 -0
  415. data/spec/fixtures/X221-HP835/1-good.txt +40 -0
  416. data/spec/fixtures/X221-HP835/2-bad.txt +51 -0
  417. data/spec/fixtures/X221-HP835/2-good.txt +40 -0
  418. data/spec/fixtures/X221-HP835/3a-bad.txt +78 -0
  419. data/spec/fixtures/X221-HP835/3a-good.txt +49 -0
  420. data/spec/fixtures/X221-HP835/3b-bad.txt +60 -0
  421. data/spec/fixtures/X221-HP835/3b-good.txt +32 -0
  422. data/spec/fixtures/X221-HP835/3c-bad.txt +55 -0
  423. data/spec/fixtures/X221-HP835/3c-good.txt +34 -0
  424. data/spec/fixtures/X222-HC837/1-bad.txt +60 -0
  425. data/spec/fixtures/X222-HC837/1-good.txt +53 -0
  426. data/spec/fixtures/X222-HC837/10a-bad.txt +52 -0
  427. data/spec/fixtures/X222-HC837/10a-good.txt +40 -0
  428. data/spec/fixtures/X222-HC837/10b-bad.txt +99 -0
  429. data/spec/fixtures/X222-HC837/10b-good.txt +80 -0
  430. data/spec/fixtures/X222-HC837/10c-bad.txt +105 -0
  431. data/spec/fixtures/X222-HC837/10c-good.txt +80 -0
  432. data/spec/fixtures/X222-HC837/11-bad.txt +69 -0
  433. data/spec/fixtures/X222-HC837/11-good.txt +45 -0
  434. data/spec/fixtures/X222-HC837/12-bad.txt +73 -0
  435. data/spec/fixtures/X222-HC837/12-good.txt +51 -0
  436. data/spec/fixtures/X222-HC837/13-bad.txt +64 -0
  437. data/spec/fixtures/X222-HC837/13-good.txt +46 -0
  438. data/spec/fixtures/X222-HC837/3a-bad.txt +83 -0
  439. data/spec/fixtures/X222-HC837/3a-good.txt +59 -0
  440. data/spec/fixtures/X222-HC837/3b-bad.txt +97 -0
  441. data/spec/fixtures/X222-HC837/3b-good.txt +70 -0
  442. data/spec/fixtures/X222-HC837/3c-bad.txt +95 -0
  443. data/spec/fixtures/X222-HC837/3c-good.txt +74 -0
  444. data/spec/fixtures/X222-HC837/4-bad.txt +67 -0
  445. data/spec/fixtures/X222-HC837/4-good.txt +48 -0
  446. data/spec/fixtures/X222-HC837/5-bad.txt +73 -0
  447. data/spec/fixtures/X222-HC837/5-good.txt +60 -0
  448. data/spec/fixtures/X222-HC837/6-bad.txt +50 -0
  449. data/spec/fixtures/X222-HC837/6-good.txt +39 -0
  450. data/spec/fixtures/X222-HC837/7-bad.txt +93 -0
  451. data/spec/fixtures/X222-HC837/7-good.txt +78 -0
  452. data/spec/fixtures/X222-HC837/8-bad.txt +64 -0
  453. data/spec/fixtures/X222-HC837/8-good.txt +52 -0
  454. data/spec/fixtures/X222-HC837/9-bad.txt +56 -0
  455. data/spec/fixtures/X222-HC837/9-good.txt +38 -0
  456. data/spec/fixtures/X223-HC837/1-bad.txt +66 -0
  457. data/spec/fixtures/X223-HC837/1-good.txt +53 -0
  458. data/spec/fixtures/X223-HC837/2-bad.txt +69 -0
  459. data/spec/fixtures/X223-HC837/2-good.txt +60 -0
  460. data/spec/fixtures/X223-HC837/3-bad.txt +89 -0
  461. data/spec/fixtures/X223-HC837/3-good.txt +61 -0
  462. data/spec/fixtures/X223-HC837/4-bad.txt +60 -0
  463. data/spec/fixtures/X223-HC837/4-good.txt +40 -0
  464. data/spec/fixtures/X223-HC837/5-bad.txt +75 -0
  465. data/spec/fixtures/X223-HC837/5-good.txt +58 -0
  466. data/spec/fixtures/X224-HC837/1-bad.txt +54 -0
  467. data/spec/fixtures/X224-HC837/1-good.txt +44 -0
  468. data/spec/fixtures/X224-HC837/2a-bad.txt +52 -0
  469. data/spec/fixtures/X224-HC837/2a-good.txt +42 -0
  470. data/spec/fixtures/X224-HC837/2b-bad.txt +67 -0
  471. data/spec/fixtures/X224-HC837/2b-good.txt +52 -0
  472. data/spec/fixtures/X224-HC837/3-bad.txt +67 -0
  473. data/spec/fixtures/X224-HC837/3-good.txt +51 -0
  474. data/spec/fixtures/X224-HC837/4-bad.txt +49 -0
  475. data/spec/fixtures/X224-HC837/4-good.txt +40 -0
  476. data/spec/fixtures/X230-FA997/1-bad.txt +19 -0
  477. data/spec/fixtures/X230-FA997/1-good.txt +16 -0
  478. data/spec/fixtures/X231-FA999/1-bad.txt +20 -0
  479. data/spec/fixtures/X231-FA999/1-good.txt +20 -0
  480. data/spec/fixtures/X279-HB271/1-bad.txt +36 -0
  481. data/spec/fixtures/X279-HB271/1-good.txt +30 -0
  482. data/spec/fixtures/X279-HB271/2-bad.txt +22 -0
  483. data/spec/fixtures/X279-HB271/2-good.txt +16 -0
  484. data/spec/fixtures/X279-HB271/3-bad.txt +44 -0
  485. data/spec/fixtures/X279-HB271/3-good.txt +36 -0
  486. data/spec/fixtures/X279-HS270/1-bad.txt +29 -0
  487. data/spec/fixtures/X279-HS270/1-good.txt +23 -0
  488. data/spec/fixtures/X279-HS270/2-bad.txt +32 -0
  489. data/spec/fixtures/X279-HS270/2-good.txt +25 -0
  490. data/spec/spec_helper.rb +34 -0
  491. data/spec/support/fixtures.rb +26 -0
  492. data/spec/support/matchers/either_matchers.rb +26 -0
  493. data/spec/support/matchers/navigation_matchers.rb +247 -0
  494. data/spec/support/node.rb +41 -0
  495. data/spec/support/quickcheck/characters.rb +28 -0
  496. data/spec/support/quickcheck/property.rb +105 -0
  497. data/spec/support/quickcheck/serialized_edi.rb +399 -0
  498. data/spec/support/quickcheck.rb +302 -0
  499. data/spec/support/rcov.rb +34 -0
  500. metadata +577 -0
data/doc/Navigating.md ADDED
@@ -0,0 +1,645 @@
1
+ Navigating the Parse Tree
2
+ =========================
3
+
4
+ Fundamentally, the [`StateMachine`][1] presents an interface for iterating the
5
+ syntax tree as if it were merely a linear sequence of segments. Purely
6
+ syntactical values like [`LoopVal`][2]s and [`TableVal`][3]s are hidden from the
7
+ programmer. While the interface only exposes segments and elements, the
8
+ information hidden by [`StateMachine`][1] provides an efficient means to search
9
+ the parse tree for specific segments.
10
+
11
+ For information on how to construct a parse tree programmatically, see the
12
+ document on {file:Generating.md Generating X12}. The [`StateMachine`][1] can be
13
+ accessed via the [`BuilderDsl#machine`][4] method. For information about how
14
+ to construct a parse tree from an input stream, see {file:Parsing.md Parsing X12}.
15
+
16
+ Iterating Segments
17
+ ------------------
18
+
19
+ ### Current Segment
20
+
21
+ When you want to access the current [`SegmentVal`][5], use the [`#segment`][6]
22
+ method. It returns an [`AbstractCursor`][7], which is a read-only pointer to the
23
+ current segment within the parse tree, wrapped by [`Either`][8]. When the parse
24
+ tree is empty, a failure will be returned.
25
+
26
+ # Success
27
+ machine.segment
28
+ #=> Either.success(#<Zipper::AbstractCursor ...>)
29
+
30
+ # Failure
31
+ machine.segment
32
+ #=> Either.failure("not a segment")
33
+
34
+ You may be wondering why the [`SegmentVal`][5] is wrapped by two wrappers. The
35
+ first, [`Either`][8], provides a manner to distinguish error return values from
36
+ normal return values. It is more sophisticated and less error-prone than using
37
+ conventions like returning `nil` on failure, because it supports chaining,
38
+ detailed error information can be returned, and the risk of neglecting to test
39
+ for an error is mitigated.
40
+
41
+ This [`Either`][8] value wraps an [`AbstractCursor`][7], which points to the
42
+ [`SegmentVal`][5] via its `#node` method. Because [`SegmentVal`][5] does not
43
+ have information about its parent or siblings (it only is aware of its
44
+ `#children`), returning only a [`SegmentVal`][5] does not always provide enough
45
+ information. The [`AbstractCursor`][7], on the other hand, allows access to any
46
+ related `AbstractVal` node.
47
+
48
+ # Success
49
+ machine.segment.map(&:node)
50
+ #=> Either.success(SegmentVal[IEA](...))
51
+
52
+ machine.segment.map(&:parent).map(&:node)
53
+ #=> Either.success(InterchangeVal[00501](...))
54
+
55
+ # Failure
56
+ machine.segment.map(&:node)
57
+ #=> Either.failure("not a segment")
58
+
59
+ For another example, the current segment identifier can be accessed like any
60
+ other method of [`SegmentVal`][5].
61
+
62
+ # When machine.segment fails, nothing is printed
63
+ machine.segment.tap do |s|
64
+ puts "Hello, #{s.node.id}"
65
+ end
66
+
67
+ ### Going Forward
68
+
69
+ The [`#next`][9] method returns a [`StateMachine`][1] positioned at the segment
70
+ immediately following the current segment. Optionally, you may specify how many
71
+ segments to advance, which defaults to `1`. You can check if the current segment
72
+ is the last segment using the `#last?` method.
73
+
74
+ # Success
75
+ machine.last?
76
+ #=> false
77
+
78
+ machine.next
79
+ #=> Either.success(StateMachine[1](SegmentVal[GS](...)))
80
+
81
+ # Success
82
+ machine.next(3)
83
+ #=> Either.success(StateMachine[1](SegmentVal[BHT](...)))
84
+
85
+ # Failure
86
+ machine.last?
87
+ #=> true
88
+
89
+ machine.next
90
+ #=> Either.failure("cannot move to next after last segment")
91
+
92
+ ### Going Backward
93
+
94
+ The [`#prev`][10] method returns a [`StateMachine`][1] positioned at the segment
95
+ immediately preceeding the current segment. Optionally, you may specify how many
96
+ segments to rewind, which defaults to `1`. You can check if the current segment
97
+ is the first segment using the `#first?` method.
98
+
99
+ # Success
100
+ machine.first?
101
+ #=> false
102
+
103
+ machine.prev
104
+ #=> Either.success(StateMachine[1](SegmentVal[GS](...)))
105
+
106
+ # Success
107
+ machine.prev(2)
108
+ #=> Either.success(StateMachine[1](SegmentVal[ISA](...)))
109
+
110
+ # Failure
111
+ machine.first?
112
+ #=> true
113
+
114
+ machine.prev
115
+ #=> Either.failure("cannot move to prev before first segment")
116
+
117
+ Accessing Elements
118
+ ------------------
119
+
120
+ Elements can be accessed using the [`#element`][11] method, which accepts up to
121
+ three numeric arguments and requires at least one. Like [`#segment`][6], the
122
+ return value is a [`AbstractCursor`][7] wrapped by an [`Either`][8].
123
+
124
+ ### Simple Elements
125
+
126
+ To access the first element of the current segment, call `#element(1)`. Notice
127
+ elements are counted starting at `1`, not `0`. Beware that [`#element`][11] will
128
+ raise an `ArgumentError` if you attempt to access the fifth element of a segment
129
+ whose declaration only indicates four elements, for instance.
130
+
131
+ # Success
132
+ machine.element(1).map(&:node)
133
+ #=> Either.success(Nn.value[ I16: Number of Included Functional Groups](1))
134
+
135
+ # Error
136
+ machine.element(3).map(&:node)
137
+ #=> IEA has only 2 elements (ArgumentError)
138
+
139
+ ### Composite Elements
140
+
141
+ If the first element of the current segment is a `CompositeElementVal`, calling
142
+ `#element(1)` will return the entire composite value. To access a specific
143
+ component, call `#element(1, n)` which will return the *nth* `SimpleElementVal`.
144
+ Beware that [`#element`][11] will raise an `ArgumentError` if you attempt, for
145
+ instance, to access the third component of a composite whose declaration only
146
+ indicates two components.
147
+
148
+ # Success
149
+ machine.element(5).map(&:node)
150
+ #=> Either.success(CompositeElementVal[C023: HEALTH CARE SERVICE LOCATION INFORMATION](...))
151
+
152
+ # Success
153
+ machine.element(5, 1).map(&:node)
154
+ #=> Either.success(AN.value[E1331: Place of Service Code](11))
155
+
156
+ # Error
157
+ machine.element(5, 4).map(&:node)
158
+ #=> CLM05 has only 3 components (ArgumentError)
159
+
160
+ ### Repeated Elements
161
+
162
+ When the first element of the current segment is a `RepeatedElementVal`, calling
163
+ `#element(1)` will return the entire sequence of element vals. To access a
164
+ specific occurrence of the element, call `#element(1, n)` which will return the
165
+ *nth* occurrence if it exists. If the element is a repeating *composite* element,
166
+ an optional third argument can be given to select a specific component. Beware
167
+ that [`#element`][11] will raise an `ArgumentError` if, for instance, you try to
168
+ access the sixth occurrence when the element definition declares the element can
169
+ occur a maximum of five times.
170
+
171
+ Taking Bigger Steps
172
+ -------------------
173
+
174
+ ### First Segment
175
+
176
+ You can position the [`StateMachine`][1] at the first segment in the parse tree
177
+ by calling [`#first`][12]. When there are no segments in the parse tree, this
178
+ method returns a failure. This will typically position the [`StateMachine`][1]
179
+ at the first `ISA` segment.
180
+
181
+ machine.first.map(&:first?)
182
+ #=> Either.success(true)
183
+
184
+ ### Last Segment
185
+
186
+ Likewise, the [`#last`][13] method will position the [`StateMachine`][1] at the
187
+ first segment in the parse tree if there is one. When the parse tree is empty,
188
+ a failure is returned instead. This will typically position the [`StateMachine`][1]
189
+ at the last `IEA` segment.
190
+
191
+ machine.last.map(&:last?)
192
+ #=> Either.success(true)
193
+
194
+ ### Searching for Segments
195
+
196
+ The [`#find`][14] method performs an efficient context-sensitive search based
197
+ on the current position. Being context-sensitive places restrictions on which
198
+ segments are reachable from the current state, unlike iterating segments one
199
+ at a time. These restrictions prevent complicated problems, like mistakenly
200
+ finding an `NM1` segment from the next interchange because there were no more
201
+ `NM1` segments in the current interchange.
202
+
203
+ The *next* matching segment is returned, or a failure if no segments matched.
204
+ Searching for a segment that, according to the definition tree, cannot exist or
205
+ is not reachable will cause [`#find`][14] to raise an exception. To be clear,
206
+ [`#find`][14] only searches *forward* for segments, not backward.
207
+
208
+ #### Sibling Segments
209
+
210
+ <iframe src="images/837P-siblings.png" frameborder="no" scrolling="yes" height="430" width="100%"></iframe>
211
+
212
+ Segments connected directly by a horizontal dashed black line are siblings and
213
+ are reachable using [`#find`][14]. For instance, from the third `NM1`, the `N3`,
214
+ `N4`, and `REF` segments are reachable.
215
+
216
+ # From 2010AA NM1 right one segment to N3
217
+ machine.find(:N3)
218
+ #=> Either.success(StateMachine[1](N3(...)))
219
+
220
+ # Right two segments to N4
221
+ machine.find(:N4)
222
+ #=> Either.success(StateMachine[1](N4(...)))
223
+
224
+ # Right three segments to REF
225
+ machine.find(:REF)
226
+ #=> Either.success(StateMachine[1](REF(...)))
227
+
228
+ Likewise, `N4` and `REF` are reachable from `N3`; however, the third `NM1` is
229
+ _not_ reachable from `N3` because it preceeds `N3`.
230
+
231
+ #### Uncle Segments
232
+
233
+ Segments that occur as siblings of an ancestor node are uncles (remember that
234
+ [`#find`][14] only proceeds forward). Common uncle segments are `GE` and `IEA`,
235
+ which are analogous to "closing tags" for envelope structures. Other examples
236
+ include the `IK5` and `AK9` segments from the *999 Functional Acknowledgement*
237
+ transaction set.
238
+
239
+ # From BHT ascend twice and move left to GE
240
+ machine.find(:GE)
241
+
242
+ # From PRV ascend four times and move left to IEA
243
+ machine.find(:IEA)
244
+
245
+ Uncles are relatively rare in X12 transaction sets, because most child
246
+ structures, like loops, are _not_ wrapped by segments at both ends. For instance
247
+ there are no segments in Loop 2000A that follow the child loops.
248
+
249
+ #### Nephew Segments
250
+
251
+ <iframe src="images/837P-nephews.png" frameborder="no" scrolling="yes" height="450" width="100%"></iframe>
252
+
253
+ Segments that occur as the _first_ direct child of a sibling node are nephews.
254
+ The siblings that _follow_ the first child are not directly reachable, but they
255
+ can be reached indirectly by [chaining](#Chaining_Method_Calls) two calls to
256
+ [`#find`][14]. For example, `GS` is a nephew of `ISA`, and `ST` has two nephews
257
+ named `NM1`; but `BHT` is _not_ a nephew of `GS` because it is not the first
258
+ child of its parent node.
259
+
260
+ # From ST move left twice and down to NM1
261
+ machine.find(:NM1)
262
+
263
+ # From 2000A PRV move left and down to NM1
264
+ machine.find(:NM1)
265
+
266
+ # From 2300 CLM move left three times and down to NM1
267
+ machine.find(:NM1)
268
+
269
+ The first-child restriction prevents potential problems of ambiguity with
270
+ certain grammars. Consider the possibility that Table 1 could be defined to
271
+ have a 1100 `PER` loop in addition to its two `NM1` loops. From the `ST`
272
+ segment, without the restriction, there would be two possibly reachable `PER`
273
+ segments. One is the sibling of 1000A `NM1`, and the other is the 1100 `PER`
274
+ that we made up. Because [`#find`][14] returns the first matching segment, it
275
+ would return 1000A `PER` if it occurred, otherwise it would return the 1100
276
+ `PER`. The caller would have to check which one it was -- the necessity of this
277
+ test is not apparent without studying the grammar. Changing the grammar to add
278
+ Loop 1100 would break existing code. The first-child restriction solves these
279
+ problems.
280
+
281
+ #### Cousin Segments
282
+
283
+ <iframe src="images/837P-cousins.png" frameborder="no" scrolling="yes" height="470" width="100%"></iframe>
284
+
285
+ Segments that occurr as the _first_ child of a sibling of the parent node are
286
+ cousins of the current segment. Similar to the restriction on nephew segments,
287
+ siblings that follow the first child are _not_ directly reachable. For example,
288
+ the second `NM1` is a cousin of the first `NM1` and `PER` segments, the 2000AB
289
+ `NM1` is a cousin of all segments in Loop 2010AA, and each `HL` is a cousin of
290
+ all segments in Table 1.
291
+
292
+ # From 1000A PER to 2000A HL
293
+ machine.find(:HL)
294
+
295
+ # From 2000BA NM1 to 2000BB NM1
296
+ machine.find(:NM1)
297
+
298
+ You may have noticed, in some cases there are more than one cousin with the same
299
+ segment identifier -- there are three cousins of `BHT` named `HL`, for instance.
300
+ See [Element Constraints](#Element_Constraints) for information on how to find a
301
+ *specific* occurrence of `HL` segment based on its qualifier elements, or
302
+ [Chaining Method Calls](#Chaining_Method_Calls) for details on iterating each
303
+ `HL` segment, one-at-a-time.
304
+
305
+ #### Parent Segments
306
+
307
+ <iframe src="images/837P-parents.png" frameborder="no" scrolling="yes" height="450" width="100%"></iframe>
308
+
309
+ Internal knowledge of the underlying tree structure makes it possible to
310
+ *rewind* to the first segment of a parent structure, using the [`#parent`][15]
311
+ method. The parent may be the first segment of a loop, table, functional group,
312
+ or interchange, but never a transaction set, because they do not parent segments
313
+ directly.
314
+
315
+ # From PRV up two nodes, left one node, and down to ST
316
+ machine.parent
317
+
318
+ # From PER left one node to NM1
319
+ machine.parent
320
+
321
+ Traversing to the parent segment, unlike [`#find`][14], always traverses
322
+ backwards in the sequence of segments. The [`#parent`][15] method can only
323
+ rewind to the segment defined by the grammar, so it will always find the same
324
+ segment from a given starting position.
325
+
326
+ #### Element Constraints
327
+
328
+ Finding the next segment by identifier alone is often not specific enough. For
329
+ example, there are several different `NM1` segments that each have a different
330
+ meaning -- one is a provider, another is the insurance company, another is the
331
+ patient, etc. In the case of `NM1`, the first element is a qualifier that
332
+ indicates the meaning. To find `NM1*QC` "Patient Name", you can iterate the
333
+ reachable `NM1` segments and stop when you find the occurrence whose first
334
+ element equals `41` or when there are no more `NM1` occurrences,
335
+
336
+ # Find the NM1 occurrence with "41" in the first element
337
+ position = Either.success(machine)
338
+ searching = true
339
+
340
+ while searching and position.defined?
341
+ # Move position to the next NM1 segment
342
+ position = position.flatmap{|m| m.find(:NM1) }
343
+
344
+ # Check the constraint
345
+ position.tap do |nm1|
346
+ nm1.element(1).tap do |element|
347
+ # Stop the while loop if we found the match
348
+ searching = element.node != "QC"
349
+ end
350
+ end
351
+ end
352
+
353
+ # Success
354
+ searching
355
+ #=> false
356
+ position
357
+ #=> Either.success(StateMachine[1](SegmentVal[NM1](...)))
358
+
359
+ # Failure
360
+ searching
361
+ #=> true
362
+ position
363
+ #=> Either.failure("NM1 segment does not occur")
364
+
365
+ There is a much simpler and less error-prone way, however. The [`#find`][14]
366
+ method accepts a variable number of filter arguments. For instance, the above
367
+ filter can be accomplished by calling,
368
+
369
+ machine.find(:NM1, "QC")
370
+
371
+ Multiple constraints can be specified and `#blank` or `nil` should be used to
372
+ indicate a wildcard, if needed. For instance to find the `NM1*PR` "Payer Name"
373
+ that has a certain organization name in element `NM103`,
374
+
375
+ machine.find(:NM1, "PR", nil, "MEDICARE")
376
+
377
+ #### Syntactic Constraints
378
+
379
+ In addition to improving readability, [`#find`][14] checks the validity of your
380
+ element constraints and raises an exception when you ask for something that is
381
+ guaranteed never to occur in a valid parse tree. For instance, if you called
382
+ `#find(:NM1, "XX")` from a position where there are no reachable `NM1` segments,
383
+
384
+ machine.find(:NM1, "XX")
385
+ #=> NM1 segment cannot be reached from the current state (ArgumentError)
386
+
387
+ Or from a position where every reachable `NM1` segment is defined such that
388
+ `"XX"` is not allowed,
389
+
390
+ machine.find(:NM1, "XX")
391
+ #=> "XX" is not allowed in NM101 (ArgumentError)
392
+
393
+ You can get a list of potentially reachable segments from the current position
394
+ by calling [`#successors`][22], which returns one [`InstructionTable`][23] per
395
+ active state. That is, when the machine is in a deterministic state, a single
396
+ [`InstructionTable`][23] will be returned. See the section on
397
+ [Non-determinism](#Non-determinism) for more information.
398
+
399
+ pp b.successors
400
+
401
+ [InstructionTable(
402
+ 1: Instruction[REF: Subscriber Secon..](pop: 0, drop: 0),
403
+ 2: Instruction[REF: Property and Cas..](pop: 0, drop: 0),
404
+ 3: Instruction[PER: Property and Cas..](pop: 0, drop: 3),
405
+ 4: Instruction[NM1: Subscriber Name ](pop: 1, drop: 0, push: LoopState),
406
+ 5: Instruction[NM1: Payer Name ](pop: 1, drop: 0, push: LoopState),
407
+ 6: Instruction[CLM: Claim Informatio..](pop: 1, drop: 2, push: LoopState),
408
+ 7: Instruction[ HL: Subscriber Hiera..](pop: 2, drop: 0, push: LoopState),
409
+ 8: Instruction[ HL: Billing Provider..](pop: 3, drop: 0, push: TableState),
410
+ 9: Instruction[ HL: Subscriber Hiera..](pop: 3, drop: 0, push: TableState),
411
+ 10: Instruction[ HL: Patient Hierachi..](pop: 3, drop: 0, push: TableState),
412
+ 11: Instruction[ SE: Transaction Set ..](pop: 3, drop: 4, push: TableState),
413
+ 12: Instruction[ ST](pop: 4, drop: 0, push: TransactionSetState),
414
+ 13: Instruction[ GE: Functional Group..](pop: 4, drop: 2),
415
+ 14: Instruction[ GS](pop: 5, drop: 0, push: FunctionalGroupState),
416
+ 15: Instruction[IEA: Interchange Cont..](pop: 5, drop: 2),
417
+ 16: Instruction[ISA](pop: 6, drop: 0, push: InterchangeState))]
418
+
419
+ ### Chaining Method Calls
420
+
421
+ The [`Either`][8] datatype allows chaining via the [`#map`][16], [`#or`][18],
422
+ [`#flatmap`][17], and [`#tap`][19] methods. The use of each method is
423
+ demonstrated in the following examples.
424
+
425
+ #### Map
426
+
427
+ The [`#map`][16] method transforms one `Either.success` into another
428
+ `Either.success`. It leaves `Either.failure` values unaltered, passing
429
+ them through.
430
+
431
+ result = machine.find(:REF).map do |ref|
432
+ # When a REF segment was found, this block is
433
+ # executed. The return value of this block is
434
+ # wrapped by Either.success. If a REF segment
435
+ # was not found, this block is not executed and
436
+ # the Either.failure is propogated by #map
437
+ ref.id
438
+ end
439
+
440
+ # Briefer syntax, implementing Symbol#to_proc
441
+ machine.find(:REF).map(&:id)
442
+ #=> Either.success(:REF)
443
+
444
+ machine.find(:REF).map(&:id).map(&:to_s)
445
+ #=> Either.success("REF")
446
+
447
+ machine.find(:REF).map(&:id).map(&:to_s).map(&:length)
448
+ #=> Either.success(3)
449
+
450
+ #### Flatmap
451
+
452
+ To transform one `Either.success` into another `Either`, which may be a success
453
+ or failure, use the [`#flatmap`][17] method. Like [`#map`][16], it also leaves
454
+ `Either.failure` values unaltered. This is an important technique to traverse
455
+ the parse tree. For instance, the following shows how to locate the "Billing
456
+ Provider Organization" of the first claim in the parse tree.
457
+
458
+ machine.first.
459
+ flatmap{|x| x.find(:GS) }.
460
+ flatmap{|x| x.find(:ST) }.
461
+ flatmap{|x| x.find(:HL, nil, nil, "20") }.
462
+ flatmap{|x| x.find(:NM1, "85") }.
463
+ flatmap{|x| x.element(3) }.
464
+ map(&:node)
465
+ #=> Either.success(AN.value[E1035: Billing Provider Last or Organizational Name](BEN KILDARE SERVICE))
466
+
467
+ You can use [`#flatmap`][17] to iterate a sequence of segments with the same
468
+ identifier, or use [`#iterate`][25] which does the same thing.
469
+
470
+ # Find the first HL segment
471
+ position = machine.first.
472
+ flatmap{|x| x.find(:GS) }.
473
+ flatmap{|x| x.find(:ST) }.
474
+ flatmap{|x| x.find(:HL) }
475
+
476
+ while position.defined?
477
+ position = position.flatmap do |hl|
478
+ # Process the HL segment
479
+ ...
480
+
481
+ # Find the next HL segment
482
+ hl.find(:HL)
483
+ end
484
+ end
485
+
486
+ Note that the value returned by the block given to [`#flatmap`][17] must return
487
+ an instance of `Either` or a `TypeError` will be raised. The [`Object#try`][20]
488
+ method is similar to [`#flatmap`][17] in many ways.
489
+
490
+ #### Iterate
491
+
492
+ The [`#iterate`][25] method is a convenience method that calls [`#find`][14]
493
+ repeatedly with the given arguments, yielding its result to a block.
494
+
495
+ # Process each ST in the first ISA envelope
496
+ machine.first.flatmap do |isa|
497
+ isa.iterate(:GS) do |gs|
498
+ gs.iterate(:ST) do |st|
499
+ # ...
500
+ end
501
+ end
502
+ end
503
+
504
+ Note that the search starts *ahead* of the current position, so the following
505
+ `machine.first.flatmap{|m| m.iterate(:ISA) {|isa| ... }}` will never yield the
506
+ first ISA segment in the document, because it started searching *after* `m`,
507
+ which is the first ISA.
508
+
509
+ Since [`#iterate`][25] calls [`#find`][14], it enforces the same [syntactic
510
+ constraints](#Syntactic_Constraints).
511
+
512
+ #### Side Effects
513
+
514
+ In cases where you do not want to transform the `Either` value, but only need to
515
+ execute a side effect on `Either.success`, the [`#tap`][19] method is suitable.
516
+ On `Either.success` values, it passes the wrapped value to the block and returns
517
+ the original value, and it passes `Either.failure` values along without calling
518
+ the block.
519
+
520
+ # Record GS06 Group Control Number
521
+ machine.first.flatmap{|x| x.find(:GS) }.tap do |gs|
522
+ # The return value of this block is discarded
523
+ gs.element(6).tap do |control|
524
+ @numbers << control.node.to_s
525
+ end
526
+ end #=> Either.success(StateMachine[1](SegmentVal[GS](...)))
527
+
528
+ # Contrived example
529
+ machine.first.
530
+ flatmap{|x| x.find(:GS) }.tap{|x| puts "Hi, GS" }
531
+ flatmap{|x| x.find(:ST) }.tap{|x| puts "Hi, ST" }
532
+ #=> Either.success(StateMachine[1](SegmentVal[ST](...)))
533
+
534
+ The [`Object#tap`][21] method is similar to [`Either#tap`][19] except `#tap`
535
+ always calls the block, even on `nil`, while `#tap` does not call the block
536
+ on `Either.failure` values.
537
+
538
+ #### Error Recovery
539
+
540
+ When a method call earlier in the chain returns a `Either.failure`, the error
541
+ will be propogated through the chain unless it the [`#or`][18] method is used to
542
+ recover from the error.
543
+
544
+ result = machine.find(:ST).map do |st|
545
+ st.class
546
+ end.or do |reason|
547
+ # This block is executed if #find failed. The
548
+ # block must return an instance of Either, or
549
+ # a TypeError will be raised
550
+ Either.success(FalseClass)
551
+ end
552
+
553
+ # Result is guaranteed to be Either.success because
554
+ # of the block given to #or. The wrapped value then
555
+ # is StateMachine or FalseClass
556
+ result.defined?
557
+ #=> true
558
+
559
+ ### Word of Caution
560
+
561
+ Beware that the [`#find`][14] method only searches _forward_ in the sequence
562
+ of segments. In some cases, you will need to save the current position to let
563
+ you restart another search from that position, rather than chaining successive
564
+ searches together.
565
+
566
+ For instance, in the X222 837P transaction set, there are sixteen different
567
+ consecutive `DTP` segments in Loop 2300. While the X222 implementation guide
568
+ arranges them in what appears to be a sequence, there is no restriction on
569
+ the order in which the `DTP` segments occur -- they all have the same position.
570
+ Thus `DTP*439` "Accident Date" can follow or preceed `DTP*096` "Discharge Date",
571
+ and one or both might not be present.
572
+
573
+ clm = machine.first.
574
+ flatmap{|x| x.find(:GS) }.
575
+ flatmap{|x| x.find(:ST) }.
576
+ flatmap{|x| x.find(:HL, nil, nil, nil, "0") }.
577
+ flatmap{|x| x.find(:CLM) }
578
+
579
+ clm. # Wrong: this assumes DTP*439 occurs before DTP*096
580
+ flatmap{|x| x.find(:DTP, "439") }.tap{|x| puts "accident ..." }
581
+ flatmap{|x| x.find(:DTP, "096") }.tap{|x| puts "discharge ..." }
582
+
583
+ # Correct: no order is assumed among the DTP segments
584
+ clm.flatmap{|x| x.find(:DTP, "439") }.tap{|x| puts "accident ..." }
585
+ clm.flatmap{|x| x.find(:DTP, "096") }.tap{|x| puts "accident ..." }
586
+
587
+ In general, siblings following the first segment in a purely syntactic node,
588
+ like a table, loop, or envelope structure cannot exist unless the syntactic node
589
+ exists -- and the syntactic node cannot exist unless an _entry segment_ from the
590
+ definition of that node occurs. With few exceptions, the entry segment of a
591
+ syntactic node is the first segment in its definition. Therefore, the 2300 `DTP`
592
+ segments cannot exist unless the 2300 `CLM` segment occurs; that is why it is
593
+ best to save the `CLM` position and use it as a starting point.
594
+
595
+ Non-determinism
596
+ ---------------
597
+
598
+ Certain sequences of input segments can be described by more than one parse
599
+ tree. Often these sequences are malformed. For instance, in an X222 837P
600
+ transaction, an `HL` segment that has an empty `HL03` qualifier could
601
+ potentially be the `HL` segment describing the "Billing Provider Detail",
602
+ "Subscriber Detail", or "Patient Detail". In this case the parser will construct
603
+ three parse trees: one for each possibility. The parser will respond to
604
+ [`#deterministic?`][24] with `false` when it is in a non-deterministic state.
605
+
606
+ In a non-determistic state, methods that normally return a single node will
607
+ return `Either.failure("non-deterministic state")`. These are [`#segment`][6],
608
+ [`#element`][11], and `#zipper`. Traversal methods, however, like [`#next`][9]
609
+ [`#first`][12], [`#last`][13], [`#find`][14], and [`#parent`][15], operate on
610
+ each parse tree in parallel.
611
+
612
+ These traversal methods will position the parser on parallel segments within
613
+ each parse tree. To use the `HL` example again, the parser would point to
614
+ each tree's version of the `HL` segment, one named "Patient Detail", one named
615
+ "Bililng Provider Detail", and another named "Subscriber Detail". These segments
616
+ all have the same element values, but have a different meaning.
617
+
618
+ ### Resolution
619
+
620
+ [1]: Stupidedi/Builder/StateMachine.html
621
+ [2]: Stupidedi/Values/LoopVal.html
622
+ [3]: Stupidedi/Values/TableVal.html
623
+ [4]: Stupidedi/Builder/BuilderDsl.html#machine-instance_method
624
+ [5]: Stupidedi/Values/SegmentVal.html
625
+ [6]: Stupidedi/Builder/Navigation.html#segment-instance_method
626
+ [7]: Stupidedi/Zipper/AbstractCursor.html
627
+ [8]: Stupidedi/Either.html
628
+ [9]: Stupidedi/Builder/Navigation.html#next-instance_method
629
+ [10]: Stupidedi/Builder/Navigation.html#prev-instance_method
630
+ [11]: Stupidedi/Builder/Navigation.html#element-instance_method
631
+ [12]: Stupidedi/Builder/Navigation.html#first-instance_method
632
+ [13]: Stupidedi/Builder/Navigation.html#last-instance_method
633
+ [14]: Stupidedi/Builder/Navigation.html#find-instance_method
634
+ [15]: Stupidedi/Builder/Navigation.html#parent-instance_method
635
+ [16]: Stupidedi/Either.html#map-instance_method
636
+ [17]: Stupidedi/Either.html#flatmap-instance_method
637
+ [18]: Stupidedi/Either.html#or-instance_method
638
+ [19]: Stupidedi/Either.html#tap-instance_method
639
+ [20]: Object.html#try-instance_method
640
+ [21]: Object.html#tap-instance_method
641
+ [22]: Stupidedi/Builder/InstructionTable.html
642
+ [23]: Stupidedi/Builder/StateMachine.html#successors-instance_method
643
+ [24]: Stupidedi/Builder/StateMachine.html#deterministic%3F-instance_method
644
+ [25]: Stupidedi/Builder/Navigation.html#iterate-instance_method
645
+
data/doc/Parsing.md ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ Serializing X12
2
+ ==============
3
+
4
+ Once you have built a parse tree, either by generating one programatically (see
5
+ {file:Generating.md Generating}) or parsing serialized input (see
6
+ {file:Parsing.md}), you can serialize it back to a string using one of the
7
+ classes in [`Stupidedi::Writer`][1].
data/doc/Tokenizing.md ADDED
File without changes
data/doc/Validating.md ADDED
File without changes
File without changes
File without changes