eyeling 1.34.3 → 1.34.5

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 (296) hide show
  1. package/README.md +7 -9
  2. package/docs/eyelang-guide.md +6 -11
  3. package/docs/eyelang-language-reference.md +47 -36
  4. package/examples/eyelang/bayes-therapy.pl +4 -4
  5. package/examples/eyelang/output/reusable-builtins.pl +5 -0
  6. package/examples/eyelang/output/term-tools.pl +6 -0
  7. package/examples/eyelang/reusable-builtins.pl +32 -0
  8. package/examples/eyelang/term-tools.pl +23 -0
  9. package/lib/eyelang/builtins/arithmetic.js +19 -6
  10. package/lib/eyelang/builtins/control.js +12 -0
  11. package/lib/eyelang/builtins/lists.js +146 -7
  12. package/lib/eyelang/builtins/registry.js +2 -3
  13. package/lib/eyelang/builtins/strings.js +165 -1
  14. package/lib/eyelang/builtins/terms.js +66 -0
  15. package/package.json +1 -1
  16. package/test/eyelang/conformance/README.md +9 -11
  17. package/test/eyelang/conformance/cases/001_fact_output.pl +4 -0
  18. package/test/eyelang/conformance/cases/002_rule_recursion.pl +7 -0
  19. package/test/eyelang/conformance/cases/002_rule_recursion.query +1 -0
  20. package/test/eyelang/conformance/cases/003_terms_and_readback.pl +16 -0
  21. package/test/eyelang/conformance/cases/003_terms_and_readback.query +1 -0
  22. package/test/eyelang/conformance/cases/004_conjunction_and_parentheses.pl +5 -0
  23. package/test/eyelang/conformance/cases/004_conjunction_and_parentheses.query +1 -0
  24. package/test/eyelang/conformance/cases/005_list_deconstruction.pl +6 -0
  25. package/test/eyelang/conformance/cases/005_list_deconstruction.query +1 -0
  26. package/test/eyelang/conformance/cases/006_comma_formula_data.pl +4 -0
  27. package/test/eyelang/conformance/cases/006_comma_formula_data.query +1 -0
  28. package/test/eyelang/conformance/cases/007_anonymous_variables.pl +5 -0
  29. package/test/eyelang/conformance/cases/007_anonymous_variables.query +1 -0
  30. package/test/eyelang/conformance/cases/008_graphic_atoms.pl +6 -0
  31. package/test/eyelang/conformance/cases/008_graphic_atoms.query +1 -0
  32. package/test/eyelang/conformance/cases/009_comments_and_whitespace.pl +5 -0
  33. package/test/eyelang/conformance/cases/009_comments_and_whitespace.query +1 -0
  34. package/test/eyelang/conformance/cases/010_variable_scope_and_reuse.pl +8 -0
  35. package/test/eyelang/conformance/cases/010_variable_scope_and_reuse.query +1 -0
  36. package/test/eyelang/conformance/cases/011_predicate_arity.pl +6 -0
  37. package/test/eyelang/conformance/cases/011_predicate_arity.query +1 -0
  38. package/test/eyelang/conformance/cases/012_nested_compound_unification.pl +5 -0
  39. package/test/eyelang/conformance/cases/012_nested_compound_unification.query +1 -0
  40. package/test/eyelang/conformance/cases/013_multiple_clauses_order.pl +6 -0
  41. package/test/eyelang/conformance/cases/013_multiple_clauses_order.query +1 -0
  42. package/test/eyelang/conformance/cases/014_failure_filters_answers.pl +7 -0
  43. package/test/eyelang/conformance/cases/014_failure_filters_answers.query +1 -0
  44. package/test/eyelang/conformance/cases/015_improper_list_unification.pl +6 -0
  45. package/test/eyelang/conformance/cases/015_improper_list_unification.query +1 -0
  46. package/test/eyelang/conformance/cases/016_zero_arity_compound.pl +4 -0
  47. package/test/eyelang/conformance/cases/016_zero_arity_compound.query +1 -0
  48. package/test/eyelang/conformance/cases/017_three_step_recursion.pl +8 -0
  49. package/test/eyelang/conformance/cases/017_three_step_recursion.query +1 -0
  50. package/test/eyelang/conformance/cases/018_quoted_atom_readback.pl +6 -0
  51. package/test/eyelang/conformance/cases/018_quoted_atom_readback.query +1 -0
  52. package/test/eyelang/conformance/cases/019_parenthesized_three_conjuncts.pl +7 -0
  53. package/test/eyelang/conformance/cases/019_parenthesized_three_conjuncts.query +1 -0
  54. package/test/eyelang/conformance/cases/020_nested_list_terms.pl +5 -0
  55. package/test/eyelang/conformance/cases/020_nested_list_terms.query +1 -0
  56. package/test/eyelang/conformance/cases/021_repeated_variable_head.pl +7 -0
  57. package/test/eyelang/conformance/cases/022_rule_head_structure.pl +5 -0
  58. package/test/eyelang/conformance/cases/023_quoted_escapes_readback.pl +5 -0
  59. package/test/eyelang/conformance/cases/024_numeric_literal_readback.pl +6 -0
  60. package/test/eyelang/conformance/cases/025_body_parentheses_with_formula_data.pl +5 -0
  61. package/test/eyelang/conformance/cases/026_underscore_named_variable_reuse.pl +5 -0
  62. package/test/eyelang/conformance/cases/027_default_derived_output.pl +5 -0
  63. package/test/eyelang/conformance/cases/028_materialize_focus.pl +5 -0
  64. package/test/eyelang/conformance/cases/029_arithmetic_and_comparison.pl +11 -0
  65. package/test/eyelang/conformance/cases/029_arithmetic_and_comparison.query +1 -0
  66. package/test/eyelang/conformance/cases/030_strings_and_atoms.pl +4 -0
  67. package/test/eyelang/conformance/cases/030_strings_and_atoms.query +1 -0
  68. package/test/eyelang/conformance/cases/031_lists_aggregation_ordering.pl +10 -0
  69. package/test/eyelang/conformance/cases/031_lists_aggregation_ordering.query +1 -0
  70. package/test/eyelang/conformance/cases/032_holds_parts.pl +4 -0
  71. package/test/eyelang/conformance/cases/033_negation_once_generators.pl +7 -0
  72. package/test/eyelang/conformance/cases/033_negation_once_generators.query +1 -0
  73. package/test/eyelang/conformance/cases/034_equality_and_inequality.pl +6 -0
  74. package/test/eyelang/conformance/cases/034_equality_and_inequality.query +1 -0
  75. package/test/eyelang/conformance/cases/035_list_relations.pl +5 -0
  76. package/test/eyelang/conformance/cases/035_list_relations.query +1 -0
  77. package/test/eyelang/conformance/cases/036_append_splits.pl +3 -0
  78. package/test/eyelang/conformance/cases/036_append_splits.query +1 -0
  79. package/test/eyelang/conformance/cases/037_matching_and_comparison.pl +7 -0
  80. package/test/eyelang/conformance/cases/037_matching_and_comparison.query +1 -0
  81. package/test/eyelang/conformance/cases/038_memoize_declaration.pl +8 -0
  82. package/test/eyelang/conformance/cases/038_memoize_declaration.query +1 -0
  83. package/test/eyelang/conformance/cases/039_numeric_functions.pl +9 -0
  84. package/test/eyelang/conformance/cases/039_numeric_functions.query +1 -0
  85. package/test/eyelang/conformance/cases/040_between_enumeration.pl +3 -0
  86. package/test/eyelang/conformance/cases/040_between_enumeration.query +1 -0
  87. package/test/eyelang/conformance/cases/041_smallest_divisor.pl +3 -0
  88. package/test/eyelang/conformance/cases/041_smallest_divisor.query +1 -0
  89. package/test/eyelang/conformance/cases/042_negation_filter.pl +7 -0
  90. package/test/eyelang/conformance/cases/042_negation_filter.query +1 -0
  91. package/test/eyelang/conformance/cases/043_once_user_predicate.pl +5 -0
  92. package/test/eyelang/conformance/cases/043_once_user_predicate.query +1 -0
  93. package/test/eyelang/conformance/cases/044_findall_user_goal.pl +6 -0
  94. package/test/eyelang/conformance/cases/044_findall_user_goal.query +1 -0
  95. package/test/eyelang/conformance/cases/045_sort_deduplicates_atoms.pl +3 -0
  96. package/test/eyelang/conformance/cases/045_sort_deduplicates_atoms.query +1 -0
  97. package/test/eyelang/conformance/cases/046_append_bound_prefix_suffix.pl +4 -0
  98. package/test/eyelang/conformance/cases/046_append_bound_prefix_suffix.query +1 -0
  99. package/test/eyelang/conformance/cases/047_nth0_index_generation.pl +3 -0
  100. package/test/eyelang/conformance/cases/047_nth0_index_generation.query +1 -0
  101. package/test/eyelang/conformance/cases/048_set_nth0_edges.pl +4 -0
  102. package/test/eyelang/conformance/cases/048_set_nth0_edges.query +1 -0
  103. package/test/eyelang/conformance/cases/049_select_duplicate_occurrences.pl +3 -0
  104. package/test/eyelang/conformance/cases/049_select_duplicate_occurrences.query +1 -0
  105. package/test/eyelang/conformance/cases/050_not_member_filter.pl +6 -0
  106. package/test/eyelang/conformance/cases/050_not_member_filter.query +1 -0
  107. package/test/eyelang/conformance/cases/051_nested_holds_parts.pl +4 -0
  108. package/test/eyelang/conformance/cases/052_holds_member.pl +3 -0
  109. package/test/eyelang/conformance/cases/053_materialize_excludes_source_fact.pl +6 -0
  110. package/test/eyelang/conformance/cases/054_numeric_and_lexical_comparison.pl +5 -0
  111. package/test/eyelang/conformance/cases/054_numeric_and_lexical_comparison.query +1 -0
  112. package/test/eyelang/conformance/cases/055_string_matching_filters.pl +6 -0
  113. package/test/eyelang/conformance/cases/055_string_matching_filters.query +1 -0
  114. package/test/eyelang/conformance/cases/056_string_and_atom_concat.pl +3 -0
  115. package/test/eyelang/conformance/cases/056_string_and_atom_concat.query +1 -0
  116. package/test/eyelang/conformance/cases/057_countall_empty_and_nonempty.pl +4 -0
  117. package/test/eyelang/conformance/cases/057_countall_empty_and_nonempty.query +1 -0
  118. package/test/eyelang/conformance/cases/058_sumall_numeric_template.pl +5 -0
  119. package/test/eyelang/conformance/cases/058_sumall_numeric_template.query +1 -0
  120. package/test/eyelang/conformance/cases/059_aggregate_min_template.pl +5 -0
  121. package/test/eyelang/conformance/cases/059_aggregate_min_template.query +1 -0
  122. package/test/eyelang/conformance/cases/060_aggregate_max_compound_key.pl +5 -0
  123. package/test/eyelang/conformance/cases/060_aggregate_max_compound_key.query +1 -0
  124. package/test/eyelang/conformance/cases/061_date_difference.pl +4 -0
  125. package/test/eyelang/conformance/cases/062_reusable_numeric_builtins.pl +10 -0
  126. package/test/eyelang/conformance/cases/063_reusable_list_builtins.pl +11 -0
  127. package/test/eyelang/conformance/cases/064_reusable_string_builtins.pl +12 -0
  128. package/test/eyelang/conformance/cases/065_reusable_term_control_builtins.pl +11 -0
  129. package/test/eyelang/conformance/cases/066_numeric_edges.pl +14 -0
  130. package/test/eyelang/conformance/cases/067_list_edges.pl +10 -0
  131. package/test/eyelang/conformance/cases/068_list_generation_order.pl +7 -0
  132. package/test/eyelang/conformance/cases/069_list_summaries_and_sets.pl +9 -0
  133. package/test/eyelang/conformance/cases/070_matches_named_captures.pl +13 -0
  134. package/test/eyelang/conformance/cases/071_string_edges.pl +10 -0
  135. package/test/eyelang/conformance/cases/072_string_conversions.pl +10 -0
  136. package/test/eyelang/conformance/cases/073_term_introspection_edges.pl +10 -0
  137. package/test/eyelang/conformance/cases/074_forall_edges.pl +10 -0
  138. package/test/eyelang/conformance/cases/075_aggregation_edges.pl +12 -0
  139. package/test/eyelang/conformance/cases/076_composed_reusable_builtins.pl +8 -0
  140. package/test/eyelang/conformance/cases/077_recursive_path_with_lists.pl +10 -0
  141. package/test/eyelang/conformance/cases/078_mutual_recursion_with_arithmetic.pl +7 -0
  142. package/test/eyelang/conformance/cases/079_big_integer_arithmetic.pl +8 -0
  143. package/test/eyelang/conformance/cases/080_rounding_modes.pl +10 -0
  144. package/test/eyelang/conformance/cases/081_zero_safe_numeric_functions.pl +9 -0
  145. package/test/eyelang/conformance/cases/082_comparison_semantics.pl +10 -0
  146. package/test/eyelang/conformance/cases/083_between_modes_and_empty_ranges.pl +8 -0
  147. package/test/eyelang/conformance/cases/084_append_and_select_composition.pl +7 -0
  148. package/test/eyelang/conformance/cases/085_nth_and_update_edges.pl +8 -0
  149. package/test/eyelang/conformance/cases/086_slicing_pipeline.pl +10 -0
  150. package/test/eyelang/conformance/cases/087_sort_reverse_length.pl +8 -0
  151. package/test/eyelang/conformance/cases/088_list_summaries_failures.pl +8 -0
  152. package/test/eyelang/conformance/cases/089_string_split_join_pipeline.pl +7 -0
  153. package/test/eyelang/conformance/cases/090_string_substring_replace_edges.pl +9 -0
  154. package/test/eyelang/conformance/cases/091_string_case_and_trim.pl +7 -0
  155. package/test/eyelang/conformance/cases/092_scalar_string_conversions.pl +9 -0
  156. package/test/eyelang/conformance/cases/093_regex_named_captures_context.pl +8 -0
  157. package/test/eyelang/conformance/cases/094_context_holds_enumeration.pl +7 -0
  158. package/test/eyelang/conformance/cases/095_term_introspection_roundtrip.pl +8 -0
  159. package/test/eyelang/conformance/cases/096_functor_scalar_edges.pl +8 -0
  160. package/test/eyelang/conformance/cases/097_control_negation_once_forall.pl +13 -0
  161. package/test/eyelang/conformance/cases/098_aggregation_nested_templates.pl +11 -0
  162. package/test/eyelang/conformance/cases/099_materialize_multiple_arities.pl +8 -0
  163. package/test/eyelang/conformance/cases/100_reusable_builtin_workflow.pl +10 -0
  164. package/test/eyelang/conformance/cases/extension/036_reusable_numeric_builtins.pl +10 -0
  165. package/test/eyelang/conformance/cases/extension/037_reusable_list_builtins.pl +11 -0
  166. package/test/eyelang/conformance/cases/extension/038_reusable_string_builtins.pl +12 -0
  167. package/test/eyelang/conformance/cases/extension/039_reusable_term_control_builtins.pl +11 -0
  168. package/test/eyelang/conformance/expected/001_fact_output.pl +1 -0
  169. package/test/eyelang/conformance/expected/002_rule_recursion.pl +2 -0
  170. package/test/eyelang/conformance/expected/003_terms_and_readback.pl +13 -0
  171. package/test/eyelang/conformance/expected/004_conjunction_and_parentheses.pl +1 -0
  172. package/test/eyelang/conformance/expected/005_list_deconstruction.pl +2 -0
  173. package/test/eyelang/conformance/expected/006_comma_formula_data.pl +1 -0
  174. package/test/eyelang/conformance/expected/007_anonymous_variables.pl +1 -0
  175. package/test/eyelang/conformance/expected/008_graphic_atoms.pl +3 -0
  176. package/test/eyelang/conformance/expected/009_comments_and_whitespace.pl +2 -0
  177. package/test/eyelang/conformance/expected/010_variable_scope_and_reuse.pl +2 -0
  178. package/test/eyelang/conformance/expected/011_predicate_arity.pl +2 -0
  179. package/test/eyelang/conformance/expected/012_nested_compound_unification.pl +2 -0
  180. package/test/eyelang/conformance/expected/013_multiple_clauses_order.pl +2 -0
  181. package/test/eyelang/conformance/expected/014_failure_filters_answers.pl +1 -0
  182. package/test/eyelang/conformance/expected/015_improper_list_unification.pl +3 -0
  183. package/test/eyelang/conformance/expected/016_zero_arity_compound.pl +1 -0
  184. package/test/eyelang/conformance/expected/017_three_step_recursion.pl +3 -0
  185. package/test/eyelang/conformance/expected/018_quoted_atom_readback.pl +3 -0
  186. package/test/eyelang/conformance/expected/019_parenthesized_three_conjuncts.pl +1 -0
  187. package/test/eyelang/conformance/expected/020_nested_list_terms.pl +2 -0
  188. package/test/eyelang/conformance/expected/021_repeated_variable_head.pl +2 -0
  189. package/test/eyelang/conformance/expected/022_rule_head_structure.pl +2 -0
  190. package/test/eyelang/conformance/expected/023_quoted_escapes_readback.pl +2 -0
  191. package/test/eyelang/conformance/expected/024_numeric_literal_readback.pl +3 -0
  192. package/test/eyelang/conformance/expected/025_body_parentheses_with_formula_data.pl +1 -0
  193. package/test/eyelang/conformance/expected/026_underscore_named_variable_reuse.pl +1 -0
  194. package/test/eyelang/conformance/expected/027_default_derived_output.pl +3 -0
  195. package/test/eyelang/conformance/expected/028_materialize_focus.pl +1 -0
  196. package/test/eyelang/conformance/expected/029_arithmetic_and_comparison.pl +9 -0
  197. package/test/eyelang/conformance/expected/030_strings_and_atoms.pl +2 -0
  198. package/test/eyelang/conformance/expected/031_lists_aggregation_ordering.pl +9 -0
  199. package/test/eyelang/conformance/expected/032_holds_parts.pl +3 -0
  200. package/test/eyelang/conformance/expected/033_negation_once_generators.pl +2 -0
  201. package/test/eyelang/conformance/expected/034_equality_and_inequality.pl +4 -0
  202. package/test/eyelang/conformance/expected/035_list_relations.pl +4 -0
  203. package/test/eyelang/conformance/expected/036_append_splits.pl +3 -0
  204. package/test/eyelang/conformance/expected/037_matching_and_comparison.pl +5 -0
  205. package/test/eyelang/conformance/expected/038_memoize_declaration.pl +2 -0
  206. package/test/eyelang/conformance/expected/039_numeric_functions.pl +7 -0
  207. package/test/eyelang/conformance/expected/040_between_enumeration.pl +3 -0
  208. package/test/eyelang/conformance/expected/041_smallest_divisor.pl +1 -0
  209. package/test/eyelang/conformance/expected/042_negation_filter.pl +2 -0
  210. package/test/eyelang/conformance/expected/043_once_user_predicate.pl +1 -0
  211. package/test/eyelang/conformance/expected/044_findall_user_goal.pl +1 -0
  212. package/test/eyelang/conformance/expected/045_sort_deduplicates_atoms.pl +1 -0
  213. package/test/eyelang/conformance/expected/046_append_bound_prefix_suffix.pl +2 -0
  214. package/test/eyelang/conformance/expected/047_nth0_index_generation.pl +1 -0
  215. package/test/eyelang/conformance/expected/048_set_nth0_edges.pl +2 -0
  216. package/test/eyelang/conformance/expected/049_select_duplicate_occurrences.pl +2 -0
  217. package/test/eyelang/conformance/expected/050_not_member_filter.pl +1 -0
  218. package/test/eyelang/conformance/expected/051_nested_holds_parts.pl +3 -0
  219. package/test/eyelang/conformance/expected/052_holds_member.pl +2 -0
  220. package/test/eyelang/conformance/expected/053_materialize_excludes_source_fact.pl +1 -0
  221. package/test/eyelang/conformance/expected/054_numeric_and_lexical_comparison.pl +3 -0
  222. package/test/eyelang/conformance/expected/055_string_matching_filters.pl +2 -0
  223. package/test/eyelang/conformance/expected/056_string_and_atom_concat.pl +1 -0
  224. package/test/eyelang/conformance/expected/057_countall_empty_and_nonempty.pl +1 -0
  225. package/test/eyelang/conformance/expected/058_sumall_numeric_template.pl +1 -0
  226. package/test/eyelang/conformance/expected/059_aggregate_min_template.pl +1 -0
  227. package/test/eyelang/conformance/expected/060_aggregate_max_compound_key.pl +1 -0
  228. package/test/eyelang/conformance/expected/061_date_difference.pl +2 -0
  229. package/test/eyelang/conformance/expected/062_reusable_numeric_builtins.pl +8 -0
  230. package/test/eyelang/conformance/expected/063_reusable_list_builtins.pl +9 -0
  231. package/test/eyelang/conformance/expected/064_reusable_string_builtins.pl +10 -0
  232. package/test/eyelang/conformance/expected/065_reusable_term_control_builtins.pl +6 -0
  233. package/test/eyelang/conformance/expected/066_numeric_edges.pl +12 -0
  234. package/test/eyelang/conformance/expected/067_list_edges.pl +8 -0
  235. package/test/eyelang/conformance/expected/068_list_generation_order.pl +10 -0
  236. package/test/eyelang/conformance/expected/069_list_summaries_and_sets.pl +7 -0
  237. package/test/eyelang/conformance/expected/070_matches_named_captures.pl +1 -0
  238. package/test/eyelang/conformance/expected/071_string_edges.pl +8 -0
  239. package/test/eyelang/conformance/expected/072_string_conversions.pl +8 -0
  240. package/test/eyelang/conformance/expected/073_term_introspection_edges.pl +8 -0
  241. package/test/eyelang/conformance/expected/074_forall_edges.pl +5 -0
  242. package/test/eyelang/conformance/expected/075_aggregation_edges.pl +7 -0
  243. package/test/eyelang/conformance/expected/076_composed_reusable_builtins.pl +5 -0
  244. package/test/eyelang/conformance/expected/077_recursive_path_with_lists.pl +3 -0
  245. package/test/eyelang/conformance/expected/078_mutual_recursion_with_arithmetic.pl +7 -0
  246. package/test/eyelang/conformance/expected/079_big_integer_arithmetic.pl +6 -0
  247. package/test/eyelang/conformance/expected/080_rounding_modes.pl +8 -0
  248. package/test/eyelang/conformance/expected/081_zero_safe_numeric_functions.pl +7 -0
  249. package/test/eyelang/conformance/expected/082_comparison_semantics.pl +8 -0
  250. package/test/eyelang/conformance/expected/083_between_modes_and_empty_ranges.pl +8 -0
  251. package/test/eyelang/conformance/expected/084_append_and_select_composition.pl +10 -0
  252. package/test/eyelang/conformance/expected/085_nth_and_update_edges.pl +8 -0
  253. package/test/eyelang/conformance/expected/086_slicing_pipeline.pl +7 -0
  254. package/test/eyelang/conformance/expected/087_sort_reverse_length.pl +6 -0
  255. package/test/eyelang/conformance/expected/088_list_summaries_failures.pl +6 -0
  256. package/test/eyelang/conformance/expected/089_string_split_join_pipeline.pl +4 -0
  257. package/test/eyelang/conformance/expected/090_string_substring_replace_edges.pl +7 -0
  258. package/test/eyelang/conformance/expected/091_string_case_and_trim.pl +5 -0
  259. package/test/eyelang/conformance/expected/092_scalar_string_conversions.pl +7 -0
  260. package/test/eyelang/conformance/expected/093_regex_named_captures_context.pl +5 -0
  261. package/test/eyelang/conformance/expected/094_context_holds_enumeration.pl +8 -0
  262. package/test/eyelang/conformance/expected/095_term_introspection_roundtrip.pl +6 -0
  263. package/test/eyelang/conformance/expected/096_functor_scalar_edges.pl +6 -0
  264. package/test/eyelang/conformance/expected/097_control_negation_once_forall.pl +5 -0
  265. package/test/eyelang/conformance/expected/098_aggregation_nested_templates.pl +5 -0
  266. package/test/eyelang/conformance/expected/099_materialize_multiple_arities.pl +4 -0
  267. package/test/eyelang/conformance/expected/100_reusable_builtin_workflow.pl +6 -0
  268. package/test/eyelang/conformance/expected/extension/036_reusable_numeric_builtins.out +8 -0
  269. package/test/eyelang/conformance/expected/extension/037_reusable_list_builtins.out +9 -0
  270. package/test/eyelang/conformance/expected/extension/038_reusable_string_builtins.out +10 -0
  271. package/test/eyelang/conformance/expected/extension/039_reusable_term_control_builtins.out +6 -0
  272. package/test/eyelang/run-conformance.mjs +20 -22
  273. package/examples/eyelang/collatz-1000.pl +0 -14
  274. package/examples/eyelang/complex-matrix-stability.pl +0 -45
  275. package/examples/eyelang/gcd-bezout-identity.pl +0 -48
  276. package/examples/eyelang/goldbach-1000.pl +0 -185
  277. package/examples/eyelang/kaprekar.pl +0 -32
  278. package/examples/eyelang/matrix.pl +0 -296
  279. package/examples/eyelang/output/collatz-1000.pl +0 -1000
  280. package/examples/eyelang/output/complex-matrix-stability.pl +0 -5
  281. package/examples/eyelang/output/gcd-bezout-identity.pl +0 -36
  282. package/examples/eyelang/output/goldbach-1000.pl +0 -667
  283. package/examples/eyelang/output/kaprekar.pl +0 -8
  284. package/examples/eyelang/output/matrix.pl +0 -10
  285. package/lib/eyelang/builtins/matrix.js +0 -226
  286. package/lib/eyelang/builtins/number-theory.js +0 -114
  287. package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +0 -3
  288. package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +0 -3
  289. package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +0 -3
  290. package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +0 -3
  291. package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +0 -5
  292. package/test/eyelang/conformance/expected/extension/036_extended_gcd.out +0 -1
  293. package/test/eyelang/conformance/expected/extension/037_collatz_trajectory.out +0 -1
  294. package/test/eyelang/conformance/expected/extension/038_kaprekar_steps.out +0 -1
  295. package/test/eyelang/conformance/expected/extension/039_goldbach_pair.out +0 -2
  296. package/test/eyelang/conformance/expected/extension/040_matrix_operations.out +0 -3
