eyelang 1.7.10 → 1.7.11

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 (37) hide show
  1. package/docs/language-reference.md +4 -4
  2. package/examples/animal.eye +1 -1
  3. package/examples/dairy-energy-balance.eye +3 -3
  4. package/examples/dijkstra-risk-path.eye +1 -1
  5. package/examples/ev-range-worlds.eye +10 -10
  6. package/examples/fft8-numeric.eye +1 -1
  7. package/examples/field-nitrogen-balance.eye +7 -7
  8. package/examples/fundamental-theorem-arithmetic.eye +2 -2
  9. package/examples/gps.eye +8 -8
  10. package/examples/illegitimate-reasoning.eye +7 -7
  11. package/examples/map-four-color-search.eye +1 -1
  12. package/examples/monkey-bananas.eye +4 -4
  13. package/examples/wolf-goat-cabbage.eye +2 -2
  14. package/examples/zebra.eye +21 -21
  15. package/package.json +1 -1
  16. package/playground.html +6 -3
  17. package/src/parser.js +8 -8
  18. package/src/term.js +1 -1
  19. package/test/conformance/cases/arithmetic/extra_division_by_zero_fails.eye +1 -1
  20. package/test/conformance/cases/atoms/103_angle_iri_atoms.eye +2 -2
  21. package/test/conformance/cases/builtins/extra_difference_end_before_start_fails.eye +1 -1
  22. package/test/conformance/cases/builtins/extra_difference_invalid_date_fails.eye +1 -1
  23. package/test/conformance/cases/context/extra_holds_ignores_string_parts.eye +1 -1
  24. package/test/conformance/cases/lists/020_nested_list_terms.eye +1 -1
  25. package/test/conformance/cases/lists/extra_length_improper_fails.eye +1 -1
  26. package/test/conformance/cases/rules/022_rule_head_structure.eye +2 -2
  27. package/test/conformance/cases/terms/extra_arg_atom_fails.eye +1 -1
  28. package/test/conformance/cases/terms/extra_arg_zero_fails.eye +1 -1
  29. package/test/conformance/cases/terms/extra_compound_name_arguments_bad_args_fails.eye +1 -1
  30. package/test/conformance/cases/variables/007_anonymous_variables.eye +1 -1
  31. package/test/conformance/cases/variables/026_question_underscore_named_variable_reuse.eye +1 -1
  32. package/test/conformance/cases/variables/anonymous_in_two_goals.eye +2 -2
  33. package/test/conformance/cases/variables/extra_anonymous_not_reused.eye +1 -1
  34. package/test/conformance/cases/variables/question_anonymous_not_reused.eye +3 -3
  35. package/test/conformance/expected-errors/syntax/bad_question_digit_variable.txt +1 -1
  36. package/test/conformance/expected-errors/variables/question_digit_rejected.txt +1 -1
  37. package/test/run-regression.mjs +2 -2
@@ -123,7 +123,7 @@ A colon outside `:-` is not part of the language. Namespace-like names SHOULD be
123
123
 
124
124
  ### 3.4 Variables
125
125
 
126
- A variable starts with `?` followed by an ASCII letter or underscore and then zero or more ASCII letters, digits, or underscores. This N3/SPARQL-style spelling is the only source-level variable spelling in eyelang.
126
+ A variable is either the bare anonymous variable `?`, or starts with `?` followed by an ASCII letter or underscore and then zero or more ASCII letters, digits, or underscores. This N3/SPARQL-style spelling is the only source-level variable spelling in eyelang.
127
127
 
128
128
  Examples:
129
129
 
@@ -131,10 +131,10 @@ Examples:
131
131
  ?x
132
132
  ?person
133
133
  ?_thing
134
- ?_
134
+ ?
135
135
  ```
136
136
 
137
- Each `?_` anonymous variable occurrence is fresh. A bare `_` is not a variable in eyelang source.
137
+ Each bare `?` anonymous variable occurrence is fresh. A bare `_` is not a variable in eyelang source.
138
138
 
139
139
  ### 3.5 Atom constants
140
140
 
@@ -171,7 +171,7 @@ A quoted atom constant is enclosed in single quotes. A single quote inside a quo
171
171
  A graphic atom constant is one or more graphic characters from this set:
172
172
 
173
173
  ```text
