eyeprolog 1.1.12 → 1.1.13
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/package.json +1 -1
- package/src/iso.js +131 -7
- package/src/lib/eyeprolog.pl +15 -1
- package/test/fixtures/number_chars_cont_quad.pl +235 -0
- package/test/run-regression.mjs +14 -0
- package/the-art-of-eyeprolog.md +1 -1
package/package.json
CHANGED
package/src/iso.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
isDecimalInteger, listFromItems, numberTerm, numberTextFromDouble,
|
|
6
6
|
properListItems, termIsGround, termToString, unify, variable, variantTerms,
|
|
7
7
|
} from './term.js';
|
|
8
|
-
import { createParserOperatorState, parseClauses } from './parser.js';
|
|
8
|
+
import { createParserOperatorState, parseClauses, parseGoalText } from './parser.js';
|
|
9
9
|
import { formatTermForWrite } from './write.js';
|
|
10
10
|
import { emptyTerminalSequence, expandDcgBody, isListOrPartialList, validateDcgEmbeddedGoals } from './dcg.js';
|
|
11
11
|
|
|
@@ -1406,12 +1406,129 @@ function* charCodeBuiltin({ goal, env }) {
|
|
|
1406
1406
|
} else if (unify(goal.args[0], atom(String.fromCodePoint(Number(code.name))), next)) yield next;
|
|
1407
1407
|
}
|
|
1408
1408
|
|
|
1409
|
+
function skipNumberLayout(text, start) {
|
|
1410
|
+
let position = start;
|
|
1411
|
+
while (true) {
|
|
1412
|
+
while (position < text.length && /[\u0009-\u000d\u0020]/.test(text[position])) {
|
|
1413
|
+
position++;
|
|
1414
|
+
}
|
|
1415
|
+
if (text[position] === '%') {
|
|
1416
|
+
const newline = text.indexOf('\n', position + 1);
|
|
1417
|
+
if (newline < 0) return text.length;
|
|
1418
|
+
position = newline + 1;
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
if (text.startsWith('/*', position)) {
|
|
1422
|
+
const end = text.indexOf('*/', position + 2);
|
|
1423
|
+
if (end < 0) return text.length;
|
|
1424
|
+
position = end + 2;
|
|
1425
|
+
continue;
|
|
1426
|
+
}
|
|
1427
|
+
return position;
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function quotedNumberSign(text, start) {
|
|
1432
|
+
if (text[start] !== "'") return null;
|
|
1433
|
+
let position = start + 1;
|
|
1434
|
+
let value = '';
|
|
1435
|
+
while (position < text.length) {
|
|
1436
|
+
let character = text[position++];
|
|
1437
|
+
if (character === "'") {
|
|
1438
|
+
if (text[position] === "'") {
|
|
1439
|
+
position++;
|
|
1440
|
+
value += "'";
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
return { value, position };
|
|
1444
|
+
}
|
|
1445
|
+
if (character !== '\\') {
|
|
1446
|
+
value += character;
|
|
1447
|
+
continue;
|
|
1448
|
+
}
|
|
1449
|
+
if (position >= text.length) return null;
|
|
1450
|
+
character = text[position++];
|
|
1451
|
+
if (character === '\n') continue;
|
|
1452
|
+
const controls = { a: '\x07', b: '\b', r: '\r', f: '\f', t: '\t', n: '\n', v: '\v' };
|
|
1453
|
+
if (controls[character] != null) {
|
|
1454
|
+
value += controls[character];
|
|
1455
|
+
continue;
|
|
1456
|
+
}
|
|
1457
|
+
value += character;
|
|
1458
|
+
}
|
|
1459
|
+
return null;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1409
1462
|
function parseIsoNumber(text) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (
|
|
1413
|
-
|
|
1414
|
-
|
|
1463
|
+
// number_chars/2 accepts leading layout, but its character list must end
|
|
1464
|
+
// with the numeric token itself rather than trailing layout text.
|
|
1465
|
+
if (text.length === 0 || /[\u0009-\u000d\u0020]$/.test(text)) return null;
|
|
1466
|
+
let position = skipNumberLayout(text, 0);
|
|
1467
|
+
let sign = '';
|
|
1468
|
+
|
|
1469
|
+
if (text[position] === '-') {
|
|
1470
|
+
if (/[\u0009-\u000d\u0020]/.test(text[position + 1] ?? '')) {
|
|
1471
|
+
position = skipNumberLayout(text, position + 1);
|
|
1472
|
+
sign = '-';
|
|
1473
|
+
}
|
|
1474
|
+
} else {
|
|
1475
|
+
const quoted = quotedNumberSign(text, position);
|
|
1476
|
+
if (quoted?.value === '-') {
|
|
1477
|
+
position = skipNumberLayout(text, quoted.position);
|
|
1478
|
+
sign = '-';
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
const numericText = `${sign}${text.slice(position)}`;
|
|
1483
|
+
// ISO floating-point syntax requires a decimal fraction before an exponent.
|
|
1484
|
+
if (/^-?\d+[eE][+-]?\d+$/.test(numericText)) return null;
|
|
1485
|
+
try {
|
|
1486
|
+
const parsed = parseGoalText(`number_chars_value(${numericText})`);
|
|
1487
|
+
if (parsed.type !== COMPOUND || parsed.name !== 'number_chars_value' ||
|
|
1488
|
+
parsed.arity !== 1 || parsed.args[0].type !== NUMBER) return null;
|
|
1489
|
+
const value = parsed.args[0];
|
|
1490
|
+
if (isDecimalInteger(value.name)) return numberTerm(BigInt(value.name).toString());
|
|
1491
|
+
const finite = Number(value.name);
|
|
1492
|
+
if (!Number.isFinite(finite)) return null;
|
|
1493
|
+
return numberTerm(numberTextFromDouble(finite));
|
|
1494
|
+
} catch (_) {
|
|
1495
|
+
return null;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function sameNumber(left, right) {
|
|
1500
|
+
const leftInteger = isDecimalInteger(left.name);
|
|
1501
|
+
const rightInteger = isDecimalInteger(right.name);
|
|
1502
|
+
if (leftInteger || rightInteger) {
|
|
1503
|
+
return leftInteger && rightInteger && BigInt(left.name) === BigInt(right.name);
|
|
1504
|
+
}
|
|
1505
|
+
const leftValue = Number(left.name);
|
|
1506
|
+
const rightValue = Number(right.name);
|
|
1507
|
+
return Number.isFinite(leftValue) && Number.isFinite(rightValue) && leftValue === rightValue;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
function numberListText(list, env, kind, valueIsBound) {
|
|
1511
|
+
const whole = deref(list, env);
|
|
1512
|
+
const { items, tail } = listElements(list, env);
|
|
1513
|
+
const proper = tail.type === ATOM && tail.name === '[]';
|
|
1514
|
+
if (tail.type !== VAR && !proper) throw new PrologError('type_error(list)', whole);
|
|
1515
|
+
|
|
1516
|
+
const invalid = items.find((item) => item.type !== VAR &&
|
|
1517
|
+
(kind === 'chars' ? !oneChar(item) : !validCharacterCode(item)));
|
|
1518
|
+
if (invalid) {
|
|
1519
|
+
if (kind === 'chars') throw new PrologError('type_error(character)', invalid);
|
|
1520
|
+
if (invalid.type !== NUMBER || !isDecimalInteger(invalid.name)) {
|
|
1521
|
+
throw new PrologError('type_error(integer)', invalid);
|
|
1522
|
+
}
|
|
1523
|
+
throw new PrologError('representation_error(character_code)');
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
const hasVariable = tail.type === VAR || items.some((item) => item.type === VAR);
|
|
1527
|
+
if (!valueIsBound && hasVariable) throw new PrologError('instantiation_error');
|
|
1528
|
+
if (hasVariable) return null;
|
|
1529
|
+
return items.map((item) => kind === 'chars'
|
|
1530
|
+
? item.name
|
|
1531
|
+
: String.fromCodePoint(Number(item.name))).join('');
|
|
1415
1532
|
}
|
|
1416
1533
|
|
|
1417
1534
|
function numberListBuiltin(kind) {
|
|
@@ -1420,14 +1537,21 @@ function numberListBuiltin(kind) {
|
|
|
1420
1537
|
if (value.type !== VAR && value.type !== NUMBER) throw new PrologError('type_error(number)', value);
|
|
1421
1538
|
const list = deref(goal.args[1], env);
|
|
1422
1539
|
if (value.type === VAR && list.type === VAR) throw new PrologError('instantiation_error');
|
|
1540
|
+
const text = numberListText(list, env, kind, value.type === NUMBER);
|
|
1423
1541
|
const next = env.clone();
|
|
1424
1542
|
if (value.type === NUMBER) {
|
|
1543
|
+
if (text != null) {
|
|
1544
|
+
const parsed = parseIsoNumber(text);
|
|
1545
|
+
if (parsed == null) throw new PrologError('syntax_error(number)');
|
|
1546
|
+
if (sameNumber(value, parsed)) yield next;
|
|
1547
|
+
return;
|
|
1548
|
+
}
|
|
1425
1549
|
const items = characters(value.name).map((ch) =>
|
|
1426
1550
|
kind === 'chars' ? atom(ch) : numberTerm(ch.codePointAt(0)));
|
|
1427
1551
|
if (unify(goal.args[1], listFromItems(items), next)) yield next;
|
|
1428
1552
|
return;
|
|
1429
1553
|
}
|
|
1430
|
-
const parsed = parseIsoNumber(
|
|
1554
|
+
const parsed = parseIsoNumber(text);
|
|
1431
1555
|
if (parsed == null) throw new PrologError('syntax_error(number)');
|
|
1432
1556
|
if (unify(goal.args[0], parsed, next)) yield next;
|
|
1433
1557
|
};
|
package/src/lib/eyeprolog.pl
CHANGED
|
@@ -622,7 +622,21 @@ number_string(Number, Text) :-
|
|
|
622
622
|
number_string(Number, Text) :-
|
|
623
623
|
var(Number),
|
|
624
624
|
eyeprolog__text_chars(Text, Chars),
|
|
625
|
-
catch(number_chars(Number, Chars), _,
|
|
625
|
+
catch(number_chars(Number, Chars), _,
|
|
626
|
+
catch(( eyeprolog__integer_exponent_chars(Chars, Expanded),
|
|
627
|
+
number_chars(Number, Expanded)
|
|
628
|
+
), _, fail)).
|
|
629
|
+
|
|
630
|
+
eyeprolog__integer_exponent_chars(Chars, Expanded) :-
|
|
631
|
+
eyeprolog__split_exponent(Chars, Mantissa, Marker, Exponent),
|
|
632
|
+
number_chars(Integer, Mantissa),
|
|
633
|
+
integer(Integer),
|
|
634
|
+
eyeprolog__append(Mantissa, ['.','0',Marker|Exponent], Expanded).
|
|
635
|
+
|
|
636
|
+
eyeprolog__split_exponent([e|Exponent], [], e, Exponent).
|
|
637
|
+
eyeprolog__split_exponent(['E'|Exponent], [], 'E', Exponent).
|
|
638
|
+
eyeprolog__split_exponent([C|Chars], [C|Mantissa], Marker, Exponent) :-
|
|
639
|
+
eyeprolog__split_exponent(Chars, Mantissa, Marker, Exponent).
|
|
626
640
|
|
|
627
641
|
atom_string(Atom, Text) :-
|
|
628
642
|
atom(Atom),
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
% ids refer to https://www.complang.tuwien.ac.at/ulrich/iso-prolog/number_chars_cont
|
|
2
|
+
|
|
3
|
+
1 ?- number_chars(1.2,"1.2").
|
|
4
|
+
true.
|
|
5
|
+
|
|
6
|
+
55 ?- number_chars(1.2,"1.20").
|
|
7
|
+
true.
|
|
8
|
+
|
|
9
|
+
2 ?- number_chars(1.0e9,"1.0E9").
|
|
10
|
+
true.
|
|
11
|
+
|
|
12
|
+
56 ?- number_chars(1.0e9,"1.0e9").
|
|
13
|
+
true.
|
|
14
|
+
|
|
15
|
+
64 ?- number_chars(0.0,"-0.0").
|
|
16
|
+
true.
|
|
17
|
+
|
|
18
|
+
3 ?- number_chars(1,"01").
|
|
19
|
+
true.
|
|
20
|
+
|
|
21
|
+
65 ?- number_chars(10,"010").
|
|
22
|
+
true.
|
|
23
|
+
|
|
24
|
+
66 ?- number_chars(N,"010").
|
|
25
|
+
N=10.
|
|
26
|
+
|
|
27
|
+
67 ?- number_chars(N,"08").
|
|
28
|
+
N=8.
|
|
29
|
+
|
|
30
|
+
68 ?- number_chars(N,"0b11").
|
|
31
|
+
N=3.
|
|
32
|
+
|
|
33
|
+
69 ?- number_chars(N,"0o11").
|
|
34
|
+
N=9.
|
|
35
|
+
|
|
36
|
+
70 ?- number_chars(N,"0x11").
|
|
37
|
+
N=17.
|
|
38
|
+
|
|
39
|
+
4 ?- number_chars(1,"a").
|
|
40
|
+
syntax_error(...).
|
|
41
|
+
|
|
42
|
+
5 ?- number_chars(1,[]).
|
|
43
|
+
syntax_error(...).
|
|
44
|
+
|
|
45
|
+
6 ?- number_chars(1,[[]]).
|
|
46
|
+
type_error(character,[]).
|
|
47
|
+
|
|
48
|
+
7 ?- number_chars(1,[' ',[]]).
|
|
49
|
+
type_error(character,[]).
|
|
50
|
+
|
|
51
|
+
8 ?- number_chars(1,[0]).
|
|
52
|
+
type_error(character,0).
|
|
53
|
+
|
|
54
|
+
9 ?- number_chars(1,[_,[]]).
|
|
55
|
+
type_error(character,[]).
|
|
56
|
+
|
|
57
|
+
10 ?- number_chars(N,[X]).
|
|
58
|
+
instantiation_error.
|
|
59
|
+
|
|
60
|
+
11 ?- number_chars(N,['0'|_]).
|
|
61
|
+
instantiation_error.
|
|
62
|
+
|
|
63
|
+
12 ?- number_chars(N,'1').
|
|
64
|
+
type_error(list,'1').
|
|
65
|
+
|
|
66
|
+
13 ?- number_chars(N,[a|a]).
|
|
67
|
+
type_error(list,[a|a]).
|
|
68
|
+
|
|
69
|
+
14 ?- number_chars(N,[49]).
|
|
70
|
+
type_error(character,49).
|
|
71
|
+
|
|
72
|
+
15 ?- number_chars(N,[]).
|
|
73
|
+
syntax_error(...).
|
|
74
|
+
|
|
75
|
+
16 ?- number_chars(N,"3 ").
|
|
76
|
+
syntax_error(...).
|
|
77
|
+
|
|
78
|
+
17 ?- number_chars(N,"3.").
|
|
79
|
+
syntax_error(...).
|
|
80
|
+
|
|
81
|
+
18 ?- number_chars(N," 1").
|
|
82
|
+
N = 1.
|
|
83
|
+
|
|
84
|
+
19 ?- number_chars(N,"\n1").
|
|
85
|
+
N = 1.
|
|
86
|
+
|
|
87
|
+
20 ?- number_chars(N," 0'a").
|
|
88
|
+
N = 0'a. N = 97.
|
|
89
|
+
|
|
90
|
+
58 ?- number_chars(N,"0'").
|
|
91
|
+
syntax_error(...).
|
|
92
|
+
|
|
93
|
+
59 ?- number_chars(N,"0'\n").
|
|
94
|
+
syntax_error(...).
|
|
95
|
+
|
|
96
|
+
60 ?- number_chars(N,"0'\\n").
|
|
97
|
+
N=0'\n. N=10.
|
|
98
|
+
|
|
99
|
+
61 ?- number_chars(N,"0'\\7\\").
|
|
100
|
+
N=7.
|
|
101
|
+
|
|
102
|
+
71 ?- number_chars(N,"0'\\7").
|
|
103
|
+
syntax_error(...).
|
|
104
|
+
|
|
105
|
+
62 ?- number_chars(N,"0'.").
|
|
106
|
+
N=0'. . N = 46.
|
|
107
|
+
|
|
108
|
+
21 ?- number_chars(N,"- 1").
|
|
109
|
+
N = -1.
|
|
110
|
+
|
|
111
|
+
54 ?- number_chars(N,"'-'1").
|
|
112
|
+
N = -1.
|
|
113
|
+
|
|
114
|
+
22 ?- number_chars(N,"/**/1").
|
|
115
|
+
N = 1.
|
|
116
|
+
|
|
117
|
+
23 ?- number_chars(N,"%\n1").
|
|
118
|
+
N = 1.
|
|
119
|
+
|
|
120
|
+
57 ?- number_chars(N,"- /**/1").
|
|
121
|
+
N = -1.
|
|
122
|
+
|
|
123
|
+
24 ?- number_chars(N,"-/**/1").
|
|
124
|
+
syntax_error(...).
|
|
125
|
+
|
|
126
|
+
63 ?- number_chars(N,"'\\\n-' 3").
|
|
127
|
+
N= -3.
|
|
128
|
+
|
|
129
|
+
25 ?- number_chars(N,"1e1").
|
|
130
|
+
syntax_error(...).
|
|
131
|
+
|
|
132
|
+
26 ?- number_chars(N,"1.0e").
|
|
133
|
+
syntax_error(...).
|
|
134
|
+
|
|
135
|
+
27 ?- number_chars(N,"1.0ee").
|
|
136
|
+
syntax_error(...).
|
|
137
|
+
|
|
138
|
+
28 ?- number_chars(N,"0x1").
|
|
139
|
+
N = 1.
|
|
140
|
+
|
|
141
|
+
29 ?- number_chars(N,"0X1").
|
|
142
|
+
syntax_error(...).
|
|
143
|
+
|
|
144
|
+
30 ?- number_chars(N,"1E1").
|
|
145
|
+
syntax_error(...).
|
|
146
|
+
|
|
147
|
+
47 ?- number_chars(1,['.'|_]).
|
|
148
|
+
false.
|
|
149
|
+
|
|
150
|
+
48 ?- number_chars(N,"+1").
|
|
151
|
+
syntax_error(...).
|
|
152
|
+
|
|
153
|
+
49 ?- number_chars(N,"+ 1").
|
|
154
|
+
syntax_error(...).
|
|
155
|
+
|
|
156
|
+
50 ?- number_chars(N,"'+'1").
|
|
157
|
+
syntax_error(...).
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
51 ?- number_chars(N,['11']).
|
|
161
|
+
type_error(character,'11').
|
|
162
|
+
|
|
163
|
+
52 ?- number_chars(N,['1.1']).
|
|
164
|
+
type_error(character,'1.1').
|
|
165
|
+
|
|
166
|
+
53 ?- number_chars(1+1,"2").
|
|
167
|
+
type_error(number,1+1).
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
31 ?- number_chars(1,[C]).
|
|
171
|
+
C = '1'.
|
|
172
|
+
|
|
173
|
+
32 ?- number_chars(1,[C,D]).
|
|
174
|
+
false.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
33 ?- number_chars(1,[C,C]).
|
|
178
|
+
false.
|
|
179
|
+
|
|
180
|
+
34 ?- number_chars(0,[C,C]).
|
|
181
|
+
false.
|
|
182
|
+
|
|
183
|
+
35 ?- number_chars(10,[C,D]).
|
|
184
|
+
C = '1', D = '0'.
|
|
185
|
+
|
|
186
|
+
36 ?- number_chars(100,[C,D]).
|
|
187
|
+
false.
|
|
188
|
+
|
|
189
|
+
37 ?- number_chars(N,[X|2]).
|
|
190
|
+
type_error(list,[_|2])
|
|
191
|
+
| instantiation_error.
|
|
192
|
+
|
|
193
|
+
38 ?- number_chars(N,[1|_]).
|
|
194
|
+
instantiation_error
|
|
195
|
+
| type_error(character,1).
|
|
196
|
+
|
|
197
|
+
39 ?- number_chars(V,[1|2]).
|
|
198
|
+
type_error(list,[1|2])
|
|
199
|
+
| type_error(character,1).
|
|
200
|
+
|
|
201
|
+
40 ?- number_chars([],1).
|
|
202
|
+
type_error(number,[])
|
|
203
|
+
| type_error(list,1).
|
|
204
|
+
|
|
205
|
+
41 ?- number_chars(1,1).
|
|
206
|
+
type_error(list,1).
|
|
207
|
+
|
|
208
|
+
42 ?- number_chars(1,[a|2]).
|
|
209
|
+
type_error(list,[a|2]).
|
|
210
|
+
|
|
211
|
+
43 ?- number_chars(1,[_|2]).
|
|
212
|
+
type_error(list,[_|2]).
|
|
213
|
+
|
|
214
|
+
44 ?- number_chars(1,[[]|_]).
|
|
215
|
+
type_error(character,[]).
|
|
216
|
+
|
|
217
|
+
45 ?- number_chars(1,[[]|2]).
|
|
218
|
+
type_error(character,[])
|
|
219
|
+
| type_error(list,[[]|2]).
|
|
220
|
+
|
|
221
|
+
72 ?- number_chars(V,[1,[],X|2]).
|
|
222
|
+
type_error(list,[1,[],_A|2])
|
|
223
|
+
| type_error(character,1)
|
|
224
|
+
| type_error(character,[])
|
|
225
|
+
| instantiation_error.
|
|
226
|
+
|
|
227
|
+
46 ?- L=['1'|L], number_chars(N,L).
|
|
228
|
+
sto, ... ; ... .
|
|
229
|
+
( sto, type_error(list,['1'|...]) % rational trees
|
|
230
|
+
| sto, false % occurs-check
|
|
231
|
+
| sto, representation_error(term)
|
|
232
|
+
| sto, instantiation_error % literal substitutions
|
|
233
|
+
| sto, resource_error(...)
|
|
234
|
+
| sto, loops
|
|
235
|
+
).
|
package/test/run-regression.mjs
CHANGED
|
@@ -322,6 +322,20 @@ c4 ?- call((!;1)).
|
|
|
322
322
|
assertEqual(result.stdout, 'quads: 58 run, 58 passed, 0 failed.\n', 'quad report');
|
|
323
323
|
},
|
|
324
324
|
},
|
|
325
|
+
{
|
|
326
|
+
name: 'runQuads passes the complete vendored number_chars continuation corpus',
|
|
327
|
+
run: () => {
|
|
328
|
+
const filename = path.join(testRoot, 'fixtures', 'number_chars_cont_quad.pl');
|
|
329
|
+
const source = fs.readFileSync(filename, 'utf8');
|
|
330
|
+
const result = publicApi.runQuads(Program.parseSources([{
|
|
331
|
+
text: source,
|
|
332
|
+
filename,
|
|
333
|
+
}]));
|
|
334
|
+
assertEqual(result.total, 72, 'quad total');
|
|
335
|
+
assertEqual(result.passed, 72, 'quad passed');
|
|
336
|
+
assertEqual(result.stdout, 'quads: 72 run, 72 passed, 0 failed.\n', 'quad report');
|
|
337
|
+
},
|
|
338
|
+
},
|
|
325
339
|
{
|
|
326
340
|
name: 'runQuads passes the complete vendored Prolog Prologue quad corpus',
|
|
327
341
|
run: () => {
|
package/the-art-of-eyeprolog.md
CHANGED
|
@@ -5636,7 +5636,7 @@ quoted_atom("ab"). % quoted_atom(ab)
|
|
|
5636
5636
|
| `sub_atom(+Atom,?Before,?Length,?After,?SubAtom)` | Enumerates substrings and their Unicode-code-point offsets. Supplied counts must be nonnegative integers. |
|
|
5637
5637
|
| `atom_chars(?Atom,?Chars)`, `atom_codes(?Atom,?Codes)` | Convert between an atom and a proper list of one-character atoms or Unicode scalar codes. At least one side must be instantiated. |
|
|
5638
5638
|
| `char_code(?Character,?Code)` | Converts one character atom and one Unicode scalar code. Surrogates and values outside `0..0x10ffff` raise a representation error. |
|
|
5639
|
-
| `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
|
|
5639
|
+
| `number_chars(?Number,?Chars)`, `number_codes(?Number,?Codes)` | Convert finite numbers to canonical text or parse a proper character/code list using ISO numeric-token syntax, including radix integers, character-code constants, and leading layout. At least one side must be instantiated; malformed numeric input raises *syntax_error(number)*. |
|
|
5640
5640
|
|
|
5641
5641
|
Conversions accept partial output lists when the atomic input is known, but
|
|
5642
5642
|
constructing an atom or number requires a complete proper list with no unbound
|