@@ -1,23 +1,62 @@
1
- // List builtins for proper lists, selection, membership, sorting, and indexing.
1
+ // List builtins for proper lists, selection, membership, sorting, indexing, slicing, and summaries.
2
2
  // Several predicates support both checking and generation, so the argument modes are handled explicitly.
3
- import { compareTerms, copyResolved, deref, isCons, lexicalValue, listFromItems, numberTerm, properListItems, unify } from '../term.js';
3
+ import {
4
+ compareTerms,
5
+ copyResolved,
6
+ deref,
7
+ isDecimalInteger,
8
+ isCons,
9
+ lexicalValue,
10
+ listFromItems,
11
+ numberTerm,
12
+ numberTextFromDouble,
13
+ parseFiniteNumber,
14
+ properListItems,
15
+ unify,
16
+ } from '../term.js';
4
17
 
5
18
  export const listBuiltins = {
6
19
  register(registry) {
7
20
  registry.add('append', 3, append);
8
21
  registry.add('nth0', 3, nth0);
9
22
  registry.add('set_nth0', 4, setNth0, { deterministic: true });
10
- registry.add('rest', 2, rest, { deterministic: true });
23
+ registry.add('head', 2, head, { deterministic: true, fallbackWhenNotReady: true, ready: firstConsReady });
24
+ registry.add('rest', 2, rest, { deterministic: true, fallbackWhenNotReady: true, ready: firstConsReady });
25
+ registry.add('last', 2, last, { deterministic: true, fallbackWhenNotReady: true, ready: firstProperListReady });
26
+ registry.add('take', 3, take, { deterministic: true, fallbackWhenNotReady: true, ready: countAndListReady });
27
+ registry.add('drop', 3, drop, { deterministic: true, fallbackWhenNotReady: true, ready: countAndListReady });
28
+ registry.add('slice', 4, slice, { deterministic: true, fallbackWhenNotReady: true, ready: sliceReady });
11
29
  registry.add('member', 2, member);
12
30
  registry.add('select', 3, select);
13
31
  registry.add('not_member', 2, notMember, { deterministic: true });
14
32
  registry.add('reverse', 2, reverse, { deterministic: true });
15
33
  registry.add('length', 2, lengthBuiltin, { deterministic: true });
34
+ registry.add('sum_list', 2, sumList, { deterministic: true, fallbackWhenNotReady: true, ready: firstProperListReady });
35
+ registry.add('min_list', 2, minList, { deterministic: true, fallbackWhenNotReady: true, ready: firstProperListReady });
36
+ registry.add('max_list', 2, maxList, { deterministic: true, fallbackWhenNotReady: true, ready: firstProperListReady });
37
+ registry.add('list_to_set', 2, listToSet, { deterministic: true, fallbackWhenNotReady: true, ready: firstProperListReady });
16
38
  registry.add('sort', 2, sortBuiltin, { deterministic: true });
17
39
  }
18
40
  };
