hecks 0.3.0 → 1.0.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 (194) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hecks/adapters/driven/d1.rb +38 -14
  3. data/lib/hecks/adapters/driven/governance_authorization.rb +41 -2
  4. data/lib/hecks/adapters/driven/heki/journal.rb +7 -2
  5. data/lib/hecks/adapters/driven/heki/saga_store.rb +0 -0
  6. data/lib/hecks/adapters/driven/heki/snapshot.rb +31 -4
  7. data/lib/hecks/adapters/driven/heki.rb +40 -9
  8. data/lib/hecks/adapters/driven/lambda.rb +16 -2
  9. data/lib/hecks/adapters/driven/postgres/codec.rb +8 -0
  10. data/lib/hecks/adapters/driven/postgres/schema_builder.rb +45 -6
  11. data/lib/hecks/adapters/driven/postgres.rb +76 -29
  12. data/lib/hecks/adapters/driven/postgres_era.adapter +11 -0
  13. data/lib/hecks/adapters/driven/sqlite/codec.rb +17 -0
  14. data/lib/hecks/adapters/driven/sqlite/projection.rb +76 -9
  15. data/lib/hecks/adapters/driven/sqlite/schema_builder.rb +17 -5
  16. data/lib/hecks/adapters/driven/sqlite.rb +30 -9
  17. data/lib/hecks/adapters/driven.rb +19 -1
  18. data/lib/hecks/behaviors/dsl.rb +29 -0
  19. data/lib/hecks/behaviors/expectations.rb +62 -2
  20. data/lib/hecks/bluebook/assembly/contracts.rb +36 -7
  21. data/lib/hecks/bluebook/assembly/marks.rb +4 -3
  22. data/lib/hecks/bluebook/assembly.rb +14 -1
  23. data/lib/hecks/bluebook/behaviour/lifecycle.rb +18 -1
  24. data/lib/hecks/bluebook/behaviour/process_manager.rb +14 -1
  25. data/lib/hecks/bluebook/chapter.rb +21 -11
  26. data/lib/hecks/bluebook/command.rb +1 -1
  27. data/lib/hecks/bluebook/dsl/aggregate_builder.rb +117 -5
  28. data/lib/hecks/bluebook/dsl/attribute_collector.rb +21 -0
  29. data/lib/hecks/bluebook/dsl/bluebook_builder.rb +71 -2
  30. data/lib/hecks/bluebook/dsl/command_builder.rb +91 -3
  31. data/lib/hecks/bluebook/dsl/entity_builder.rb +129 -4
  32. data/lib/hecks/bluebook/dsl/policy_builder.rb +18 -3
  33. data/lib/hecks/bluebook/dsl/port_builder.rb +12 -3
  34. data/lib/hecks/bluebook/dsl/process_manager_builder.rb +109 -10
  35. data/lib/hecks/bluebook/dsl/rule_reference.rb +1 -0
  36. data/lib/hecks/bluebook/dsl/word_gate.rb +9 -2
  37. data/lib/hecks/bluebook/dsl/world_builder.rb +44 -4
  38. data/lib/hecks/bluebook/expression/canonical_form.rb +71 -3
  39. data/lib/hecks/bluebook/expression/evaluator.rb +50 -7
  40. data/lib/hecks/bluebook/expression/projection.json +48 -0
  41. data/lib/hecks/bluebook/expression/resolver.rb +161 -10
  42. data/lib/hecks/bluebook/hexagon.rb +1 -1
  43. data/lib/hecks/bluebook/meta_validator/judge.rb +78 -16
  44. data/lib/hecks/bluebook/meta_validator/port_judge.rb +4 -0
  45. data/lib/hecks/bluebook/meta_validator/readings.rb +14 -4
  46. data/lib/hecks/bluebook/meta_validator/reconstruction.rb +42 -3
  47. data/lib/hecks/bluebook/meta_validator/shapes.rb +30 -10
  48. data/lib/hecks/bluebook/meta_validator.rb +103 -13
  49. data/lib/hecks/bluebook/model_check.rb +132 -5
  50. data/lib/hecks/bluebook/pattern_subset.rb +66 -2
  51. data/lib/hecks/bluebook/process_manager.rb +53 -11
  52. data/lib/hecks/bluebook/value_object.rb +9 -1
  53. data/lib/hecks/doc/reference.rb +22 -1
  54. data/lib/hecks/facade/cli_door.rb +6 -3
  55. data/lib/hecks/facade/json_door.rb +16 -4
  56. data/lib/hecks/forms/app.rb +47 -6
  57. data/lib/hecks/forms/command_form_renderer.rb +1 -1
  58. data/lib/hecks/forms/field_renderer.rb +11 -4
  59. data/lib/hecks/forms/html.rb +31 -0
  60. data/lib/hecks/forms/params.rb +30 -1
  61. data/lib/hecks/forms/port_argument.rb +46 -0
  62. data/lib/hecks/forms/record_renderer.rb +6 -2
  63. data/lib/hecks/forms/record_table.rb +6 -1
  64. data/lib/hecks/framework/bluebook/console_settings.bluebook +19 -19
  65. data/lib/hecks/framework/bluebook/governance.bluebook +26 -11
  66. data/lib/hecks/framework/bluebook/identity.bluebook +2 -2
  67. data/lib/hecks/fuzzing/bounded_exhaustive_expressions.rb +527 -0
  68. data/lib/hecks/fuzzing/isolated_boot.rb +212 -18
  69. data/lib/hecks/fuzzing/properties.rb +52 -6
  70. data/lib/hecks/fuzzing/replay.rb +51 -18
  71. data/lib/hecks/fuzzing/sequence_generator/outcome_tracker.rb +28 -2
  72. data/lib/hecks/fuzzing/sequence_generator/step_builder.rb +71 -6
  73. data/lib/hecks/fuzzing/sequence_generator.rb +5 -4
  74. data/lib/hecks/fuzzing/value_generator.rb +25 -2
  75. data/lib/hecks/grammar/evolve.rb +33 -0
  76. data/lib/hecks/grammar/expression.bluebook +8 -5
  77. data/lib/hecks/grammar/expression_operators.json +497 -1
  78. data/lib/hecks/language/bluebook/aggregate.bluebook +12 -12
  79. data/lib/hecks/language/bluebook/bluebook.bluebook +3 -3
  80. data/lib/hecks/language/bluebook/command.bluebook +42 -8
  81. data/lib/hecks/language/bluebook/entity.bluebook +86 -10
  82. data/lib/hecks/language/bluebook/policy.bluebook +21 -4
  83. data/lib/hecks/language/bluebook/process_manager.bluebook +135 -18
  84. data/lib/hecks/language/bluebook/projection.bluebook +6 -6
  85. data/lib/hecks/language/bluebook/query.bluebook +4 -4
  86. data/lib/hecks/language/bluebook/shape.bluebook +6 -6
  87. data/lib/hecks/language/bluebook/syntax.bluebook +12 -11
  88. data/lib/hecks/language/bluebook/vocabulary.bluebook +22 -7
  89. data/lib/hecks/language/oidc.json +20 -0
  90. data/lib/hecks/language/port.bluebook +30 -2
  91. data/lib/hecks/naming.rb +54 -1
  92. data/lib/hecks/ports/access_control.port +7 -2
  93. data/lib/hecks/ports/access_control.rb +1 -1
  94. data/lib/hecks/ports/agent.port +6 -2
  95. data/lib/hecks/ports/agent.rb +1 -1
  96. data/lib/hecks/ports/authentication.port +4 -2
  97. data/lib/hecks/ports/authentication.rb +1 -1
  98. data/lib/hecks/ports/authorization.port +5 -2
  99. data/lib/hecks/ports/authorization.rb +14 -11
  100. data/lib/hecks/ports/clock.port +3 -2
  101. data/lib/hecks/ports/clock.rb +1 -1
  102. data/lib/hecks/ports/extraction.port +3 -2
  103. data/lib/hecks/ports/extraction.rb +1 -1
  104. data/lib/hecks/ports/identity_assignment.port +3 -2
  105. data/lib/hecks/ports/identity_assignment.rb +1 -1
  106. data/lib/hecks/ports/identity_generation.port +3 -2
  107. data/lib/hecks/ports/identity_generation.rb +1 -1
  108. data/lib/hecks/ports/identity_resolution.port +3 -2
  109. data/lib/hecks/ports/identity_resolution.rb +1 -1
  110. data/lib/hecks/ports/persistence/append_only.rb +40 -4
  111. data/lib/hecks/ports/persistence/execution.rb +6 -1
  112. data/lib/hecks/ports/persistence/plugin.rb +54 -0
  113. data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_check.rb +41 -8
  114. data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_guard.rb +24 -56
  115. data/lib/hecks/ports/persistence/{lineage.rb → plugins/era/lineage.rb} +31 -4
  116. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/era_store.rb +3 -3
  117. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/head_compiler.rb +59 -10
  118. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/mint_transaction.rb +2 -2
  119. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/provisioning.rb +29 -1
  120. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/tail_merge.rb +11 -4
  121. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/transform_installer.rb +20 -0
  122. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage.rb +1 -1
  123. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/coverage_check.rb +5 -5
  124. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/era_resolver.rb +5 -2
  125. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/merge_coordinator.rb +2 -2
  126. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager/minter.rb +4 -4
  127. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage_manager.rb +2 -2
  128. data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era.rb +116 -29
  129. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/approval_digest.rb +1 -1
  130. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/layer_one.rb +14 -5
  131. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/layer_two.rb +31 -6
  132. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit/unfed_report.rb +5 -1
  133. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/reattest.rb +3 -3
  134. data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/differ.rb +1 -1
  135. data/lib/hecks/ports/persistence/plugins/era.rb +48 -0
  136. data/lib/hecks/ports/persistence.rb +1 -1
  137. data/lib/hecks/ports/projection.rb +20 -0
  138. data/lib/hecks/projections/diagrams.rb +230 -1
  139. data/lib/hecks/projections/shape.rb +15 -2
  140. data/lib/hecks/projector/cli_projector.rb +10 -1
  141. data/lib/hecks/projector/exporter.rb +19 -2
  142. data/lib/hecks/query_ir.rb +19 -0
  143. data/lib/hecks/query_specification/common/null_policy.rb +13 -1
  144. data/lib/hecks/query_specification/field_path.rb +20 -2
  145. data/lib/hecks/query_specification/hop_path.rb +7 -5
  146. data/lib/hecks/runtime/aggregate_lock.rb +45 -0
  147. data/lib/hecks/runtime/boot_gates.rb +41 -0
  148. data/lib/hecks/runtime/caller.rb +19 -3
  149. data/lib/hecks/runtime/command_interpreter/argument_gate.rb +13 -2
  150. data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -0
  151. data/lib/hecks/runtime/command_interpreter.rb +97 -13
  152. data/lib/hecks/runtime/command_rules/admissibility.rb +64 -14
  153. data/lib/hecks/runtime/command_rules/arithmetic.rb +7 -1
  154. data/lib/hecks/runtime/command_rules/authorization.rb +2 -1
  155. data/lib/hecks/runtime/command_rules/references.rb +27 -19
  156. data/lib/hecks/runtime/dependency_planning.rb +14 -0
  157. data/lib/hecks/runtime/dispatcher.rb +19 -4
  158. data/lib/hecks/runtime/entity_interpreter.rb +85 -14
  159. data/lib/hecks/runtime/errors.rb +22 -0
  160. data/lib/hecks/runtime/identity.rb +30 -2
  161. data/lib/hecks/runtime/instance.rb +59 -4
  162. data/lib/hecks/runtime/interpreting.rb +21 -0
  163. data/lib/hecks/runtime/loader.rb +59 -18
  164. data/lib/hecks/runtime/query_interpreter.rb +36 -4
  165. data/lib/hecks/runtime/reaction_invocation.rb +9 -1
  166. data/lib/hecks/runtime/read_model_interpreter.rb +76 -1
  167. data/lib/hecks/runtime/refusal_wording.rb +2 -0
  168. data/lib/hecks/runtime/registry/saga_persistence.rb +75 -3
  169. data/lib/hecks/runtime/registry/verification.rb +88 -0
  170. data/lib/hecks/runtime/registry.rb +69 -8
  171. data/lib/hecks/runtime/saga_interpreter.rb +215 -13
  172. data/lib/hecks/runtime/saga_pending_dispatch.rb +45 -0
  173. data/lib/hecks/runtime/value/admission.rb +19 -1
  174. data/lib/hecks/runtime/value/coercion.rb +75 -10
  175. data/lib/hecks/runtime.rb +17 -5
  176. data/lib/hecks/storehouse.rb +632 -0
  177. data/lib/hecks/version.rb +1 -1
  178. data/lib/hecks/vocabulary.rb +6 -1
  179. data/lib/hecks.rb +7 -2
  180. data/lib/rubocop/cop/hecks/fallback_hash_lookup.rb +90 -0
  181. data/lib/rubocop/cop/hecks/sequential_hash_rename_in_loop.rb +128 -0
  182. data/lib/rubocop/cop/hecks/thread_shared_ivar_mutation.rb +160 -0
  183. metadata +48 -37
  184. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_guard/shape_diff.rb +0 -0
  185. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/era_tamper.rb +0 -0
  186. /data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/field_cache.rb +0 -0
  187. /data/lib/hecks/{adapters/driven → ports/persistence/plugins/era}/postgres_era/lineage/resumable_backfill.rb +0 -0
  188. /data/lib/hecks/{runtime → ports/persistence/plugins/era}/storage_shape.rb +0 -0
  189. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/audit.rb +0 -0
  190. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/rule_compiler.rb +0 -0
  191. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/renderer.rb +0 -0
  192. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold/writer.rb +0 -0
  193. /data/lib/hecks/{translation → ports/persistence/plugins/era/translation}/scaffold.rb +0 -0
  194. /data/lib/hecks/{translation.rb → ports/persistence/plugins/era/translation.rb} +0 -0
