eyeprolog 0.5.13 → 0.5.15
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 +2 -0
- package/conformance-report.md +2 -2
- package/examples/auroracare.pl +26 -0
- package/examples/bmi.pl +20 -1
- package/examples/cdcl-sat-solver.pl +6 -6
- package/examples/composition-of-injective-functions-is-injective.pl +4 -1
- package/examples/deep-taxonomy-10.pl +2 -0
- package/examples/deep-taxonomy-100.pl +2 -0
- package/examples/deep-taxonomy-1000.pl +2 -0
- package/examples/deep-taxonomy-10000.pl +2 -0
- package/examples/exoplanet-validation-worlds.pl +3 -0
- package/examples/family-cousins.pl +2 -0
- package/examples/fastpow.pl +6 -6
- package/examples/flandor.pl +14 -0
- package/examples/gd-step-certified.pl +3 -0
- package/examples/group-inverse-uniqueness.pl +9 -9
- package/examples/illegitimate-reasoning.pl +6 -0
- package/examples/input/odrl-dpv-fpv-trust-flow-rules.pl +3 -9
- package/examples/list-collection.pl +6 -6
- package/examples/lldm.pl +2 -0
- package/examples/odrl-dpv-fpv-trust-flow.pl +3 -9
- package/examples/output/aliases-and-namespaces.pl +1 -1
- package/examples/output/bmi.pl +9 -0
- package/examples/output/dijkstra-findall-sort.pl +1 -1
- package/examples/output/dijkstra.pl +16 -16
- package/examples/output/drone-corridor-planner.pl +17 -17
- package/examples/output/iso-atomic-conversion.pl +1 -1
- package/examples/output/list-collection.pl +6 -6
- package/examples/pointer-analysis.pl +3 -0
- package/examples/proof/aliases-and-namespaces.pl +5 -5
- package/examples/proof/composition-of-injective-functions-is-injective.pl +80 -97
- package/examples/proof/graph-reachability.pl +22 -22
- package/examples/proof/group-inverse-uniqueness.pl +4 -4
- package/examples/proof/list-collection.pl +73 -29
- package/examples/proof/spacecraft-battery-diagnosis.pl +8 -8
- package/examples/proof/term-tools.pl +1 -1
- package/examples/relational-cube-lookup.pl +2002 -2000
- package/examples/riemann-hypothesis.pl +5 -0
- package/examples/spacecraft-battery-diagnosis.pl +8 -8
- package/examples/vulnerability-impact.pl +4 -0
- package/index.d.ts +5 -2
- package/package.json +1 -1
- package/src/cli.js +17 -9
- package/src/index.js +15 -5
- package/src/iso.js +1 -0
- package/src/parser.js +57 -12
- package/src/program.js +8 -5
- package/src/solver.js +11 -3
- package/src/term.js +41 -10
- package/src/write.js +6 -0
- package/test/conformance/README.md +1 -1
- package/test/conformance/cases/arithmetic/082_comparison_semantics.pl +2 -2
- package/test/conformance/cases/arithmetic/extra_le_equal_string.pl +1 -1
- package/test/conformance/cases/context/extra_context_ignores_string_parts.pl +1 -1
- package/test/conformance/cases/iso/double_quoted_lists.pl +27 -0
- package/test/conformance/cases/strings/023_quoted_escapes_readback.pl +2 -3
- package/test/conformance/cases/terms/003_terms_and_readback.pl +1 -1
- package/test/conformance/cases/terms/073_term_introspection_edges.pl +1 -1
- package/test/conformance/cases/terms/096_functor_scalar_edges.pl +2 -2
- package/test/conformance/cases/terms/extra_functor_string_scalar.pl +1 -1
- package/test/conformance/cases/terms/functor_number_and_string.pl +1 -1
- package/test/conformance/cases/unification/extra_eq_scalar_string_number.pl +1 -1
- package/test/conformance/cases/unification/scalar_cross_type_same_lexical_value.pl +2 -2
- package/test/conformance/expected/aggregation/044_findall_user_goal.pl +1 -1
- package/test/conformance/expected/arithmetic/082_comparison_semantics.pl +1 -1
- package/test/conformance/expected/arithmetic/extra_le_equal_string.pl +1 -1
- package/test/conformance/expected/context/extra_context_atom_parts.pl +1 -1
- package/test/conformance/expected/context/extra_context_ignores_string_parts.pl +1 -0
- package/test/conformance/expected/context/extra_context_list_parts.pl +1 -1
- package/test/conformance/expected/iso/atomic_term_processing.pl +1 -1
- package/test/conformance/expected/iso/double_quoted_lists.pl +8 -0
- package/test/conformance/expected/iso/exceptions_and_flags.pl +1 -1
- package/test/conformance/expected/iso/grouped_solutions_and_clauses.pl +1 -1
- package/test/conformance/expected/iso/logtalk_atom_chars.pl +1 -1
- package/test/conformance/expected/iso/logtalk_number_chars.pl +1 -1
- package/test/conformance/expected/iso/logtalk_prolog_flags.pl +1 -1
- package/test/conformance/expected/iso/logtalk_univ.pl +1 -1
- package/test/conformance/expected/iso/scryer_lexical_terms.pl +1 -1
- package/test/conformance/expected/lists/005_list_deconstruction.pl +1 -1
- package/test/conformance/expected/lists/031_lists_aggregation_ordering.pl +4 -4
- package/test/conformance/expected/lists/035_list_relations.pl +3 -3
- package/test/conformance/expected/lists/036_append_splits.pl +3 -3
- package/test/conformance/expected/lists/045_sort_deduplicates_atoms.pl +1 -1
- package/test/conformance/expected/lists/046_append_bound_prefix_suffix.pl +2 -2
- package/test/conformance/expected/lists/048_set_nth0_edges.pl +2 -2
- package/test/conformance/expected/lists/049_select_duplicate_occurrences.pl +2 -2
- package/test/conformance/expected/lists/067_list_edges.pl +1 -1
- package/test/conformance/expected/lists/068_list_generation_order.pl +6 -6
- package/test/conformance/expected/lists/084_append_and_select_composition.pl +9 -9
- package/test/conformance/expected/lists/085_nth_and_update_edges.pl +1 -1
- package/test/conformance/expected/lists/087_sort_reverse_length.pl +2 -2
- package/test/conformance/expected/lists/extra_append_all_splits_three.pl +4 -4
- package/test/conformance/expected/lists/extra_list_to_set_stable_order.pl +1 -1
- package/test/conformance/expected/lists/extra_reverse_nested_terms.pl +1 -1
- package/test/conformance/expected/lists/extra_slice_end.pl +1 -1
- package/test/conformance/expected/lists/extra_take_all.pl +1 -1
- package/test/conformance/expected/lists/list_pattern_last.pl +1 -1
- package/test/conformance/expected/lists/select_all_occurrences.pl +3 -3
- package/test/conformance/expected/lists/set_nth0_middle.pl +1 -1
- package/test/conformance/expected/rules/077_recursive_path_with_lists.pl +3 -3
- package/test/conformance/expected/strings/023_quoted_escapes_readback.pl +1 -1
- package/test/conformance/expected/strings/071_string_edges.pl +1 -1
- package/test/conformance/expected/strings/extra_split_empty_separator.pl +1 -1
- package/test/conformance/expected/terms/003_terms_and_readback.pl +3 -3
- package/test/conformance/expected/terms/065_reusable_term_control_builtins.pl +1 -1
- package/test/conformance/expected/terms/073_term_introspection_edges.pl +2 -2
- package/test/conformance/expected/terms/095_term_introspection_roundtrip.pl +1 -1
- package/test/conformance/expected/terms/096_functor_scalar_edges.pl +1 -1
- package/test/conformance/expected/terms/extra_compound_name_arguments_decompose_list.pl +1 -1
- package/test/conformance/expected/terms/extra_functor_string_scalar.pl +1 -1
- package/test/conformance/expected/terms/functor_number_and_string.pl +1 -1
- package/test/conformance/expected/unification/012_nested_compound_unification.pl +1 -1
- package/test/conformance/expected/unification/034_equality_and_inequality.pl +1 -1
- package/test/conformance/expected/unification/extra_eq_list_tail_binding.pl +1 -1
- package/test/conformance/expected/unification/list_tail_binding.pl +1 -1
- package/test/conformance/expected-proofs/proofs/bindings_nested_term.pl +5 -5
- package/test/conformance/expected-proofs/proofs/extra_compound_name_arguments.pl +4 -4
- package/test/conformance/expected-proofs/proofs/extra_findall_builtin.pl +5 -5
- package/test/conformance/expected-proofs/proofs/extra_nested_list_binding.pl +5 -5
- package/test/conformance/expected-proofs/proofs/extra_once_member.pl +2 -2
- package/test/conformance/expected-proofs/proofs/list_builtin.pl +1 -1
- package/test/run-playground.mjs +2 -2
- package/test/run-regression.mjs +69 -11
- package/the-art-of-eyeprolog.md +297 -38
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
%% goal: zero_check(X0, X1, X2)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
:- discontiguous(zeta_zero/1).
|
|
15
|
+
:- discontiguous(real_part/2).
|
|
16
|
+
:- discontiguous(imaginary_part/2).
|
|
17
|
+
:- discontiguous(source/2).
|
|
18
|
+
|
|
14
19
|
% --- Sample zero catalogue -------------------------------------------------
|
|
15
20
|
%
|
|
16
21
|
% The three z* entries stand for catalogued non-trivial zeros of the
|
|
@@ -23,14 +23,14 @@ telemetry(bp1, cell_delta_v, 0.19).
|
|
|
23
23
|
redundant_telemetry(bp1, temperature_c, 76.0).
|
|
24
24
|
|
|
25
25
|
% Illustrative engineering limits and available cooling power.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
safety_limit(max_safe_temperature_c, 60.0).
|
|
27
|
+
safety_limit(max_temperature_rise_c_per_min, 1.5).
|
|
28
|
+
safety_limit(max_cell_delta_v, 0.08).
|
|
29
29
|
cooling_capacity_w(bp1, 12.0).
|
|
30
30
|
|
|
31
31
|
% Thermal margin is positive below the limit and negative above it.
|
|
32
32
|
metric(Pack, thermal_margin_c, Margin) :-
|
|
33
|
-
|
|
33
|
+
safety_limit(max_safe_temperature_c, Maximum),
|
|
34
34
|
telemetry(Pack, temperature_c, Temperature),
|
|
35
35
|
(Margin is Maximum - Temperature).
|
|
36
36
|
|
|
@@ -43,17 +43,17 @@ metric(Pack, resistive_heating_w, Heating) :-
|
|
|
43
43
|
|
|
44
44
|
over_temperature(Pack) :-
|
|
45
45
|
telemetry(Pack, temperature_c, Temperature),
|
|
46
|
-
|
|
46
|
+
safety_limit(max_safe_temperature_c, Maximum),
|
|
47
47
|
(Temperature > Maximum).
|
|
48
48
|
|
|
49
49
|
rapid_heating(Pack) :-
|
|
50
50
|
telemetry(Pack, temperature_rise_c_per_min, Rate),
|
|
51
|
-
|
|
51
|
+
safety_limit(max_temperature_rise_c_per_min, Maximum),
|
|
52
52
|
(Rate > Maximum).
|
|
53
53
|
|
|
54
54
|
cell_imbalance(Pack) :-
|
|
55
55
|
telemetry(Pack, cell_delta_v, Delta),
|
|
56
|
-
|
|
56
|
+
safety_limit(max_cell_delta_v, Maximum),
|
|
57
57
|
(Delta > Maximum).
|
|
58
58
|
|
|
59
59
|
heating_exceeds_cooling(Pack) :-
|
|
@@ -65,7 +65,7 @@ heating_exceeds_cooling(Pack) :-
|
|
|
65
65
|
corroborated_over_temperature(Pack) :-
|
|
66
66
|
telemetry(Pack, temperature_c, Primary),
|
|
67
67
|
redundant_telemetry(Pack, temperature_c, Redundant),
|
|
68
|
-
|
|
68
|
+
safety_limit(max_safe_temperature_c, Maximum),
|
|
69
69
|
(Primary > Maximum),
|
|
70
70
|
(Redundant > Maximum).
|
|
71
71
|
|
package/index.d.ts
CHANGED
|
@@ -16,6 +16,8 @@ export interface EyePrologRunOptions {
|
|
|
16
16
|
sourceMetadata?: boolean;
|
|
17
17
|
strictNegation?: boolean;
|
|
18
18
|
analyzeNegation?: boolean;
|
|
19
|
+
/** Initial ISO interpretation of double-quoted list notation. Defaults to chars. */
|
|
20
|
+
doubleQuotes?: 'chars' | 'codes' | 'atom';
|
|
19
21
|
ioOptions?: {
|
|
20
22
|
input?: string;
|
|
21
23
|
write?: (text: string) => void;
|
|
@@ -88,6 +90,7 @@ export class Program {
|
|
|
88
90
|
constructor(clauses?: EyePrologClause[], options?: EyePrologRunOptions);
|
|
89
91
|
clauses: EyePrologClause[];
|
|
90
92
|
groups: Map<string, EyePrologPredicateGroup>;
|
|
93
|
+
doubleQuotes: 'chars' | 'codes' | 'atom';
|
|
91
94
|
negationDependencies: Array<{ from: string; to: string; negative: boolean }>;
|
|
92
95
|
negationStratificationErrors: Array<{ from: string; to: string }>;
|
|
93
96
|
stratifiedNegation: boolean;
|
|
@@ -101,7 +104,7 @@ export class Program {
|
|
|
101
104
|
assertStratifiedNegation(): true;
|
|
102
105
|
isStratifiedNegation(): boolean;
|
|
103
106
|
groupHasRule(group: EyePrologPredicateGroup): boolean;
|
|
104
|
-
sourceFactLines(predicateKeys?: Set<string> | null): Set<string>;
|
|
107
|
+
sourceFactLines(predicateKeys?: Set<string> | null, options?: { doubleQuotes?: 'chars' | 'codes' | 'atom' }): Set<string>;
|
|
105
108
|
}
|
|
106
109
|
|
|
107
110
|
export interface BuiltinDefinition {
|
|
@@ -181,7 +184,7 @@ export function compareNumberText(left: string, right: string): number;
|
|
|
181
184
|
export function makeProgram(source: string, options?: EyePrologRunOptions): Program;
|
|
182
185
|
export function parseClauses(source: string, options?: EyePrologRunOptions): EyePrologClause[];
|
|
183
186
|
export function parseProgramText(source: string, options?: EyePrologRunOptions): EyePrologClause[];
|
|
184
|
-
export function parseGoalText(source: string): EyePrologTerm;
|
|
187
|
+
export function parseGoalText(source: string, options?: EyePrologRunOptions): EyePrologTerm;
|
|
185
188
|
export function createDefaultRegistry(): BuiltinRegistry;
|
|
186
189
|
export function createEyePrologRegistry(): BuiltinRegistry;
|
|
187
190
|
export function getDefaultRegistry(): BuiltinRegistry;
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -128,22 +128,27 @@ async function loadExplanation() {
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
async function runDefault(engine, program, options) {
|
|
131
|
+
const registry = engine.getEyePrologRegistry();
|
|
132
|
+
const solver = new engine.Solver(program, {
|
|
133
|
+
registry,
|
|
134
|
+
ioOptions: { write: (text) => process.stdout.write(String(text)) },
|
|
135
|
+
});
|
|
136
|
+
program = solver.program;
|
|
131
137
|
const goals = options.goals.map((text) => {
|
|
132
|
-
const goal = engine.parseGoalText(text
|
|
138
|
+
const goal = engine.parseGoalText(text, {
|
|
139
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
140
|
+
});
|
|
133
141
|
if (goal.type === 'var') throw new engine.PrologError('instantiation_error');
|
|
134
142
|
if (goal.type !== 'atom' && goal.type !== 'compound') throw new engine.PrologError('type_error(callable)', goal);
|
|
135
143
|
return goal;
|
|
136
144
|
});
|
|
137
145
|
const queriedKeys = new Set(goals.map((goal) => `${goal.name}/${goal.arity}`));
|
|
138
|
-
const
|
|
146
|
+
const writeOptions = {
|
|
147
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
148
|
+
};
|
|
149
|
+
const facts = program.sourceFactLines(queriedKeys, writeOptions);
|
|
139
150
|
const lines = new Set();
|
|
140
|
-
const registry = engine.getEyePrologRegistry();
|
|
141
151
|
const explanation = options.proof ? await loadExplanation() : null;
|
|
142
|
-
const solver = new engine.Solver(program, {
|
|
143
|
-
registry,
|
|
144
|
-
ioOptions: { write: (text) => process.stdout.write(String(text)) },
|
|
145
|
-
});
|
|
146
|
-
program = solver.program;
|
|
147
152
|
try {
|
|
148
153
|
solver.runInitializations();
|
|
149
154
|
for (const goal of goals) {
|
|
@@ -151,7 +156,10 @@ async function runDefault(engine, program, options) {
|
|
|
151
156
|
for (const env of solver.solve([goal], new engine.Env(), 0)) {
|
|
152
157
|
if (!engine.termIsGround(goal, env)) continue;
|
|
153
158
|
|
|
154
|
-
const
|
|
159
|
+
const currentWriteOptions = {
|
|
160
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
161
|
+
};
|
|
162
|
+
const line = `${engine.termToString(goal, env, true, currentWriteOptions)}.\n`;
|
|
155
163
|
if (facts.has(line) || lines.has(line)) continue;
|
|
156
164
|
|
|
157
165
|
lines.add(line);
|
package/src/index.js
CHANGED
|
@@ -47,9 +47,12 @@ export function run(source, options = {}) {
|
|
|
47
47
|
},
|
|
48
48
|
});
|
|
49
49
|
program = solver.program;
|
|
50
|
-
const goals = normalizeGoals(options);
|
|
50
|
+
const goals = normalizeGoals(options, solver);
|
|
51
|
+
const writeOptions = {
|
|
52
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
53
|
+
};
|
|
51
54
|
const queriedKeys = new Set(goals.map((goal) => `${goal.name}/${goal.arity}`));
|
|
52
|
-
const facts = program.sourceFactLines(queriedKeys);
|
|
55
|
+
const facts = program.sourceFactLines(queriedKeys, writeOptions);
|
|
53
56
|
const seen = new Set();
|
|
54
57
|
let haltCode = null;
|
|
55
58
|
try {
|
|
@@ -59,7 +62,10 @@ export function run(source, options = {}) {
|
|
|
59
62
|
for (const env of solver.solve([goal], new Env(), 0)) {
|
|
60
63
|
const resolved = copyResolved(goal, env);
|
|
61
64
|
if (!termIsGround(resolved)) continue;
|
|
62
|
-
const
|
|
65
|
+
const currentWriteOptions = {
|
|
66
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
67
|
+
};
|
|
68
|
+
const line = `${termToString(resolved, new Env(), true, currentWriteOptions)}.\n`;
|
|
63
69
|
if (facts.has(line) || seen.has(line)) continue;
|
|
64
70
|
seen.add(line);
|
|
65
71
|
output.push(line);
|
|
@@ -73,10 +79,14 @@ export function run(source, options = {}) {
|
|
|
73
79
|
return { stdout: output.join(''), stats: solver.stats, haltCode };
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
function normalizeGoals(options) {
|
|
82
|
+
function normalizeGoals(options, solver) {
|
|
77
83
|
const requested = options.goals ?? (options.goal == null ? [] : [options.goal]);
|
|
78
84
|
return requested.map((requestedGoal) => {
|
|
79
|
-
const goal = typeof requestedGoal === 'string'
|
|
85
|
+
const goal = typeof requestedGoal === 'string'
|
|
86
|
+
? parseGoalText(requestedGoal, {
|
|
87
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
88
|
+
})
|
|
89
|
+
: requestedGoal;
|
|
80
90
|
if (goal.type === VAR) throw new PrologError('instantiation_error');
|
|
81
91
|
if (goal.type !== ATOM && goal.type !== COMPOUND) throw new PrologError('type_error(callable)', goal);
|
|
82
92
|
return goal;
|
package/src/iso.js
CHANGED
|
@@ -920,6 +920,7 @@ function readTermFromStream(stream, solver) {
|
|
|
920
920
|
const clauses = parseClauses(convertedTermText(candidate.text, solver), {
|
|
921
921
|
sourceMetadata: false,
|
|
922
922
|
operatorState,
|
|
923
|
+
doubleQuotes: solver.prologFlags.get('double_quotes')?.value?.name ?? 'chars',
|
|
923
924
|
});
|
|
924
925
|
if (clauses.length !== 1 || clauses[0].body.length) throw new Error('bad term');
|
|
925
926
|
stream.position = candidate.end;
|
package/src/parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Tokenizer and recursive-descent parser for the EyeProlog source language.
|
|
2
2
|
// It preserves the compact Prolog-like syntax while producing Term objects for the solver.
|
|
3
|
-
import { atom, compound, cons, emptyList, numberTerm,
|
|
3
|
+
import { atom, compound, cons, emptyList, numberTerm, variable } from './term.js';
|
|
4
4
|
|
|
5
5
|
const TOK = {
|
|
6
6
|
EOF: 'eof', ATOM: 'atom', VAR: 'var', STRING: 'string', NUMBER: 'number',
|
|
@@ -143,6 +143,12 @@ class Parser {
|
|
|
143
143
|
this.line = 1;
|
|
144
144
|
this.anonymous = 0;
|
|
145
145
|
this.sourceMetadata = options.sourceMetadata !== false;
|
|
146
|
+
this.parserFlagState = options.parserFlagState ?? {
|
|
147
|
+
doubleQuotes: options.doubleQuotes ?? 'chars',
|
|
148
|
+
};
|
|
149
|
+
if (!['chars', 'codes', 'atom'].includes(this.parserFlagState.doubleQuotes)) {
|
|
150
|
+
throw new Error(`invalid double_quotes parser flag: ${this.parserFlagState.doubleQuotes}`);
|
|
151
|
+
}
|
|
146
152
|
const operatorState = options.operatorState ?? createParserOperatorState(
|
|
147
153
|
options.operatorDefinitions ?? [],
|
|
148
154
|
options.includeDefaultOperators !== false,
|
|
@@ -174,6 +180,14 @@ class Parser {
|
|
|
174
180
|
for (const name of names) this.defineOperator(priority, specifierTerm.name, name);
|
|
175
181
|
return true;
|
|
176
182
|
}
|
|
183
|
+
applyParserFlagDirective(directive) {
|
|
184
|
+
if (directive.type !== 'compound' || directive.name !== 'set_prolog_flag' || directive.arity !== 2) return;
|
|
185
|
+
const [flag, value] = directive.args;
|
|
186
|
+
if (flag.type === 'atom' && flag.name === 'double_quotes' &&
|
|
187
|
+
value.type === 'atom' && ['chars', 'codes', 'atom'].includes(value.name)) {
|
|
188
|
+
this.parserFlagState.doubleQuotes = value.name;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
177
191
|
peek(offset = 0) {
|
|
178
192
|
return this.source[this.pos + offset] ?? '';
|
|
179
193
|
}
|
|
@@ -494,7 +508,31 @@ class Parser {
|
|
|
494
508
|
if (this.token.type === TOK.STRING) {
|
|
495
509
|
const value = this.token.text;
|
|
496
510
|
this.advance();
|
|
497
|
-
|
|
511
|
+
if (this.parserFlagState.doubleQuotes === 'atom') {
|
|
512
|
+
if (this.token.type === TOK.LPAREN) {
|
|
513
|
+
this.advance();
|
|
514
|
+
const args = [];
|
|
515
|
+
if (this.token.type === TOK.RPAREN) {
|
|
516
|
+
throw new Error(`parse line ${this.token.line}: zero-arity compound syntax is not supported; use atom ${JSON.stringify(value)} for arity zero data`);
|
|
517
|
+
}
|
|
518
|
+
while (true) {
|
|
519
|
+
args.push(this.parseTerm(0, false));
|
|
520
|
+
if (this.token.type !== TOK.COMMA) break;
|
|
521
|
+
this.advance();
|
|
522
|
+
}
|
|
523
|
+
this.expect(TOK.RPAREN, ')');
|
|
524
|
+
this.advance();
|
|
525
|
+
return compound(value, args);
|
|
526
|
+
}
|
|
527
|
+
return atom(value);
|
|
528
|
+
}
|
|
529
|
+
const items = Array.from(value, (character) =>
|
|
530
|
+
this.parserFlagState.doubleQuotes === 'chars'
|
|
531
|
+
? atom(character)
|
|
532
|
+
: numberTerm(character.codePointAt(0)));
|
|
533
|
+
let list = emptyList();
|
|
534
|
+
for (let i = items.length - 1; i >= 0; i--) list = cons(items[i], list);
|
|
535
|
+
return list;
|
|
498
536
|
}
|
|
499
537
|
if (this.token.type === TOK.NUMBER) {
|
|
500
538
|
const value = this.token.text;
|
|
@@ -544,6 +582,7 @@ class Parser {
|
|
|
544
582
|
throw new Error(`parse line ${line}: bad term`);
|
|
545
583
|
}
|
|
546
584
|
this.expect(TOK.DOT, '.');
|
|
585
|
+
this.applyParserFlagDirective(directive);
|
|
547
586
|
this.advance();
|
|
548
587
|
const clause = { head: compound(':-', [directive]), body: [] };
|
|
549
588
|
clauseNumber++;
|
|
@@ -588,11 +627,17 @@ function listAtomNames(term) {
|
|
|
588
627
|
|
|
589
628
|
|
|
590
629
|
export function parseClauses(source, options = {}) {
|
|
630
|
+
const ownsParserFlagState = options.parserFlagState == null;
|
|
631
|
+
const initialDoubleQuotes = options.doubleQuotes ?? 'chars';
|
|
632
|
+
const parserOptions = ownsParserFlagState
|
|
633
|
+
? { ...options, parserFlagState: { doubleQuotes: initialDoubleQuotes } }
|
|
634
|
+
: options;
|
|
591
635
|
if (options.sourceMetadata === false) {
|
|
592
|
-
const clauses = parseClausesFastNoSource(source);
|
|
636
|
+
const clauses = parseClausesFastNoSource(source, null, null, parserOptions);
|
|
593
637
|
if (clauses) return clauses;
|
|
638
|
+
if (ownsParserFlagState) parserOptions.parserFlagState.doubleQuotes = initialDoubleQuotes;
|
|
594
639
|
}
|
|
595
|
-
return new Parser(source,
|
|
640
|
+
return new Parser(source, parserOptions).parseProgram();
|
|
596
641
|
}
|
|
597
642
|
|
|
598
643
|
// Streaming parser entry points used by ProgramBuilder. The ordinary public
|
|
@@ -602,8 +647,8 @@ export function parseClausesInto(source, options = {}, emit) {
|
|
|
602
647
|
new Parser(source, options).parseProgram(emit);
|
|
603
648
|
}
|
|
604
649
|
|
|
605
|
-
export function tryParseClausesFastInto(source, emit, emitBinary = null) {
|
|
606
|
-
return parseClausesFastNoSource(source, emit, emitBinary) !== null;
|
|
650
|
+
export function tryParseClausesFastInto(source, emit, emitBinary = null, options = {}) {
|
|
651
|
+
return parseClausesFastNoSource(source, emit, emitBinary, options) !== null;
|
|
607
652
|
}
|
|
608
653
|
|
|
609
654
|
function isSimpleName(text) {
|
|
@@ -624,7 +669,7 @@ const SIMPLE_VARIABLE = /^(?:_|[A-Z_][A-Za-z0-9_]*)$/;
|
|
|
624
669
|
const SIMPLE_ATOM = /^[a-z][A-Za-z0-9_]*$/;
|
|
625
670
|
const GRAPHIC_ATOM = /^[#$&*+\-\/<=>@^~\\]+$/;
|
|
626
671
|
|
|
627
|
-
function parseClausesFastNoSource(source, emit = null, emitBinary = null) {
|
|
672
|
+
function parseClausesFastNoSource(source, emit = null, emitBinary = null, options = {}) {
|
|
628
673
|
source = String(source ?? '');
|
|
629
674
|
const numberCache = new Map();
|
|
630
675
|
const stringCache = new Map();
|
|
@@ -896,7 +941,7 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null) {
|
|
|
896
941
|
return true;
|
|
897
942
|
}
|
|
898
943
|
try {
|
|
899
|
-
const parsed = new Parser(text, { sourceMetadata: false }).parseProgram();
|
|
944
|
+
const parsed = new Parser(text, { ...options, sourceMetadata: false }).parseProgram();
|
|
900
945
|
for (const clause of parsed) accept(clause);
|
|
901
946
|
return true;
|
|
902
947
|
} catch (_) {
|
|
@@ -938,12 +983,12 @@ function parseClausesFastNoSource(source, emit = null, emitBinary = null) {
|
|
|
938
983
|
return clauses ?? true;
|
|
939
984
|
}
|
|
940
985
|
|
|
941
|
-
export function parseProgramText(source) {
|
|
942
|
-
return parseClauses(source);
|
|
986
|
+
export function parseProgramText(source, options = {}) {
|
|
987
|
+
return parseClauses(source, options);
|
|
943
988
|
}
|
|
944
989
|
|
|
945
|
-
export function parseGoalText(text) {
|
|
946
|
-
const clauses = parseClauses(`zz_goal((${text}))
|
|
990
|
+
export function parseGoalText(text, options = {}) {
|
|
991
|
+
const clauses = parseClauses(`zz_goal((${text})).`, options);
|
|
947
992
|
const head = clauses[0]?.head;
|
|
948
993
|
if (clauses.length !== 1 || head?.type !== 'compound' ||
|
|
949
994
|
head.name !== 'zz_goal' || head.arity !== 1 || clauses[0].body.length !== 0) {
|
package/src/program.js
CHANGED
|
@@ -97,6 +97,7 @@ export class Program {
|
|
|
97
97
|
this.initializations = [];
|
|
98
98
|
this.prologFlagDirectives = [];
|
|
99
99
|
this.charConversionDirectives = [];
|
|
100
|
+
this.doubleQuotes = options.doubleQuotes ?? 'chars';
|
|
100
101
|
this._revisionState = { value: 0 };
|
|
101
102
|
this.mutable = false;
|
|
102
103
|
this._negationAnalysis = null;
|
|
@@ -372,19 +373,19 @@ export class Program {
|
|
|
372
373
|
groupHasRule(group) {
|
|
373
374
|
return group.clauses.some((clause) => clauseBodyLength(clause) > 0);
|
|
374
375
|
}
|
|
375
|
-
sourceFactLines(predicateKeys = null) {
|
|
376
|
+
sourceFactLines(predicateKeys = null, options = {}) {
|
|
376
377
|
const lines = new Set();
|
|
377
378
|
const env = new Env();
|
|
378
379
|
for (const clause of this.clauses) {
|
|
379
380
|
if (isCompactBinaryClause(clause)) {
|
|
380
381
|
if (clause.bodyName != null) continue;
|
|
381
382
|
if (predicateKeys && !predicateKeys.has(`${clause.headName}/2`)) continue;
|
|
382
|
-
lines.add(`${termToString(clause.head, env, true)}.\n`);
|
|
383
|
+
lines.add(`${termToString(clause.head, env, true, options)}.\n`);
|
|
383
384
|
continue;
|
|
384
385
|
}
|
|
385
386
|
if (clause.body.length !== 0 || (clause.head.type !== ATOM && clause.head.type !== COMPOUND)) continue;
|
|
386
387
|
if (predicateKeys && !predicateKeys.has(`${clause.head.name}/${clause.head.arity}`)) continue;
|
|
387
|
-
lines.add(`${termToString(clause.head, env, true)}.\n`);
|
|
388
|
+
lines.add(`${termToString(clause.head, env, true, options)}.\n`);
|
|
388
389
|
}
|
|
389
390
|
return lines;
|
|
390
391
|
}
|
|
@@ -530,9 +531,10 @@ function buildProgramFromSources(sources, options) {
|
|
|
530
531
|
function loadSourcesIntoBuilder(builder, sources, options, fast) {
|
|
531
532
|
const ensured = new Set();
|
|
532
533
|
const operatorState = createParserOperatorState();
|
|
534
|
+
const parserFlagState = { doubleQuotes: options.doubleQuotes ?? 'chars' };
|
|
533
535
|
const prepared = sources.map((source) => ({
|
|
534
536
|
source,
|
|
535
|
-
options: { ...sourceOptionsFor(source, options), operatorState },
|
|
537
|
+
options: { ...sourceOptionsFor(source, options), operatorState, parserFlagState },
|
|
536
538
|
}));
|
|
537
539
|
for (const item of prepared) {
|
|
538
540
|
const filename = sourcePath(item.options);
|
|
@@ -545,6 +547,7 @@ function loadSourcesIntoBuilder(builder, sources, options, fast) {
|
|
|
545
547
|
: item.source?.text ?? item.source?.source ?? '';
|
|
546
548
|
if (!loadSourceIntoBuilder(builder, text, item.options, ensured, fast)) return false;
|
|
547
549
|
}
|
|
550
|
+
builder.program.doubleQuotes = parserFlagState.doubleQuotes;
|
|
548
551
|
return true;
|
|
549
552
|
} catch (error) {
|
|
550
553
|
if (error === FAST_PARSE_ABORT) return false;
|
|
@@ -600,7 +603,7 @@ function loadSourceIntoBuilder(builder, source, options, ensured, fast) {
|
|
|
600
603
|
));
|
|
601
604
|
if (batch.length >= PROGRAM_BUILD_BATCH_SIZE) flush();
|
|
602
605
|
};
|
|
603
|
-
const parsed = tryParseClausesFastInto(source, accept, acceptBinary);
|
|
606
|
+
const parsed = tryParseClausesFastInto(source, accept, acceptBinary, options);
|
|
604
607
|
if (parsed) flush();
|
|
605
608
|
return parsed;
|
|
606
609
|
}
|
package/src/solver.js
CHANGED
|
@@ -27,11 +27,19 @@ export class Solver {
|
|
|
27
27
|
this.prologFlags = options.prologFlags ?? defaultPrologFlags(this.registry?.eyePrologLibrary ? 'fail' : 'error');
|
|
28
28
|
this.charConversions = options.charConversions ?? new Map();
|
|
29
29
|
if (!options.prologFlags) {
|
|
30
|
+
if (['chars', 'codes', 'atom'].includes(program.doubleQuotes)) {
|
|
31
|
+
this.prologFlags.get('double_quotes').value = compound(program.doubleQuotes, []);
|
|
32
|
+
}
|
|
30
33
|
for (const [flag, value] of program.prologFlagDirectives ?? []) {
|
|
34
|
+
if (flag.type === 'var' || value.type === 'var') throw new PrologError('instantiation_error');
|
|
35
|
+
if (flag.type !== 'atom') throw new PrologError('type_error(atom)', flag);
|
|
31
36
|
const definition = this.prologFlags.get(flag.name);
|
|
32
|
-
if (
|
|
33
|
-
|
|
37
|
+
if (!definition) throw new PrologError('domain_error(prolog_flag)', flag);
|
|
38
|
+
if (value.type !== 'atom' || !definition.allowed.includes(value.name)) {
|
|
39
|
+
throw new PrologError('domain_error(flag_value)', compound('+', [flag, value]));
|
|
34
40
|
}
|
|
41
|
+
if (!definition.changeable) throw new PrologError('permission_error(modify, flag)', flag);
|
|
42
|
+
definition.value = value;
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
45
|
if (!options.charConversions) {
|
|
@@ -383,7 +391,7 @@ function defaultPrologFlags(unknown = 'error') {
|
|
|
383
391
|
['debug', { value: compound('off', []), allowed: ['on', 'off'], changeable: true }],
|
|
384
392
|
['max_arity', { value: compound('unbounded', []), allowed: ['unbounded'], changeable: false }],
|
|
385
393
|
['unknown', { value: compound(unknown, []), allowed: ['error', 'fail', 'warning'], changeable: true }],
|
|
386
|
-
['double_quotes', { value: compound('
|
|
394
|
+
['double_quotes', { value: compound('chars', []), allowed: ['chars', 'codes', 'atom'], changeable: true }],
|
|
387
395
|
]);
|
|
388
396
|
}
|
|
389
397
|
|
package/src/term.js
CHANGED
|
@@ -314,37 +314,68 @@ function writeString(value, quoteStrings) {
|
|
|
314
314
|
let out = '"';
|
|
315
315
|
for (const ch of value) {
|
|
316
316
|
if (ch === '"' || ch === '\\') out += `\\${ch}`;
|
|
317
|
+
else if (ch === '\x07') out += '\\a';
|
|
318
|
+
else if (ch === '\b') out += '\\b';
|
|
319
|
+
else if (ch === '\r') out += '\\r';
|
|
320
|
+
else if (ch === '\f') out += '\\f';
|
|
321
|
+
else if (ch === '\t') out += '\\t';
|
|
317
322
|
else if (ch === '\n') out += '\\n';
|
|
323
|
+
else if (ch === '\v') out += '\\v';
|
|
318
324
|
else out += ch;
|
|
319
325
|
}
|
|
320
326
|
return out + '"';
|
|
321
327
|
}
|
|
322
328
|
|
|
323
|
-
function
|
|
329
|
+
function quotedListText(term, env, doubleQuotes) {
|
|
330
|
+
if (doubleQuotes !== 'chars' && doubleQuotes !== 'codes') return null;
|
|
331
|
+
const characters = [];
|
|
332
|
+
let cursor = term;
|
|
333
|
+
while (true) {
|
|
334
|
+
cursor = deref(cursor, env);
|
|
335
|
+
if (isEmptyList(cursor)) return characters.length === 0 ? null : characters.join('');
|
|
336
|
+
if (!isCons(cursor)) return null;
|
|
337
|
+
const item = deref(cursor.args[0], env);
|
|
338
|
+
if (doubleQuotes === 'chars') {
|
|
339
|
+
if (item.type !== ATOM || Array.from(item.name).length !== 1) return null;
|
|
340
|
+
characters.push(item.name);
|
|
341
|
+
} else {
|
|
342
|
+
if (item.type !== NUMBER || !/^\d+$/.test(item.name)) return null;
|
|
343
|
+
const code = BigInt(item.name);
|
|
344
|
+
if (code < 0n || code > 0x10ffffn || (code >= 0xd800n && code <= 0xdfffn)) return null;
|
|
345
|
+
characters.push(String.fromCodePoint(Number(code)));
|
|
346
|
+
}
|
|
347
|
+
cursor = cursor.args[1];
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function writeList(term, env, options) {
|
|
352
|
+
const quotedText = quotedListText(term, env, options.doubleQuotes);
|
|
353
|
+
if (quotedText != null) return writeString(quotedText, true);
|
|
324
354
|
const parts = [];
|
|
325
355
|
let cursor = term;
|
|
326
356
|
while (true) {
|
|
327
357
|
cursor = deref(cursor, env);
|
|
328
358
|
if (isEmptyList(cursor)) return `[${parts.join(', ')}]`;
|
|
329
359
|
if (!isCons(cursor)) {
|
|
330
|
-
if (parts.length) return `[${parts.join(', ')} | ${termToString(cursor, env, true)}]`;
|
|
331
|
-
return `[${termToString(cursor, env, true)}]`;
|
|
360
|
+
if (parts.length) return `[${parts.join(', ')} | ${termToString(cursor, env, true, options)}]`;
|
|
361
|
+
return `[${termToString(cursor, env, true, options)}]`;
|
|
332
362
|
}
|
|
333
|
-
parts.push(termToString(cursor.args[0], env, true));
|
|
363
|
+
parts.push(termToString(cursor.args[0], env, true, options));
|
|
334
364
|
cursor = cursor.args[1];
|
|
335
365
|
}
|
|
336
366
|
}
|
|
337
367
|
|
|
338
|
-
export function termToString(term, env = new Env(), quoteStrings = true) {
|
|
368
|
+
export function termToString(term, env = new Env(), quoteStrings = true, options = {}) {
|
|
369
|
+
options = { doubleQuotes: options.doubleQuotes ?? 'chars' };
|
|
339
370
|
const resolved = deref(term, env);
|
|
340
371
|
if (resolved.type === VAR) return writeVariable(resolved.name);
|
|
341
|
-
if (isCons(resolved)) return writeList(resolved, env);
|
|
372
|
+
if (isCons(resolved)) return writeList(resolved, env, options);
|
|
342
373
|
if (resolved.type === STRING) return writeString(resolved.name, quoteStrings);
|
|
343
374
|
if (resolved.type === ATOM) return writeAtom(resolved.name);
|
|
344
375
|
if (resolved.type === NUMBER) return resolved.name;
|
|
345
376
|
if (resolved.type === COMPOUND && resolved.arity === 0) return writeAtom(resolved.name);
|
|
346
377
|
if (resolved.type === COMPOUND && resolved.name === '{}' && resolved.arity === 1) {
|
|
347
|
-
return `{${termToString(resolved.args[0], env, true)}}`;
|
|
378
|
+
return `{${termToString(resolved.args[0], env, true, options)}}`;
|
|
348
379
|
}
|
|
349
380
|
if (isConjunction(resolved)) {
|
|
350
381
|
const parts = [];
|
|
@@ -352,16 +383,16 @@ export function termToString(term, env = new Env(), quoteStrings = true) {
|
|
|
352
383
|
while (true) {
|
|
353
384
|
cursor = deref(cursor, env);
|
|
354
385
|
if (isConjunction(cursor)) {
|
|
355
|
-
parts.push(termToString(cursor.args[0], env, true));
|
|
386
|
+
parts.push(termToString(cursor.args[0], env, true, options));
|
|
356
387
|
cursor = cursor.args[1];
|
|
357
388
|
} else {
|
|
358
|
-
parts.push(termToString(cursor, env, true));
|
|
389
|
+
parts.push(termToString(cursor, env, true, options));
|
|
359
390
|
break;
|
|
360
391
|
}
|
|
361
392
|
}
|
|
362
393
|
return `(${parts.join(', ')})`;
|
|
363
394
|
}
|
|
364
|
-
return `${writeAtom(resolved.name)}(${resolved.args.map((arg) => termToString(arg, env, true)).join(', ')})`;
|
|
395
|
+
return `${writeAtom(resolved.name)}(${resolved.args.map((arg) => termToString(arg, env, true, options)).join(', ')})`;
|
|
365
396
|
}
|
|
366
397
|
|
|
367
398
|
export function lexicalValue(term, env) {
|
package/src/write.js
CHANGED
|
@@ -52,7 +52,13 @@ function writeString(value) {
|
|
|
52
52
|
let out = '"';
|
|
53
53
|
for (const ch of value) {
|
|
54
54
|
if (ch === '"' || ch === '\\') out += `\\${ch}`;
|
|
55
|
+
else if (ch === '\x07') out += '\\a';
|
|
56
|
+
else if (ch === '\b') out += '\\b';
|
|
57
|
+
else if (ch === '\r') out += '\\r';
|
|
58
|
+
else if (ch === '\f') out += '\\f';
|
|
59
|
+
else if (ch === '\t') out += '\\t';
|
|
55
60
|
else if (ch === '\n') out += '\\n';
|
|
61
|
+
else if (ch === '\v') out += '\\v';
|
|
56
62
|
else out += ch;
|
|
57
63
|
}
|
|
58
64
|
return out + '"';
|
|
@@ -90,7 +90,7 @@ Selected cases are adapted from the ISO and standard-core suites of Scryer
|
|
|
90
90
|
Prolog, Trealla Prolog, and SWI-Prolog. Their upstream identifiers and licenses
|
|
91
91
|
are recorded in [THIRD_PARTY.md](THIRD_PARTY.md).
|
|
92
92
|
|
|
93
|
-
The corpus has
|
|
93
|
+
The corpus has 280 cases in `iso/` and 686 file-based conformance cases in
|
|
94
94
|
total. The generated `conformance-report.md` is the authoritative source for
|
|
95
95
|
current category totals. Together with regression, documentation-sync, API,
|
|
96
96
|
RDF-tool, example, and book-example checks, `npm test` is the release gate.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
% Reference 9.3: comparison handles numbers, durations,
|
|
1
|
+
% Reference 9.3: comparison handles numbers, durations, character lists, and lexical scalars.
|
|
2
2
|
%% goal: answer(X0, X1)
|
|
3
3
|
|
|
4
4
|
answer(integer_order, ok) :- (9 < 10).
|
|
@@ -7,5 +7,5 @@ answer(numeric_not_lexical, ok) :- (10 < 100).
|
|
|
7
7
|
answer(duration_years, ok) :- ('P1Y' @< 'P2Y').
|
|
8
8
|
answer(duration_months, ok) :- ('P1Y1M' @> 'P1Y').
|
|
9
9
|
answer(atom_lexical, ok) :- (alpha @< beta).
|
|
10
|
-
answer(
|
|
10
|
+
answer(char_list_order, ok) :- ("z" @> "a").
|
|
11
11
|
answer(compound_lexical_text, ok) :- (pair(a, 1) @< pair(b, 1)).
|
|
@@ -4,4 +4,4 @@ context_member((Left, _right), Member) :- context_member(Left, Member).
|
|
|
4
4
|
context_member((_left, Right), Member) :- context_member(Right, Member).
|
|
5
5
|
context_member(Member, Member) :- Member \= (_left, _right).
|
|
6
6
|
context_parts(Context, Name, Args) :- context_member(Context, Statement), (Statement =.. [Name | Args]), atom(Name).
|
|
7
|
-
answer(
|
|
7
|
+
answer(context_inspects_char_list) :- context_parts("not a term", _, _).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
% ISO double-quoted list notation follows the active double_quotes flag.
|
|
2
|
+
%% goal: default_chars(X0)
|
|
3
|
+
%% goal: default_empty(X0)
|
|
4
|
+
%% goal: default_shape(X0)
|
|
5
|
+
%% goal: atom_mode(X0)
|
|
6
|
+
%% goal: atom_functor(X0)
|
|
7
|
+
%% goal: atom_shape(X0)
|
|
8
|
+
%% goal: codes_mode(X0)
|
|
9
|
+
%% goal: codes_shape(X0)
|
|
10
|
+
|
|
11
|
+
stored_chars("ab").
|
|
12
|
+
stored_empty("").
|
|
13
|
+
default_chars(Value) :- stored_chars(Value).
|
|
14
|
+
default_empty(Value) :- stored_empty(Value).
|
|
15
|
+
default_shape(compound) :- compound("a"), \+ atomic("a").
|
|
16
|
+
|
|
17
|
+
:- set_prolog_flag(double_quotes, atom).
|
|
18
|
+
stored_atom("ab").
|
|
19
|
+
stored_compound("pair"(a, b)).
|
|
20
|
+
atom_mode(Value) :- stored_atom(Value).
|
|
21
|
+
atom_functor(Value) :- stored_compound(Value).
|
|
22
|
+
atom_shape(atomic) :- atom("a"), atomic("a").
|
|
23
|
+
|
|
24
|
+
:- set_prolog_flag(double_quotes, codes).
|
|
25
|
+
stored_codes("ab").
|
|
26
|
+
codes_mode(Value) :- stored_codes(Value).
|
|
27
|
+
codes_shape(compound) :- compound("a"), \+ atomic("a").
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
% Reference 3.5, 11: quoted
|
|
2
|
-
raw(
|
|
1
|
+
% Reference 3.5, 11: double-quoted lists and quoted atoms preserve escapes at read-back.
|
|
2
|
+
raw(char_list, "line\nnext\t\\slash").
|
|
3
3
|
raw(atom, 'line\nnext\t\\slash').
|
|
4
4
|
answer(K, V) :- raw(K, V).
|
|
5
5
|
%% goal: answer(X0, X1)
|
|
6
|
-
|
|
@@ -5,7 +5,7 @@ raw_value(atom, pat).
|
|
|
5
5
|
raw_value(quoted_atom, 'atom with spaces').
|
|
6
6
|
raw_value(quoted_quote, 'needs''quote').
|
|
7
7
|
raw_value(empty_atom, '').
|
|
8
|
-
raw_value(
|
|
8
|
+
raw_value(char_list, "line\nquote: \"ok\"").
|
|
9
9
|
raw_value(integer, -42).
|
|
10
10
|
raw_value(decimal, 0.25).
|
|
11
11
|
raw_value(scientific, 1.25e-3).
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
answer(functor_atom, pair(Name, Arity)) :- functor(alpha, Name, Arity).
|
|
5
5
|
answer(functor_number, pair(Name, Arity)) :- functor(42, Name, Arity).
|
|
6
|
-
answer(
|
|
6
|
+
answer(functor_char_list, pair(Name, Arity)) :- functor("hi", Name, Arity).
|
|
7
7
|
answer(arg_nested, X) :- arg(1, path(edge(a, b), c), X).
|
|
8
8
|
answer(compose_nested, X) :- (X =.. [outer | [inner(a), [b, c]]]).
|
|
9
9
|
answer(compose_atom_empty_args, X) :- (X =.. [z | []]).
|