19
41
 
20
42
 
43
+
44
+ function firstConsReady(goal, env) {
45
+ return isCons(deref(goal.args[0], env));
46
+ }
47
+
48
+ function firstProperListReady(goal, env) {
49
+ return properListItems(goal.args[0], env) !== null;
50
+ }
51
+
52
+ function countAndListReady(goal, env) {
53
+ return safeIndex(goal.args[0], env) !== null && properListItems(goal.args[1], env) !== null;
54
+ }
55
+
56
+ function sliceReady(goal, env) {
57
+ return safeIndex(goal.args[0], env) !== null && safeIndex(goal.args[1], env) !== null && properListItems(goal.args[2], env) !== null;
58
+ }
59
+
21
60
  function listFromItemsExcept(items, skip) {
22
61
  const copy = new Array(items.length - 1);
23
62
  for (let i = 0, j = 0; i < items.length; i++) if (i !== skip) copy[j++] = items[i];
@@ -62,10 +101,8 @@ function* nth0({ goal, env }) {
62
101
  }
63
102
 
64
103
  function* setNth0({ goal, env }) {
65
- const indexText = lexicalValue(goal.args[0], env);
66
- if (!/^-?\d+$/.test(indexText ?? '')) return;
67
- const index = Number(indexText);
68
- if (!Number.isSafeInteger(index) || index < 0) return;
104
+ const index = safeIndex(goal.args[0], env);
105
+ if (index == null) return;
69
106
  const items = properListItems(goal.args[1], env);
70
107
  if (!items || index >= items.length) return;
71
108
  const out = items.slice();
@@ -74,6 +111,13 @@ function* setNth0({ goal, env }) {
74
111
  if (unify(goal.args[3], listFromItems(out), next)) yield next;
75
112
  }
76
113
 
114
+ function* head({ goal, env }) {
115
+ const list = deref(goal.args[0], env);
116
+ if (!isCons(list)) return;
117
+ const next = env.clone();
118
+ if (unify(goal.args[1], list.args[0], next)) yield next;
119
+ }
120
+
77
121
  function* rest({ goal, env }) {
78
122
  const list = deref(goal.args[0], env);
79
123
  if (!isCons(list)) return;
@@ -81,6 +125,41 @@ function* rest({ goal, env }) {
81
125
  if (unify(goal.args[1], list.args[1], next)) yield next;
82
126
  }
83
127
 
128
+ function* last({ goal, env }) {
129
+ const items = properListItems(goal.args[0], env);
130
+ if (!items || items.length === 0) return;
131
+ const next = env.clone();
132
+ if (unify(goal.args[1], items[items.length - 1], next)) yield next;
133
+ }
134
+
135
+ function* take({ goal, env }) {
136
+ const count = safeIndex(goal.args[0], env);
137
+ if (count == null) return;
138
+ const items = properListItems(goal.args[1], env);
139
+ if (!items || count > items.length) return;
140
+ const next = env.clone();
141
+ if (unify(goal.args[2], listFromItems(items, 0, count), next)) yield next;
142
+ }
143
+
144
+ function* drop({ goal, env }) {
145
+ const count = safeIndex(goal.args[0], env);
146
+ if (count == null) return;
147
+ const items = properListItems(goal.args[1], env);
148
+ if (!items || count > items.length) return;
149
+ const next = env.clone();
150
+ if (unify(goal.args[2], listFromItems(items, count, items.length), next)) yield next;
151
+ }
152
+
153
+ function* slice({ goal, env }) {
154
+ const start = safeIndex(goal.args[0], env);
155
+ const count = safeIndex(goal.args[1], env);
156
+ if (start == null || count == null) return;
157
+ const items = properListItems(goal.args[2], env);
158
+ if (!items || start + count > items.length) return;
159
+ const next = env.clone();
160
+ if (unify(goal.args[3], listFromItems(items, start, start + count), next)) yield next;
161
+ }
162
+
84
163
  function* member({ goal, env }) {
85
164
  const items = properListItems(goal.args[1], env);
86
165
  if (!items) return;
@@ -132,6 +211,59 @@ function* lengthBuiltin({ goal, env }) {
132
211
  if (unify(goal.args[1], numberTerm(items.length), next)) yield next;
133
212
  }
134
213
 
214
+ function* sumList({ goal, env }) {
215
+ const items = properListItems(goal.args[0], env);
216
+ if (!items) return;
217
+ let intSum = 0n;
218
+ let floatMode = false;
219
+ let floatSum = 0;
220
+ for (const item of items) {
221
+ const text = lexicalValue(item, env);
222
+ if (text == null) return;
223
+ if (!floatMode && isDecimalInteger(text)) intSum += BigInt(text);
224
+ else {
225
+ const value = parseFiniteNumber(text);
226
+ if (value == null) return;
227
+ if (!floatMode) { floatSum = Number(intSum); floatMode = true; }
228
+ floatSum += value;
229
+ }
230
+ }
231
+ const out = floatMode ? numberTextFromDouble(floatSum) : intSum.toString();
232
+ const next = env.clone();
233
+ if (unify(goal.args[1], numberTerm(out), next)) yield next;
234
+ }
235
+
236
+ function* minList({ goal, env }) {
237
+ yield* minMaxList(goal, env, true);
238
+ }
239
+
240
+ function* maxList({ goal, env }) {
241
+ yield* minMaxList(goal, env, false);
242
+ }
243
+
244
+ function* minMaxList(goal, env, wantMin) {
245
+ const items = properListItems(goal.args[0], env);
246
+ if (!items || items.length === 0) return;
247
+ let best = copyResolved(items[0], env);
248
+ for (let i = 1; i < items.length; i++) {
249
+ const item = copyResolved(items[i], env);
250
+ const cmp = compareTerms(item, best);
251
+ if ((wantMin && cmp < 0) || (!wantMin && cmp > 0)) best = item;
252
+ }
253
+ const next = env.clone();
254
+ if (unify(goal.args[1], best, next)) yield next;
255
+ }
256
+
257
+ function* listToSet({ goal, env }) {
258
+ const items = properListItems(goal.args[0], env);
259
+ if (!items) return;
260
+ const unique = [];
261
+ for (const item of items.map((entry) => copyResolved(entry, env))) {
262
+ if (!unique.some((seen) => compareTerms(seen, item) === 0)) unique.push(item);
263
+ }
264
+ const next = env.clone();
265
+ if (unify(goal.args[1], listFromItems(unique), next)) yield next;
266
+ }
135
267
 
136
268
  function* sortBuiltin({ goal, env }) {
137
269
  const items = properListItems(goal.args[0], env);
@@ -142,3 +274,10 @@ function* sortBuiltin({ goal, env }) {
142
274
  const next = env.clone();
143
275
  if (unify(goal.args[1], listFromItems(unique), next)) yield next;
144
276
  }
277
+
278
+ function safeIndex(term, env) {
279
+ const text = lexicalValue(term, env);
280
+ if (!/^-?\d+$/.test(text ?? '')) return null;
281
+ const index = Number(text);
282
+ return Number.isSafeInteger(index) && index >= 0 ? index : null;
283
+ }
@@ -7,8 +7,7 @@ import { listBuiltins } from './lists.js';
7
7
  import { aggregationBuiltins } from './aggregation.js';
8
8
  import { contextBuiltins } from './context.js';
9
9
  import { controlBuiltins } from './control.js';
10
- import { numberTheoryBuiltins } from './number-theory.js';
11
- import { matrixBuiltins } from './matrix.js';
10
+ import { termBuiltins } from './terms.js';
12
11
 
13
12
  export class BuiltinRegistry {
14
13
  constructor() {
@@ -35,7 +34,7 @@ export class BuiltinRegistry {
35
34
 
36
35
  export function createDefaultRegistry() {
37
36
  const registry = new BuiltinRegistry();
38
- for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins, contextBuiltins, controlBuiltins, numberTheoryBuiltins, matrixBuiltins]) {
37
+ for (const mod of [coreBuiltins, arithmeticBuiltins, stringBuiltins, listBuiltins, aggregationBuiltins, contextBuiltins, controlBuiltins, termBuiltins]) {
39
38
  mod.register(registry);
40
39
  }
41
40
  return registry;
@@ -1,16 +1,76 @@
1
1
  // String builtins.
2
2
  // They mostly project from already-ground terms to avoid guessing string domains.
3
- import { compound, lexicalValue, stringTerm, unify } from '../term.js';
3
+ import {
4
+ atom,
5
+ compound,
6
+ deref,
7
+ isDecimalInteger,
8
+ lexicalValue,
9
+ listFromItems,
10
+ numberTerm,
11
+ parseFiniteNumber,
12
+ properListItems,
13
+ stringTerm,
14
+ termToString,
15
+ unify,
16
+ } from '../term.js';
4
17
 
5
18
  export const stringBuiltins = {
6
19
  register(registry) {
7
20
  registry.add('str_concat', 3, concat, { deterministic: true });
8
21
  for (const name of ['contains', 'matches', 'not_matches']) registry.add(name, 2, contains(name), { deterministic: true });
9
22
  registry.add('matches', 3, matchCaptures, { deterministic: true });
23
+ registry.add('split', 3, split, { deterministic: true, fallbackWhenNotReady: true, ready: firstTwoLexicalReady });
24
+ registry.add('join', 3, join, { deterministic: true, fallbackWhenNotReady: true, ready: listAndSecondLexicalReady });
25
+ registry.add('substring', 4, substring, { deterministic: true, fallbackWhenNotReady: true, ready: substringReady });
26
+ registry.add('replace', 4, replace, { deterministic: true, fallbackWhenNotReady: true, ready: firstThreeLexicalReady });
27
+ registry.add('lowercase', 2, caseFold('lower'), { deterministic: true, fallbackWhenNotReady: true, ready: firstLexicalReady });
28
+ registry.add('uppercase', 2, caseFold('upper'), { deterministic: true, fallbackWhenNotReady: true, ready: firstLexicalReady });
29
+ registry.add('trim', 2, trim, { deterministic: true, fallbackWhenNotReady: true, ready: firstLexicalReady });
30
+ registry.add('number_string', 2, numberString, { deterministic: true, fallbackWhenNotReady: true, ready: numberStringReady });
31
+ registry.add('atom_string', 2, atomString, { deterministic: true, fallbackWhenNotReady: true, ready: atomStringReady });
32
+ registry.add('term_string', 2, termString, { deterministic: true, fallbackWhenNotReady: true, ready: firstNonvarReady });
10
33
  }
11
34
  };
12
35
 
13
36
 
37
+
38
+ function firstLexicalReady(goal, env) {
39
+ return lexicalValue(goal.args[0], env) !== null;
40
+ }
41
+
42
+ function firstTwoLexicalReady(goal, env) {
43
+ return lexicalValue(goal.args[0], env) !== null && lexicalValue(goal.args[1], env) !== null;
44
+ }
45
+
46
+ function firstThreeLexicalReady(goal, env) {
47
+ return firstTwoLexicalReady(goal, env) && lexicalValue(goal.args[2], env) !== null;
48
+ }
49
+
50
+ function listAndSecondLexicalReady(goal, env) {
51
+ return properListItems(goal.args[0], env) !== null && lexicalValue(goal.args[1], env) !== null;
52
+ }
53
+
54
+ function substringReady(goal, env) {
55
+ return lexicalValue(goal.args[0], env) !== null && safeIndex(goal.args[1], env) !== null && safeIndex(goal.args[2], env) !== null;
56
+ }
57
+
58
+ function numberStringReady(goal, env) {
59
+ const left = deref(goal.args[0], env);
60
+ const right = deref(goal.args[1], env);
61
+ return left.type === 'number' || right.type === 'string' || right.type === 'atom';
62
+ }
63
+
64
+ function atomStringReady(goal, env) {
65
+ const left = deref(goal.args[0], env);
66
+ const right = deref(goal.args[1], env);
67
+ return left.type === 'atom' || right.type === 'string' || right.type === 'atom' || right.type === 'number';
68
+ }
69
+
70
+ function firstNonvarReady(goal, env) {
71
+ return deref(goal.args[0], env).type !== 'var';
72
+ }
73
+
14
74
  function* concat({ goal, env }) {
15
75
  const left = lexicalValue(goal.args[0], env);
16
76
  const right = lexicalValue(goal.args[1], env);
@@ -53,6 +113,99 @@ function* matchCaptures({ goal, env }) {
53
113
  if (unify(goal.args[2], context, next)) yield next;
54
114
  }
55
115
 
116
+ function* split({ goal, env }) {
117
+ const text = lexicalValue(goal.args[0], env);
118
+ const separator = lexicalValue(goal.args[1], env);
119
+ if (text == null || separator == null) return;
120
+ const parts = text.split(separator).map(stringTerm);
121
+ const next = env.clone();
122
+ if (unify(goal.args[2], listFromItems(parts), next)) yield next;
123
+ }
124
+
125
+ function* join({ goal, env }) {
126
+ const items = properListItems(goal.args[0], env);
127
+ const separator = lexicalValue(goal.args[1], env);
128
+ if (!items || separator == null) return;
129
+ const strings = [];
130
+ for (const item of items) {
131
+ const value = lexicalValue(item, env);
132
+ if (value == null) return;
133
+ strings.push(value);
134
+ }
135
+ const next = env.clone();
136
+ if (unify(goal.args[2], stringTerm(strings.join(separator)), next)) yield next;
137
+ }
138
+
139
+ function* substring({ goal, env }) {
140
+ const text = lexicalValue(goal.args[0], env);
141
+ const start = safeIndex(goal.args[1], env);
142
+ const count = safeIndex(goal.args[2], env);
143
+ if (text == null || start == null || count == null || start + count > text.length) return;
144
+ const next = env.clone();
145
+ if (unify(goal.args[3], stringTerm(text.slice(start, start + count)), next)) yield next;
146
+ }
147
+
148
+ function* replace({ goal, env }) {
149
+ const text = lexicalValue(goal.args[0], env);
150
+ const search = lexicalValue(goal.args[1], env);
151
+ const replacement = lexicalValue(goal.args[2], env);
152
+ if (text == null || search == null || replacement == null) return;
153
+ const out = search === '' ? text : text.split(search).join(replacement);
154
+ const next = env.clone();
155
+ if (unify(goal.args[3], stringTerm(out), next)) yield next;
156
+ }
157
+
158
+ function caseFold(direction) {
159
+ return function* ({ goal, env }) {
160
+ const text = lexicalValue(goal.args[0], env);
161
+ if (text == null) return;
162
+ const next = env.clone();
163
+ const out = direction === 'lower' ? text.toLowerCase() : text.toUpperCase();
164
+ if (unify(goal.args[1], stringTerm(out), next)) yield next;
165
+ };
166
+ }
167
+
168
+ function* trim({ goal, env }) {
169
+ const text = lexicalValue(goal.args[0], env);
170
+ if (text == null) return;
171
+ const next = env.clone();
172
+ if (unify(goal.args[1], stringTerm(text.trim()), next)) yield next;
173
+ }
174
+
175
+ function* numberString({ goal, env }) {
176
+ const left = deref(goal.args[0], env);
177
+ const right = deref(goal.args[1], env);
178
+ const next = env.clone();
179
+ if (left.type === 'number') {
180
+ if (unify(goal.args[1], stringTerm(left.name), next)) yield next;
181
+ return;
182
+ }
183
+ if (right.type === 'string' || right.type === 'atom') {
184
+ if (!numericText(right.name)) return;
185
+ if (unify(goal.args[0], numberTerm(right.name), next)) yield next;
186
+ }
187
+ }
188
+
189
+ function* atomString({ goal, env }) {
190
+ const left = deref(goal.args[0], env);
191
+ const right = deref(goal.args[1], env);
192
+ const next = env.clone();
193
+ if (left.type === 'atom') {
194
+ if (unify(goal.args[1], stringTerm(left.name), next)) yield next;
195
+ return;
196
+ }
197
+ if (right.type === 'string' || right.type === 'atom' || right.type === 'number') {
198
+ if (unify(goal.args[0], atom(right.name), next)) yield next;
199
+ }
200
+ }
201
+
202
+ function* termString({ goal, env }) {
203
+ const term = deref(goal.args[0], env);
204
+ if (term.type === 'var') return;
205
+ const next = env.clone();
206
+ if (unify(goal.args[1], stringTerm(termToString(term, env, true)), next)) yield next;
207
+ }
208
+
56
209
  function contextFromGroups(groups) {
57
210
  const terms = [];
58
211
  for (const [name, value] of Object.entries(groups)) {
@@ -68,3 +221,14 @@ function contextFromGroups(groups) {
68
221
  function simpleAlternationMatch(haystack, pattern) {
69
222
  return pattern.split('|').some((part) => part === '' || haystack.includes(part));
70
223
  }
224
+
225
+ function safeIndex(term, env) {
226
+ const text = lexicalValue(term, env);
227
+ if (!/^-?\d+$/.test(text ?? '')) return null;
228
+ const index = Number(text);
229
+ return Number.isSafeInteger(index) && index >= 0 ? index : null;
230
+ }
231
+
232
+ function numericText(text) {
233
+ return isDecimalInteger(text) || parseFiniteNumber(text) != null;
234
+ }
@@ -0,0 +1,66 @@
1
+ // Term-inspection builtins for reusable meta-programming over eyelang terms.
2
+ import { atom, compound, deref, listFromItems, lexicalValue, numberTerm, properListItems, stringTerm, unify } from '../term.js';
3
+
4
+ export const termBuiltins = {
5
+ register(registry) {
6
+ registry.add('functor', 3, functorBuiltin, { deterministic: true, fallbackWhenNotReady: true, ready: firstNonvarReady });
7
+ registry.add('arg', 3, argBuiltin, { deterministic: true, fallbackWhenNotReady: true, ready: argReady });
8
+ registry.add('compound_name_arguments', 3, compoundNameArguments, { deterministic: true, fallbackWhenNotReady: true, ready: compoundNameArgumentsReady });
9
+ }
10
+ };
11
+
12
+
13
+ function firstNonvarReady(goal, env) {
14
+ return deref(goal.args[0], env).type !== 'var';
15
+ }
16
+
17
+ function argReady(goal, env) {
18
+ return /^\d+$/.test(lexicalValue(goal.args[0], env) ?? '') && deref(goal.args[1], env).type === 'compound';
19
+ }
20
+
21
+ function compoundNameArgumentsReady(goal, env) {
22
+ const term = deref(goal.args[0], env);
23
+ if (term.type === 'compound') return true;
24
+ return term.type === 'var' && lexicalValue(goal.args[1], env) !== null && properListItems(goal.args[2], env) !== null;
25
+ }
26
+
27
+ function* functorBuiltin({ goal, env }) {
28
+ const term = deref(goal.args[0], env);
29
+ if (term.type === 'var') return;
30
+ const nameTerm = term.type === 'compound' ? atom(term.name) : scalarNameTerm(term);
31
+ const arity = term.type === 'compound' ? term.arity : 0;
32
+ const next = env.clone();
33
+ if (unify(goal.args[1], nameTerm, next) && unify(goal.args[2], numberTerm(arity), next)) yield next;
34
+ }
35
+
36
+ function* argBuiltin({ goal, env }) {
37
+ const indexText = lexicalValue(goal.args[0], env);
38
+ if (!/^\d+$/.test(indexText ?? '')) return;
39
+ const index = Number(indexText);
40
+ const term = deref(goal.args[1], env);
41
+ if (term.type !== 'compound' || !Number.isSafeInteger(index) || index < 1 || index > term.arity) return;
42
+ const next = env.clone();
43
+ if (unify(goal.args[2], term.args[index - 1], next)) yield next;
44
+ }
45
+
46
+ function* compoundNameArguments({ goal, env }) {
47
+ const term = deref(goal.args[0], env);
48
+ if (term.type === 'compound') {
49
+ const next = env.clone();
50
+ if (unify(goal.args[1], atom(term.name), next) && unify(goal.args[2], listFromItems(term.args), next)) yield next;
51
+ return;
52
+ }
53
+ if (term.type !== 'var') return;
54
+
55
+ const name = lexicalValue(goal.args[1], env);
56
+ const args = properListItems(goal.args[2], env);
57
+ if (name == null || !args) return;
58
+ const next = env.clone();
59
+ if (unify(goal.args[0], compound(name, args), next)) yield next;
60
+ }
61
+
62
+ function scalarNameTerm(term) {
63
+ if (term.type === 'atom') return atom(term.name);
64
+ if (term.type === 'number') return numberTerm(term.name);
65
+ return stringTerm(term.name);
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyeling",
3
- "version": "1.34.3",
3
+ "version": "1.34.5",
4
4
  "description": "A minimal Notation3 (N3) reasoner in JavaScript.",
5
5
  "main": "./index.js",
6
6
  "keywords": [
@@ -4,8 +4,8 @@ This directory contains the executable conformance cases for the eyelang languag
4
4
 
5
5
  The suite is intentionally file-based so another implementation can run the same programs and compare exact standard output. A case consists of:
6
6
 
7
- - `conformance/cases/<profile>/<name>.pl` — input program;
8
- - `conformance/expected/<profile>/<name>.out` — exact expected standard output.
7
+ - `conformance/cases/<name>.pl` — input program;
8
+ - `conformance/expected/<name>.pl` — exact expected standard output, stored as eyelang-readable facts.
9
9
 
10
10
  The current runner compares standard output from normal execution. Proof explanations are opt-in in the CLI and are not part of these conformance goldens. Standard error, performance, and resource limits are outside this suite.
11
11
 
@@ -23,23 +23,21 @@ Run only the conformance suite:
23
23
  node test/eyelang/run-conformance.mjs
24
24
  ```
25
25
 
26
- Run a single conformance profile directly:
26
+ Run matching conformance cases by passing a filename fragment:
27
27
 
28
28
  ```sh
29
- node test/eyelang/run-conformance.mjs core
30
- node test/eyelang/run-conformance.mjs extension
29
+ node test/eyelang/run-conformance.mjs reusable
30
+ node test/eyelang/run-conformance.mjs 092_scalar_string_conversions
31
31
  ```
32
32
 
33
33
  The runner executes materialized programs in-process through the public JavaScript API so small conformance cases avoid measuring Node startup overhead.
34
34
 
35
- ## Profiles
35
+ ## Scope
36
36
 
37
- `core` covers the portable core language profile from the [eyelang language reference](../../../docs/eyelang-language-reference.md): lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification through user predicates, left-to-right goal-directed proof search, materialized output, and read-back printing.
37
+ The conformance corpus is a single eyelang suite. It covers the standard language described by the language reference: lexical syntax, facts, definite clauses, first-order terms, lists, conjunction, structured unification, left-to-right goal-directed proof search, materialized output, read-back printing, standard built-ins, declarations, and standard host behavior.
38
38
 
39
- `extension` covers the standard built-in and host behavior exercised by the current reference implementation: arithmetic, comparison, strings, list relations, aggregation, context-term helpers, number-theory helpers, matrix helpers, `memoize/2`, `materialize/2`, and default derived output.
40
-
41
- The profile name `extension` is a test-suite grouping name. It does not mean that these cases are outside the eyelang language reference; most of them correspond to the standard built-in profile and standard host profile in the [eyelang language reference](../../../docs/eyelang-language-reference.md).
39
+ The suite deliberately does not separate `core` and `extension` profiles. Reusable built-ins such as arithmetic, strings, lists, aggregation, context terms, term inspection, and search control are part of the standard eyelang conformance surface. Implementation-specific built-ins may still exist in downstream hosts, but they should have their own tests outside this corpus unless they are standardized.
42
40
 
43
41
  ## Updating expected output
44
42
 
45
- There is no committed auto-accept mode. To update an expected file, run the matching case with the conformance runner, inspect the result, and replace the corresponding file under `conformance/expected/<profile>/` deliberately.
43
+ There is no committed auto-accept mode. To update an expected file, run the matching case with the conformance runner, inspect the result, and replace the corresponding file under `conformance/expected/` deliberately.
@@ -0,0 +1,4 @@
1
+ % Reference 6, 7, 11: facts can be exposed through a materialized derived predicate.
2
+ materialize(parent, 2).
3
+ base_parent(pat, jan).
4
+ parent(X, Y) :- base_parent(X, Y).
@@ -0,0 +1,7 @@
1
+ % Reference 6, 7: definite clauses, conjunction, and recursive proof search.
2
+ materialize(ancestor, 2).
3
+ parent(pat, jan).
4
+ parent(jan, emma).
5
+ ancestor_any(X, Y) :- parent(X, Y).
6
+ ancestor_any(X, Z) :- parent(X, Y), ancestor_any(Y, Z).
7
+ ancestor(pat, Y) :- ancestor_any(pat, Y).
@@ -0,0 +1 @@
1
+ ancestor(pat, X)
@@ -0,0 +1,16 @@
1
+ % Reference 3, 5, 11: scalars, compounds, lists, and read-back printing.
2
+ materialize(value, 2).
3
+ raw_value(atom, pat).
4
+ raw_value(quoted_atom, 'atom with spaces').
5
+ raw_value(quoted_quote, 'needs''quote').
6
+ raw_value(empty_atom, '').
7
+ raw_value(string, "line\nquote: \"ok\"").
8
+ raw_value(integer, -42).
9
+ raw_value(decimal, 0.25).
10
+ raw_value(scientific, 1.25e-3).
11
+ raw_value(compound, pair(3, nested(atom, [x, y]))).
12
+ raw_value(zero_arity, nil()).
13
+ raw_value(empty_list, []).
14
+ raw_value(proper_list, [a, b, c]).
15
+ raw_value(improper_list, [a, b | tail]).
16
+ value(K, V) :- raw_value(K, V).
@@ -0,0 +1,5 @@
1
+ % Reference 5.5, 7: parenthesized comma terms are conjunctions in goal position.
2
+ p(a).
3
+ q(a).
4
+ ok(X, yes) :- (p(X), q(X)).
5
+ materialize(ok, 2).
@@ -0,0 +1,6 @@
1
+ % Reference 5.4, 12.1: list syntax and unification in rule heads.
2
+ first([X | _Rest], X).
3
+ tail([_Head | Tail], Tail).
4
+ answer(first, X) :- first([a, b, c], X).
5
+ answer(tail, Tail) :- tail([a, b, c], Tail).
6
+ materialize(answer, 2).
@@ -0,0 +1,4 @@
1
+ % Reference 5.5: comma terms remain data outside goal position.
2
+ record((name(alice, "Alice"), knows(alice, bob))).
3
+ answer(formula, F) :- record(F).
4
+ materialize(answer, 2).
@@ -0,0 +1,5 @@
1
+ % Reference 3.4, 5.1, 7.1: each anonymous variable occurrence is fresh.
2
+ pair(a, one).
3
+ pair(b, two).
4
+ answer(fresh, yes) :- pair(a, _), pair(b, _).
5
+ materialize(answer, 2).
@@ -0,0 +1,6 @@
1
+ % Reference 3.5, 5.2, 11: graphic atom constants are scalar terms.
2
+ materialize(value, 2).
3
+ raw_value(hash, #).
4
+ raw_value(arrow, =>).
5
+ raw_value(comparison, =<).
6
+ value(K, V) :- raw_value(K, V).
@@ -0,0 +1,5 @@
1
+ % Reference 3.2, 3.6: comments are ignored outside quoted text and atoms.
2
+ item(quoted_percent, "% not a comment"). % trailing comment
3
+ item(quoted_atom, 'has % sign').
4
+ answer(K, V) :- item(K, V).
5
+ materialize(answer, 2).
@@ -0,0 +1,8 @@
1
+ % Reference 5.1, 7.1: variable occurrences are scoped per clause and reused within a clause.
2
+ edge(a, b).
3
+ edge(b, c).
4
+ edge(c, d).
5
+ two_step(X, Z) :- edge(X, Y), edge(Y, Z).
6
+ answer(from_a, Z) :- two_step(a, Z).
7
+ answer(from_b, Z) :- two_step(b, Z).
8
+ materialize(answer, 2).
@@ -0,0 +1,6 @@
1
+ % Reference 6: predicate name and arity both identify a predicate group.
2
+ p(a).
3
+ p(a, b).
4
+ answer(unary, X) :- p(X).
5
+ answer(binary, pair(X, Y)) :- p(X, Y).
6
+ materialize(answer, 2).