@@ -41,8 +41,31 @@ module Hecks
41
41
  "", "x", "with \"quotes\"", "with, a comma", "with a \\backslash",
42
42
  "unicode héllo wörld 🎉", "x" * 200, " leading and trailing "
43
43
  ].freeze
44
- INTEGER_EDGE_CASES = [0, -1, 2_147_483_647, -2_147_483_648].freeze
45
- FLOAT_EDGE_CASES = [0.0, -0.5, -100.25].freeze
44
+ # PRD 05 (numeric-boundary-coverage) Bignum (`2**100`, past i64's
45
+ # own ceiling, which Ruby's own `Integer` has no such ceiling for —
46
+ # `rust/src/kernel/json.rs`'s own `integral_i64` doc comment names
47
+ # exactly this: a Rust kernel value CANNOT represent it, so this
48
+ # exercises a real cross-runtime capability gap, not a Ruby-only
49
+ # edge) and its negative twin. Both round-trip through Ruby's own
50
+ # arithmetic/JSON cleanly (confirmed directly: `(2**100).clamp(...)`
51
+ # and `JSON.generate(2**100)` both just work — Integer has no
52
+ # ceiling here), so nothing in THIS runtime needed a fix for these;
53
+ # they're included so a real generated sequence occasionally
54
+ # produces the value at all, since nothing had, repo-wide, before.
55
+ INTEGER_EDGE_CASES = [0, -1, 2_147_483_647, -2_147_483_648, 2**100, -(2**100)].freeze
56
+ # NaN and +/-Infinity — the real find (see `spec/runtime/
57
+ # numeric_boundary_spec.rb`): `Value::Coercion#check_numeric_fields`
58
+ # used to let all three sail through untyped-checked (each really
59
+ # is a Float), reaching either `CommandRules::Arithmetic#clamp`
60
+ # (raw `ArgumentError`, not a domain refusal) or `JSON.generate`
61
+ # (`JSON::GeneratorError`, also not a domain refusal) — both fixed
62
+ # at the source now, so these are safe to generate. -0.0 is
63
+ # deliberately included too even though it was ALREADY safe
64
+ # (finite, round-trips through JSON as `-0.0` cleanly) — a signed
65
+ # zero is exactly the kind of boundary a hand-written corpus never
66
+ # happens to type, and the fuzzer existing to cover it is the
67
+ # point.
68
+ FLOAT_EDGE_CASES = [0.0, -0.0, -0.5, -100.25, Float::NAN, Float::INFINITY, -Float::INFINITY].freeze
46
69
  WORDS = %w[alpha bravo charlie delta echo foxtrot golf hotel india juliet].freeze
