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.
- package/README.md +7 -9
- package/docs/eyelang-guide.md +6 -11
- package/docs/eyelang-language-reference.md +47 -36
- package/examples/eyelang/bayes-therapy.pl +4 -4
- package/examples/eyelang/output/reusable-builtins.pl +5 -0
- package/examples/eyelang/output/term-tools.pl +6 -0
- package/examples/eyelang/reusable-builtins.pl +32 -0
- package/examples/eyelang/term-tools.pl +23 -0
- package/lib/eyelang/builtins/arithmetic.js +19 -6
- package/lib/eyelang/builtins/control.js +12 -0
- package/lib/eyelang/builtins/lists.js +146 -7
- package/lib/eyelang/builtins/registry.js +2 -3
- package/lib/eyelang/builtins/strings.js +165 -1
- package/lib/eyelang/builtins/terms.js +66 -0
- package/package.json +1 -1
- package/test/eyelang/conformance/README.md +9 -11
- package/test/eyelang/conformance/cases/001_fact_output.pl +4 -0
- package/test/eyelang/conformance/cases/002_rule_recursion.pl +7 -0
- package/test/eyelang/conformance/cases/002_rule_recursion.query +1 -0
- package/test/eyelang/conformance/cases/003_terms_and_readback.pl +16 -0
- package/test/eyelang/conformance/cases/003_terms_and_readback.query +1 -0
- package/test/eyelang/conformance/cases/004_conjunction_and_parentheses.pl +5 -0
- package/test/eyelang/conformance/cases/004_conjunction_and_parentheses.query +1 -0
- package/test/eyelang/conformance/cases/005_list_deconstruction.pl +6 -0
- package/test/eyelang/conformance/cases/005_list_deconstruction.query +1 -0
- package/test/eyelang/conformance/cases/006_comma_formula_data.pl +4 -0
- package/test/eyelang/conformance/cases/006_comma_formula_data.query +1 -0
- package/test/eyelang/conformance/cases/007_anonymous_variables.pl +5 -0
- package/test/eyelang/conformance/cases/007_anonymous_variables.query +1 -0
- package/test/eyelang/conformance/cases/008_graphic_atoms.pl +6 -0
- package/test/eyelang/conformance/cases/008_graphic_atoms.query +1 -0
- package/test/eyelang/conformance/cases/009_comments_and_whitespace.pl +5 -0
- package/test/eyelang/conformance/cases/009_comments_and_whitespace.query +1 -0
- package/test/eyelang/conformance/cases/010_variable_scope_and_reuse.pl +8 -0
- package/test/eyelang/conformance/cases/010_variable_scope_and_reuse.query +1 -0
- package/test/eyelang/conformance/cases/011_predicate_arity.pl +6 -0
- package/test/eyelang/conformance/cases/011_predicate_arity.query +1 -0
- package/test/eyelang/conformance/cases/012_nested_compound_unification.pl +5 -0
- package/test/eyelang/conformance/cases/012_nested_compound_unification.query +1 -0
- package/test/eyelang/conformance/cases/013_multiple_clauses_order.pl +6 -0
- package/test/eyelang/conformance/cases/013_multiple_clauses_order.query +1 -0
- package/test/eyelang/conformance/cases/014_failure_filters_answers.pl +7 -0
- package/test/eyelang/conformance/cases/014_failure_filters_answers.query +1 -0
- package/test/eyelang/conformance/cases/015_improper_list_unification.pl +6 -0
- package/test/eyelang/conformance/cases/015_improper_list_unification.query +1 -0
- package/test/eyelang/conformance/cases/016_zero_arity_compound.pl +4 -0
- package/test/eyelang/conformance/cases/016_zero_arity_compound.query +1 -0
- package/test/eyelang/conformance/cases/017_three_step_recursion.pl +8 -0
- package/test/eyelang/conformance/cases/017_three_step_recursion.query +1 -0
- package/test/eyelang/conformance/cases/018_quoted_atom_readback.pl +6 -0
- package/test/eyelang/conformance/cases/018_quoted_atom_readback.query +1 -0
- package/test/eyelang/conformance/cases/019_parenthesized_three_conjuncts.pl +7 -0
- package/test/eyelang/conformance/cases/019_parenthesized_three_conjuncts.query +1 -0
- package/test/eyelang/conformance/cases/020_nested_list_terms.pl +5 -0
- package/test/eyelang/conformance/cases/020_nested_list_terms.query +1 -0
- package/test/eyelang/conformance/cases/021_repeated_variable_head.pl +7 -0
- package/test/eyelang/conformance/cases/022_rule_head_structure.pl +5 -0
- package/test/eyelang/conformance/cases/023_quoted_escapes_readback.pl +5 -0
- package/test/eyelang/conformance/cases/024_numeric_literal_readback.pl +6 -0
- package/test/eyelang/conformance/cases/025_body_parentheses_with_formula_data.pl +5 -0
- package/test/eyelang/conformance/cases/026_underscore_named_variable_reuse.pl +5 -0
- package/test/eyelang/conformance/cases/027_default_derived_output.pl +5 -0
- package/test/eyelang/conformance/cases/028_materialize_focus.pl +5 -0
- package/test/eyelang/conformance/cases/029_arithmetic_and_comparison.pl +11 -0
- package/test/eyelang/conformance/cases/029_arithmetic_and_comparison.query +1 -0
- package/test/eyelang/conformance/cases/030_strings_and_atoms.pl +4 -0
- package/test/eyelang/conformance/cases/030_strings_and_atoms.query +1 -0
- package/test/eyelang/conformance/cases/031_lists_aggregation_ordering.pl +10 -0
- package/test/eyelang/conformance/cases/031_lists_aggregation_ordering.query +1 -0
- package/test/eyelang/conformance/cases/032_holds_parts.pl +4 -0
- package/test/eyelang/conformance/cases/033_negation_once_generators.pl +7 -0
- package/test/eyelang/conformance/cases/033_negation_once_generators.query +1 -0
- package/test/eyelang/conformance/cases/034_equality_and_inequality.pl +6 -0
- package/test/eyelang/conformance/cases/034_equality_and_inequality.query +1 -0
- package/test/eyelang/conformance/cases/035_list_relations.pl +5 -0
- package/test/eyelang/conformance/cases/035_list_relations.query +1 -0
- package/test/eyelang/conformance/cases/036_append_splits.pl +3 -0
- package/test/eyelang/conformance/cases/036_append_splits.query +1 -0
- package/test/eyelang/conformance/cases/037_matching_and_comparison.pl +7 -0
- package/test/eyelang/conformance/cases/037_matching_and_comparison.query +1 -0
- package/test/eyelang/conformance/cases/038_memoize_declaration.pl +8 -0
- package/test/eyelang/conformance/cases/038_memoize_declaration.query +1 -0
- package/test/eyelang/conformance/cases/039_numeric_functions.pl +9 -0
- package/test/eyelang/conformance/cases/039_numeric_functions.query +1 -0
- package/test/eyelang/conformance/cases/040_between_enumeration.pl +3 -0
- package/test/eyelang/conformance/cases/040_between_enumeration.query +1 -0
- package/test/eyelang/conformance/cases/041_smallest_divisor.pl +3 -0
- package/test/eyelang/conformance/cases/041_smallest_divisor.query +1 -0
- package/test/eyelang/conformance/cases/042_negation_filter.pl +7 -0
- package/test/eyelang/conformance/cases/042_negation_filter.query +1 -0
- package/test/eyelang/conformance/cases/043_once_user_predicate.pl +5 -0
- package/test/eyelang/conformance/cases/043_once_user_predicate.query +1 -0
- package/test/eyelang/conformance/cases/044_findall_user_goal.pl +6 -0
- package/test/eyelang/conformance/cases/044_findall_user_goal.query +1 -0
- package/test/eyelang/conformance/cases/045_sort_deduplicates_atoms.pl +3 -0
- package/test/eyelang/conformance/cases/045_sort_deduplicates_atoms.query +1 -0
- package/test/eyelang/conformance/cases/046_append_bound_prefix_suffix.pl +4 -0
- package/test/eyelang/conformance/cases/046_append_bound_prefix_suffix.query +1 -0
- package/test/eyelang/conformance/cases/047_nth0_index_generation.pl +3 -0
- package/test/eyelang/conformance/cases/047_nth0_index_generation.query +1 -0
- package/test/eyelang/conformance/cases/048_set_nth0_edges.pl +4 -0
- package/test/eyelang/conformance/cases/048_set_nth0_edges.query +1 -0
- package/test/eyelang/conformance/cases/049_select_duplicate_occurrences.pl +3 -0
- package/test/eyelang/conformance/cases/049_select_duplicate_occurrences.query +1 -0
- package/test/eyelang/conformance/cases/050_not_member_filter.pl +6 -0
- package/test/eyelang/conformance/cases/050_not_member_filter.query +1 -0
- package/test/eyelang/conformance/cases/051_nested_holds_parts.pl +4 -0
- package/test/eyelang/conformance/cases/052_holds_member.pl +3 -0
- package/test/eyelang/conformance/cases/053_materialize_excludes_source_fact.pl +6 -0
- package/test/eyelang/conformance/cases/054_numeric_and_lexical_comparison.pl +5 -0
- package/test/eyelang/conformance/cases/054_numeric_and_lexical_comparison.query +1 -0
- package/test/eyelang/conformance/cases/055_string_matching_filters.pl +6 -0
- package/test/eyelang/conformance/cases/055_string_matching_filters.query +1 -0
- package/test/eyelang/conformance/cases/056_string_and_atom_concat.pl +3 -0
- package/test/eyelang/conformance/cases/056_string_and_atom_concat.query +1 -0
- package/test/eyelang/conformance/cases/057_countall_empty_and_nonempty.pl +4 -0
- package/test/eyelang/conformance/cases/057_countall_empty_and_nonempty.query +1 -0
- package/test/eyelang/conformance/cases/058_sumall_numeric_template.pl +5 -0
- package/test/eyelang/conformance/cases/058_sumall_numeric_template.query +1 -0
- package/test/eyelang/conformance/cases/059_aggregate_min_template.pl +5 -0
- package/test/eyelang/conformance/cases/059_aggregate_min_template.query +1 -0
- package/test/eyelang/conformance/cases/060_aggregate_max_compound_key.pl +5 -0
- package/test/eyelang/conformance/cases/060_aggregate_max_compound_key.query +1 -0
- package/test/eyelang/conformance/cases/061_date_difference.pl +4 -0
- package/test/eyelang/conformance/cases/062_reusable_numeric_builtins.pl +10 -0
- package/test/eyelang/conformance/cases/063_reusable_list_builtins.pl +11 -0
- package/test/eyelang/conformance/cases/064_reusable_string_builtins.pl +12 -0
- package/test/eyelang/conformance/cases/065_reusable_term_control_builtins.pl +11 -0
- package/test/eyelang/conformance/cases/066_numeric_edges.pl +14 -0
- package/test/eyelang/conformance/cases/067_list_edges.pl +10 -0
- package/test/eyelang/conformance/cases/068_list_generation_order.pl +7 -0
- package/test/eyelang/conformance/cases/069_list_summaries_and_sets.pl +9 -0
- package/test/eyelang/conformance/cases/070_matches_named_captures.pl +13 -0
- package/test/eyelang/conformance/cases/071_string_edges.pl +10 -0
- package/test/eyelang/conformance/cases/072_string_conversions.pl +10 -0
- package/test/eyelang/conformance/cases/073_term_introspection_edges.pl +10 -0
- package/test/eyelang/conformance/cases/074_forall_edges.pl +10 -0
- package/test/eyelang/conformance/cases/075_aggregation_edges.pl +12 -0
- package/test/eyelang/conformance/cases/076_composed_reusable_builtins.pl +8 -0
- package/test/eyelang/conformance/cases/077_recursive_path_with_lists.pl +10 -0
- package/test/eyelang/conformance/cases/078_mutual_recursion_with_arithmetic.pl +7 -0
- package/test/eyelang/conformance/cases/079_big_integer_arithmetic.pl +8 -0
- package/test/eyelang/conformance/cases/080_rounding_modes.pl +10 -0
- package/test/eyelang/conformance/cases/081_zero_safe_numeric_functions.pl +9 -0
- package/test/eyelang/conformance/cases/082_comparison_semantics.pl +10 -0
- package/test/eyelang/conformance/cases/083_between_modes_and_empty_ranges.pl +8 -0
- package/test/eyelang/conformance/cases/084_append_and_select_composition.pl +7 -0
- package/test/eyelang/conformance/cases/085_nth_and_update_edges.pl +8 -0
- package/test/eyelang/conformance/cases/086_slicing_pipeline.pl +10 -0
- package/test/eyelang/conformance/cases/087_sort_reverse_length.pl +8 -0
- package/test/eyelang/conformance/cases/088_list_summaries_failures.pl +8 -0
- package/test/eyelang/conformance/cases/089_string_split_join_pipeline.pl +7 -0
- package/test/eyelang/conformance/cases/090_string_substring_replace_edges.pl +9 -0
- package/test/eyelang/conformance/cases/091_string_case_and_trim.pl +7 -0
- package/test/eyelang/conformance/cases/092_scalar_string_conversions.pl +9 -0
- package/test/eyelang/conformance/cases/093_regex_named_captures_context.pl +8 -0
- package/test/eyelang/conformance/cases/094_context_holds_enumeration.pl +7 -0
- package/test/eyelang/conformance/cases/095_term_introspection_roundtrip.pl +8 -0
- package/test/eyelang/conformance/cases/096_functor_scalar_edges.pl +8 -0
- package/test/eyelang/conformance/cases/097_control_negation_once_forall.pl +13 -0
- package/test/eyelang/conformance/cases/098_aggregation_nested_templates.pl +11 -0
- package/test/eyelang/conformance/cases/099_materialize_multiple_arities.pl +8 -0
- package/test/eyelang/conformance/cases/100_reusable_builtin_workflow.pl +10 -0
- package/test/eyelang/conformance/cases/extension/036_reusable_numeric_builtins.pl +10 -0
- package/test/eyelang/conformance/cases/extension/037_reusable_list_builtins.pl +11 -0
- package/test/eyelang/conformance/cases/extension/038_reusable_string_builtins.pl +12 -0
- package/test/eyelang/conformance/cases/extension/039_reusable_term_control_builtins.pl +11 -0
- package/test/eyelang/conformance/expected/001_fact_output.pl +1 -0
- package/test/eyelang/conformance/expected/002_rule_recursion.pl +2 -0
- package/test/eyelang/conformance/expected/003_terms_and_readback.pl +13 -0
- package/test/eyelang/conformance/expected/004_conjunction_and_parentheses.pl +1 -0
- package/test/eyelang/conformance/expected/005_list_deconstruction.pl +2 -0
- package/test/eyelang/conformance/expected/006_comma_formula_data.pl +1 -0
- package/test/eyelang/conformance/expected/007_anonymous_variables.pl +1 -0
- package/test/eyelang/conformance/expected/008_graphic_atoms.pl +3 -0
- package/test/eyelang/conformance/expected/009_comments_and_whitespace.pl +2 -0
- package/test/eyelang/conformance/expected/010_variable_scope_and_reuse.pl +2 -0
- package/test/eyelang/conformance/expected/011_predicate_arity.pl +2 -0
- package/test/eyelang/conformance/expected/012_nested_compound_unification.pl +2 -0
- package/test/eyelang/conformance/expected/013_multiple_clauses_order.pl +2 -0
- package/test/eyelang/conformance/expected/014_failure_filters_answers.pl +1 -0
- package/test/eyelang/conformance/expected/015_improper_list_unification.pl +3 -0
- package/test/eyelang/conformance/expected/016_zero_arity_compound.pl +1 -0
- package/test/eyelang/conformance/expected/017_three_step_recursion.pl +3 -0
- package/test/eyelang/conformance/expected/018_quoted_atom_readback.pl +3 -0
- package/test/eyelang/conformance/expected/019_parenthesized_three_conjuncts.pl +1 -0
- package/test/eyelang/conformance/expected/020_nested_list_terms.pl +2 -0
- package/test/eyelang/conformance/expected/021_repeated_variable_head.pl +2 -0
- package/test/eyelang/conformance/expected/022_rule_head_structure.pl +2 -0
- package/test/eyelang/conformance/expected/023_quoted_escapes_readback.pl +2 -0
- package/test/eyelang/conformance/expected/024_numeric_literal_readback.pl +3 -0
- package/test/eyelang/conformance/expected/025_body_parentheses_with_formula_data.pl +1 -0
- package/test/eyelang/conformance/expected/026_underscore_named_variable_reuse.pl +1 -0
- package/test/eyelang/conformance/expected/027_default_derived_output.pl +3 -0
- package/test/eyelang/conformance/expected/028_materialize_focus.pl +1 -0
- package/test/eyelang/conformance/expected/029_arithmetic_and_comparison.pl +9 -0
- package/test/eyelang/conformance/expected/030_strings_and_atoms.pl +2 -0
- package/test/eyelang/conformance/expected/031_lists_aggregation_ordering.pl +9 -0
- package/test/eyelang/conformance/expected/032_holds_parts.pl +3 -0
- package/test/eyelang/conformance/expected/033_negation_once_generators.pl +2 -0
- package/test/eyelang/conformance/expected/034_equality_and_inequality.pl +4 -0
- package/test/eyelang/conformance/expected/035_list_relations.pl +4 -0
- package/test/eyelang/conformance/expected/036_append_splits.pl +3 -0
- package/test/eyelang/conformance/expected/037_matching_and_comparison.pl +5 -0
- package/test/eyelang/conformance/expected/038_memoize_declaration.pl +2 -0
- package/test/eyelang/conformance/expected/039_numeric_functions.pl +7 -0
- package/test/eyelang/conformance/expected/040_between_enumeration.pl +3 -0
- package/test/eyelang/conformance/expected/041_smallest_divisor.pl +1 -0
- package/test/eyelang/conformance/expected/042_negation_filter.pl +2 -0
- package/test/eyelang/conformance/expected/043_once_user_predicate.pl +1 -0
- package/test/eyelang/conformance/expected/044_findall_user_goal.pl +1 -0
- package/test/eyelang/conformance/expected/045_sort_deduplicates_atoms.pl +1 -0
- package/test/eyelang/conformance/expected/046_append_bound_prefix_suffix.pl +2 -0
- package/test/eyelang/conformance/expected/047_nth0_index_generation.pl +1 -0
- package/test/eyelang/conformance/expected/048_set_nth0_edges.pl +2 -0
- package/test/eyelang/conformance/expected/049_select_duplicate_occurrences.pl +2 -0
- package/test/eyelang/conformance/expected/050_not_member_filter.pl +1 -0
- package/test/eyelang/conformance/expected/051_nested_holds_parts.pl +3 -0
- package/test/eyelang/conformance/expected/052_holds_member.pl +2 -0
- package/test/eyelang/conformance/expected/053_materialize_excludes_source_fact.pl +1 -0
- package/test/eyelang/conformance/expected/054_numeric_and_lexical_comparison.pl +3 -0
- package/test/eyelang/conformance/expected/055_string_matching_filters.pl +2 -0
- package/test/eyelang/conformance/expected/056_string_and_atom_concat.pl +1 -0
- package/test/eyelang/conformance/expected/057_countall_empty_and_nonempty.pl +1 -0
- package/test/eyelang/conformance/expected/058_sumall_numeric_template.pl +1 -0
- package/test/eyelang/conformance/expected/059_aggregate_min_template.pl +1 -0
- package/test/eyelang/conformance/expected/060_aggregate_max_compound_key.pl +1 -0
- package/test/eyelang/conformance/expected/061_date_difference.pl +2 -0
- package/test/eyelang/conformance/expected/062_reusable_numeric_builtins.pl +8 -0
- package/test/eyelang/conformance/expected/063_reusable_list_builtins.pl +9 -0
- package/test/eyelang/conformance/expected/064_reusable_string_builtins.pl +10 -0
- package/test/eyelang/conformance/expected/065_reusable_term_control_builtins.pl +6 -0
- package/test/eyelang/conformance/expected/066_numeric_edges.pl +12 -0
- package/test/eyelang/conformance/expected/067_list_edges.pl +8 -0
- package/test/eyelang/conformance/expected/068_list_generation_order.pl +10 -0
- package/test/eyelang/conformance/expected/069_list_summaries_and_sets.pl +7 -0
- package/test/eyelang/conformance/expected/070_matches_named_captures.pl +1 -0
- package/test/eyelang/conformance/expected/071_string_edges.pl +8 -0
- package/test/eyelang/conformance/expected/072_string_conversions.pl +8 -0
- package/test/eyelang/conformance/expected/073_term_introspection_edges.pl +8 -0
- package/test/eyelang/conformance/expected/074_forall_edges.pl +5 -0
- package/test/eyelang/conformance/expected/075_aggregation_edges.pl +7 -0
- package/test/eyelang/conformance/expected/076_composed_reusable_builtins.pl +5 -0
- package/test/eyelang/conformance/expected/077_recursive_path_with_lists.pl +3 -0
- package/test/eyelang/conformance/expected/078_mutual_recursion_with_arithmetic.pl +7 -0
- package/test/eyelang/conformance/expected/079_big_integer_arithmetic.pl +6 -0
- package/test/eyelang/conformance/expected/080_rounding_modes.pl +8 -0
- package/test/eyelang/conformance/expected/081_zero_safe_numeric_functions.pl +7 -0
- package/test/eyelang/conformance/expected/082_comparison_semantics.pl +8 -0
- package/test/eyelang/conformance/expected/083_between_modes_and_empty_ranges.pl +8 -0
- package/test/eyelang/conformance/expected/084_append_and_select_composition.pl +10 -0
- package/test/eyelang/conformance/expected/085_nth_and_update_edges.pl +8 -0
- package/test/eyelang/conformance/expected/086_slicing_pipeline.pl +7 -0
- package/test/eyelang/conformance/expected/087_sort_reverse_length.pl +6 -0
- package/test/eyelang/conformance/expected/088_list_summaries_failures.pl +6 -0
- package/test/eyelang/conformance/expected/089_string_split_join_pipeline.pl +4 -0
- package/test/eyelang/conformance/expected/090_string_substring_replace_edges.pl +7 -0
- package/test/eyelang/conformance/expected/091_string_case_and_trim.pl +5 -0
- package/test/eyelang/conformance/expected/092_scalar_string_conversions.pl +7 -0
- package/test/eyelang/conformance/expected/093_regex_named_captures_context.pl +5 -0
- package/test/eyelang/conformance/expected/094_context_holds_enumeration.pl +8 -0
- package/test/eyelang/conformance/expected/095_term_introspection_roundtrip.pl +6 -0
- package/test/eyelang/conformance/expected/096_functor_scalar_edges.pl +6 -0
- package/test/eyelang/conformance/expected/097_control_negation_once_forall.pl +5 -0
- package/test/eyelang/conformance/expected/098_aggregation_nested_templates.pl +5 -0
- package/test/eyelang/conformance/expected/099_materialize_multiple_arities.pl +4 -0
- package/test/eyelang/conformance/expected/100_reusable_builtin_workflow.pl +6 -0
- package/test/eyelang/conformance/expected/extension/036_reusable_numeric_builtins.out +8 -0
- package/test/eyelang/conformance/expected/extension/037_reusable_list_builtins.out +9 -0
- package/test/eyelang/conformance/expected/extension/038_reusable_string_builtins.out +10 -0
- package/test/eyelang/conformance/expected/extension/039_reusable_term_control_builtins.out +6 -0
- package/test/eyelang/run-conformance.mjs +20 -22
- package/examples/eyelang/collatz-1000.pl +0 -14
- package/examples/eyelang/complex-matrix-stability.pl +0 -45
- package/examples/eyelang/gcd-bezout-identity.pl +0 -48
- package/examples/eyelang/goldbach-1000.pl +0 -185
- package/examples/eyelang/kaprekar.pl +0 -32
- package/examples/eyelang/matrix.pl +0 -296
- package/examples/eyelang/output/collatz-1000.pl +0 -1000
- package/examples/eyelang/output/complex-matrix-stability.pl +0 -5
- package/examples/eyelang/output/gcd-bezout-identity.pl +0 -36
- package/examples/eyelang/output/goldbach-1000.pl +0 -667
- package/examples/eyelang/output/kaprekar.pl +0 -8
- package/examples/eyelang/output/matrix.pl +0 -10
- package/lib/eyelang/builtins/matrix.js +0 -226
- package/lib/eyelang/builtins/number-theory.js +0 -114
- package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +0 -3
- package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +0 -3
- package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +0 -3
- package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +0 -3
- package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +0 -5
- package/test/eyelang/conformance/expected/extension/036_extended_gcd.out +0 -1
- package/test/eyelang/conformance/expected/extension/037_collatz_trajectory.out +0 -1
- package/test/eyelang/conformance/expected/extension/038_kaprekar_steps.out +0 -1
- package/test/eyelang/conformance/expected/extension/039_goldbach_pair.out +0 -2
- 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
|
|
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 {
|
|
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('
|
|
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
|
|
66
|
-
if (
|
|
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 {
|
|
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,
|
|
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 {
|
|
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
|
@@ -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/<
|
|
8
|
-
- `conformance/expected/<
|
|
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
|
|
26
|
+
Run matching conformance cases by passing a filename fragment:
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
node test/eyelang/run-conformance.mjs
|
|
30
|
-
node test/eyelang/run-conformance.mjs
|
|
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
|
-
##
|
|
35
|
+
## Scope
|
|
36
36
|
|
|
37
|
-
|
|
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
|
-
|
|
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
|
|
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,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 @@
|
|
|
1
|
+
value(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ok(X, Y)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
value(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(K, V)
|
|
@@ -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 @@
|
|
|
1
|
+
answer(K, V)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
answer(K, V)
|