eyeprolog 1.3.36 → 1.3.38
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 +28 -3
- package/examples/book/chapter-37/01-write_event.pl +8 -5
- package/package.json +3 -2
- package/src/ARCHITECTURE.md +49 -0
- package/src/cleanup.js +339 -0
- package/src/cli.js +5 -1
- package/src/dcg.js +1 -1
- package/src/errors.js +23 -0
- package/src/index.js +5 -0
- package/src/iso-arithmetic.js +166 -0
- package/src/iso.js +4 -176
- package/src/program-analysis.js +515 -0
- package/src/program-indexing.js +378 -0
- package/src/program.js +8 -873
- package/src/standard-library.js +2 -0
- package/test/conformance/ISO-IMPLEMENTATION-DEFINED.md +8 -8
- package/test/run-all.mjs +4 -0
- package/test/run-architecture.mjs +82 -0
- package/test/run-cleanup.mjs +135 -0
- package/test/run-regression.mjs +5 -1
- package/the-art-of-eyeprolog.md +84 -25
- package/why-eyeprolog.md +22 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// ISO arithmetic evaluation and comparison semantics.
|
|
2
|
+
import {
|
|
3
|
+
ATOM, COMPOUND, NUMBER, VAR, atom, compound, deref, isDecimalInteger,
|
|
4
|
+
numberTerm, numberTextFromDouble, unify,
|
|
5
|
+
} from './term.js';
|
|
6
|
+
import { PrologError } from './errors.js';
|
|
7
|
+
|
|
8
|
+
function evaluate(term, env) {
|
|
9
|
+
term = deref(term, env);
|
|
10
|
+
if (term.type === VAR) throw new PrologError('instantiation_error');
|
|
11
|
+
if (term.type === NUMBER) {
|
|
12
|
+
if (isDecimalInteger(term.name)) return { integer: true, value: BigInt(term.name) };
|
|
13
|
+
const value = Number(term.name);
|
|
14
|
+
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
15
|
+
return { integer: false, value };
|
|
16
|
+
}
|
|
17
|
+
if (term.type === ATOM) {
|
|
18
|
+
if (term.name === 'pi') return { integer: false, value: Math.PI };
|
|
19
|
+
if (term.name === 'e') return { integer: false, value: Math.E };
|
|
20
|
+
}
|
|
21
|
+
if (term.type !== COMPOUND) throw new PrologError('type_error(evaluable)', term);
|
|
22
|
+
const args = term.args.map((arg) => evaluate(arg, env));
|
|
23
|
+
return evaluateOperation(term, args);
|
|
24
|
+
}
|
|
25
|
+
function evaluateOperation(term, args) {
|
|
26
|
+
const name = term.name;
|
|
27
|
+
const arity = term.arity;
|
|
28
|
+
if (arity === 1 && (name === '+' || name === '-')) {
|
|
29
|
+
return name === '+' ? args[0] : args[0].integer
|
|
30
|
+
? { integer: true, value: -args[0].value }
|
|
31
|
+
: { integer: false, value: -args[0].value };
|
|
32
|
+
}
|
|
33
|
+
if (arity === 1 && name === '\\') {
|
|
34
|
+
if (!args[0].integer) throw new PrologError('type_error(integer)', numericTerm(args[0]));
|
|
35
|
+
return { integer: true, value: ~args[0].value };
|
|
36
|
+
}
|
|
37
|
+
if (arity === 1 && ['abs', 'sign', 'float', 'truncate', 'round', 'ceiling', 'floor',
|
|
38
|
+
'float_integer_part', 'float_fractional_part',
|
|
39
|
+
'sin', 'cos', 'atan', 'asin', 'acos', 'tan', 'exp', 'log', 'sqrt'].includes(name)) {
|
|
40
|
+
const a = Number(args[0].value);
|
|
41
|
+
if (name === 'abs' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -args[0].value : args[0].value };
|
|
42
|
+
if (name === 'sign' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -1n : args[0].value > 0n ? 1n : 0n };
|
|
43
|
+
if (name === 'truncate' || name === 'round' || name === 'ceiling' || name === 'floor') {
|
|
44
|
+
const fn = name === 'truncate' ? Math.trunc : name === 'round' ? Math.round : name === 'ceiling' ? Math.ceil : Math.floor;
|
|
45
|
+
return { integer: true, value: BigInt(fn(a)) };
|
|
46
|
+
}
|
|
47
|
+
if (name === 'float_integer_part' || name === 'float_fractional_part') {
|
|
48
|
+
if (args[0].integer) throw new PrologError('type_error(float)', numericTerm(args[0]));
|
|
49
|
+
const value = name === 'float_integer_part' ? Math.trunc(a) : a - Math.trunc(a);
|
|
50
|
+
return { integer: false, value };
|
|
51
|
+
}
|
|
52
|
+
const fn = name === 'float' ? (x) => x : name === 'abs' ? Math.abs : name === 'sign' ? Math.sign : Math[name];
|
|
53
|
+
const value = fn(a);
|
|
54
|
+
if (Number.isNaN(value) || (name === 'log' && a === 0)) throw new PrologError('evaluation_error(undefined)');
|
|
55
|
+
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
56
|
+
return { integer: false, value };
|
|
57
|
+
}
|
|
58
|
+
if (arity !== 2) throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
59
|
+
const bothInteger = args[0].integer && args[1].integer;
|
|
60
|
+
const a = args[0].value, b = args[1].value;
|
|
61
|
+
if (['//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name) && !bothInteger) {
|
|
62
|
+
const invalid = !args[0].integer ? args[0] : args[1];
|
|
63
|
+
throw new PrologError('type_error(integer)', numericTerm(invalid));
|
|
64
|
+
}
|
|
65
|
+
if (bothInteger && name === '^') {
|
|
66
|
+
if (b >= 0n) return { integer: true, value: a ** b };
|
|
67
|
+
if (a === 0n) throw new PrologError('evaluation_error(undefined)');
|
|
68
|
+
if (a === 1n) return { integer: true, value: 1n };
|
|
69
|
+
if (a === -1n) return { integer: true, value: (-b) % 2n === 0n ? 1n : -1n };
|
|
70
|
+
// Corrigendum 3: the defined real result needs a floating-point base.
|
|
71
|
+
throw new PrologError('type_error(float)', numericTerm(args[0]));
|
|
72
|
+
}
|
|
73
|
+
if (bothInteger && ['+', '-', '*', '//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name)) {
|
|
74
|
+
if ((name === '//' || name === 'div' || name === 'mod' || name === 'rem') && b === 0n) throw new PrologError('evaluation_error(zero_divisor)');
|
|
75
|
+
if (name === '+') return { integer: true, value: a + b };
|
|
76
|
+
if (name === '-') return { integer: true, value: a - b };
|
|
77
|
+
if (name === '*') return { integer: true, value: a * b };
|
|
78
|
+
if (name === '//') return { integer: true, value: a / b };
|
|
79
|
+
if (name === 'div') {
|
|
80
|
+
const quotient = a / b;
|
|
81
|
+
const remainder = a % b;
|
|
82
|
+
return { integer: true, value: remainder !== 0n && ((a < 0n) !== (b < 0n)) ? quotient - 1n : quotient };
|
|
83
|
+
}
|
|
84
|
+
if (name === 'rem') return { integer: true, value: a % b };
|
|
85
|
+
if (name === 'mod') return { integer: true, value: ((a % b) + b) % b };
|
|
86
|
+
if (name === '/\\') return { integer: true, value: a & b };
|
|
87
|
+
if (name === '\\/') return { integer: true, value: a | b };
|
|
88
|
+
if (name === 'xor') return { integer: true, value: a ^ b };
|
|
89
|
+
if (name === '<<') return { integer: true, value: a << b };
|
|
90
|
+
if (name === '>>') return { integer: true, value: a >> b };
|
|
91
|
+
}
|
|
92
|
+
const x = Number(a), y = Number(b);
|
|
93
|
+
if ((!Number.isFinite(x) || !Number.isFinite(y)) && name !== 'max' && name !== 'min') {
|
|
94
|
+
throw new PrologError('evaluation_error(float_overflow)');
|
|
95
|
+
}
|
|
96
|
+
if (name === '/' && y === 0) throw new PrologError('evaluation_error(zero_divisor)');
|
|
97
|
+
let value;
|
|
98
|
+
if (name === 'max' || name === 'min') {
|
|
99
|
+
const cmp = compareArithmeticValues(args[0], args[1]);
|
|
100
|
+
const chooseLeft = name === 'max' ? cmp >= 0 : cmp <= 0;
|
|
101
|
+
return chooseLeft ? args[0] : args[1];
|
|
102
|
+
}
|
|
103
|
+
if (name === 'atan2') {
|
|
104
|
+
if (x === 0 && y === 0) throw new PrologError('evaluation_error(undefined)');
|
|
105
|
+
value = Math.atan2(x, y);
|
|
106
|
+
}
|
|
107
|
+
else if (name === '+') value = x + y;
|
|
108
|
+
else if (name === '-') value = x - y;
|
|
109
|
+
else if (name === '*') value = x * y;
|
|
110
|
+
else if (name === '/') value = x / y;
|
|
111
|
+
else if (name === '**' || name === '^') value = Math.pow(x, y);
|
|
112
|
+
else throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
113
|
+
if (Number.isNaN(value)) throw new PrologError('evaluation_error(undefined)');
|
|
114
|
+
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
115
|
+
return { integer: false, value };
|
|
116
|
+
}
|
|
117
|
+
export function arithmeticValueTerm(value) {
|
|
118
|
+
return value.integer ? numberTerm(value.value.toString()) : numberTerm(numberTextFromDouble(value.value));
|
|
119
|
+
}
|
|
120
|
+
function numericTerm(value) {
|
|
121
|
+
return arithmeticValueTerm(value);
|
|
122
|
+
}
|
|
123
|
+
export function evaluateArithmetic(term, env) {
|
|
124
|
+
return evaluate(term, env);
|
|
125
|
+
}
|
|
126
|
+
function compareIntegerToFloat(integerValue, floatValue) {
|
|
127
|
+
if (!Number.isFinite(floatValue)) throw new PrologError('evaluation_error(float_overflow)');
|
|
128
|
+
|
|
129
|
+
// Do not round an unbounded integer through JavaScript Number before a
|
|
130
|
+
// mixed arithmetic comparison. Every integral IEEE-754 double can be
|
|
131
|
+
// converted back to the exact integer value it represents; fractional
|
|
132
|
+
// doubles necessarily have magnitude below 2^53, so their truncation is
|
|
133
|
+
// also exact. This preserves mathematical ordering across the I/F boundary
|
|
134
|
+
// (STC #50), e.g. 9007199254740993 > 9007199254740992.0.
|
|
135
|
+
if (Number.isInteger(floatValue)) {
|
|
136
|
+
const floatInteger = BigInt(floatValue);
|
|
137
|
+
return integerValue < floatInteger ? -1 : integerValue > floatInteger ? 1 : 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const truncated = BigInt(Math.trunc(floatValue));
|
|
141
|
+
if (integerValue < truncated) return -1;
|
|
142
|
+
if (integerValue > truncated) return 1;
|
|
143
|
+
return floatValue > 0 ? -1 : 1;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function compareArithmeticValues(left, right) {
|
|
147
|
+
const a = left.value;
|
|
148
|
+
const b = right.value;
|
|
149
|
+
if (left.integer && right.integer) return a < b ? -1 : a > b ? 1 : 0;
|
|
150
|
+
if (left.integer) return compareIntegerToFloat(a, b);
|
|
151
|
+
if (right.integer) return -compareIntegerToFloat(b, a);
|
|
152
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
153
|
+
}
|
|
154
|
+
export function* isBuiltin({ goal, env }) {
|
|
155
|
+
const result = arithmeticValueTerm(evaluateArithmetic(goal.args[1], env));
|
|
156
|
+
const next = env.clone();
|
|
157
|
+
if (unify(goal.args[0], result, next)) yield next;
|
|
158
|
+
}
|
|
159
|
+
export function arithmeticComparison(test) {
|
|
160
|
+
return function* ({ goal, env }) {
|
|
161
|
+
const left = evaluateArithmetic(goal.args[0], env);
|
|
162
|
+
const right = evaluateArithmetic(goal.args[1], env);
|
|
163
|
+
const cmp = compareArithmeticValues(left, right);
|
|
164
|
+
if (test(cmp)) yield env;
|
|
165
|
+
};
|
|
166
|
+
}
|
package/src/iso.js
CHANGED
|
@@ -18,23 +18,8 @@ import {
|
|
|
18
18
|
|
|
19
19
|
let isoFresh = 0;
|
|
20
20
|
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
const detail = culprit == null ? formal : `${formal}, ${termToString(culprit)}`;
|
|
24
|
-
super(`error(${detail})`);
|
|
25
|
-
this.name = 'PrologError';
|
|
26
|
-
this.formal = formal;
|
|
27
|
-
this.culprit = culprit;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class HaltSignal extends Error {
|
|
32
|
-
constructor(code = 0) {
|
|
33
|
-
super(`halt(${code})`);
|
|
34
|
-
this.name = 'HaltSignal';
|
|
35
|
-
this.code = code;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
21
|
+
export { PrologError, HaltSignal } from './errors.js';
|
|
22
|
+
import { PrologError, HaltSignal } from './errors.js';
|
|
38
23
|
|
|
39
24
|
class ThrownTerm extends Error {
|
|
40
25
|
constructor(term) {
|
|
@@ -2318,165 +2303,8 @@ function* ifThenBuiltin({ solver, goal, env }) {
|
|
|
2318
2303
|
}
|
|
2319
2304
|
}
|
|
2320
2305
|
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
if (term.type === VAR) throw new PrologError('instantiation_error');
|
|
2324
|
-
if (term.type === NUMBER) {
|
|
2325
|
-
if (isDecimalInteger(term.name)) return { integer: true, value: BigInt(term.name) };
|
|
2326
|
-
const value = Number(term.name);
|
|
2327
|
-
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
2328
|
-
return { integer: false, value };
|
|
2329
|
-
}
|
|
2330
|
-
if (term.type === ATOM) {
|
|
2331
|
-
if (term.name === 'pi') return { integer: false, value: Math.PI };
|
|
2332
|
-
if (term.name === 'e') return { integer: false, value: Math.E };
|
|
2333
|
-
}
|
|
2334
|
-
if (term.type !== COMPOUND) throw new PrologError('type_error(evaluable)', term);
|
|
2335
|
-
const args = term.args.map((arg) => evaluate(arg, env));
|
|
2336
|
-
return evaluateOperation(term, args);
|
|
2337
|
-
}
|
|
2338
|
-
function evaluateOperation(term, args) {
|
|
2339
|
-
const name = term.name;
|
|
2340
|
-
const arity = term.arity;
|
|
2341
|
-
if (arity === 1 && (name === '+' || name === '-')) {
|
|
2342
|
-
return name === '+' ? args[0] : args[0].integer
|
|
2343
|
-
? { integer: true, value: -args[0].value }
|
|
2344
|
-
: { integer: false, value: -args[0].value };
|
|
2345
|
-
}
|
|
2346
|
-
if (arity === 1 && name === '\\') {
|
|
2347
|
-
if (!args[0].integer) throw new PrologError('type_error(integer)', numericTerm(args[0]));
|
|
2348
|
-
return { integer: true, value: ~args[0].value };
|
|
2349
|
-
}
|
|
2350
|
-
if (arity === 1 && ['abs', 'sign', 'float', 'truncate', 'round', 'ceiling', 'floor',
|
|
2351
|
-
'float_integer_part', 'float_fractional_part',
|
|
2352
|
-
'sin', 'cos', 'atan', 'asin', 'acos', 'tan', 'exp', 'log', 'sqrt'].includes(name)) {
|
|
2353
|
-
const a = Number(args[0].value);
|
|
2354
|
-
if (name === 'abs' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -args[0].value : args[0].value };
|
|
2355
|
-
if (name === 'sign' && args[0].integer) return { integer: true, value: args[0].value < 0n ? -1n : args[0].value > 0n ? 1n : 0n };
|
|
2356
|
-
if (name === 'truncate' || name === 'round' || name === 'ceiling' || name === 'floor') {
|
|
2357
|
-
const fn = name === 'truncate' ? Math.trunc : name === 'round' ? Math.round : name === 'ceiling' ? Math.ceil : Math.floor;
|
|
2358
|
-
return { integer: true, value: BigInt(fn(a)) };
|
|
2359
|
-
}
|
|
2360
|
-
if (name === 'float_integer_part' || name === 'float_fractional_part') {
|
|
2361
|
-
if (args[0].integer) throw new PrologError('type_error(float)', numericTerm(args[0]));
|
|
2362
|
-
const value = name === 'float_integer_part' ? Math.trunc(a) : a - Math.trunc(a);
|
|
2363
|
-
return { integer: false, value };
|
|
2364
|
-
}
|
|
2365
|
-
const fn = name === 'float' ? (x) => x : name === 'abs' ? Math.abs : name === 'sign' ? Math.sign : Math[name];
|
|
2366
|
-
const value = fn(a);
|
|
2367
|
-
if (Number.isNaN(value) || (name === 'log' && a === 0)) throw new PrologError('evaluation_error(undefined)');
|
|
2368
|
-
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
2369
|
-
return { integer: false, value };
|
|
2370
|
-
}
|
|
2371
|
-
if (arity !== 2) throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
2372
|
-
const bothInteger = args[0].integer && args[1].integer;
|
|
2373
|
-
const a = args[0].value, b = args[1].value;
|
|
2374
|
-
if (['//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name) && !bothInteger) {
|
|
2375
|
-
const invalid = !args[0].integer ? args[0] : args[1];
|
|
2376
|
-
throw new PrologError('type_error(integer)', numericTerm(invalid));
|
|
2377
|
-
}
|
|
2378
|
-
if (bothInteger && name === '^') {
|
|
2379
|
-
if (b >= 0n) return { integer: true, value: a ** b };
|
|
2380
|
-
if (a === 0n) throw new PrologError('evaluation_error(undefined)');
|
|
2381
|
-
if (a === 1n) return { integer: true, value: 1n };
|
|
2382
|
-
if (a === -1n) return { integer: true, value: (-b) % 2n === 0n ? 1n : -1n };
|
|
2383
|
-
// Corrigendum 3: the defined real result needs a floating-point base.
|
|
2384
|
-
throw new PrologError('type_error(float)', numericTerm(args[0]));
|
|
2385
|
-
}
|
|
2386
|
-
if (bothInteger && ['+', '-', '*', '//', 'div', 'mod', 'rem', '/\\', '\\/', 'xor', '<<', '>>'].includes(name)) {
|
|
2387
|
-
if ((name === '//' || name === 'div' || name === 'mod' || name === 'rem') && b === 0n) throw new PrologError('evaluation_error(zero_divisor)');
|
|
2388
|
-
if (name === '+') return { integer: true, value: a + b };
|
|
2389
|
-
if (name === '-') return { integer: true, value: a - b };
|
|
2390
|
-
if (name === '*') return { integer: true, value: a * b };
|
|
2391
|
-
if (name === '//') return { integer: true, value: a / b };
|
|
2392
|
-
if (name === 'div') {
|
|
2393
|
-
const quotient = a / b;
|
|
2394
|
-
const remainder = a % b;
|
|
2395
|
-
return { integer: true, value: remainder !== 0n && ((a < 0n) !== (b < 0n)) ? quotient - 1n : quotient };
|
|
2396
|
-
}
|
|
2397
|
-
if (name === 'rem') return { integer: true, value: a % b };
|
|
2398
|
-
if (name === 'mod') return { integer: true, value: ((a % b) + b) % b };
|
|
2399
|
-
if (name === '/\\') return { integer: true, value: a & b };
|
|
2400
|
-
if (name === '\\/') return { integer: true, value: a | b };
|
|
2401
|
-
if (name === 'xor') return { integer: true, value: a ^ b };
|
|
2402
|
-
if (name === '<<') return { integer: true, value: a << b };
|
|
2403
|
-
if (name === '>>') return { integer: true, value: a >> b };
|
|
2404
|
-
}
|
|
2405
|
-
const x = Number(a), y = Number(b);
|
|
2406
|
-
if ((!Number.isFinite(x) || !Number.isFinite(y)) && name !== 'max' && name !== 'min') {
|
|
2407
|
-
throw new PrologError('evaluation_error(float_overflow)');
|
|
2408
|
-
}
|
|
2409
|
-
if (name === '/' && y === 0) throw new PrologError('evaluation_error(zero_divisor)');
|
|
2410
|
-
let value;
|
|
2411
|
-
if (name === 'max' || name === 'min') {
|
|
2412
|
-
const cmp = compareArithmeticValues(args[0], args[1]);
|
|
2413
|
-
const chooseLeft = name === 'max' ? cmp >= 0 : cmp <= 0;
|
|
2414
|
-
return chooseLeft ? args[0] : args[1];
|
|
2415
|
-
}
|
|
2416
|
-
if (name === 'atan2') {
|
|
2417
|
-
if (x === 0 && y === 0) throw new PrologError('evaluation_error(undefined)');
|
|
2418
|
-
value = Math.atan2(x, y);
|
|
2419
|
-
}
|
|
2420
|
-
else if (name === '+') value = x + y;
|
|
2421
|
-
else if (name === '-') value = x - y;
|
|
2422
|
-
else if (name === '*') value = x * y;
|
|
2423
|
-
else if (name === '/') value = x / y;
|
|
2424
|
-
else if (name === '**' || name === '^') value = Math.pow(x, y);
|
|
2425
|
-
else throw new PrologError('type_error(evaluable)', compound('/', [atom(name), numberTerm(arity)]));
|
|
2426
|
-
if (Number.isNaN(value)) throw new PrologError('evaluation_error(undefined)');
|
|
2427
|
-
if (!Number.isFinite(value)) throw new PrologError('evaluation_error(float_overflow)');
|
|
2428
|
-
return { integer: false, value };
|
|
2429
|
-
}
|
|
2430
|
-
export function arithmeticValueTerm(value) {
|
|
2431
|
-
return value.integer ? numberTerm(value.value.toString()) : numberTerm(numberTextFromDouble(value.value));
|
|
2432
|
-
}
|
|
2433
|
-
function numericTerm(value) {
|
|
2434
|
-
return arithmeticValueTerm(value);
|
|
2435
|
-
}
|
|
2436
|
-
export function evaluateArithmetic(term, env) {
|
|
2437
|
-
return evaluate(term, env);
|
|
2438
|
-
}
|
|
2439
|
-
function compareIntegerToFloat(integerValue, floatValue) {
|
|
2440
|
-
if (!Number.isFinite(floatValue)) throw new PrologError('evaluation_error(float_overflow)');
|
|
2441
|
-
|
|
2442
|
-
// Do not round an unbounded integer through JavaScript Number before a
|
|
2443
|
-
// mixed arithmetic comparison. Every integral IEEE-754 double can be
|
|
2444
|
-
// converted back to the exact integer value it represents; fractional
|
|
2445
|
-
// doubles necessarily have magnitude below 2^53, so their truncation is
|
|
2446
|
-
// also exact. This preserves mathematical ordering across the I/F boundary
|
|
2447
|
-
// (STC #50), e.g. 9007199254740993 > 9007199254740992.0.
|
|
2448
|
-
if (Number.isInteger(floatValue)) {
|
|
2449
|
-
const floatInteger = BigInt(floatValue);
|
|
2450
|
-
return integerValue < floatInteger ? -1 : integerValue > floatInteger ? 1 : 0;
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
const truncated = BigInt(Math.trunc(floatValue));
|
|
2454
|
-
if (integerValue < truncated) return -1;
|
|
2455
|
-
if (integerValue > truncated) return 1;
|
|
2456
|
-
return floatValue > 0 ? -1 : 1;
|
|
2457
|
-
}
|
|
2458
|
-
|
|
2459
|
-
export function compareArithmeticValues(left, right) {
|
|
2460
|
-
const a = left.value;
|
|
2461
|
-
const b = right.value;
|
|
2462
|
-
if (left.integer && right.integer) return a < b ? -1 : a > b ? 1 : 0;
|
|
2463
|
-
if (left.integer) return compareIntegerToFloat(a, b);
|
|
2464
|
-
if (right.integer) return -compareIntegerToFloat(b, a);
|
|
2465
|
-
return a < b ? -1 : a > b ? 1 : 0;
|
|
2466
|
-
}
|
|
2467
|
-
function* isBuiltin({ goal, env }) {
|
|
2468
|
-
const result = arithmeticValueTerm(evaluateArithmetic(goal.args[1], env));
|
|
2469
|
-
const next = env.clone();
|
|
2470
|
-
if (unify(goal.args[0], result, next)) yield next;
|
|
2471
|
-
}
|
|
2472
|
-
function arithmeticComparison(test) {
|
|
2473
|
-
return function* ({ goal, env }) {
|
|
2474
|
-
const left = evaluateArithmetic(goal.args[0], env);
|
|
2475
|
-
const right = evaluateArithmetic(goal.args[1], env);
|
|
2476
|
-
const cmp = compareArithmeticValues(left, right);
|
|
2477
|
-
if (test(cmp)) yield env;
|
|
2478
|
-
};
|
|
2479
|
-
}
|
|
2306
|
+
export { arithmeticValueTerm, evaluateArithmetic, compareArithmeticValues } from './iso-arithmetic.js';
|
|
2307
|
+
import { isBuiltin, arithmeticComparison } from './iso-arithmetic.js';
|
|
2480
2308
|
|
|
2481
2309
|
|
|
2482
2310
|
export class BuiltinRegistry {
|