174
- #$&*+-/<=>?@^~\
174
+ #$&*+-/<=>@^~\
175
175
  ```
176
176
 
177
177
  Angle-bracket IRI syntax is recognized only for absolute IRI-like contents. Graphic atoms such as `<=>`, `<`, and `>=` remain graphic atoms.
@@ -18,4 +18,4 @@ animal(?x) :- human(?x).
18
18
  type(joe, human) :- human(joe).
19
19
  type(joe, animal) :- animal(joe).
20
20
  subclassOf(human, animal) :- animal(human).
21
- succeeds(animalExample, true) :- animal(?_).
21
+ succeeds(animalExample, true) :- animal(?).
@@ -17,15 +17,15 @@ cow(grazing, 540, 18, 5.8, 21).
17
17
 
18
18
  % Maintenance scales with body weight; milk requirement scales with daily milk.
19
19
  maintenance(?c, ?m) :-
20
- cow(?c, ?weight, ?_, ?_, ?_),
20
+ cow(?c, ?weight, ?, ?, ?),
21
21
  mul(?weight, 0.08, ?m).
22
22
 
23
23
  milk_requirement(?c, ?r) :-
24
- cow(?c, ?_, ?milk, ?_, ?_),
24
+ cow(?c, ?, ?milk, ?, ?),
25
25
  mul(?milk, 5.0, ?r).
26
26
 
27
27
  ration_supply(?c, ?s) :-
28
- cow(?c, ?_, ?_, ?density, ?intake),
28
+ cow(?c, ?, ?, ?density, ?intake),
29
29
  mul(?density, ?intake, ?s).
30
30
 
31
31
  total_requirement(?c, ?r) :-
@@ -41,7 +41,7 @@ candidate(pathRelay, [depotA, relay, labD]).
41
41
  candidate(pathDirectC, [depotA, labD]).
42
42
  candidate(pathViaC, [depotA, depotC, depotB, labD]).
43
43
 
44
- route_cost([?_], 0.0, 0.0, 0).
44
+ route_cost([?], 0.0, 0.0, 0).
45
45
  route_cost([?from, ?to|?rest], ?raw, ?risk, ?edges) :-
46
46
  route_segment(?from, ?to, ?stepraw, ?steprisk),
47
47
  route_cost([?to|?rest], ?restraw, ?restrisk, ?restedges),
@@ -24,18 +24,18 @@ trip_data(cold_commute, 120, 90, -8, 100, 35, 0.19).
24
24
 
25
25
  % Each world adds a different combination of speed, temperature, payload, and
26
26
  % reserve factors before comparing required energy with usable battery.
27
- speed_factor(?t, 1.20) :- trip_data(?t, ?_, ?s, ?_, ?_, ?_, ?_), gt(?s, 100).
28
- speed_factor(?t, 1.00) :- trip_data(?t, ?_, ?s, ?_, ?_, ?_, ?_), le(?s, 100).
27
+ speed_factor(?t, 1.20) :- trip_data(?t, ?, ?s, ?, ?, ?, ?), gt(?s, 100).
28
+ speed_factor(?t, 1.00) :- trip_data(?t, ?, ?s, ?, ?, ?, ?), le(?s, 100).
29
29
 
30
- temperature_factor(?t, 1.15) :- trip_data(?t, ?_, ?_, ?temp, ?_, ?_, ?_), lt(?temp, 0).
31
- temperature_factor(?t, 1.00) :- trip_data(?t, ?_, ?_, ?temp, ?_, ?_, ?_), ge(?temp, 0).
30
+ temperature_factor(?t, 1.15) :- trip_data(?t, ?, ?, ?temp, ?, ?, ?), lt(?temp, 0).
31
+ temperature_factor(?t, 1.00) :- trip_data(?t, ?, ?, ?temp, ?, ?, ?), ge(?temp, 0).
32
32
 
33
- payload_factor(?t, 1.15) :- trip_data(?t, ?_, ?_, ?_, ?p, ?_, ?_), gt(?p, 500).
34
- payload_factor(?t, 1.08) :- trip_data(?t, ?_, ?_, ?_, ?p, ?_, ?_), gt(?p, 250), le(?p, 500).
35
- payload_factor(?t, 1.00) :- trip_data(?t, ?_, ?_, ?_, ?p, ?_, ?_), le(?p, 250).
33
+ payload_factor(?t, 1.15) :- trip_data(?t, ?, ?, ?, ?p, ?, ?), gt(?p, 500).
34
+ payload_factor(?t, 1.08) :- trip_data(?t, ?, ?, ?, ?p, ?, ?), gt(?p, 250), le(?p, 500).
35
+ payload_factor(?t, 1.00) :- trip_data(?t, ?, ?, ?, ?p, ?, ?), le(?p, 250).
36
36
 
37
37
  base_energy(?t, ?e) :-
38
- trip_data(?t, ?d, ?_, ?_, ?_, ?_, ?b),
38
+ trip_data(?t, ?d, ?, ?, ?, ?, ?b),
39
39
  mul(?d, ?b, ?e).
40
40
 
41
41
  required_energy(?t, w1, ?e) :-
@@ -61,12 +61,12 @@ required_energy(?t, w3, ?e) :-
61
61
 
62
62
  % safe_in_world/2 compares required trip energy with usable battery capacity.
63
63
  safe_in_world(?t, ?w) :-
64
- trip_data(?t, ?_, ?_, ?_, ?_, ?battery, ?_),
64
+ trip_data(?t, ?, ?, ?, ?, ?battery, ?),
65
65
  required_energy(?t, ?w, ?required),
66
66
  le(?required, ?battery).
67
67
 
68
68
  risky_in_world(?t, ?w) :-
69
- trip_data(?t, ?_, ?_, ?_, ?_, ?battery, ?_),
69
+ trip_data(?t, ?, ?, ?, ?, ?battery, ?),
70
70
  required_energy(?t, ?w, ?required),
71
71
  gt(?required, ?battery).
72
72
 
@@ -82,4 +82,4 @@ fft(?wave, ?spectrum) :-
82
82
 
83
83
  dcComponent(?wave, ?dc) :-
84
84
  wave(?wave, ?samples),
85
- fft8(?samples, [?dc|?_]).
85
+ fft8(?samples, [?dc|?]).
@@ -21,41 +21,41 @@ field(clay_surplus, 70, 90, 0.08, 120).
21
21
  % total_n/2 and available_n/2 build the nutrient budget; surplus/deficit and
22
22
  % leaching rules then explain the resulting field status.
23
23
  total_n(?f, ?total) :-
24
- field(?f, ?soil, ?fert, ?_, ?_),
24
+ field(?f, ?soil, ?fert, ?, ?),
25
25
  add(?soil, ?fert, ?total).
26
26
 
27
27
  available_n(?f, ?avail) :-
28
28
  total_n(?f, ?total),
29
- field(?f, ?_, ?_, ?loss, ?_),
29
+ field(?f, ?, ?, ?loss, ?),
30
30
  sub(1.0, ?loss, ?retained),
31
31
  mul(?total, ?retained, ?avail).
32
32
 
33
33
  % surplus_n/2 and deficit_n/2 split the signed balance into reportable quantities.
34
34
  surplus_n(?f, ?surplus) :-
35
35
  available_n(?f, ?avail),
36
- field(?f, ?_, ?_, ?_, ?demand),
36
+ field(?f, ?, ?, ?, ?demand),
37
37
  gt(?avail, ?demand),
38
38
  sub(?avail, ?demand, ?surplus).
39
39
 
40
40
  surplus_n(?f, 0.0) :-
41
41
  available_n(?f, ?avail),
42
- field(?f, ?_, ?_, ?_, ?demand),
42
+ field(?f, ?, ?, ?, ?demand),
43
43
  le(?avail, ?demand).
44
44
 
45
45
  deficit_n(?f, ?deficit) :-
46
46
  available_n(?f, ?avail),
47
- field(?f, ?_, ?_, ?_, ?demand),
47
+ field(?f, ?, ?, ?, ?demand),
48
48
  lt(?avail, ?demand),
49
49
  sub(?demand, ?avail, ?deficit).
50
50
 
51
51
  deficit_n(?f, 0.0) :-
52
52
  available_n(?f, ?avail),
53
- field(?f, ?_, ?_, ?_, ?demand),
53
+ field(?f, ?, ?, ?, ?demand),
54
54
  ge(?avail, ?demand).
55
55
 
56
56
  leaching_index(?f, ?index) :-
57
57
  surplus_n(?f, ?surplus),
58
- field(?f, ?_, ?_, ?loss, ?_),
58
+ field(?f, ?, ?, ?loss, ?),
59
59
  mul(?surplus, ?loss, ?index).
60
60
 
61
61
  status(?f, under_supplied) :- deficit_n(?f, ?d), gt(?d, 10.0).
@@ -112,8 +112,8 @@ distinctPrimeCount(case, 4) :-
112
112
 
113
113
  smallestPrimeFactor(case, 3) :-
114
114
  case(fta, ?n),
115
- factor_smallest(?n, [3|?_]).
115
+ factor_smallest(?n, [3|?]).
116
116
 
117
117
  largestPrimeFactor(case, 3881) :-
118
118
  case(fta, ?n),
119
- factor_largest(?n, [3881|?_]).
119
+ factor_largest(?n, [3881|?]).
package/examples/gps.eye CHANGED
@@ -78,24 +78,24 @@ outcome(routeDirect, "Take the direct route via Brugge.").
78
78
 
79
79
  % Verification checks, analogous to the false-producing guards in gps.n3.
80
80
  check(c1, true) :-
81
- traveller_path(i1, [drive_gent_brugge, drive_brugge_oostende], ?_, ?_, ?_, ?_).
81
+ traveller_path(i1, [drive_gent_brugge, drive_brugge_oostende], ?, ?, ?, ?).
82
82
 
83
83
  check(c2, true) :-
84
- traveller_path(i1, [drive_gent_kortrijk, drive_kortrijk_brugge, drive_brugge_oostende], ?_, ?_, ?_, ?_).
84
+ traveller_path(i1, [drive_gent_kortrijk, drive_kortrijk_brugge, drive_brugge_oostende], ?, ?, ?, ?).
85
85
 
86
86
  check(c3, true) :-
87
- route_metrics(routeDirect, ?d1, ?_, ?_, ?_),
88
- route_metrics(routeViaKortrijk, ?d2, ?_, ?_, ?_),
87
+ route_metrics(routeDirect, ?d1, ?, ?, ?),
88
+ route_metrics(routeViaKortrijk, ?d2, ?, ?, ?),
89
89
  lt(?d1, ?d2).
90
90
 
91
91
  check(c4, true) :-
92
- route_metrics(routeDirect, ?_, ?c1, ?_, ?_),
93
- route_metrics(routeViaKortrijk, ?_, ?c2, ?_, ?_),
92
+ route_metrics(routeDirect, ?, ?c1, ?, ?),
93
+ route_metrics(routeViaKortrijk, ?, ?c2, ?, ?),
94
94
  lt(?c1, ?c2).
95
95
 
96
96
  check(c5, true) :-
97
- route_metrics(routeDirect, ?_, ?_, ?b1, ?f1),
98
- route_metrics(routeViaKortrijk, ?_, ?_, ?b2, ?f2),
97
+ route_metrics(routeDirect, ?, ?, ?b1, ?f1),
98
+ route_metrics(routeViaKortrijk, ?, ?, ?b2, ?f2),
99
99
  gt(?b1, ?b2),
100
100
  gt(?f1, ?f2).
101
101
 
@@ -67,22 +67,22 @@ fallacy(?a, hasty_generalization) :-
67
67
  sample_size(?a, ?n),
68
68
  required_sample_size(?a, ?min),
69
69
  lt(?n, ?min),
70
- concludes(?a, all(?_, ?_)).
70
+ concludes(?a, all(?, ?)).
71
71
 
72
72
  fallacy(?a, false_dilemma) :-
73
73
  argument(?a),
74
- presented_alternatives(?a, ?_),
75
- omitted_alternative(?a, ?_),
76
- concludes(?a, ?_).
74
+ presented_alternatives(?a, ?),
75
+ omitted_alternative(?a, ?),
76
+ concludes(?a, ?).
77
77
 
78
78
  reason(arg_affirming_consequent, "observing the consequent does not prove the antecedent").
79
79
  reason(arg_denying_antecedent, "denying the antecedent does not disprove the consequent").
80
80
  reason(arg_hasty_generalization, "sample size is below the threshold for a universal conclusion").
81
81
  reason(arg_false_dilemma, "a relevant alternative is omitted").
82
82
 
83
- type(?a, illegitimate_reasoning) :- fallacy(?a, ?_).
84
- conclusion(?a, ?c) :- fallacy(?a, ?_), concludes(?a, ?c).
85
- reason(?a, ?r) :- fallacy(?a, ?_), reason(?a, ?r).
83
+ type(?a, illegitimate_reasoning) :- fallacy(?a, ?).
84
+ conclusion(?a, ?c) :- fallacy(?a, ?), concludes(?a, ?c).
85
+ reason(?a, ?r) :- fallacy(?a, ?), reason(?a, ?r).
86
86
  sampleSize(?a, ?n) :- fallacy(?a, hasty_generalization), sample_size(?a, ?n).
87
87
  requiredSampleSize(?a, ?min) :- fallacy(?a, hasty_generalization), required_sample_size(?a, ?min).
88
88
  omittedAlternative(?a, ?alt) :- fallacy(?a, false_dilemma), omitted_alternative(?a, ?alt).
@@ -50,7 +50,7 @@ valid_color(?place, ?color, ?assigned) :-
50
50
  not((member([?neighbor, ?color], ?assigned), member(?neighbor, ?neighbors))).
51
51
 
52
52
  place_pairs([], []).
53
- place_pairs([?place|?rest], [[?place, ?_]|?pairs]) :-
53
+ place_pairs([?place|?rest], [[?place, ?]|?pairs]) :-
54
54
  place_pairs(?rest, ?pairs).
55
55
 
56
56
  color_places([]).
@@ -19,9 +19,9 @@ plan(?moves) :-
19
19
  goal_state(?g),
20
20
  reachable(?i, ?moves, ?g).
21
21
 
22
- candidate_plan([?_, ?_, ?_]).
23
- candidate_plan([?_, ?_, ?_, ?_]).
24
- candidate_plan([?_, ?_, ?_, ?_, ?_]).
22
+ candidate_plan([?, ?, ?]).
23
+ candidate_plan([?, ?, ?, ?]).
24
+ candidate_plan([?, ?, ?, ?, ?]).
25
25
 
26
26
  reachable(?s, [], ?s).
27
27
  reachable(?s1, [?m|?l], ?s3) :-
@@ -29,7 +29,7 @@ reachable(?s1, [?m|?l], ?s3) :-
29
29
  reachable(?s2, ?l, ?s3).
30
30
 
31
31
  initial_state([loc1, loc2, loc3, n, n]).
32
- goal_state([?_, ?_, ?_, ?_, y]).
32
+ goal_state([?, ?, ?, ?, y]).
33
33
 
34
34
  legal_move([?b, ?m, ?m, n, ?h], climb_on, [?b, ?m, ?m, y, ?h]).
35
35
  legal_move([?b, ?m, ?m, y, ?h], climb_off, [?b, ?m, ?m, n, ?h]).
@@ -46,8 +46,8 @@ safe([?man, ?wolf, ?goat, ?cabbage]) :-
46
46
  one_eq(?man, ?goat, ?wolf),
47
47
  one_eq(?man, ?goat, ?cabbage).
48
48
 
49
- one_eq(?x, ?x, ?_).
50
- one_eq(?x, ?_, ?x).
49
+ one_eq(?x, ?x, ?).
50
+ one_eq(?x, ?, ?x).
51
51
 
52
52
  solution(puzzle, ?moves) :-
53
53
  solution(?moves).
@@ -12,33 +12,33 @@ materialize(solved, 2).
12
12
 
13
13
  % The single zebra/2 rule is a finite constraint model over the five house slots.
14
14
  zebra(?waterdrinker, ?zebraowner) :-
15
- eq(?houses, [?_, ?_, ?_, ?_, ?_]),
16
- first(?houses, house(?_, norwegian, ?_, ?_, ?_)),
17
- third(?houses, house(?_, ?_, ?_, milk, ?_)),
18
- adjacent(house(?_, norwegian, ?_, ?_, ?_), house(blue, ?_, ?_, ?_, ?_), ?houses),
19
- next_to(house(ivory, ?_, ?_, ?_, ?_), house(green, ?_, ?_, ?_, ?_), ?houses),
20
- member(house(red, english, ?_, ?_, ?_), ?houses),
21
- member(house(green, ?_, ?_, coffee, ?_), ?houses),
22
- member(house(yellow, ?_, ?_, ?_, kools), ?houses),
23
- member(house(?_, spanish, dog, ?_, ?_), ?houses),
24
- member(house(?_, ukrainian, ?_, tea, ?_), ?houses),
25
- member(house(?_, ?_, snail, ?_, old_gold), ?houses),
26
- adjacent(house(?_, ?_, ?_, ?_, chesterfields), house(?_, ?_, fox, ?_, ?_), ?houses),
27
- adjacent(house(?_, ?_, ?_, ?_, kools), house(?_, ?_, horse, ?_, ?_), ?houses),
28
- member(house(?_, ?_, ?_, orange_juice, lucky_strike), ?houses),
29
- member(house(?_, japanese, ?_, ?_, parliaments), ?houses),
30
- member(house(?_, ?waterdrinker, ?_, water, ?_), ?houses),
31
- member(house(?_, ?zebraowner, zebra, ?_, ?_), ?houses).
15
+ eq(?houses, [?, ?, ?, ?, ?]),
16
+ first(?houses, house(?, norwegian, ?, ?, ?)),
17
+ third(?houses, house(?, ?, ?, milk, ?)),
18
+ adjacent(house(?, norwegian, ?, ?, ?), house(blue, ?, ?, ?, ?), ?houses),
19
+ next_to(house(ivory, ?, ?, ?, ?), house(green, ?, ?, ?, ?), ?houses),
20
+ member(house(red, english, ?, ?, ?), ?houses),
21
+ member(house(green, ?, ?, coffee, ?), ?houses),
22
+ member(house(yellow, ?, ?, ?, kools), ?houses),
23
+ member(house(?, spanish, dog, ?, ?), ?houses),
24
+ member(house(?, ukrainian, ?, tea, ?), ?houses),
25
+ member(house(?, ?, snail, ?, old_gold), ?houses),
26
+ adjacent(house(?, ?, ?, ?, chesterfields), house(?, ?, fox, ?, ?), ?houses),
27
+ adjacent(house(?, ?, ?, ?, kools), house(?, ?, horse, ?, ?), ?houses),
28
+ member(house(?, ?, ?, orange_juice, lucky_strike), ?houses),
29
+ member(house(?, japanese, ?, ?, parliaments), ?houses),
30
+ member(house(?, ?waterdrinker, ?, water, ?), ?houses),
31
+ member(house(?, ?zebraowner, zebra, ?, ?), ?houses).
32
32
 
33
33
  % Positional and neighborhood helpers keep the clue encoding readable.
34
- first([?x|?_], ?x).
35
- third([?_, ?_, ?x|?_], ?x).
34
+ first([?x|?], ?x).
35
+ third([?, ?, ?x|?], ?x).
36
36
 
37
37
  adjacent(?a, ?b, ?list) :- next_to(?a, ?b, ?list).
38
38
  adjacent(?a, ?b, ?list) :- next_to(?b, ?a, ?list).
39
39
 
40
- next_to(?x, ?y, [?x, ?y|?_]).
41
- next_to(?x, ?y, [?_|?zs]) :- next_to(?x, ?y, ?zs).
40
+ next_to(?x, ?y, [?x, ?y|?]).
41
+ next_to(?x, ?y, [?|?zs]) :- next_to(?x, ?y, ?zs).
42
42
 
43
43
  waterDrinker(zebraPuzzle, ?waterdrinker) :- zebra(?waterdrinker, ?_zebraowner).
44
44
  zebraOwner(zebraPuzzle, ?zebraowner) :- zebra(?_waterdrinker, ?zebraowner).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyelang",
3
- "version": "1.7.10",
3
+ "version": "1.7.11",
4
4
  "description": "A small Prolog-like logic programming language for rules, goals, answers, and proofs.",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/playground.html CHANGED
@@ -869,9 +869,12 @@ answer(ok) :- eq(ok, ok).
869
869
  while (j < text.length && /[\d.]/.test(text[j])) j++;
870
870
  out += span('number', text.slice(i, j));
871
871
  i = j;
872
- } else if (ch === '?' && /[A-Za-z_]/.test(text[i + 1] || '')) {
873
- let j = i + 2;
874
- while (j < text.length && /[A-Za-z0-9_]/.test(text[j])) j++;
872
+ } else if (ch === '?') {
873
+ let j = i + 1;
874
+ if (/[A-Za-z_]/.test(text[j] || '')) {
875
+ j++;
876
+ while (j < text.length && /[A-Za-z0-9_]/.test(text[j])) j++;
877
+ }
875
878
  out += span('variable', text.slice(i, j));
876
879
  i = j;
877
880
  } else if (/[a-z]/.test(ch)) {
package/src/parser.js CHANGED
@@ -43,14 +43,14 @@ function isNameContinueCode(code) {
43
43
  function isQuestionVariableStart(source, pos) {
44
44
  if (source[pos] !== '?') return false;
45
45
  const code = (source[pos + 1] ?? '').charCodeAt(0);
46
- return code === 95 || isAsciiLetterCode(code);
46
+ return !source[pos + 1] || code === 95 || isAsciiLetterCode(code) || !isNameContinueCode(code);
47
47
  }
48
48
 
49
49
  function isPlainAtomStartCode(code) {
50
50
  return code >= 97 && code <= 122;
51
51
  }
52
52
 
53
- const graphicAtomChars = '#$&*+-/<=>?@^~\\';
53
+ const graphicAtomChars = '#$&*+-/<=>@^~\\';
54
54
 
55
55
  function isGraphicAtomCode(code) {
56
56
  return graphicAtomChars.includes(String.fromCharCode(code));
@@ -172,10 +172,9 @@ class Parser {
172
172
  if (isQuestionVariableStart(this.source, this.pos)) {
173
173
  const start = this.pos;
174
174
  this.take(); // ?
175
- this.take();
175
+ if (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
176
176
  while (isNameContinueCode(this.peek().charCodeAt(0))) this.take();
177
- let text = this.source.slice(start, this.pos);
178
- if (text === '?_') text = `__anon${this.anonymous++}`;
177
+ const text = this.source.slice(start, this.pos);
179
178
  return { type: TOK.VAR, text, line };
180
179
  }
181
180
 
@@ -259,6 +258,7 @@ class Parser {
259
258
  if (this.token.type === TOK.VAR) {
260
259
  const name = this.token.text;
261
260
  this.advance();
261
+ if (name === '?') return variable(`__anon${this.anonymous++}`);
262
262
  return variable(name);
263
263
  }
264
264
  if (this.token.type === TOK.STRING) {
@@ -346,9 +346,9 @@ function isSimpleName(text) {
346
346
  const SIMPLE_NUMBER = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
347
347
  const FAST_BINARY_FACT = /^([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\.$/;
348
348
  const FAST_BINARY_RULE = /^([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\s*:-\s*([a-z][A-Za-z0-9_]*)\(\s*([^,\s()[\]|"']+)\s*,\s*([^,\s()[\]|"']+)\s*\)\.$/;
349
- const SIMPLE_VARIABLE = /^\?[A-Za-z_][A-Za-z0-9_]*$/;
349
+ const SIMPLE_VARIABLE = /^\?(?:[A-Za-z_][A-Za-z0-9_]*)?$/;
350
350
  const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*$/;
351
- const GRAPHIC_ATOM = /^[#$&*+\-\/<=>?@^~\\]+$/;
351
+ const GRAPHIC_ATOM = /^[#$&*+\-\/<=>@^~\\]+$/;
352
352
 
353
353
  function parseClausesFastNoSource(source) {
354
354
  source = String(source ?? '');
@@ -370,7 +370,7 @@ function parseClausesFastNoSource(source) {
370
370
  const scalarOrVariableFast = (text) => {
371
371
  if (!text || !isFastScalarToken(text)) throw new Error('bad simple term');
372
372
  const first = text.charCodeAt(0);
373
- if (text === '?_') return variable(`__anon${anonymous++}`);
373
+ if (text === '?') return variable(`__anon${anonymous++}`);
374
374
  if (SIMPLE_VARIABLE.test(text)) {
375
375
  const existing = variableCache.get(text);
376
376
  if (existing) return existing;
package/src/term.js CHANGED
@@ -136,7 +136,7 @@ export function termIsGround(term, env = new Env()) {
136
136
  return resolved.args.every((arg) => termIsGround(arg, env));
137
137
  }
138
138
 
139
- const graphicAtomChars = new Set('#$&*+-/<=>?@^~\\'.split(''));
139
+ const graphicAtomChars = new Set('#$&*+-/<=>@^~\\'.split(''));
140
140
 
141
141
  function isAbsoluteIriText(text) {
142
142
  return /^[A-Za-z][A-Za-z0-9+.-]*:[^\s<>"'{}|\\^`]*$/.test(text);
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(division_by_zero_fails) :- div(1, 0, ?_).
2
+ answer(division_by_zero_fails) :- div(1, 0, ?).
@@ -3,5 +3,5 @@ materialize(iri_subject, 1).
3
3
  materialize(iri_object, 1).