47
70
  CURRENCY_CODES = %w[USD EUR GBP JPY].freeze
48
71
 
@@ -17,6 +17,39 @@ module Hecks
17
17
 
18
18
  module_function
19
19
 
20
+ # bin/evolve's own `--name value` flag reader. Only consumes the
21
+ # NEXT argv element as the value when that element doesn't itself
22
+ # look like a flag — otherwise `--foo --bar` would swallow `--bar`
23
+ # as `--foo`'s value (and `--bar` would then never be seen at
24
+ # all), and a value-less `--foo` at the end of argv would bypass
25
+ # whatever default `foo` promised instead of falling back to it.
26
+ def option(argv, name, default = nil)
27
+ index = argv.index("--#{name}")
28
+ return default unless index
29
+
30
+ value = argv[index + 1]
31
+ value.nil? || value.start_with?("-") ? default : value
32
+ end
33
+
34
+ # The snapshot/restore ceremony bin/evolve wraps every mutating
35
+ # command in: read every `paths` file, run the block, and put
36
+ # every file back exactly as it was if the block raises partway
37
+ # through — `rename`'s keyword-row write followed by its
38
+ # argument cascade across several files (`cascade_argument_rename`
39
+ # above) included, not only a declared gate failing after the
40
+ # block has already returned cleanly (bin/evolve's own `guarded`
41
+ # still handles that half on its own, since it depends on running
42
+ # the gate specs, not on anything this method knows about). A
43
+ # clean return leaves the snapshots unused; the caller decides
44
+ # from there whether the tree stands.
45
+ def restore_on_raise(paths)
46
+ snapshots = paths.to_h { |path| [path, File.read(path)] }
47
+ yield
48
+ rescue StandardError
49
+ snapshots.each { |path, content| File.write(path, content) }
50
+ raise
51
+ end
52
+
20
53
  def syntax_paths
