eyeprolog 1.1.0 → 1.1.2
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 +12 -2
- package/conformance-report.md +2 -2
- package/examples/book/README.md +2 -1
- package/examples/book/chapter-22/04-sentence.pl +10 -14
- package/examples/book/chapter-38/02-look_ahead.pl +2 -0
- package/examples/dcg-command-parser.pl +26 -0
- package/examples/output/dcg-command-parser.pl +4 -0
- package/package.json +1 -1
- package/playground.html +1 -0
- package/src/dcg.js +245 -0
- package/src/iso.js +42 -0
- package/src/parser.js +23 -2
- package/src/program.js +22 -5
- package/src/solver.js +6 -2
- package/test/conformance/ISO-MATRIX.md +3 -1
- package/test/conformance/README.md +8 -6
- package/test/conformance/THIRD_PARTY.md +7 -0
- package/test/conformance/cases/iso/cut_disjunction_scope.pl +5 -0
- package/test/conformance/cases/iso/dcg_ambiguous_alternatives.pl +8 -0
- package/test/conformance/cases/iso/dcg_call_phrase_semicontext.pl +12 -0
- package/test/conformance/cases/iso/dcg_control_constructs.pl +12 -0
- package/test/conformance/cases/iso/dcg_declaration_nonterminal_indicators.pl +7 -0
- package/test/conformance/cases/iso/dcg_double_quotes_atom.pl +7 -0
- package/test/conformance/cases/iso/dcg_double_quotes_codes.pl +6 -0
- package/test/conformance/cases/iso/dcg_dynamic_body_control.pl +15 -0
- package/test/conformance/cases/iso/dcg_dynamic_nonterminal_indicator.pl +7 -0
- package/test/conformance/cases/iso/dcg_explicit_qualified_head.pl +6 -0
- package/test/conformance/cases/iso/dcg_module_nonterminal_indicator.pl +8 -0
- package/test/conformance/cases/iso/dcg_nested_phrase_call.pl +6 -0
- package/test/conformance/cases/iso/dcg_operator_table.pl +5 -0
- package/test/conformance/cases/iso/dcg_partial_sequences.pl +12 -0
- package/test/conformance/cases/iso/dcg_phrase_steadfastness.pl +14 -0
- package/test/conformance/cases/iso/dcg_qualified_chain.pl +10 -0
- package/test/conformance/cases/iso/dcg_recursive_sequences.pl +8 -0
- package/test/conformance/cases/iso/dcg_terminals_and_remainder.pl +8 -0
- package/test/conformance/cases/iso/dcg_variable_hygiene.pl +9 -0
- package/test/conformance/cases/iso/logtalk_dcg_conjunction_disjunction.pl +14 -0
- package/test/conformance/cases/iso/logtalk_dcg_cut.pl +12 -0
- package/test/conformance/cases/iso/logtalk_dcg_cut_positions.pl +13 -0
- package/test/conformance/cases/iso/logtalk_dcg_embedded_goals.pl +12 -0
- package/test/conformance/cases/iso/logtalk_dcg_embedded_sequences.pl +10 -0
- package/test/conformance/cases/iso/logtalk_dcg_graphic_nonterminal.pl +7 -0
- package/test/conformance/cases/iso/logtalk_dcg_if_then_else.pl +15 -0
- package/test/conformance/cases/iso/logtalk_dcg_negation_positions.pl +11 -0
- package/test/conformance/cases/iso/logtalk_dcg_nonterminals.pl +11 -0
- package/test/conformance/cases/iso/logtalk_dcg_phrase_identity.pl +12 -0
- package/test/conformance/cases/iso/logtalk_dcg_semicontext_variants.pl +12 -0
- package/test/conformance/cases/iso/logtalk_dcg_semicontexts.pl +12 -0
- package/test/conformance/cases/iso/logtalk_dcg_terminal_variables.pl +9 -0
- package/test/conformance/cases/iso/logtalk_dcg_terminals.pl +15 -0
- package/test/conformance/cases/iso/logtalk_dcg_variable_bodies.pl +10 -0
- package/test/conformance/errors/iso/dcg_call_noncallable.pl +3 -0
- package/test/conformance/errors/iso/dcg_call_variable.pl +3 -0
- package/test/conformance/errors/iso/dcg_nonatom_qualified_module.pl +3 -0
- package/test/conformance/errors/iso/dcg_partial_semicontext.pl +3 -0
- package/test/conformance/errors/iso/dcg_phrase_bad_input.pl +1 -0
- package/test/conformance/errors/iso/dcg_phrase_bad_output.pl +1 -0
- package/test/conformance/errors/iso/dcg_phrase_noncallable.pl +1 -0
- package/test/conformance/errors/iso/dcg_phrase_nonterminal_variable.pl +3 -0
- package/test/conformance/errors/iso/dcg_phrase_variable.pl +1 -0
- package/test/conformance/errors/iso/dcg_qualified_unknown_nonterminal.pl +4 -0
- package/test/conformance/errors/iso/dcg_variable_qualified_module.pl +3 -0
- package/test/conformance/errors/iso/dcg_variable_rule_body.pl +3 -0
- package/test/conformance/errors/iso/dcg_variable_rule_head.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_bad_input_precedence.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_bad_output_precedence.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_curly_head.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_cut_head.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_embedded_noncallable.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_improper_semicontext.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_improper_terminal_rule.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_invalid_semicontext.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_multiple_semicontexts.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_nested_noncallable.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_noncallable_after_failure.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_noncallable_after_success.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_noncallable_precedence.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_numeric_head.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_numeric_nonterminal.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_partial_terminal_rule.pl +4 -0
- package/test/conformance/errors/iso/logtalk_dcg_phrase_curly_variable.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_phrase_improper_input.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_phrase_improper_output.pl +2 -0
- package/test/conformance/errors/iso/logtalk_dcg_reversed_semicontext.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_terminal_head.pl +3 -0
- package/test/conformance/errors/iso/logtalk_dcg_unknown_nonterminal.pl +2 -0
- package/test/conformance/expected/iso/cut_disjunction_scope.pl +2 -0
- package/test/conformance/expected/iso/dcg_ambiguous_alternatives.pl +1 -0
- package/test/conformance/expected/iso/dcg_call_phrase_semicontext.pl +4 -0
- package/test/conformance/expected/iso/dcg_control_constructs.pl +6 -0
- package/test/conformance/expected/iso/dcg_declaration_nonterminal_indicators.pl +1 -0
- package/test/conformance/expected/iso/dcg_double_quotes_atom.pl +1 -0
- package/test/conformance/expected/iso/dcg_double_quotes_codes.pl +1 -0
- package/test/conformance/expected/iso/dcg_dynamic_body_control.pl +5 -0
- package/test/conformance/expected/iso/dcg_dynamic_nonterminal_indicator.pl +1 -0
- package/test/conformance/expected/iso/dcg_explicit_qualified_head.pl +1 -0
- package/test/conformance/expected/iso/dcg_module_nonterminal_indicator.pl +2 -0
- package/test/conformance/expected/iso/dcg_nested_phrase_call.pl +1 -0
- package/test/conformance/expected/iso/dcg_operator_table.pl +2 -0
- package/test/conformance/expected/iso/dcg_partial_sequences.pl +3 -0
- package/test/conformance/expected/iso/dcg_phrase_steadfastness.pl +4 -0
- package/test/conformance/expected/iso/dcg_qualified_chain.pl +2 -0
- package/test/conformance/expected/iso/dcg_recursive_sequences.pl +3 -0
- package/test/conformance/expected/iso/dcg_terminals_and_remainder.pl +2 -0
- package/test/conformance/expected/iso/dcg_variable_hygiene.pl +2 -0
- package/test/conformance/expected/iso/logtalk_dcg_conjunction_disjunction.pl +8 -0
- package/test/conformance/expected/iso/logtalk_dcg_cut.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_cut_positions.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_embedded_goals.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_embedded_sequences.pl +3 -0
- package/test/conformance/expected/iso/logtalk_dcg_graphic_nonterminal.pl +1 -0
- package/test/conformance/expected/iso/logtalk_dcg_if_then_else.pl +7 -0
- package/test/conformance/expected/iso/logtalk_dcg_negation_positions.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_nonterminals.pl +3 -0
- package/test/conformance/expected/iso/logtalk_dcg_phrase_identity.pl +6 -0
- package/test/conformance/expected/iso/logtalk_dcg_semicontext_variants.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_semicontexts.pl +4 -0
- package/test/conformance/expected/iso/logtalk_dcg_terminal_variables.pl +3 -0
- package/test/conformance/expected/iso/logtalk_dcg_terminals.pl +6 -0
- package/test/conformance/expected/iso/logtalk_dcg_variable_bodies.pl +4 -0
- package/test/conformance/expected-errors/iso/dcg_call_noncallable.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_call_variable.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_nonatom_qualified_module.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_partial_semicontext.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_phrase_bad_input.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_phrase_bad_output.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_phrase_noncallable.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_phrase_nonterminal_variable.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_phrase_variable.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_qualified_unknown_nonterminal.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_variable_qualified_module.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_variable_rule_body.txt +1 -0
- package/test/conformance/expected-errors/iso/dcg_variable_rule_head.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_bad_input_precedence.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_bad_output_precedence.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_curly_head.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_cut_head.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_embedded_noncallable.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_improper_semicontext.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_improper_terminal_rule.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_invalid_semicontext.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_multiple_semicontexts.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_nested_noncallable.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_noncallable_after_failure.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_noncallable_after_success.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_noncallable_precedence.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_numeric_head.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_numeric_nonterminal.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_partial_terminal_rule.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_phrase_curly_variable.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_phrase_improper_input.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_phrase_improper_output.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_reversed_semicontext.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_terminal_head.txt +1 -0
- package/test/conformance/expected-errors/iso/logtalk_dcg_unknown_nonterminal.txt +1 -0
- package/test/run-regression.mjs +24 -2
- package/the-art-of-eyeprolog.md +89 -39
- package/tools/extract-book-examples.mjs +1 -1
- package/why-eyeprolog.md +5 -1
package/README.md
CHANGED
|
@@ -33,9 +33,19 @@ Programs may declare their default queries with `%% goal:` comments.
|
|
|
33
33
|
Double-quoted text follows the ISO `double_quotes` flag and defaults to a
|
|
34
34
|
proper list of one-character atoms (`chars`), matching Trealla and Scryer.
|
|
35
35
|
|
|
36
|
-
##
|
|
36
|
+
## ISO modules and definite clause grammars
|
|
37
37
|
|
|
38
|
-
EyeProlog
|
|
38
|
+
EyeProlog implements ISO/IEC 13211-2 modules and the grammar rules and
|
|
39
|
+
`phrase/2-3` predicates of ISO/IEC TS 13211-3:2025. For example:
|
|
40
|
+
|
|
41
|
+
```prolog
|
|
42
|
+
sentence --> [hello], noun.
|
|
43
|
+
noun --> [world] | [prolog].
|
|
44
|
+
|
|
45
|
+
%% goal: phrase(sentence, Words)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
EyeProlog also adds 44 public library predicates to its 129-entry ISO registry.
|
|
39
49
|
**All 44 are ordinary Prolog clauses** in `src/lib/eyeprolog.pl` and
|
|
40
50
|
`src/lib/lists.pl`. They are ISO/IEC 13211-2 modules, loaded explicitly with
|
|
41
51
|
`use_module(library(eyeprolog))` or `use_module(library(lists))`, following the
|
package/conformance-report.md
CHANGED
|
@@ -11,7 +11,7 @@ This report summarizes the file-based conformance corpus under `test/conformance
|
|
|
11
11
|
| builtins | 11 | 0 | 0 | 0 | 11 |
|
|
12
12
|
| context | 11 | 0 | 0 | 0 | 11 |
|
|
13
13
|
| control | 15 | 0 | 0 | 0 | 15 |
|
|
14
|
-
| iso |
|
|
14
|
+
| iso | 164 | 209 | 0 | 0 | 373 |
|
|
15
15
|
| lists | 52 | 3 | 0 | 0 | 55 |
|
|
16
16
|
| modules | 2 | 0 | 0 | 0 | 2 |
|
|
17
17
|
| negation | 8 | 0 | 19 | 0 | 27 |
|
|
@@ -23,4 +23,4 @@ This report summarizes the file-based conformance corpus under `test/conformance
|
|
|
23
23
|
| terms | 26 | 3 | 0 | 0 | 29 |
|
|
24
24
|
| unification | 18 | 0 | 0 | 0 | 18 |
|
|
25
25
|
| variables | 16 | 9 | 0 | 0 | 25 |
|
|
26
|
-
| **Total** | **
|
|
26
|
+
| **Total** | **479** | **260** | **19** | **21** | **779** |
|
package/examples/book/README.md
CHANGED
|
@@ -127,7 +127,7 @@ npm run generate
|
|
|
127
127
|
- [01-tree.pl](chapter-22/01-tree.pl)
|
|
128
128
|
- [02-tree_member.pl](chapter-22/02-tree_member.pl)
|
|
129
129
|
- [03-mirror.pl](chapter-22/03-mirror.pl) — Transforming a tree
|
|
130
|
-
- [04-sentence.pl](chapter-22/04-sentence.pl) — A
|
|
130
|
+
- [04-sentence.pl](chapter-22/04-sentence.pl) — A standard definite clause grammar
|
|
131
131
|
- [05-evaluate.pl](chapter-22/05-evaluate.pl) — Interpreting an expression
|
|
132
132
|
- [06-lookup.pl](chapter-22/06-lookup.pl)
|
|
133
133
|
- [07-simplify.pl](chapter-22/07-simplify.pl) — Rewriting symbolic expressions
|
|
@@ -233,6 +233,7 @@ npm run generate
|
|
|
233
233
|
## Chapter 38: Language and ISO profile
|
|
234
234
|
|
|
235
235
|
- [01-city.pl](chapter-38/01-city.pl)
|
|
236
|
+
- [02-look_ahead.pl](chapter-38/02-look_ahead.pl) — ISO Part 3 definite clause grammars
|
|
236
237
|
|
|
237
238
|
## Chapter 39: Built-in predicates by programming role
|
|
238
239
|
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
% From The Art of EyeProlog, Chapter 22 — A
|
|
2
|
-
sentence
|
|
3
|
-
noun_phrase(Input, AfterNoun),
|
|
4
|
-
verb_phrase(AfterNoun, Rest).
|
|
1
|
+
% From The Art of EyeProlog, Chapter 22 — A standard definite clause grammar.
|
|
2
|
+
sentence --> noun_phrase, verb_phrase.
|
|
5
3
|
|
|
6
|
-
noun_phrase
|
|
7
|
-
noun_phrase
|
|
4
|
+
noun_phrase --> [the], noun.
|
|
5
|
+
noun_phrase --> [a], noun.
|
|
8
6
|
|
|
9
|
-
noun
|
|
10
|
-
noun
|
|
7
|
+
noun --> [robot].
|
|
8
|
+
noun --> [scientist].
|
|
11
9
|
|
|
12
|
-
verb_phrase
|
|
13
|
-
verb(Input, AfterVerb),
|
|
14
|
-
noun_phrase(AfterVerb, Rest).
|
|
10
|
+
verb_phrase --> verb, noun_phrase.
|
|
15
11
|
|
|
16
|
-
verb
|
|
17
|
-
verb
|
|
12
|
+
verb --> [helps].
|
|
13
|
+
verb --> [observes].
|
|
18
14
|
|
|
19
|
-
complete_sentence(Words) :- sentence
|
|
15
|
+
complete_sentence(Words) :- phrase(sentence, Words).
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
% Parse and generate commands for a tiny home-automation language with a DCG.
|
|
2
|
+
%
|
|
3
|
+
% The grammar separates surface tokens from the structured command term used
|
|
4
|
+
% by an application. phrase/2 requires complete input, while phrase/3 leaves
|
|
5
|
+
% unconsumed tokens available to a surrounding parser.
|
|
6
|
+
%% goal: dcg_example(X0, X1)
|
|
7
|
+
|
|
8
|
+
command(set(light(Room), State)) -->
|
|
9
|
+
[set], room(Room), [light, to], state(State).
|
|
10
|
+
|
|
11
|
+
room(kitchen) --> [kitchen].
|
|
12
|
+
room(hall) --> [hall].
|
|
13
|
+
|
|
14
|
+
state(on) --> [on].
|
|
15
|
+
state(off) --> [off].
|
|
16
|
+
|
|
17
|
+
dcg_example(parsed, Command) :-
|
|
18
|
+
phrase(command(Command), [set, kitchen, light, to, on]).
|
|
19
|
+
dcg_example(generated, Tokens) :-
|
|
20
|
+
phrase(command(set(light(hall), off)), Tokens).
|
|
21
|
+
dcg_example(remainder, Rest) :-
|
|
22
|
+
phrase(command(set(light(kitchen), on)),
|
|
23
|
+
[set, kitchen, light, to, on, then, wait],
|
|
24
|
+
Rest).
|
|
25
|
+
dcg_example(rejected, invalid_command) :-
|
|
26
|
+
\+ phrase(command(_), [set, garage, light, to, blinking]).
|
package/package.json
CHANGED
package/playground.html
CHANGED
package/src/dcg.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// ISO/IEC TS 13211-3:2025 definite clause grammar expansion.
|
|
2
|
+
// Grammar rules are lowered to ordinary clauses during program preparation;
|
|
3
|
+
// phrase/2-3 use the same body expansion for dynamically supplied grammars.
|
|
4
|
+
import {
|
|
5
|
+
ATOM, COMPOUND, VAR, Env, atom, compound, deref, emptyList,
|
|
6
|
+
flattenConjunction, variable,
|
|
7
|
+
} from './term.js';
|
|
8
|
+
import { PrologError } from './iso.js';
|
|
9
|
+
|
|
10
|
+
let dcgFresh = 0;
|
|
11
|
+
|
|
12
|
+
function freshDcgVariable(label = 'S') {
|
|
13
|
+
// NUL cannot occur in a source variable name, so generated difference-list
|
|
14
|
+
// variables cannot capture a variable written by the program.
|
|
15
|
+
return variable(`\u0000dcg${++dcgFresh}:${label}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function conjunction(first, second) {
|
|
19
|
+
return compound(',', [first, second]);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function equality(left, right) {
|
|
23
|
+
return compound('=', [left, right]);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function listWithTail(items, tail) {
|
|
27
|
+
let list = tail;
|
|
28
|
+
for (let index = items.length - 1; index >= 0; index--) {
|
|
29
|
+
list = compound('.', [items[index], list]);
|
|
30
|
+
}
|
|
31
|
+
return list;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function terminalItems(term, env = new Env()) {
|
|
35
|
+
const items = [];
|
|
36
|
+
const original = term;
|
|
37
|
+
const seen = new Set();
|
|
38
|
+
let cursor = deref(term, env);
|
|
39
|
+
while (cursor.type === COMPOUND && cursor.name === '.' && cursor.arity === 2) {
|
|
40
|
+
if (seen.has(cursor)) throw new PrologError('type_error(terminal_sequence)', original);
|
|
41
|
+
seen.add(cursor);
|
|
42
|
+
items.push(cursor.args[0]);
|
|
43
|
+
cursor = deref(cursor.args[1], env);
|
|
44
|
+
}
|
|
45
|
+
if (cursor.type === ATOM && cursor.name === '[]') return items;
|
|
46
|
+
if (cursor.type === VAR) throw new PrologError('instantiation_error');
|
|
47
|
+
throw new PrologError('type_error(terminal_sequence)', original);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function terminalsGoal(terminals, input, output, env) {
|
|
51
|
+
return equality(input, listWithTail(terminalItems(terminals, env), output));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function appendStateArguments(nonterminal, input, output, module) {
|
|
55
|
+
nonterminal = deref(nonterminal, new Env());
|
|
56
|
+
if (nonterminal.type === ATOM) {
|
|
57
|
+
const goal = compound(nonterminal.name, [input, output]);
|
|
58
|
+
goal.module = module;
|
|
59
|
+
return goal;
|
|
60
|
+
}
|
|
61
|
+
if (nonterminal.type !== COMPOUND) throw new PrologError('type_error(callable)', nonterminal);
|
|
62
|
+
if (nonterminal.name === ':' && nonterminal.arity === 2) {
|
|
63
|
+
const qualifier = nonterminal.args[0];
|
|
64
|
+
if (qualifier.type === VAR) throw new PrologError('instantiation_error');
|
|
65
|
+
if (qualifier.type !== ATOM) throw new PrologError('type_error(atom)', qualifier);
|
|
66
|
+
return compound(':', [qualifier, appendStateArguments(nonterminal.args[1], input, output, qualifier.name)]);
|
|
67
|
+
}
|
|
68
|
+
const goal = compound(nonterminal.name, [...nonterminal.args, input, output]);
|
|
69
|
+
goal.module = nonterminal.module ?? module;
|
|
70
|
+
return goal;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function markGoalModule(term, module) {
|
|
74
|
+
if (!term || (term.type !== ATOM && term.type !== COMPOUND)) return term;
|
|
75
|
+
term.module ??= module;
|
|
76
|
+
if (term.type === COMPOUND &&
|
|
77
|
+
[',', ';', '->', '\\+', 'call', 'once', 'catch', 'phrase'].includes(term.name)) {
|
|
78
|
+
for (const argument of term.args) markGoalModule(argument, module);
|
|
79
|
+
}
|
|
80
|
+
return term;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isTerminalSequence(term) {
|
|
84
|
+
return (term.type === ATOM && term.name === '[]') ||
|
|
85
|
+
(term.type === COMPOUND && term.name === '.' && term.arity === 2);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function expandDcgBody(body, input, output, options = {}) {
|
|
89
|
+
const env = options.env ?? new Env();
|
|
90
|
+
const module = options.module ?? 'user';
|
|
91
|
+
body = deref(body, env);
|
|
92
|
+
|
|
93
|
+
if (body.type === VAR) {
|
|
94
|
+
const goal = compound('phrase', [body, input, output]);
|
|
95
|
+
goal.module = module;
|
|
96
|
+
return goal;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Empty braces are the empty embedded-goal sequence. Like {true}, they
|
|
100
|
+
// consume no terminals and leave the grammar state unchanged.
|
|
101
|
+
if (body.type === ATOM && body.name === '{}') return equality(input, output);
|
|
102
|
+
|
|
103
|
+
if (isTerminalSequence(body)) return terminalsGoal(body, input, output, env);
|
|
104
|
+
|
|
105
|
+
if (body.type === COMPOUND && body.name === ',' && body.arity === 2) {
|
|
106
|
+
const middle = freshDcgVariable('sequence');
|
|
107
|
+
return conjunction(
|
|
108
|
+
expandDcgBody(body.args[0], input, middle, options),
|
|
109
|
+
expandDcgBody(body.args[1], middle, output, options),
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (body.type === COMPOUND && [';', '|'].includes(body.name) && body.arity === 2) {
|
|
114
|
+
const left = body.args[0];
|
|
115
|
+
if (body.name === ';' && left.type === COMPOUND && left.name === '->' && left.arity === 2) {
|
|
116
|
+
const middle = freshDcgVariable('condition');
|
|
117
|
+
return compound(';', [
|
|
118
|
+
compound('->', [
|
|
119
|
+
expandDcgBody(left.args[0], input, middle, options),
|
|
120
|
+
expandDcgBody(left.args[1], middle, output, options),
|
|
121
|
+
]),
|
|
122
|
+
expandDcgBody(body.args[1], input, output, options),
|
|
123
|
+
]);
|
|
124
|
+
}
|
|
125
|
+
return compound(';', [
|
|
126
|
+
expandDcgBody(left, input, output, options),
|
|
127
|
+
expandDcgBody(body.args[1], input, output, options),
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (body.type === COMPOUND && body.name === '{}' && body.arity === 1) {
|
|
132
|
+
return conjunction(markGoalModule(body.args[0], module), equality(input, output));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (body.type === COMPOUND && body.name === 'call' && body.arity === 1) {
|
|
136
|
+
const closure = body.args[0];
|
|
137
|
+
if (closure.type === ATOM || closure.type === COMPOUND) closure.module ??= module;
|
|
138
|
+
const goal = compound('call', [closure, input, output]);
|
|
139
|
+
goal.module = module;
|
|
140
|
+
return goal;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (body.type === COMPOUND && body.name === 'phrase' && body.arity === 1) {
|
|
144
|
+
const goal = compound('phrase', [body.args[0], input, output]);
|
|
145
|
+
goal.module = module;
|
|
146
|
+
return goal;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (body.type === ATOM && body.name === '!') {
|
|
150
|
+
return conjunction(body, equality(input, output));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// TS 13211-3 leaves \+//1 and ->//2 implementation dependent. EyeProlog
|
|
154
|
+
// uses the widespread non-consuming negation and state-threading if-then
|
|
155
|
+
// definitions and documents these choices in its conformance profile.
|
|
156
|
+
if (body.type === COMPOUND && body.name === '\\+' && body.arity === 1) {
|
|
157
|
+
const ignored = freshDcgVariable('negated');
|
|
158
|
+
return conjunction(
|
|
159
|
+
compound('\\+', [expandDcgBody(body.args[0], input, ignored, options)]),
|
|
160
|
+
equality(input, output),
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (body.type === COMPOUND && body.name === '->' && body.arity === 2) {
|
|
165
|
+
const middle = freshDcgVariable('condition');
|
|
166
|
+
return compound('->', [
|
|
167
|
+
expandDcgBody(body.args[0], input, middle, options),
|
|
168
|
+
expandDcgBody(body.args[1], middle, output, options),
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (body.type !== ATOM && body.type !== COMPOUND) {
|
|
173
|
+
throw new PrologError('type_error(callable)', body);
|
|
174
|
+
}
|
|
175
|
+
return appendStateArguments(body, input, output, module);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function splitGrammarHead(head) {
|
|
179
|
+
let terminals = null;
|
|
180
|
+
if (head.type === COMPOUND && head.name === ',' && head.arity === 2) {
|
|
181
|
+
terminals = head.args[1];
|
|
182
|
+
head = head.args[0];
|
|
183
|
+
}
|
|
184
|
+
let module = null;
|
|
185
|
+
if (head.type === COMPOUND && head.name === ':' && head.arity === 2) {
|
|
186
|
+
if (head.args[0].type === VAR) throw new PrologError('instantiation_error');
|
|
187
|
+
if (head.args[0].type !== ATOM) throw new PrologError('type_error(atom)', head.args[0]);
|
|
188
|
+
module = head.args[0].name;
|
|
189
|
+
head = head.args[1];
|
|
190
|
+
}
|
|
191
|
+
if (head.type === VAR) throw new PrologError('instantiation_error');
|
|
192
|
+
if (head.type !== ATOM && head.type !== COMPOUND) throw new PrologError('type_error(callable)', head);
|
|
193
|
+
if (isTerminalSequence(head) ||
|
|
194
|
+
(head.type === COMPOUND && [',', ';', '|', '->', '{}'].includes(head.name)) ||
|
|
195
|
+
(head.type === ATOM && ['!', '{}'].includes(head.name))) {
|
|
196
|
+
throw new PrologError('permission_error(define, grammar_rule)', head);
|
|
197
|
+
}
|
|
198
|
+
return { head, terminals, module };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function expandDcgRuleClause(clause, defaultModule = 'user') {
|
|
202
|
+
if (clause?.body?.length !== 0 || clause?.head?.type !== COMPOUND ||
|
|
203
|
+
clause.head.name !== '-->' || clause.head.arity !== 2) return null;
|
|
204
|
+
|
|
205
|
+
const declaration = splitGrammarHead(clause.head.args[0]);
|
|
206
|
+
const module = declaration.module ?? defaultModule;
|
|
207
|
+
const input = freshDcgVariable('input');
|
|
208
|
+
const output = freshDcgVariable('output');
|
|
209
|
+
const expandedHead = appendStateArguments(declaration.head, input, output, module);
|
|
210
|
+
delete expandedHead.module;
|
|
211
|
+
|
|
212
|
+
let bodyOutput = output;
|
|
213
|
+
if (declaration.terminals != null) bodyOutput = freshDcgVariable('semicontext');
|
|
214
|
+
let expandedBody = expandDcgBody(clause.head.args[1], input, bodyOutput, { module });
|
|
215
|
+
if (declaration.terminals != null) {
|
|
216
|
+
expandedBody = conjunction(
|
|
217
|
+
expandedBody,
|
|
218
|
+
terminalsGoal(declaration.terminals, output, bodyOutput, new Env()),
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const expanded = {
|
|
223
|
+
head: expandedHead,
|
|
224
|
+
body: flattenConjunction(expandedBody),
|
|
225
|
+
module,
|
|
226
|
+
grammarRule: clause.head,
|
|
227
|
+
};
|
|
228
|
+
if (clause.source) expanded.source = clause.source;
|
|
229
|
+
return expanded;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function isListOrPartialList(term, env) {
|
|
233
|
+
const seen = new Set();
|
|
234
|
+
let cursor = deref(term, env);
|
|
235
|
+
while (cursor.type === COMPOUND && cursor.name === '.' && cursor.arity === 2) {
|
|
236
|
+
if (seen.has(cursor)) return false;
|
|
237
|
+
seen.add(cursor);
|
|
238
|
+
cursor = deref(cursor.args[1], env);
|
|
239
|
+
}
|
|
240
|
+
return cursor.type === VAR || (cursor.type === ATOM && cursor.name === '[]');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function emptyTerminalSequence() {
|
|
244
|
+
return emptyList();
|
|
245
|
+
}
|
package/src/iso.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from './term.js';
|
|
8
8
|
import { createParserOperatorState, parseClauses } from './parser.js';
|
|
9
9
|
import { formatTermForWrite } from './write.js';
|
|
10
|
+
import { emptyTerminalSequence, expandDcgBody, isListOrPartialList } from './dcg.js';
|
|
10
11
|
|
|
11
12
|
let isoFresh = 0;
|
|
12
13
|
|
|
@@ -150,6 +151,8 @@ export const isoBuiltins = {
|
|
|
150
151
|
registry.add('repeat', 0, repeatBuiltin);
|
|
151
152
|
registry.add(';', 2, disjunctionBuiltin);
|
|
152
153
|
registry.add('->', 2, ifThenBuiltin);
|
|
154
|
+
registry.add('phrase', 2, phraseBuiltin);
|
|
155
|
+
registry.add('phrase', 3, phraseBuiltin);
|
|
153
156
|
|
|
154
157
|
registry.add('is', 2, isBuiltin, { deterministic: true });
|
|
155
158
|
registry.add('=:=', 2, arithmeticComparison((n) => n === 0), { deterministic: true });
|
|
@@ -1545,6 +1548,38 @@ function* callClosureBuiltin({ solver, goal, env }) {
|
|
|
1545
1548
|
solver.absorbStatsFrom(child);
|
|
1546
1549
|
}
|
|
1547
1550
|
}
|
|
1551
|
+
|
|
1552
|
+
function* phraseBuiltin({ solver, goal, env }) {
|
|
1553
|
+
const grammarBody = deref(goal.args[0], env);
|
|
1554
|
+
if (grammarBody.type === VAR) throw new PrologError('instantiation_error');
|
|
1555
|
+
if (grammarBody.type !== ATOM && grammarBody.type !== COMPOUND) {
|
|
1556
|
+
throw new PrologError('type_error(callable)', grammarBody);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
const input = goal.args[1];
|
|
1560
|
+
const requestedOutput = goal.arity === 2 ? emptyTerminalSequence() : goal.args[2];
|
|
1561
|
+
if (!isListOrPartialList(input, env)) {
|
|
1562
|
+
throw new PrologError('type_error(terminal_sequence)', deref(input, env));
|
|
1563
|
+
}
|
|
1564
|
+
if (!isListOrPartialList(requestedOutput, env)) {
|
|
1565
|
+
throw new PrologError('type_error(terminal_sequence)', deref(requestedOutput, env));
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// Delay the final output unification to keep phrase/3 steadfast in its
|
|
1569
|
+
// third argument, as required by the Part 3 execution model.
|
|
1570
|
+
const finalOutput = variable(`\u0000phrase:${++isoFresh}`);
|
|
1571
|
+
const expanded = expandDcgBody(grammarBody, input, finalOutput, {
|
|
1572
|
+
env,
|
|
1573
|
+
module: goal.module ?? grammarBody.module ?? 'user',
|
|
1574
|
+
});
|
|
1575
|
+
const finish = compound('=', [finalOutput, requestedOutput]);
|
|
1576
|
+
const child = solver.cloneForInnerGoal();
|
|
1577
|
+
try {
|
|
1578
|
+
yield* child.solve([expanded, finish], env, 0);
|
|
1579
|
+
} finally {
|
|
1580
|
+
solver.absorbStatsFrom(child);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1548
1583
|
function formalErrorTerm(error) {
|
|
1549
1584
|
const parse = (text) => {
|
|
1550
1585
|
const open = text.indexOf('(');
|
|
@@ -1625,7 +1660,14 @@ function* disjunctionBuiltin({ solver, goal, env }) {
|
|
|
1625
1660
|
yield* solver.solve([callable(goal.args[1], env)], env.clone(), 0);
|
|
1626
1661
|
return;
|
|
1627
1662
|
}
|
|
1663
|
+
const marker = solver.active[solver.active.length - 1] ?? null;
|
|
1664
|
+
const markerCutEpoch = marker?.cutEpoch ?? 0;
|
|
1665
|
+
const solverCutEpoch = solver.cutEpoch;
|
|
1628
1666
|
yield* solver.solve([callable(goal.args[0], env)], env.clone(), 0);
|
|
1667
|
+
const cutThisScope = marker == null
|
|
1668
|
+
? solver.cutEpoch !== solverCutEpoch
|
|
1669
|
+
: (marker.cutEpoch ?? 0) !== markerCutEpoch;
|
|
1670
|
+
if (cutThisScope) return;
|
|
1629
1671
|
yield* solver.solve([callable(goal.args[1], env)], env.clone(), 0);
|
|
1630
1672
|
}
|
|
1631
1673
|
function* ifThenBuiltin({ solver, goal, env }) {
|
package/src/parser.js
CHANGED
|
@@ -39,6 +39,8 @@ const graphicAtomChars = '#$&*+-./<=>@^~\\;:';
|
|
|
39
39
|
// canonical notation. Commas remain separators except inside parentheses.
|
|
40
40
|
const INFIX_OPERATORS = new Map([
|
|
41
41
|
[':-', { precedence: 2, associativity: 'none' }],
|
|
42
|
+
['-->', { precedence: 2, associativity: 'none' }],
|
|
43
|
+
['|', { precedence: 4, associativity: 'right' }],
|
|
42
44
|
[';', { precedence: 5, associativity: 'right' }],
|
|
43
45
|
['->', { precedence: 7, associativity: 'right' }],
|
|
44
46
|
[',', { precedence: 10, associativity: 'right' }],
|
|
@@ -82,7 +84,8 @@ const PREFIX_OPERATORS = new Map([
|
|
|
82
84
|
]);
|
|
83
85
|
|
|
84
86
|
export const ISO_OPERATOR_DEFINITIONS = [
|
|
85
|
-
[1200, 'xfx', ':-'], [1200, 'fx', ':-'],
|
|
87
|
+
[1200, 'xfx', ':-'], [1200, 'fx', ':-'], [1200, 'xfx', '-->'],
|
|
88
|
+
[1105, 'xfy', '|'],
|
|
86
89
|
[1100, 'xfy', ';'], [1050, 'xfy', '->'], [1000, 'xfy', ','],
|
|
87
90
|
[900, 'fy', '\\+'],
|
|
88
91
|
...['=', '=..', '\\=', '==', '\\==', '@<', '@=<', '@>', '@>=', 'is',
|
|
@@ -618,7 +621,25 @@ class Parser {
|
|
|
618
621
|
accept(clause);
|
|
619
622
|
continue;
|
|
620
623
|
}
|
|
621
|
-
|
|
624
|
+
let head = this.parseTerm(3);
|
|
625
|
+
// ISO/IEC TS 13211-3 grammar rules use -->/2 at the same top-level
|
|
626
|
+
// priority as clauses. The left side may include an unparenthesized
|
|
627
|
+
// semicontext: NonTerminal, Terminals --> Body.
|
|
628
|
+
if (this.token.type === TOK.COMMA) {
|
|
629
|
+
this.advance();
|
|
630
|
+
head = compound(',', [head, this.parseTerm(3)]);
|
|
631
|
+
}
|
|
632
|
+
if (this.operatorTokenName() === '-->') {
|
|
633
|
+
this.advance();
|
|
634
|
+
const grammarBody = this.parseTerm(0, true);
|
|
635
|
+
this.expect(TOK.DOT, '.');
|
|
636
|
+
this.advance();
|
|
637
|
+
const clause = { head: compound('-->', [head, grammarBody]), body: [] };
|
|
638
|
+
clauseNumber++;
|
|
639
|
+
if (this.sourceMetadata) clause.source = { filename: this.filename, line, clause: clauseNumber };
|
|
640
|
+
accept(clause);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
622
643
|
const body = [];
|
|
623
644
|
if (this.token.type === TOK.IF) {
|
|
624
645
|
this.advance();
|
package/src/program.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { PrologError } from './iso.js';
|
|
12
12
|
import { currentWorkingDirectory, fs, path } from './platform.js';
|
|
13
13
|
import { standardLibrarySources } from './standard-library.js';
|
|
14
|
+
import { expandDcgRuleClause } from './dcg.js';
|
|
14
15
|
|
|
15
16
|
const DEFER_PROGRAM_BUILD = Symbol('deferProgramBuild');
|
|
16
17
|
const FAST_PARSE_ABORT = Symbol('fastParseAbort');
|
|
@@ -663,6 +664,8 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
663
664
|
batch.length = 0;
|
|
664
665
|
};
|
|
665
666
|
const accept = (clause) => {
|
|
667
|
+
const grammarClause = expandDcgRuleClause(clause, context.module);
|
|
668
|
+
if (grammarClause) clause = grammarClause;
|
|
666
669
|
const moduleDeclaration = moduleDirective(clause);
|
|
667
670
|
if (moduleDeclaration) {
|
|
668
671
|
flush();
|
|
@@ -690,9 +693,9 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
690
693
|
}
|
|
691
694
|
const include = includeDirective(clause);
|
|
692
695
|
if (!include) {
|
|
693
|
-
clause.module
|
|
696
|
+
clause.module ??= context.module;
|
|
694
697
|
if (!isDirectiveClause(clause)) {
|
|
695
|
-
for (const goal of clause.body) annotateGoalModule(goal,
|
|
698
|
+
for (const goal of clause.body) annotateGoalModule(goal, clause.module);
|
|
696
699
|
}
|
|
697
700
|
batch.push(clause);
|
|
698
701
|
if (batch.length >= PROGRAM_BUILD_BATCH_SIZE) flush();
|
|
@@ -759,9 +762,14 @@ function moduleExportIndicators(term) {
|
|
|
759
762
|
if (items == null) return null;
|
|
760
763
|
const indicators = [];
|
|
761
764
|
for (const item of items) {
|
|
762
|
-
if (item.type !== COMPOUND || item.name
|
|
765
|
+
if (item.type !== COMPOUND || !['/', '//'].includes(item.name) || item.arity !== 2) return null;
|
|
763
766
|
const indicator = predicateIndicator(item.args[0], item.args[1]);
|
|
764
767
|
if (!indicator) return null;
|
|
768
|
+
if (item.name === '//') {
|
|
769
|
+
indicator.arity += 2;
|
|
770
|
+
indicator.key = `${indicator.name}/${indicator.arity}`;
|
|
771
|
+
indicator.nonterminalArity = indicator.arity - 2;
|
|
772
|
+
}
|
|
765
773
|
indicators.push(indicator);
|
|
766
774
|
}
|
|
767
775
|
return indicators;
|
|
@@ -844,12 +852,21 @@ function dynamicDirectiveIndicators(clause) {
|
|
|
844
852
|
if (directive.type !== COMPOUND || directive.name !== 'dynamic' || directive.arity !== 1) return [];
|
|
845
853
|
const terms = properListItems(directive.args[0], new Env()) ?? flattenDirectiveSequence(directive.args[0]);
|
|
846
854
|
return terms.map((indicator) =>
|
|
847
|
-
indicator.type === COMPOUND && indicator.name
|
|
848
|
-
?
|
|
855
|
+
indicator.type === COMPOUND && ['/', '//'].includes(indicator.name) && indicator.arity === 2
|
|
856
|
+
? nonterminalOrPredicateIndicator(indicator)
|
|
849
857
|
: null
|
|
850
858
|
).filter(Boolean);
|
|
851
859
|
}
|
|
852
860
|
|
|
861
|
+
function nonterminalOrPredicateIndicator(term) {
|
|
862
|
+
const indicator = predicateIndicator(term.args[0], term.args[1]);
|
|
863
|
+
if (!indicator || term.name !== '//') return indicator;
|
|
864
|
+
indicator.arity += 2;
|
|
865
|
+
indicator.key = `${indicator.name}/${indicator.arity}`;
|
|
866
|
+
indicator.nonterminalArity = indicator.arity - 2;
|
|
867
|
+
return indicator;
|
|
868
|
+
}
|
|
869
|
+
|
|
853
870
|
function flattenDirectiveSequence(term) {
|
|
854
871
|
if (term.type === COMPOUND && term.name === ',' && term.arity === 2) {
|
|
855
872
|
return [...flattenDirectiveSequence(term.args[0]), ...flattenDirectiveSequence(term.args[1])];
|
package/src/solver.js
CHANGED
|
@@ -61,6 +61,7 @@ export class Solver {
|
|
|
61
61
|
this.io = options.io ?? new StreamManager(options.ioOptions);
|
|
62
62
|
this.solveStacks = [];
|
|
63
63
|
this.active = [];
|
|
64
|
+
this.cutEpoch = 0;
|
|
64
65
|
this.memo = new Map();
|
|
65
66
|
this.tableCoordinator = null;
|
|
66
67
|
this.groundChainSuccess = new Set();
|
|
@@ -215,10 +216,12 @@ export class Solver {
|
|
|
215
216
|
// be run early as pure filters. Stop at internal sentinels so rule-body
|
|
216
217
|
// active guards are released before the caller's remaining goals are seen.
|
|
217
218
|
const selectedIndex = selectReadyDeterministicBuiltin(goals, env, this.registry);
|
|
218
|
-
const goal = goals[selectedIndex];
|
|
219
|
+
const goal = deref(goals[selectedIndex], env);
|
|
219
220
|
const rest = selectedIndex === 0 ? goals.slice(1) : [...goals.slice(0, selectedIndex), ...goals.slice(selectedIndex + 1)];
|
|
220
221
|
if (goal.type === 'atom' && goal.name === '!' && goal.arity === 0) {
|
|
221
222
|
const marker = active[active.length - 1] ?? null;
|
|
223
|
+
this.cutEpoch++;
|
|
224
|
+
if (marker) marker.cutEpoch = (marker.cutEpoch ?? 0) + 1;
|
|
222
225
|
for (const solveStack of this.solveStacks) {
|
|
223
226
|
for (let i = solveStack.length - 1; i >= 0; i--) {
|
|
224
227
|
if (marker == null || solveStack[i].active?.includes(marker)) solveStack.splice(i, 1);
|
|
@@ -247,7 +250,9 @@ export class Solver {
|
|
|
247
250
|
continue;
|
|
248
251
|
}
|
|
249
252
|
|
|
253
|
+
if (goal.type === 'var') throw new PrologError('instantiation_error');
|
|
250
254
|
const callable = goal.type === COMPOUND || goal.type === 'atom';
|
|
255
|
+
if (!callable) throw new PrologError('type_error(callable)', goal);
|
|
251
256
|
const def = callable ? this.registry.get(goal.name, goal.arity) : null;
|
|
252
257
|
this.active = active;
|
|
253
258
|
if (def && builtinIsReadyOrAuthoritative(def, this, goal, env)) {
|
|
@@ -274,7 +279,6 @@ export class Solver {
|
|
|
274
279
|
}
|
|
275
280
|
|
|
276
281
|
this.stats.solve_one_goal_calls++;
|
|
277
|
-
if (!callable) break;
|
|
278
282
|
const group = this.program.findGroup(goal.name, goal.arity, goal.module ?? 'user');
|
|
279
283
|
if (!group) {
|
|
280
284
|
if (this.prologFlags.get('unknown')?.value?.name === 'error') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ISO
|
|
1
|
+
# ISO Prolog conformance matrix
|
|
2
2
|
|
|
3
3
|
The normative baseline for the `iso/` corpus is ISO/IEC 13211-1:1995 plus
|
|
4
4
|
Technical Corrigenda 1:2007, 2:2012, and 3:2017. This matrix links the standard
|
|
@@ -18,6 +18,8 @@ case totals.
|
|
|
18
18
|
| 8.16 atomic processing | atoms, characters, codes and number conversion with prescribed errors | `atomic_term_processing`, focused forward/reverse cases, Logtalk-derived error cases |
|
|
19
19
|
| 8.17 flags and hooks | required flags, mutation permissions, halt and character conversion | `exceptions_and_flags`, `remaining_builtins_and_directives`, flag error cases |
|
|
20
20
|
| Clause 9 evaluable functors | integer, float, rounding, transcendental and bitwise operations | `arithmetic`, `corrigenda_arithmetic`, `corrigenda_atan2_zero`, `corrigenda_integer_negative_power` |
|
|
21
|
+
| ISO/IEC 13211-2 modules | module declarations, exports, imports, qualification, meta-predicate context | `modules/qualified_call`, `modules/selective_library_import`, `dcg_module_nonterminal_indicator` |
|
|
22
|
+
| ISO/IEC TS 13211-3 grammar rules | `-->`, terminal and partial sequences, grammar control constructs, semicontexts, nonterminal indicators, modules, `phrase/2-3`, steadfastness and errors | `dcg_terminals_and_remainder`, `dcg_control_constructs`, `dcg_partial_sequences`, `dcg_phrase_steadfastness`, `dcg_dynamic_nonterminal_indicator`, `logtalk_dcg_phrase_identity`, `logtalk_dcg_semicontexts`, DCG error and precedence cases |
|
|
21
23
|
|
|
22
24
|
Corrigendum-specific coverage includes double-quoted atom operator priority
|
|
23
25
|
(Cor.1); the added predicates and evaluable functors, bar-operator rules,
|
|
@@ -7,13 +7,15 @@ supported ISO Prolog profile, built-ins, extensions, and reasoner behavior.
|
|
|
7
7
|
|
|
8
8
|
The suite is intentionally file-based. Exact standard output, errors, warnings,
|
|
9
9
|
and proof output test the behavior of the JavaScript implementation.
|
|
10
|
-
[ISO-MATRIX.md](ISO-MATRIX.md) maps normative clause families
|
|
11
|
-
corrigenda to representative
|
|
10
|
+
[ISO-MATRIX.md](ISO-MATRIX.md) maps Part 1 normative clause families, all three
|
|
11
|
+
corrigenda, Part 2 modules, and Part 3 definite clause grammars to representative
|
|
12
|
+
executable cases.
|
|
12
13
|
|
|
13
14
|
“Conformance” here means conformance to EyeProlog's documented ISO compatibility
|
|
14
15
|
profile and implementation extensions. The default registry covers the exact
|
|
15
16
|
predicate indicators listed in Appendix B of the book across the supported
|
|
16
|
-
ISO/IEC 13211-1:1995
|
|
17
|
+
ISO/IEC 13211-1:1995, ISO/IEC 13211-2:2000, and ISO/IEC TS 13211-3:2025
|
|
18
|
+
standard families. This suite is still not an independent certification of
|
|
17
19
|
every processor requirement, lexical edge, option combination, or prescribed
|
|
18
20
|
error precedence. Cases under `iso/` identify standards-derived behavior;
|
|
19
21
|
other directories cover EyeProlog host contracts and extensions.
|
|
@@ -88,11 +90,11 @@ ISO/IEC 13211-1 clauses 7 and 8. In particular, isolated negative cases keep
|
|
|
88
90
|
instantiation, type, domain, permission, representation, and evaluation errors
|
|
89
91
|
independently observable.
|
|
90
92
|
|
|
91
|
-
Selected cases are adapted from the ISO and standard-core suites of
|
|
92
|
-
Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
|
|
93
|
+
Selected cases are adapted from the ISO and standard-core suites of Logtalk,
|
|
94
|
+
Scryer Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
|
|
93
95
|
are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
|
|
94
96
|
|
|
95
|
-
The corpus has
|
|
97
|
+
The corpus has 373 cases in `iso/` and 779 file-based conformance cases in
|
|
96
98
|
total. The generated `conformance-report.md` is the authoritative source for
|
|
97
99
|
current category totals. Together with regression, documentation-sync, API,
|
|
98
100
|
example, and book-example checks, `npm test` is the release gate.
|