eyeprolog 1.3.69 → 1.3.71
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 +25 -23
- package/examples/clpz-n-queens.pl +5 -5
- package/examples/clpz-sudoku-9x9.pl +21 -3
- package/examples/output/clpz-resource-allocation.pl +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +5 -7
- package/src/expansion-builtins.js +5 -2
- package/src/iso-arithmetic.js +12 -0
- package/src/iso.js +19 -8
- package/src/lib/clpz.pl +8038 -103
- package/src/program.js +38 -6
- package/src/solver.js +58 -58
- package/src/source-expansion.js +1 -1
- package/src/standard-library.js +0 -2
- package/src/term.js +0 -3
- package/src/write.js +29 -29
- package/test/conformance/WG17-SYNTAX-STATUS.md +5 -1
- package/test/conformance/wg17-syntax-cases.json +44 -44
- package/test/run-regression.mjs +44 -5
- package/test/run-wg17.mjs +36 -0
- package/the-art-of-eyeprolog.md +20 -24
- package/tools/report-wg17-syntax-coverage.mjs +5 -1
- package/tools/upgrade-wg17.mjs +2 -1
- package/src/CLPZ-MIGRATION.md +0 -99
- package/src/clpz.js +0 -1062
package/src/program.js
CHANGED
|
@@ -576,8 +576,16 @@ class ProgramBuilder {
|
|
|
576
576
|
targetSet.add(key);
|
|
577
577
|
declaredMap.set(`${textUnit}\u0000${key}`, { ...indicator, key, module, textUnit });
|
|
578
578
|
if (kind === 'dynamic') {
|
|
579
|
-
|
|
580
|
-
|
|
579
|
+
// A dynamic declaration creates the procedure immediately, not only
|
|
580
|
+
// when ProgramBuilder.finish() runs. Compile-time source-expansion
|
|
581
|
+
// hooks can therefore call an explicitly declared empty dynamic
|
|
582
|
+
// predicate while the remainder of the same file is still loading.
|
|
583
|
+
let existing = program.groups.get(key);
|
|
584
|
+
if (!existing) {
|
|
585
|
+
existing = program.makeGroup(indicator.name, indicator.arity, module);
|
|
586
|
+
program.groups.set(key, existing);
|
|
587
|
+
}
|
|
588
|
+
existing.dynamic = true;
|
|
581
589
|
}
|
|
582
590
|
}
|
|
583
591
|
return true;
|
|
@@ -1065,6 +1073,7 @@ function sourcePath(options) {
|
|
|
1065
1073
|
|
|
1066
1074
|
function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules, fast, context) {
|
|
1067
1075
|
const batch = [];
|
|
1076
|
+
const deferredGoalExpansions = [];
|
|
1068
1077
|
const flush = () => {
|
|
1069
1078
|
if (batch.length === 0) return;
|
|
1070
1079
|
builder.addClauses(batch);
|
|
@@ -1114,10 +1123,23 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
1114
1123
|
clause = normalizeQualifiedClauseHead(clause, lexicalModule);
|
|
1115
1124
|
|
|
1116
1125
|
if (!builder.program.strictIso && !isDirectiveClause(clause)) {
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1126
|
+
try {
|
|
1127
|
+
expandClauseGoals(builder.program, clause, lexicalModule, {
|
|
1128
|
+
module: lexicalModule,
|
|
1129
|
+
filename: options.filename ?? '<input>',
|
|
1130
|
+
});
|
|
1131
|
+
} catch (error) {
|
|
1132
|
+
// Scryer-compatible goal-expansion hooks may refer to helper predicates
|
|
1133
|
+
// that occur later in the same source file. Keep loading so those
|
|
1134
|
+
// helpers become visible, then retry this clause at end-of-source.
|
|
1135
|
+
// A still-missing helper (or any other expansion error) is reported on
|
|
1136
|
+
// the final retry rather than being silently ignored.
|
|
1137
|
+
if (!(error instanceof PrologError) || error.formal !== 'existence_error(procedure)') throw error;
|
|
1138
|
+
clause.module ??= lexicalModule;
|
|
1139
|
+
clause.textUnit ??= context.textUnit;
|
|
1140
|
+
deferredGoalExpansions.push({ clause, lexicalModule });
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1121
1143
|
}
|
|
1122
1144
|
|
|
1123
1145
|
const moduleDeclaration = moduleDirective(clause);
|
|
@@ -1207,6 +1229,16 @@ function loadSourceIntoBuilder(builder, source, options, ensured, loadedModules,
|
|
|
1207
1229
|
}
|
|
1208
1230
|
parseClausesInto(source, options, processClause);
|
|
1209
1231
|
flush();
|
|
1232
|
+
if (!builder.program.strictIso && deferredGoalExpansions.length > 0) {
|
|
1233
|
+
for (const { clause, lexicalModule } of deferredGoalExpansions) {
|
|
1234
|
+
expandClauseGoals(builder.program, clause, lexicalModule, {
|
|
1235
|
+
module: lexicalModule,
|
|
1236
|
+
filename: options.filename ?? '<input>',
|
|
1237
|
+
});
|
|
1238
|
+
for (const goal of clause.body) annotateGoalModule(goal, clause.lexicalModule ?? lexicalModule);
|
|
1239
|
+
builder.addClauses([clause]);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1210
1242
|
return true;
|
|
1211
1243
|
}
|
|
1212
1244
|
|
package/src/solver.js
CHANGED
|
@@ -10,7 +10,6 @@ import { PrologError, getStrictIsoRegistry } from './iso.js';
|
|
|
10
10
|
import { getEyePrologRegistry } from './standard-library.js';
|
|
11
11
|
import { selectClauseCandidates, selectClauseCandidatesForValues, selectGroundClauseCandidates } from './program-indexing.js';
|
|
12
12
|
import { StreamManager } from './io.js';
|
|
13
|
-
import { clpzStateConsistent } from './clpz.js';
|
|
14
13
|
import { hardHeapLimit, softHeapLimit, usedHeapSize } from './platform.js';
|
|
15
14
|
import { evaluateWfs, relationForGroup, truthOfGroundGoal } from './wfs.js';
|
|
16
15
|
import { ISO_MAX_ARITY } from './iso-limits.js';
|
|
@@ -428,6 +427,45 @@ export class Solver {
|
|
|
428
427
|
});
|
|
429
428
|
continue;
|
|
430
429
|
}
|
|
430
|
+
if (frame.kind === 'userClause') {
|
|
431
|
+
if (this.solutionsSeen >= this.solutionLimit) continue;
|
|
432
|
+
const { clause, goal: clauseGoal, rest: clauseRest, env: clauseEnv, depth: clauseDepth,
|
|
433
|
+
active: clauseActive, release } = frame;
|
|
434
|
+
let next;
|
|
435
|
+
if (clause.body.length === 0 && clause.scalarHead) {
|
|
436
|
+
next = matchScalarFact(clauseGoal, clause.head, clauseEnv);
|
|
437
|
+
if (!next) continue;
|
|
438
|
+
this.stats.unify_calls++;
|
|
439
|
+
stack.push({
|
|
440
|
+
kind: 'goals',
|
|
441
|
+
goals: [...release, ...clauseRest],
|
|
442
|
+
env: next,
|
|
443
|
+
depth: clauseDepth + 1,
|
|
444
|
+
active: clauseActive,
|
|
445
|
+
});
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const id = nextFreshId();
|
|
449
|
+
const freshVariables = new Map();
|
|
450
|
+
const freshHead = freshTerm(clause.head, id, freshVariables);
|
|
451
|
+
const freshBody = clause.body.map((term) => freshTerm(term, id, freshVariables));
|
|
452
|
+
const localFreshPlan = clauseLocalFreshPlan(clause);
|
|
453
|
+
const headLocalFresh = freshVariableSet(localFreshPlan.head, freshVariables);
|
|
454
|
+
attachBodyLocalFreshVariables(freshBody, localFreshPlan.body, freshVariables);
|
|
455
|
+
next = clauseEnv.clone();
|
|
456
|
+
this.stats.unify_calls++;
|
|
457
|
+
if (!unify(clauseGoal, freshHead, next, { knownNonoccurringVariables: headLocalFresh })) continue;
|
|
458
|
+
stack.push({
|
|
459
|
+
kind: 'goals',
|
|
460
|
+
goals: freshBody.length === 0
|
|
461
|
+
? [...release, ...clauseRest]
|
|
462
|
+
: [...freshBody, ...release, ...clauseRest],
|
|
463
|
+
env: next,
|
|
464
|
+
depth: clauseDepth + 1,
|
|
465
|
+
active: clauseActive,
|
|
466
|
+
});
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
431
469
|
if (frame.kind === 'completeTableFixpointRound') {
|
|
432
470
|
if (frame.revision !== this.programRevision) continue;
|
|
433
471
|
frame.entry.computing = false;
|
|
@@ -491,7 +529,6 @@ export class Solver {
|
|
|
491
529
|
}
|
|
492
530
|
|
|
493
531
|
if (goals.length === 0) {
|
|
494
|
-
if (!clpzStateConsistent(env)) break;
|
|
495
532
|
this.solutionsSeen++;
|
|
496
533
|
this.stats.completed_goal_lists++;
|
|
497
534
|
this.active = active;
|
|
@@ -1182,53 +1219,27 @@ function pushUserGoalUncachedFrames(stack, solver, group, goal, rest, env, depth
|
|
|
1182
1219
|
for (const pass of [candidates.primary, candidates.fallback]) {
|
|
1183
1220
|
for (let candidateIndex = 0; candidateIndex < clauseCandidateLength(pass); candidateIndex++) {
|
|
1184
1221
|
const clause = clauseCandidateAt(pass, candidateIndex);
|
|
1185
|
-
if (clause.body.length === 0 && clause.scalarHead) {
|
|
1186
|
-
const next = matchScalarFact(goal, clause.head, env);
|
|
1187
|
-
if (next) {
|
|
1188
|
-
solver.stats.unify_calls++;
|
|
1189
|
-
frames.push({
|
|
1190
|
-
kind: 'goals',
|
|
1191
|
-
goals: [...release, ...rest],
|
|
1192
|
-
env: next,
|
|
1193
|
-
depth: depth + 1,
|
|
1194
|
-
active: nextActive,
|
|
1195
|
-
});
|
|
1196
|
-
}
|
|
1197
|
-
continue;
|
|
1198
|
-
}
|
|
1199
1222
|
if (headCannotMatch(goal, clause.head, env)) continue;
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
env: next,
|
|
1215
|
-
depth: depth + 1,
|
|
1216
|
-
active: nextActive,
|
|
1217
|
-
});
|
|
1218
|
-
} else {
|
|
1219
|
-
frames.push({
|
|
1220
|
-
kind: 'goals',
|
|
1221
|
-
goals: [...freshBody, ...release, ...rest],
|
|
1222
|
-
env: next,
|
|
1223
|
-
depth: depth + 1,
|
|
1224
|
-
active: nextActive,
|
|
1225
|
-
});
|
|
1226
|
-
}
|
|
1223
|
+
// Do not speculatively unify alternative heads. With attributed variables,
|
|
1224
|
+
// head unification can run verify_attributes/3, queue wakeups, fail, or
|
|
1225
|
+
// throw. Those effects are observable and must occur only if Prolog search
|
|
1226
|
+
// actually reaches this clause (in particular after earlier cuts).
|
|
1227
|
+
frames.push({
|
|
1228
|
+
kind: 'userClause',
|
|
1229
|
+
clause,
|
|
1230
|
+
goal,
|
|
1231
|
+
rest,
|
|
1232
|
+
env,
|
|
1233
|
+
depth,
|
|
1234
|
+
active: nextActive,
|
|
1235
|
+
release,
|
|
1236
|
+
});
|
|
1227
1237
|
}
|
|
1228
1238
|
}
|
|
1229
1239
|
for (let i = frames.length - 1; i >= 0; i--) stack.push(frames[i]);
|
|
1230
1240
|
}
|
|
1231
1241
|
|
|
1242
|
+
|
|
1232
1243
|
function clauseLocalFreshPlan(clause) {
|
|
1233
1244
|
if (clause._localFreshPlan != null) return clause._localFreshPlan;
|
|
1234
1245
|
const seen = new Set();
|
|
@@ -1273,19 +1284,7 @@ function attachBodyLocalFreshVariables(freshBody, plan, freshVariables) {
|
|
|
1273
1284
|
if (knownNonoccurringVariables == null || goal?.type !== COMPOUND) continue;
|
|
1274
1285
|
if (goal.name === '=' && goal.arity === 2) {
|
|
1275
1286
|
goal._knownNonoccurringVariables = knownNonoccurringVariables;
|
|
1276
|
-
continue;
|
|
1277
|
-
}
|
|
1278
|
-
// Only a compiler-generated DCG state first handed as a complete argument
|
|
1279
|
-
// to another callable receives a longer-lived local marker. Equality-created
|
|
1280
|
-
// sequence variables keep the existing one-unification proof instead.
|
|
1281
|
-
const localFirstUseVariables = new Set();
|
|
1282
|
-
for (const argument of goal.args) {
|
|
1283
|
-
if (argument?.type === VAR && argument.name.startsWith('\u0000dcg') &&
|
|
1284
|
-
knownNonoccurringVariables.has(argument.name)) {
|
|
1285
|
-
localFirstUseVariables.add(argument.name);
|
|
1286
|
-
}
|
|
1287
1287
|
}
|
|
1288
|
-
if (localFirstUseVariables.size !== 0) goal._localFirstUseVariables = localFirstUseVariables;
|
|
1289
1288
|
}
|
|
1290
1289
|
}
|
|
1291
1290
|
|
|
@@ -1538,7 +1537,7 @@ function bundledLengthIterator(solver, group, goal, env) {
|
|
|
1538
1537
|
|
|
1539
1538
|
// Delayed and constrained variables need the ordinary solver's wake-up
|
|
1540
1539
|
// points. The fast path is deliberately limited to plain finite-tree terms.
|
|
1541
|
-
if (env.
|
|
1540
|
+
if (env._prologAttributes != null) return null;
|
|
1542
1541
|
const length = deref(goal.args[1], env);
|
|
1543
1542
|
if (length.type === VAR && env._delays?.has(length.name)) return null;
|
|
1544
1543
|
|
|
@@ -2043,7 +2042,7 @@ function fastCountBranch(branch, env) {
|
|
|
2043
2042
|
|
|
2044
2043
|
function fastCountPureGoal(solver, goal, env) {
|
|
2045
2044
|
if (solver.isoStrict || solver.solutionLimit !== Infinity || solver.maxInferences !== Infinity ||
|
|
2046
|
-
env.
|
|
2045
|
+
env._variableConstraints != null || env._prologAttributes != null || (env._delays != null && env._delays.size !== 0)) return null;
|
|
2047
2046
|
const budget = { remaining: 8192 };
|
|
2048
2047
|
const branches = expandPureCountGoal(solver, goal, goal.module ?? 'user', new Set(), budget);
|
|
2049
2048
|
if (branches == null) return null;
|
|
@@ -2681,7 +2680,8 @@ function activeVariantIn(goal, env, active) {
|
|
|
2681
2680
|
// Variant calls must have the same predicate indicator. Avoid walking
|
|
2682
2681
|
// large matrix/list arguments for every unrelated active predicate.
|
|
2683
2682
|
if (candidate?.type !== goal.type || candidate?.name !== goal.name ||
|
|
2684
|
-
candidate?.arity !== goal.arity
|
|
2683
|
+
candidate?.arity !== goal.arity ||
|
|
2684
|
+
(candidate?.module ?? 'user') !== (goal.module ?? 'user')) continue;
|
|
2685
2685
|
goalShape ??= variantShape(goal, env);
|
|
2686
2686
|
entry.variantShape ??= variantShape(candidate, entry.env);
|
|
2687
2687
|
if (goalShape !== entry.variantShape) continue;
|
package/src/source-expansion.js
CHANGED
|
@@ -71,7 +71,7 @@ function runHook(program, module, name, input, loadContext = null) {
|
|
|
71
71
|
const goal = compound(name, [input, output]);
|
|
72
72
|
goal.module = owner;
|
|
73
73
|
const solver = new Solver(program, {
|
|
74
|
-
solutionLimit:
|
|
74
|
+
solutionLimit: Infinity,
|
|
75
75
|
maxInferences: MAX_EXPANSION_INFERENCES,
|
|
76
76
|
sourceLoadContext: loadContext,
|
|
77
77
|
});
|
package/src/standard-library.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// Modules are loaded on demand by explicit use_module/1-2 or by the conservative
|
|
4
4
|
// source-level interop autoloader declared below.
|
|
5
5
|
import { PrologError, createDefaultRegistry, eyePrologLibraryBuiltins } from './iso.js';
|
|
6
|
-
import { clpzBuiltins } from './clpz.js';
|
|
7
6
|
import { attsBuiltins } from './atts.js';
|
|
8
7
|
import { expansionBuiltins } from './expansion-builtins.js';
|
|
9
8
|
import { scryerCompatibilityBuiltins } from './scryer-compat.js';
|
|
@@ -203,7 +202,6 @@ export function createEyePrologRegistry() {
|
|
|
203
202
|
attsBuiltins.register(registry);
|
|
204
203
|
expansionBuiltins.register(registry);
|
|
205
204
|
scryerCompatibilityBuiltins.register(registry);
|
|
206
|
-
clpzBuiltins.register(registry);
|
|
207
205
|
registry.eyePrologLibrary = true;
|
|
208
206
|
return registry;
|
|
209
207
|
}
|
package/src/term.js
CHANGED
|
@@ -112,7 +112,6 @@ export class Env {
|
|
|
112
112
|
// Backtrackable blackboard entries used by Scryer-compatible libraries.
|
|
113
113
|
// Values are logical terms and the map is copy-on-write across Env clones.
|
|
114
114
|
this._backtrackableBlackboard = null;
|
|
115
|
-
this._clpz = null;
|
|
116
115
|
this._occursCheckHandler = null;
|
|
117
116
|
this._localVariables = null;
|
|
118
117
|
}
|
|
@@ -131,7 +130,6 @@ export class Env {
|
|
|
131
130
|
clone._attributeHookRunner = this._attributeHookRunner;
|
|
132
131
|
clone._pendingAttributeGoals = this._pendingAttributeGoals;
|
|
133
132
|
clone._backtrackableBlackboard = this._backtrackableBlackboard;
|
|
134
|
-
clone._clpz = this._clpz;
|
|
135
133
|
clone._occursCheckHandler = this._occursCheckHandler;
|
|
136
134
|
clone._localVariables = this._localVariables;
|
|
137
135
|
return clone;
|
|
@@ -153,7 +151,6 @@ export class Env {
|
|
|
153
151
|
this._prologAttributes = other._prologAttributes;
|
|
154
152
|
this._pendingAttributeGoals = other._pendingAttributeGoals;
|
|
155
153
|
this._backtrackableBlackboard = other._backtrackableBlackboard;
|
|
156
|
-
this._clpz = other._clpz;
|
|
157
154
|
// Execution callbacks belong to the Solver driving this Env, not to the
|
|
158
155
|
// logical branch being adopted from an inner attribute-hook call.
|
|
159
156
|
this._localVariables = other._localVariables;
|
package/src/write.js
CHANGED
|
@@ -86,7 +86,12 @@ function isWordOperatorToken(token) {
|
|
|
86
86
|
|
|
87
87
|
function compactPrefixOperator(token, argument) {
|
|
88
88
|
if (isWordOperatorToken(token)) return `${token} ${argument}`;
|
|
89
|
-
|
|
89
|
+
// Without layout, `op(...)` is functional notation rather than prefix-operator
|
|
90
|
+
// notation. ISO 7.10.5 h requires operator-form output and layout whenever
|
|
91
|
+
// ambiguity could otherwise arise, so a parenthesized prefix argument must
|
|
92
|
+
// never be glued directly to the operator token.
|
|
93
|
+
const needsLayout = argument.startsWith('(') || compactBoundaryNeedsSpace(token, argument);
|
|
94
|
+
return `${token}${needsLayout ? ' ' : ''}${argument}`;
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
function compactPostfixOperator(argument, token) {
|
|
@@ -188,28 +193,6 @@ function chooseOperator(term, table) {
|
|
|
188
193
|
return null;
|
|
189
194
|
}
|
|
190
195
|
|
|
191
|
-
function startsWithUnsignedNumber(term, env, table, options) {
|
|
192
|
-
const resolved = deref(term, env);
|
|
193
|
-
if (resolved.type === NUMBER) return !resolved.name.startsWith('-');
|
|
194
|
-
if (resolved.type !== COMPOUND || isCons(resolved)) return false;
|
|
195
|
-
if (options.numbervars && resolved.name === '$VAR' && resolved.arity === 1) {
|
|
196
|
-
const index = deref(resolved.args[0], env);
|
|
197
|
-
if (index.type === NUMBER && /^\d+$/.test(index.name) &&
|
|
198
|
-
writeNumberedVariable(Number(index.name)) != null) {
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const definition = chooseOperator(resolved, table);
|
|
203
|
-
if (definition == null) return false;
|
|
204
|
-
if (definition.specifier === 'xf' || definition.specifier === 'yf') {
|
|
205
|
-
return startsWithUnsignedNumber(resolved.args[0], env, table, options);
|
|
206
|
-
}
|
|
207
|
-
if (definition.specifier === 'xfx' || definition.specifier === 'xfy' || definition.specifier === 'yfx') {
|
|
208
|
-
return startsWithUnsignedNumber(resolved.args[0], env, table, options);
|
|
209
|
-
}
|
|
210
|
-
return false;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
196
|
function printableReadVariableNames(term, env, explicit) {
|
|
214
197
|
const names = new Map(explicit);
|
|
215
198
|
const used = new Set(names.values());
|
|
@@ -353,13 +336,30 @@ function format(term, env, options, table, maxPriority = 1200, context = 'term')
|
|
|
353
336
|
let text;
|
|
354
337
|
if (specifier === 'fx' || specifier === 'fy') {
|
|
355
338
|
const argumentPriority = specifier === 'fx' ? priority - 1 : priority;
|
|
356
|
-
//
|
|
357
|
-
//
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
339
|
+
// Corrigendum 3 adds mandatory parentheses for prefix - when its
|
|
340
|
+
// argument is a non-negative number or is written in infix/postfix
|
|
341
|
+
// operator form (ISO 7.10.5 h 2(iii-iv)).
|
|
342
|
+
const child = deref(resolved.args[0], env);
|
|
343
|
+
const childNumbervar = options.numbervars && child.type === COMPOUND &&
|
|
344
|
+
child.name === '$VAR' && child.arity === 1 && (() => {
|
|
345
|
+
const index = deref(child.args[0], env);
|
|
346
|
+
return index.type === NUMBER && /^\d+$/.test(index.name) &&
|
|
347
|
+
writeNumberedVariable(Number(index.name)) != null;
|
|
348
|
+
})();
|
|
349
|
+
const childUsesSpecialNotation = isCons(child) ||
|
|
350
|
+
(child.type === COMPOUND && child.name === '{}' && child.arity === 1) || childNumbervar;
|
|
351
|
+
const childDefinition = childUsesSpecialNotation ? null : chooseOperator(child, table);
|
|
352
|
+
const negativeNeedsParentheses = resolved.name === '-' && (
|
|
353
|
+
(child.type === NUMBER && !child.name.startsWith('-')) ||
|
|
354
|
+
['xf', 'yf', 'xfx', 'xfy', 'yfx'].includes(childDefinition?.specifier)
|
|
355
|
+
);
|
|
356
|
+
if (negativeNeedsParentheses) {
|
|
357
|
+
// ISO 7.10.5 h 2(iii-iv): prefix - requires parentheses around a
|
|
358
|
+
// non-negative number and around an argument written in infix or
|
|
359
|
+
// postfix operator form. Keep layout before `(` so the result is
|
|
360
|
+
// operator notation, not functional notation such as `-(...)`.
|
|
361
361
|
const argument = format(resolved.args[0], env, options, table, 1200);
|
|
362
|
-
text =
|
|
362
|
+
text = `${token} (${argument})`;
|
|
363
363
|
} else {
|
|
364
364
|
const argument = format(resolved.args[0], env, options, table, argumentPriority);
|
|
365
365
|
text = options.minimalOperatorSpacing ? compactPrefixOperator(token, argument) : `${token} ${argument}`;
|
|
@@ -11,10 +11,14 @@ upstream assertion.
|
|
|
11
11
|
|
|
12
12
|
## Current standing
|
|
13
13
|
|
|
14
|
+
The percentage below is **traceability coverage**, not a conformance pass rate.
|
|
15
|
+
A represented case still has to pass the executable upstream assertion in
|
|
16
|
+
`test/run-wg17.mjs`.
|
|
17
|
+
|
|
14
18
|
| Measure | Count |
|
|
15
19
|
| --- | ---: |
|
|
16
20
|
| Active upstream cases | 366 |
|
|
17
|
-
|
|
|
21
|
+
| Upstream cases represented in the executable matrix | 366 (100.0%) |
|
|
18
22
|
| Not yet traced one-by-one | 0 |
|
|
19
23
|
| Deleted upstream identifiers | #20, #273 |
|
|
20
24
|
|