21
54
  Dir.glob(File.expand_path("../language/**/*.bluebook", __dir__)).select do |path|
22
55
  source = File.read(path)
@@ -10,11 +10,14 @@ Hecks.bluebook "Expression" do
10
10
  # has two rules that are not operators: parenthesization (a structural
11
11
  # strip-and-recurse, no symbol of its own) and the bare-leaf fallback
12
12
  # (whatever matches nothing else falls through to the inner grammar).
13
- # Its inner grammar has three more: integer/float/string/bool/nil
14
- # literals and the dotted-lookup fallback are TERMINAL productions —
15
- # they recognize text, they do not combine operands, and they have
16
- # nothing meaningful to render per target (a literal's spelling does
17
- # not differ between targets the way an operation's might).
13
+ # Its inner grammar has three more: integer/float/string/bool/nil/
14
+ # array literals and the dotted-lookup fallback are TERMINAL
15
+ # productions — they recognize text, they do not combine operands,
16
+ # and they have nothing meaningful to render per target (a literal's
17
+ # spelling does not differ between targets the way an operation's
18
+ # might — `rust/src/kernel/expr.rs`'s own `Expr::Array` is a LEAF
19
+ # there too, for the identical reason, not routed through
20
+ # `OperatorCategory`).
18
21
  # Forcing them into this shape would stretch `renderings` and the
