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.
- package/docs/language-reference.md +4 -4
- package/examples/animal.eye +1 -1
- package/examples/dairy-energy-balance.eye +3 -3
- package/examples/dijkstra-risk-path.eye +1 -1
- package/examples/ev-range-worlds.eye +10 -10
- package/examples/fft8-numeric.eye +1 -1
- package/examples/field-nitrogen-balance.eye +7 -7
- package/examples/fundamental-theorem-arithmetic.eye +2 -2
- package/examples/gps.eye +8 -8
- package/examples/illegitimate-reasoning.eye +7 -7
- package/examples/map-four-color-search.eye +1 -1
- package/examples/monkey-bananas.eye +4 -4
- package/examples/wolf-goat-cabbage.eye +2 -2
- package/examples/zebra.eye +21 -21
- package/package.json +1 -1
- package/playground.html +6 -3
- package/src/parser.js +8 -8
- package/src/term.js +1 -1
- package/test/conformance/cases/arithmetic/extra_division_by_zero_fails.eye +1 -1
- package/test/conformance/cases/atoms/103_angle_iri_atoms.eye +2 -2
- package/test/conformance/cases/builtins/extra_difference_end_before_start_fails.eye +1 -1
- package/test/conformance/cases/builtins/extra_difference_invalid_date_fails.eye +1 -1
- package/test/conformance/cases/context/extra_holds_ignores_string_parts.eye +1 -1
- package/test/conformance/cases/lists/020_nested_list_terms.eye +1 -1
- package/test/conformance/cases/lists/extra_length_improper_fails.eye +1 -1
- package/test/conformance/cases/rules/022_rule_head_structure.eye +2 -2
- package/test/conformance/cases/terms/extra_arg_atom_fails.eye +1 -1
- package/test/conformance/cases/terms/extra_arg_zero_fails.eye +1 -1
- package/test/conformance/cases/terms/extra_compound_name_arguments_bad_args_fails.eye +1 -1
- package/test/conformance/cases/variables/007_anonymous_variables.eye +1 -1
- package/test/conformance/cases/variables/026_question_underscore_named_variable_reuse.eye +1 -1
- package/test/conformance/cases/variables/anonymous_in_two_goals.eye +2 -2
- package/test/conformance/cases/variables/extra_anonymous_not_reused.eye +1 -1
- package/test/conformance/cases/variables/question_anonymous_not_reused.eye +3 -3
- package/test/conformance/expected-errors/syntax/bad_question_digit_variable.txt +1 -1
- package/test/conformance/expected-errors/variables/question_digit_rejected.txt +1 -1
- 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
|
|
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.
|
package/examples/animal.eye
CHANGED
|
@@ -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,
|
|
24
|
+
cow(?c, ?, ?milk, ?, ?),
|
|
25
25
|
mul(?milk, 5.0, ?r).
|
|
26
26
|
|
|
27
27
|
ration_supply(?c, ?s) :-
|
|
28
|
-
cow(?c,
|
|
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([?
|
|
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,
|
|
28
|
-
speed_factor(?t, 1.00) :- trip_data(?t,
|
|
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,
|
|
31
|
-
temperature_factor(?t, 1.00) :- trip_data(?t,
|
|
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,
|
|
34
|
-
payload_factor(?t, 1.08) :- trip_data(?t,
|
|
35
|
-
payload_factor(?t, 1.00) :- trip_data(?t,
|
|
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,
|
|
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,
|
|
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,
|
|
69
|
+
trip_data(?t, ?, ?, ?, ?, ?battery, ?),
|
|
70
70
|
required_energy(?t, ?w, ?required),
|
|
71
71
|
gt(?required, ?battery).
|
|
72
72
|
|
|
@@ -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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
93
|
-
route_metrics(routeViaKortrijk,
|
|
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,
|
|
98
|
-
route_metrics(routeViaKortrijk,
|
|
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, ?
|
|
85
|
-
reason(?a, ?r) :- fallacy(?a, ?
|
|
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, ?
|
|
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([
|
|
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]).
|
package/examples/zebra.eye
CHANGED
|
@@ -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(
|
|
17
|
-
third(?houses, house(
|
|
18
|
-
adjacent(house(
|
|
19
|
-
next_to(house(ivory,
|
|
20
|
-
member(house(red, english,
|
|
21
|
-
member(house(green,
|
|
22
|
-
member(house(yellow,
|
|
23
|
-
member(house(
|
|
24
|
-
member(house(
|
|
25
|
-
member(house(
|
|
26
|
-
adjacent(house(
|
|
27
|
-
adjacent(house(
|
|
28
|
-
member(house(
|
|
29
|
-
member(house(
|
|
30
|
-
member(house(
|
|
31
|
-
member(house(
|
|
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|?
|
|
35
|
-
third([
|
|
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, [
|
|
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
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 === '?'
|
|
873
|
-
let j = i +
|
|
874
|
-
|
|
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
|
-
|
|
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 === '?
|
|
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('
|
|
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(
|
|
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, ?
|
|
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),
|
|
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(
|
|
2
|
+
answer(compound_name_arguments_bad_args_fails) :- compound_name_arguments(?, pair, not_a_list).
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
materialize(answer, 1).
|
|
2
|
-
answer(anonymous_not_reused) :- eq(pair(
|
|
2
|
+
answer(anonymous_not_reused) :- eq(pair(?, ?), pair(a, b)).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
% Each
|
|
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(
|
|
5
|
+
answer(left(?x)) :- pair(?x, ?).
|
|
6
|
+
answer(right(?y)) :- pair(?, ?y).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
parse line 1:
|
|
1
|
+
parse line 1: bad character "?"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
parse line 1:
|
|
1
|
+
parse line 1: bad character "?"
|
package/test/run-regression.mjs
CHANGED
|
@@ -639,9 +639,9 @@ function whiteBoxCases() {
|
|
|
639
639
|
},
|
|
640
640
|
},
|
|
641
641
|
{
|
|
642
|
-
name: 'parser treats question
|
|
642
|
+
name: 'parser treats bare question mark as anonymous',
|
|
643
643
|
run: () => {
|
|
644
|
-
const clauses = parseProgramText('p(
|
|
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');
|