4
4
  triple(<https://example.org/alice>, <https://schema.org/name>, "Alice").
5
5
  triple(<urn:example:bob>, <https://schema.org/knows>, <https://example.org/alice>).
6
- iri_subject(?iri) :- triple(?iri, ?_, ?_).
7
- iri_object(?iri) :- triple(?_, <https://schema.org/knows>, ?iri).
6
+ iri_subject(?iri) :- triple(?iri, ?, ?).
7
+ iri_object(?iri) :- triple(?, <https://schema.org/knows>, ?iri).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(difference_end_before_start_fails) :- difference("2024-01-01", "2024-01-02", ?_).
2
+ answer(difference_end_before_start_fails) :- difference("2024-01-01", "2024-01-02", ?).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(difference_invalid_date_fails) :- difference("2024-02-30", "2024-02-01", ?_).
2
+ answer(difference_invalid_date_fails) :- difference("2024-02-30", "2024-02-01", ?).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(holds_ignores_string_parts) :- holds("not a term", ?_, ?_).
2
+ answer(holds_ignores_string_parts) :- holds("not a term", ?, ?).
@@ -1,5 +1,5 @@
1
1
  % Reference 5.3, 5.4: lists may contain structured terms that unify positionally.
2
2
  node([pair(a, b), pair(c, d)]).
3
- answer(first_key, ?x) :- node([pair(?x, ?_), pair(c, d)]).
3
+ answer(first_key, ?x) :- node([pair(?x, ?), pair(c, d)]).
4
4
  answer(second_key, ?x) :- node([pair(a, b), pair(?x, d)]).
5
5
  materialize(answer, 2).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(length_improper_fails) :- length([a | b], ?_).
2
+ answer(length_improper_fails) :- length([a | b], ?).
@@ -1,5 +1,5 @@
1
1
  % Reference 5.3, 6, 7.1: structured rule heads destructure matching goals.
2
2
  unpack(pair(?x, ?y), ?x, ?y).
3
- answer(first, ?a) :- unpack(pair(alpha, beta), ?a, ?_).
4
- answer(second, ?b) :- unpack(pair(alpha, beta), ?_, ?b).
3
+ answer(first, ?a) :- unpack(pair(alpha, beta), ?a, ?).
4
+ answer(second, ?b) :- unpack(pair(alpha, beta), ?, ?b).
5
5
  materialize(answer, 2).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(arg_atom_fails) :- arg(1, atom, ?_).
2
+ answer(arg_atom_fails) :- arg(1, atom, ?).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(arg_zero_fails) :- arg(0, pair(a, b), ?_).
2
+ answer(arg_zero_fails) :- arg(0, pair(a, b), ?).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(compound_name_arguments_bad_args_fails) :- compound_name_arguments(?_, pair, not_a_list).
2
+ answer(compound_name_arguments_bad_args_fails) :- compound_name_arguments(?, pair, not_a_list).
@@ -1,5 +1,5 @@
1
1
  % Reference 3.4, 5.1, 7.1: each anonymous variable occurrence is fresh.
2
2
  pair(a, one).
3
3
  pair(b, two).
4
- answer(fresh, yes) :- pair(a, ?_), pair(b, ?_).
4
+ answer(fresh, yes) :- pair(a, ?), pair(b, ?).
5
5
  materialize(answer, 2).
@@ -1,4 +1,4 @@
1
- % Reference 3.4, 5.1: `?_name` variables are named variables; only exactly `?_` is anonymous.
1
+ % Reference 3.4, 5.1: `?_name` variables are named variables; only exactly `?` is anonymous.
2
2
  pair(a, a).
3
3
  pair(a, b).
4
4
  answer(shared, ?_value) :- pair(?_value, ?_value).
@@ -1,4 +1,4 @@
1
- % Each ?_ occurrence is fresh, so these two goals do not have to agree.
1
+ % Each ? occurrence is fresh, so these two goals do not have to agree.
2
2
  materialize(answer, 1).
3
3
  pair(a, b).
4
- answer(ok) :- pair(?_, ?_).
4
+ answer(ok) :- pair(?, ?).
@@ -1,2 +1,2 @@
1
1
  materialize(answer, 1).
2
- answer(anonymous_not_reused) :- eq(pair(?_, ?_), pair(a, b)).
2
+ answer(anonymous_not_reused) :- eq(pair(?, ?), pair(a, b)).
@@ -1,6 +1,6 @@
1
- % Each `?_` occurrence is anonymous and independent.
1
+ % Each `?` occurrence is anonymous and independent.
2
2
  materialize(answer, 1).
3
3
  pair(a, b).
4
4
  pair(c, d).
5
- answer(left(?x)) :- pair(?x, ?_).
6
- answer(right(?y)) :- pair(?_, ?y).
5
+ answer(left(?x)) :- pair(?x, ?).
6
+ answer(right(?y)) :- pair(?, ?y).
@@ -1 +1 @@
1
- parse line 1: expected ), got 1
1
+ parse line 1: bad character "?"
@@ -1 +1 @@
1
- parse line 1: expected ), got 1
1
+ parse line 1: bad character "?"
@@ -639,9 +639,9 @@ function whiteBoxCases() {
639
639
  },
640
640
  },
641
641
  {
642
- name: 'parser treats question-mark underscore as anonymous',
642
+ name: 'parser treats bare question mark as anonymous',
643
643
  run: () => {
644
- const clauses = parseProgramText('p(?_, ?_).\n');
644
+ const clauses = parseProgramText('p(?, ?).\n');
645
645
  const left = clauses[0].head.args[0].name;
646
646
  const right = clauses[0].head.args[1].name;
647
647
  assertEqual(left.startsWith('__anon'), true, 'left anonymous');