19
22
  # Arity invariant past what they mean. They stay declared only in
20
23
  # prose — named here rather than silently left out.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expression-operators",
3
- "note": "The admission ledger \u2014 every operator and normalisation rule the expression machinery runs, driven through the grammar chapter's own lifecycle. Propose, then a rendering per target, then Admit, so the 'reads in every target' guard gates each admission for real. spec/operator_conformance_spec.rb replays this and holds the evaluator's tables equal to what survives \u2014 an operator absent here is not a slow operator, it is not an operator.",
3
+ "note": "The admission ledger every operator and normalisation rule the expression machinery runs, driven through the grammar chapter's own lifecycle. Propose, then a rendering per target, then Admit, so the 'reads in every target' guard gates each admission for real. spec/operator_conformance_spec.rb replays this and holds the evaluator's tables equal to what survives an operator absent here is not a slow operator, it is not an operator.",
4
4
  "steps": [
5
5
  {
6
6
  "verb": "Expression::Operator.Propose",
@@ -1058,6 +1058,502 @@
1058
1058
  }
1059
1059
  }
1060
1060
  },
1061
+ {
1062
+ "verb": "Expression::Operator.Propose",
1063
+ "args": {
1064
+ "symbol": {
1065
+ "value": ".match?"
1066
+ },
1067
+ "category": {
1068
+ "value": "pattern_match"
1069
+ },
1070
+ "precedence": {
1071
+ "value": 7
1072
+ },
1073
+ "arity": {
1074
+ "value": 2
1075
+ },
1076
+ "grammar": {
1077
+ "value": "inner"
1078
+ },
1079
+ "strategy": {
1080
+ "value": "call_pattern_match"
1081
+ },
1082
+ "position": {
1083
+ "value": 13
1084
+ }
1085
+ }
1086
+ },
1087
+ {
1088
+ "verb": "Expression::Operator.Render",
1089
+ "args": {
1090
+ "symbol": {
1091
+ "value": ".match?"
1092
+ },
1093
+ "target": {
1094
+ "value": "ruby"
1095
+ },
1096
+ "form": {
1097
+ "value": "receiver.match?(/pattern/)"
1098
+ }
1099
+ }
1100
+ },
1101
+ {
1102
+ "verb": "Expression::Operator.Render",
1103
+ "args": {
1104
+ "symbol": {
1105
+ "value": ".match?"
1106
+ },
1107
+ "target": {
1108
+ "value": "rust"
1109
+ },
1110
+ "form": {
1111
+ "value": "receiver.match?(/pattern/) -> regex crate, Ruby-Regexp-compatible subset"
1112
+ }
1113
+ }
1114
+ },
1115
+ {
1116
+ "verb": "Expression::Operator.Admit",
1117
+ "args": {
1118
+ "symbol": {
1119
+ "value": ".match?"
1120
+ }
1121
+ }
1122
+ },
1123
+ {
1124
+ "verb": "Expression::Operator.Propose",
1125
+ "args": {
1126
+ "symbol": {
1127
+ "value": ".present?"
1128
+ },
1129
+ "category": {
1130
+ "value": "presence"
1131
+ },
1132
+ "precedence": {
1133
+ "value": 7
1134
+ },
1135
+ "arity": {
1136
+ "value": 1
1137
+ },
1138
+ "grammar": {
1139
+ "value": "inner"
1140
+ },
1141
+ "strategy": {
1142
+ "value": "suffix_match"
1143
+ },
1144
+ "position": {
1145
+ "value": 14
1146
+ }
1147
+ }
1148
+ },
1149
+ {
1150
+ "verb": "Expression::Operator.Render",
1151
+ "args": {
1152
+ "symbol": {
1153
+ "value": ".present?"
1154
+ },
1155
+ "target": {
1156
+ "value": "ruby"
1157
+ },
1158
+ "form": {
1159
+ "value": "a.present?"
1160
+ }
1161
+ }
1162
+ },
1163
+ {
1164
+ "verb": "Expression::Operator.Render",
1165
+ "args": {
1166
+ "symbol": {
1167
+ "value": ".present?"
1168
+ },
1169
+ "target": {
1170
+ "value": "rust"
1171
+ },
1172
+ "form": {
1173
+ "value": "a.present? -> !blank(a)"
1174
+ }
1175
+ }
1176
+ },
1177
+ {
1178
+ "verb": "Expression::Operator.Admit",
1179
+ "args": {
1180
+ "symbol": {
1181
+ "value": ".present?"
1182
+ }
1183
+ }
1184
+ },
1185
+ {
1186
+ "verb": "Expression::Operator.Propose",
1187
+ "args": {
1188
+ "symbol": {
1189
+ "value": ".blank?"
1190
+ },
1191
+ "category": {
1192
+ "value": "presence"
1193
+ },
1194
+ "precedence": {
1195
+ "value": 7
1196
+ },
1197
+ "arity": {
1198
+ "value": 1
1199
+ },
1200
+ "grammar": {
1201
+ "value": "inner"
1202
+ },
1203
+ "strategy": {
1204
+ "value": "suffix_match"
1205
+ },
1206
+ "position": {
1207
+ "value": 15
1208
+ }
1209
+ }
1210
+ },
1211
+ {
1212
+ "verb": "Expression::Operator.Render",
1213
+ "args": {
1214
+ "symbol": {
1215
+ "value": ".blank?"
1216
+ },
1217
+ "target": {
1218
+ "value": "ruby"
1219
+ },
1220
+ "form": {
1221
+ "value": "a.blank?"
1222
+ }
1223
+ }
1224
+ },
1225
+ {
1226
+ "verb": "Expression::Operator.Render",
1227
+ "args": {
1228
+ "symbol": {
1229
+ "value": ".blank?"
1230
+ },
1231
+ "target": {
1232
+ "value": "rust"
1233
+ },
1234
+ "form": {
1235
+ "value": "a.blank? -> blank(a)"
1236
+ }
1237
+ }
1238
+ },
1239
+ {
1240
+ "verb": "Expression::Operator.Admit",
1241
+ "args": {
1242
+ "symbol": {
1243
+ "value": ".blank?"
1244
+ }
1245
+ }
1246
+ },
1247
+ {
1248
+ "verb": "Expression::Operator.Propose",
1249
+ "args": {
1250
+ "symbol": {
1251
+ "value": ".split"
1252
+ },
1253
+ "category": {
1254
+ "value": "text"
1255
+ },
1256
+ "precedence": {
1257
+ "value": 7
1258
+ },
1259
+ "arity": {
1260
+ "value": 2
1261
+ },
1262
+ "grammar": {
1263
+ "value": "inner"
1264
+ },
1265
+ "strategy": {
1266
+ "value": "call_pattern_match"
1267
+ },
1268
+ "position": {
1269
+ "value": 16
1270
+ }
1271
+ }
1272
+ },
1273
+ {
1274
+ "verb": "Expression::Operator.Render",
1275
+ "args": {
1276
+ "symbol": {
1277
+ "value": ".split"
1278
+ },
1279
+ "target": {
1280
+ "value": "ruby"
1281
+ },
1282
+ "form": {
1283
+ "value": "a.split(\"SEP\")"
1284
+ }
1285
+ }
1286
+ },
1287
+ {
1288
+ "verb": "Expression::Operator.Render",
1289
+ "args": {
1290
+ "symbol": {
1291
+ "value": ".split"
1292
+ },
1293
+ "target": {
1294
+ "value": "rust"
1295
+ },
1296
+ "form": {
1297
+ "value": "a.split(\"SEP\") -> Value::Array of Value::Str"
1298
+ }
1299
+ }
1300
+ },
1301
+ {
1302
+ "verb": "Expression::Operator.Admit",
1303
+ "args": {
1304
+ "symbol": {
1305
+ "value": ".split"
1306
+ }
1307
+ }
1308
+ },
1309
+ {
1310
+ "verb": "Expression::Operator.Propose",
1311
+ "args": {
1312
+ "symbol": {
1313
+ "value": ".start_with?"
1314
+ },
1315
+ "category": {
1316
+ "value": "text"
1317
+ },
1318
+ "precedence": {
1319
+ "value": 7
1320
+ },
1321
+ "arity": {
1322
+ "value": 2
1323
+ },
1324
+ "grammar": {
1325
+ "value": "inner"
1326
+ },
1327
+ "strategy": {
1328
+ "value": "call_pattern_match"
1329
+ },
1330
+ "position": {
1331
+ "value": 17
1332
+ }
1333
+ }
1334
+ },
1335
+ {
1336
+ "verb": "Expression::Operator.Render",
1337
+ "args": {
1338
+ "symbol": {
1339
+ "value": ".start_with?"
1340
+ },
1341
+ "target": {
1342
+ "value": "ruby"
1343
+ },
1344
+ "form": {
1345
+ "value": "a.start_with?(\"prefix\")"
1346
+ }
1347
+ }
1348
+ },
1349
+ {
1350
+ "verb": "Expression::Operator.Render",
1351
+ "args": {
1352
+ "symbol": {
1353
+ "value": ".start_with?"
1354
+ },
1355
+ "target": {
1356
+ "value": "rust"
1357
+ },
1358
+ "form": {
1359
+ "value": "a.start_with?(\"prefix\")"
1360
+ }
1361
+ }
1362
+ },
1363
+ {
1364
+ "verb": "Expression::Operator.Admit",
1365
+ "args": {
1366
+ "symbol": {
1367
+ "value": ".start_with?"
1368
+ }
1369
+ }
1370
+ },
1371
+ {
1372
+ "verb": "Expression::Operator.Propose",
1373
+ "args": {
1374
+ "symbol": {
1375
+ "value": ".end_with?"
1376
+ },
1377
+ "category": {
1378
+ "value": "text"
1379
+ },
1380
+ "precedence": {
1381
+ "value": 7
1382
+ },
1383
+ "arity": {
1384
+ "value": 2
1385
+ },
1386
+ "grammar": {
1387
+ "value": "inner"
1388
+ },
1389
+ "strategy": {
1390
+ "value": "call_pattern_match"
1391
+ },
1392
+ "position": {
1393
+ "value": 18
1394
+ }
1395
+ }
1396
+ },
1397
+ {
1398
+ "verb": "Expression::Operator.Render",
1399
+ "args": {
1400
+ "symbol": {
1401
+ "value": ".end_with?"
1402
+ },
1403
+ "target": {
1404
+ "value": "ruby"
1405
+ },
1406
+ "form": {
1407
+ "value": "a.end_with?(\"suffix\")"
1408
+ }
1409
+ }
1410
+ },
1411
+ {
1412
+ "verb": "Expression::Operator.Render",
1413
+ "args": {
1414
+ "symbol": {
1415
+ "value": ".end_with?"
1416
+ },
1417
+ "target": {
1418
+ "value": "rust"
1419
+ },
1420
+ "form": {
1421
+ "value": "a.end_with?(\"suffix\")"
1422
+ }
1423
+ }
1424
+ },
1425
+ {
1426
+ "verb": "Expression::Operator.Admit",
1427
+ "args": {
1428
+ "symbol": {
1429
+ "value": ".end_with?"
1430
+ }
1431
+ }
1432
+ },
1433
+ {
1434
+ "verb": "Expression::Operator.Propose",
1435
+ "args": {
1436
+ "symbol": {
1437
+ "value": ".first"
1438
+ },
1439
+ "category": {
1440
+ "value": "positional"
1441
+ },
1442
+ "precedence": {
1443
+ "value": 7
1444
+ },
1445
+ "arity": {
1446
+ "value": 1
1447
+ },
1448
+ "grammar": {
1449
+ "value": "inner"
1450
+ },
1451
+ "strategy": {
1452
+ "value": "suffix_match"
1453
+ },
1454
+ "position": {
1455
+ "value": 19
1456
+ }
1457
+ }
1458
+ },
1459
+ {
1460
+ "verb": "Expression::Operator.Render",
1461
+ "args": {
1462
+ "symbol": {
1463
+ "value": ".first"
1464
+ },
1465
+ "target": {
1466
+ "value": "ruby"
1467
+ },
1468
+ "form": {
1469
+ "value": "a.first"
1470
+ }
1471
+ }
1472
+ },
1473
+ {
1474
+ "verb": "Expression::Operator.Render",
1475
+ "args": {
1476
+ "symbol": {
1477
+ "value": ".first"
1478
+ },
1479
+ "target": {
1480
+ "value": "rust"
1481
+ },
1482
+ "form": {
1483
+ "value": "a.first -> first element or Nil"
1484
+ }
1485
+ }
1486
+ },
1487
+ {
1488
+ "verb": "Expression::Operator.Admit",
1489
+ "args": {
1490
+ "symbol": {
1491
+ "value": ".first"
1492
+ }
1493
+ }
1494
+ },
1495
+ {
1496
+ "verb": "Expression::Operator.Propose",
1497
+ "args": {
1498
+ "symbol": {
1499
+ "value": ".last"
1500
+ },
1501
+ "category": {
1502
+ "value": "positional"
1503
+ },
1504
+ "precedence": {
1505
+ "value": 7
1506
+ },
1507
+ "arity": {
1508
+ "value": 1
1509
+ },
1510
+ "grammar": {
1511
+ "value": "inner"
1512
+ },
1513
+ "strategy": {
1514
+ "value": "suffix_match"
1515
+ },
1516
+ "position": {
1517
+ "value": 20
1518
+ }
1519
+ }
1520
+ },
1521
+ {
1522
+ "verb": "Expression::Operator.Render",
1523
+ "args": {
1524
+ "symbol": {
1525
+ "value": ".last"
1526
+ },
1527
+ "target": {
1528
+ "value": "ruby"
1529
+ },
1530
+ "form": {
1531
+ "value": "a.last"
1532
+ }
1533
+ }
1534
+ },
1535
+ {
1536
+ "verb": "Expression::Operator.Render",
1537
+ "args": {
1538
+ "symbol": {
1539
+ "value": ".last"
1540
+ },
1541
+ "target": {
1542
+ "value": "rust"
1543
+ },
1544
+ "form": {
1545
+ "value": "a.last -> last element or Nil"
1546
+ }
1547
+ }
1548
+ },
1549
+ {
1550
+ "verb": "Expression::Operator.Admit",
1551
+ "args": {
1552
+ "symbol": {
1553
+ "value": ".last"
1554
+ }
1555
+ }
1556
+ },
1061
1557
  {
1062
1558
  "verb": "Expression::Normalisation.Propose",
1063
1559
  "args": {