@asmlift/core 0.8.0 → 0.9.0
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 +34 -30
- package/package.json +1 -1
- package/src/aggregate.ts +248 -0
- package/src/backend/c.ts +5 -2
- package/src/backend/cfamily.ts +236 -37
- package/src/backend/cpp.ts +114 -9
- package/src/backend/pascal.ts +39 -0
- package/src/cdecl/constant.ts +59 -0
- package/src/cdecl/lex.ts +209 -0
- package/src/cdecl/parse.ts +975 -0
- package/src/cdecl/spell.ts +69 -0
- package/src/codegen-flags.ts +38 -6
- package/src/compiler-diagnostics.ts +194 -0
- package/src/contracts.ts +615 -79
- package/src/declare.ts +75 -5
- package/src/frontend/asmdata.ts +19 -5
- package/src/frontend/call-plan.ts +686 -0
- package/src/frontend/device-pins.ts +277 -0
- package/src/frontend/errors.ts +5 -0
- package/src/frontend/flags-edge.ts +101 -0
- package/src/frontend/fpu.ts +167 -0
- package/src/frontend/frame-objects.ts +2131 -0
- package/src/frontend/high-half.ts +2 -1
- package/src/frontend/local-object.ts +490 -0
- package/src/frontend/mips.ts +363 -40
- package/src/frontend/opaque.ts +97 -18
- package/src/frontend/ppc.ts +520 -129
- package/src/frontend/reloc-symbol.ts +94 -13
- package/src/frontend/splat.ts +108 -5
- package/src/frontend/ssa.ts +669 -76
- package/src/frontend/stackargs.ts +120 -39
- package/src/frontend/thumb.ts +2764 -2018
- package/src/ir/alias.ts +29 -0
- package/src/ir/core.ts +101 -3
- package/src/ir/discipline.ts +248 -0
- package/src/ir/float-bits.ts +47 -0
- package/src/ir/opcodes.ts +84 -112
- package/src/ir/parse.ts +1 -0
- package/src/ir/types.ts +118 -2
- package/src/ir/verify.ts +150 -3
- package/src/l3/address.ts +5 -0
- package/src/l3/argcopy.ts +38 -6
- package/src/l3/ast.ts +186 -14
- package/src/l3/coalesce.ts +21 -13
- package/src/l3/dce.ts +32 -10
- package/src/l3/gates.ts +9 -3
- package/src/l3/hoist.ts +8 -7
- package/src/l3/initfirst.ts +27 -9
- package/src/l3/intcell.ts +97 -0
- package/src/l3/mentions.ts +10 -4
- package/src/l3/narrowdecl.ts +218 -0
- package/src/l3/nearbase.ts +10 -5
- package/src/l3/parkfirst.ts +8 -5
- package/src/l3/pollguard.ts +3 -2
- package/src/l3/ptrcell.ts +357 -0
- package/src/l3/regspell.ts +17 -3
- package/src/l3/reindex.ts +10 -7
- package/src/l3/symbol-refs.ts +104 -23
- package/src/l3/tailmerge.ts +6 -19
- package/src/l3/typing.ts +137 -25
- package/src/l3/unreduce.ts +4 -4
- package/src/l3/volstore.ts +96 -7
- package/src/mangle.ts +11 -1
- package/src/pattern/engine.ts +19 -16
- package/src/pipeline.ts +57 -12
- package/src/proto-context.ts +788 -0
- package/src/proto.ts +689 -50
- package/src/raise/arrays.ts +4 -3
- package/src/raise/declared-volatile.ts +131 -0
- package/src/raise/divpow2.ts +2 -2
- package/src/raise/errors.ts +17 -0
- package/src/raise/floathelpers.ts +243 -0
- package/src/raise/globalshape.ts +11 -5
- package/src/raise/gvn.ts +26 -5
- package/src/raise/narrow.ts +29 -2
- package/src/raise/narrowlocal.ts +11 -6
- package/src/raise/offsetnames.ts +510 -0
- package/src/raise/pairparams.ts +81 -0
- package/src/raise/paramwidth.ts +7 -0
- package/src/raise/pre-recovery.ts +78 -4
- package/src/raise/recover.ts +14 -10
- package/src/raise/retsink.ts +1 -1
- package/src/raise/shortcircuit.ts +41 -19
- package/src/raise/softdiv.ts +21 -23
- package/src/raise/struct-arrays.ts +5 -4
- package/src/raise/structs.ts +311 -98
- package/src/raise/truncload.ts +366 -0
- package/src/raise/widehelpers.ts +106 -0
- package/src/rank-declare.ts +30 -5
- package/src/rank-variations.ts +36 -8
- package/src/rank.ts +276 -80
- package/src/runtime-helpers.ts +287 -0
- package/src/stillborn.ts +289 -0
- package/src/structure/analysis.ts +1285 -448
- package/src/structure/hazards.ts +633 -58
- package/src/structure/local-statics.ts +381 -0
- package/src/structure/namecoalesce.ts +1 -1
- package/src/structure/redundant-test.ts +99 -0
- package/src/structure/structure.ts +1772 -395
- package/src/structure/switch-recover.ts +159 -78
- package/src/symbols.ts +38 -3
- package/src/target.ts +662 -59
- package/src/trace.ts +44 -24
- package/src/variation-definitions.ts +119 -1
- package/src/variation-tokens.ts +20 -6
package/src/backend/pascal.ts
CHANGED
|
@@ -52,6 +52,13 @@ function pasType(t: IrType): string {
|
|
|
52
52
|
return '^' + pasType(t.to);
|
|
53
53
|
}
|
|
54
54
|
if (t.kind === 'int') {
|
|
55
|
+
// `Integer`/`Cardinal` are the machine word, and a NARROWER parameter or return fits in one,
|
|
56
|
+
// which is what this backend spells it as (a narrow local is refused where it is declared). A WIDER one does not: upas has no verified 64-bit type,
|
|
57
|
+
// and spelling a 64-bit value `Integer` would silently drop its high half. Loud, like every
|
|
58
|
+
// other unspellable construct in this backend.
|
|
59
|
+
if (t.width > 32) {
|
|
60
|
+
throw new Error(`pascal backend: no spelling for a ${t.width}-bit integer`);
|
|
61
|
+
}
|
|
55
62
|
return t.signed ? 'Integer' : 'Cardinal';
|
|
56
63
|
}
|
|
57
64
|
// `unknown` reaching a backend means recovery's totality contract already failed upstream —
|
|
@@ -81,7 +88,17 @@ function makePrinter(vt: VarTypes) {
|
|
|
81
88
|
throw new Error(`pascal backend: address-of global '${e.name}' has no IDO Pascal spelling yet`);
|
|
82
89
|
case 'const':
|
|
83
90
|
return String(e.value);
|
|
91
|
+
case 'fconst':
|
|
92
|
+
throw new Error('pascal backend: a double literal has no IDO Pascal spelling yet');
|
|
84
93
|
case 'call':
|
|
94
|
+
if (typeof e.fn !== 'string') {
|
|
95
|
+
throw new Error('pascal backend: a call through a pointer has no IDO Pascal spelling yet');
|
|
96
|
+
}
|
|
97
|
+
if (e.sret !== undefined) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`pascal backend: the call to '${e.fn}' returns a struct through memory, which has no spelling here`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
85
102
|
return `${e.fn}(${e.args.map(pe).join(', ')})`;
|
|
86
103
|
case 'index': {
|
|
87
104
|
// The width-carrying access node (l3/ast.ts): each backend legalizes its own derefs. The
|
|
@@ -112,7 +129,14 @@ function makePrinter(vt: VarTypes) {
|
|
|
112
129
|
case 'field':
|
|
113
130
|
throw new Error(`pascal backend: struct field access '${e.name}' has no IDO Pascal spelling yet`);
|
|
114
131
|
case 'un':
|
|
132
|
+
if (e.op === 'f-') {
|
|
133
|
+
throw new Error(`pascal backend: a float negation has no faithful IDO Pascal spelling`);
|
|
134
|
+
}
|
|
115
135
|
return e.op === '~' ? `bitnot(${pe(e.e)})` : `(${e.op === '!' ? 'not ' : e.op}${pe(e.e)})`;
|
|
136
|
+
// IDO Pascal has no increment operator and no expression with a side effect to put one in,
|
|
137
|
+
// so the update has no place to go — decline LOUD rather than drop it, like the casts below.
|
|
138
|
+
case 'postincr':
|
|
139
|
+
throw new Error(`pascal backend: post-increment of '${e.name}' has no IDO Pascal spelling yet`);
|
|
116
140
|
// Casts have no faithful IDO-Pascal spelling yet — fail LOUD rather than emit silently-wrong
|
|
117
141
|
// source. Tree-level producers reaching here: the width-narrowing idiom casts (agbcc-gated,
|
|
118
142
|
// so never on this path today), structure.ts's STRUCT-pointer casts (unreachable too — the
|
|
@@ -278,6 +302,13 @@ export const pascalBackend: LanguageBackend = {
|
|
|
278
302
|
// The declaration list is put into the target's own frame order HERE, as the C family does it
|
|
279
303
|
// in its shared assembler — owned by `emit`, never by a `.emit(` call site (l3/slotorder.ts).
|
|
280
304
|
const fn = orderSlotLocals(fn0);
|
|
305
|
+
// A function-scope static is a definition the MIPS frontends never recover, and IDO Pascal's
|
|
306
|
+
// spelling of one (a typed constant, or a unit-level var) has not been measured — loud.
|
|
307
|
+
if (fn.statics?.length) {
|
|
308
|
+
throw new Error(
|
|
309
|
+
`pascal backend: the function-scope static '${fn.statics[0].name}' has no IDO Pascal spelling yet`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
281
312
|
// Same env discipline as the C family (cfamily.ts cFamilyBody): the printer judges derefs
|
|
282
313
|
// against the exact declarations it emits.
|
|
283
314
|
const ps = makePrinter(declaredTypes(fn));
|
|
@@ -290,6 +321,14 @@ export const pascalBackend: LanguageBackend = {
|
|
|
290
321
|
: `function ${fn.name}(${params}): ${pasType(fn.retType)};`,
|
|
291
322
|
];
|
|
292
323
|
if (fn.locals.length) {
|
|
324
|
+
// A narrow LOCAL truncates every value assigned to it, and `Integer`/`Cardinal` would not: the
|
|
325
|
+
// width is the declaration's whole meaning, so it has no spelling here. A narrow parameter
|
|
326
|
+
// arrives already narrowed and keeps the word spelling.
|
|
327
|
+
for (const l of fn.locals) {
|
|
328
|
+
if (l.type.kind === 'int' && l.type.width < 32) {
|
|
329
|
+
throw new Error(`pascal backend: no spelling for a narrow local (${l.type.width} bits)`);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
293
332
|
lines.push('var', ...fn.locals.map((l) => ` ${l.name}: ${pasType(l.type)};`));
|
|
294
333
|
}
|
|
295
334
|
lines.push('begin');
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// asmlift — the INTEGER CONSTANTS a declaration writes: a literal's value, and the value of a constant
|
|
2
|
+
// expression of literals, as an array's extent or a bit-field's width is written.
|
|
3
|
+
import type { Tokens } from './lex';
|
|
4
|
+
import type { Range } from './parse';
|
|
5
|
+
|
|
6
|
+
/** An integer literal's value and its suffix (`u`, `l`, `ll` in any case and order), read as C reads
|
|
7
|
+
* it: `0x` hexadecimal, a leading `0` octal. Undefined for any other text: `08`, `1.5`, `SIZE`. */
|
|
8
|
+
export function integerLiteral(text: string): { value: bigint; suffix: string } | undefined {
|
|
9
|
+
const m = /^(?:0x([0-9a-f]+)|0([0-7]*)|([1-9]\d*))([ul]*)$/i.exec(text);
|
|
10
|
+
if (m === null) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
const [, hex, octal, decimal, suffix] = m;
|
|
14
|
+
const value =
|
|
15
|
+
hex !== undefined ? BigInt(`0x${hex}`) : octal !== undefined ? BigInt(`0o${octal || '0'}`) : BigInt(decimal);
|
|
16
|
+
return { value, suffix };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The value of the integer constant expression in `r`, of literals, `+ - * /` and parentheses — the
|
|
20
|
+
* `u8 pad3[0x4 - 0x3]` a decomp header sizes its padding with — or undefined for anything else. */
|
|
21
|
+
export function constantValue(tokens: Tokens, r: Range): number | undefined {
|
|
22
|
+
let at = r.from;
|
|
23
|
+
const next = (): string | undefined => (at < r.to ? tokens.text(at++) : undefined);
|
|
24
|
+
const primary = (): number | undefined => {
|
|
25
|
+
const k = at;
|
|
26
|
+
const t = next();
|
|
27
|
+
if (t === '(') {
|
|
28
|
+
const v = sum();
|
|
29
|
+
return next() === ')' ? v : undefined;
|
|
30
|
+
}
|
|
31
|
+
if (t === '-') {
|
|
32
|
+
const v = primary();
|
|
33
|
+
return v === undefined ? undefined : -v;
|
|
34
|
+
}
|
|
35
|
+
const literal = t !== undefined && tokens.kind(k) === 'number' ? integerLiteral(t) : undefined;
|
|
36
|
+
return literal === undefined ? undefined : Number(literal.value);
|
|
37
|
+
};
|
|
38
|
+
const product = (): number | undefined => {
|
|
39
|
+
let v = primary();
|
|
40
|
+
while (v !== undefined && at < r.to && (tokens.is(at, '*') || tokens.is(at, '/'))) {
|
|
41
|
+
const op = next();
|
|
42
|
+
const right = primary();
|
|
43
|
+
v =
|
|
44
|
+
right === undefined || (op === '/' && right === 0) ? undefined : op === '*' ? v * right : Math.trunc(v / right);
|
|
45
|
+
}
|
|
46
|
+
return v;
|
|
47
|
+
};
|
|
48
|
+
const sum = (): number | undefined => {
|
|
49
|
+
let v = product();
|
|
50
|
+
while (v !== undefined && at < r.to && (tokens.is(at, '+') || tokens.is(at, '-'))) {
|
|
51
|
+
const op = next();
|
|
52
|
+
const right = product();
|
|
53
|
+
v = right === undefined ? undefined : op === '+' ? v + right : v - right;
|
|
54
|
+
}
|
|
55
|
+
return v;
|
|
56
|
+
};
|
|
57
|
+
const v = sum();
|
|
58
|
+
return at === r.to ? v : undefined;
|
|
59
|
+
}
|
package/src/cdecl/lex.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
// asmlift — the TOKENS of a C or C++ declaration context: one pass that decides what is code.
|
|
2
|
+
//
|
|
3
|
+
// Whitespace, comments and preprocessor lines (continuations included) are not tokens: a context is
|
|
4
|
+
// preprocessed, so a `#` line left in it is a line marker or a pragma, and a raw header's
|
|
5
|
+
// conditionals are read as if every branch were taken. A string or character literal is one token,
|
|
6
|
+
// and ends at its newline when nothing closes it. `::` and `...` are one token each; every other
|
|
7
|
+
// punctuator is one character, so `>>` closing two template argument lists is two `>`.
|
|
8
|
+
//
|
|
9
|
+
// Brackets are matched once, here: a `}` closes everything opened since its `{`, and a `)` or `]`
|
|
10
|
+
// that does not close the innermost open bracket matches nothing. So an unbalanced `(` inside a
|
|
11
|
+
// body cannot carry the rest of the context with it.
|
|
12
|
+
//
|
|
13
|
+
// Storage is one typed array per field, indexed by token: a context is up to a few megabytes, and an
|
|
14
|
+
// object per token is what would make reading it slow.
|
|
15
|
+
|
|
16
|
+
export type TokenKind = 'identifier' | 'number' | 'string' | 'punct' | 'end';
|
|
17
|
+
|
|
18
|
+
export interface Tokens {
|
|
19
|
+
readonly src: string;
|
|
20
|
+
readonly count: number;
|
|
21
|
+
/** `end` past the last token */
|
|
22
|
+
kind(i: number): TokenKind;
|
|
23
|
+
text(i: number): string;
|
|
24
|
+
is(i: number, s: string): boolean;
|
|
25
|
+
/** a punctuator's first character code, or -1 for any other token */
|
|
26
|
+
char(i: number): number;
|
|
27
|
+
/** the matching close of an open `{ ( [`, the open of a close, or -1 */
|
|
28
|
+
match(i: number): number;
|
|
29
|
+
/** the source offset of the token's first character */
|
|
30
|
+
start(i: number): number;
|
|
31
|
+
/** the source offset just past the token */
|
|
32
|
+
end(i: number): number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const KINDS: readonly TokenKind[] = ['end', 'identifier', 'number', 'string', 'punct'];
|
|
36
|
+
const IDENTIFIER = 1;
|
|
37
|
+
const NUMBER = 2;
|
|
38
|
+
const STRING = 3;
|
|
39
|
+
const PUNCT = 4;
|
|
40
|
+
|
|
41
|
+
const isIdentifierStart = (c: number): boolean => (c >= 97 && c <= 122) || (c >= 65 && c <= 90) || c === 95 || c === 36;
|
|
42
|
+
const isDigit = (c: number): boolean => c >= 48 && c <= 57;
|
|
43
|
+
/** whether the newline at `i` ends a line that a backslash continues */
|
|
44
|
+
const continued = (src: string, i: number): boolean =>
|
|
45
|
+
src.charCodeAt(i - 1) === 92 || (src.charCodeAt(i - 1) === 13 && src.charCodeAt(i - 2) === 92);
|
|
46
|
+
|
|
47
|
+
class TokenArrays implements Tokens {
|
|
48
|
+
readonly src: string;
|
|
49
|
+
readonly count: number;
|
|
50
|
+
readonly #kind: Uint8Array;
|
|
51
|
+
readonly #start: Int32Array;
|
|
52
|
+
readonly #end: Int32Array;
|
|
53
|
+
readonly #match: Int32Array;
|
|
54
|
+
|
|
55
|
+
constructor(src: string, count: number, kind: Uint8Array, start: Int32Array, end: Int32Array, match: Int32Array) {
|
|
56
|
+
this.src = src;
|
|
57
|
+
this.count = count;
|
|
58
|
+
this.#kind = kind;
|
|
59
|
+
this.#start = start;
|
|
60
|
+
this.#end = end;
|
|
61
|
+
this.#match = match;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
kind(i: number): TokenKind {
|
|
65
|
+
return i < this.count ? KINDS[this.#kind[i]] : 'end';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
text(i: number): string {
|
|
69
|
+
return this.src.slice(this.#start[i], this.#end[i]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
is(i: number, s: string): boolean {
|
|
73
|
+
return i < this.count && this.#end[i] - this.#start[i] === s.length && this.src.startsWith(s, this.#start[i]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
char(i: number): number {
|
|
77
|
+
return i < this.count && this.#kind[i] === PUNCT ? this.src.charCodeAt(this.#start[i]) : -1;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
match(i: number): number {
|
|
81
|
+
return i < this.count ? this.#match[i] : -1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
start(i: number): number {
|
|
85
|
+
return i < this.count ? this.#start[i] : this.src.length;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
end(i: number): number {
|
|
89
|
+
return i < this.count ? this.#end[i] : this.src.length;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function lex(src: string): Tokens {
|
|
94
|
+
let capacity = Math.max(16, src.length >> 2);
|
|
95
|
+
let kind = new Uint8Array(capacity);
|
|
96
|
+
let start = new Int32Array(capacity);
|
|
97
|
+
let end = new Int32Array(capacity);
|
|
98
|
+
let count = 0;
|
|
99
|
+
const push = (k: number, from: number, to: number): void => {
|
|
100
|
+
if (count === capacity) {
|
|
101
|
+
capacity *= 2;
|
|
102
|
+
const k2 = new Uint8Array(capacity);
|
|
103
|
+
k2.set(kind);
|
|
104
|
+
kind = k2;
|
|
105
|
+
const s2 = new Int32Array(capacity);
|
|
106
|
+
s2.set(start);
|
|
107
|
+
start = s2;
|
|
108
|
+
const e2 = new Int32Array(capacity);
|
|
109
|
+
e2.set(end);
|
|
110
|
+
end = e2;
|
|
111
|
+
}
|
|
112
|
+
kind[count] = k;
|
|
113
|
+
start[count] = from;
|
|
114
|
+
end[count] = to;
|
|
115
|
+
count++;
|
|
116
|
+
};
|
|
117
|
+
const length = src.length;
|
|
118
|
+
// only whitespace and comments since the last newline: a `#` here starts a directive
|
|
119
|
+
let lineStart = true;
|
|
120
|
+
let i = 0;
|
|
121
|
+
while (i < length) {
|
|
122
|
+
const c = src.charCodeAt(i);
|
|
123
|
+
if (c === 10) {
|
|
124
|
+
lineStart = true;
|
|
125
|
+
i++;
|
|
126
|
+
} else if (c === 32 || c === 9 || c === 13 || c === 12 || c === 11) {
|
|
127
|
+
i++;
|
|
128
|
+
} else if (c === 47 && src.charCodeAt(i + 1) === 47) {
|
|
129
|
+
const newline = src.indexOf('\n', i);
|
|
130
|
+
i = newline < 0 ? length : newline;
|
|
131
|
+
} else if (c === 47 && src.charCodeAt(i + 1) === 42) {
|
|
132
|
+
const close = src.indexOf('*/', i + 2);
|
|
133
|
+
i = close < 0 ? length : close + 2;
|
|
134
|
+
} else if (c === 35 && lineStart) {
|
|
135
|
+
while (i < length && !(src.charCodeAt(i) === 10 && !continued(src, i))) {
|
|
136
|
+
i++;
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
lineStart = false;
|
|
140
|
+
let j = i + 1;
|
|
141
|
+
if (isIdentifierStart(c)) {
|
|
142
|
+
while (j < length && (isIdentifierStart(src.charCodeAt(j)) || isDigit(src.charCodeAt(j)))) {
|
|
143
|
+
j++;
|
|
144
|
+
}
|
|
145
|
+
push(IDENTIFIER, i, j);
|
|
146
|
+
} else if (isDigit(c)) {
|
|
147
|
+
// a preprocessing number: `0x1F`, `1.5f`, `1e-3`
|
|
148
|
+
for (; j < length; j++) {
|
|
149
|
+
const d = src.charCodeAt(j);
|
|
150
|
+
const sign = (d === 43 || d === 45) && /[eEpP]/.test(src[j - 1]);
|
|
151
|
+
if (!(isIdentifierStart(d) || isDigit(d) || d === 46 || sign)) {
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
push(NUMBER, i, j);
|
|
156
|
+
} else if (c === 34 || c === 39) {
|
|
157
|
+
while (j < length && src.charCodeAt(j) !== c && src.charCodeAt(j) !== 10) {
|
|
158
|
+
j += src.charCodeAt(j) === 92 ? 2 : 1;
|
|
159
|
+
}
|
|
160
|
+
if (j < length && src.charCodeAt(j) === c) {
|
|
161
|
+
j++;
|
|
162
|
+
}
|
|
163
|
+
push(STRING, i, j);
|
|
164
|
+
} else if (c === 58 && src.charCodeAt(j) === 58) {
|
|
165
|
+
j++;
|
|
166
|
+
push(PUNCT, i, j);
|
|
167
|
+
} else if (c === 46 && src.charCodeAt(j) === 46 && src.charCodeAt(j + 1) === 46) {
|
|
168
|
+
j += 2;
|
|
169
|
+
push(PUNCT, i, j);
|
|
170
|
+
} else {
|
|
171
|
+
push(PUNCT, i, j);
|
|
172
|
+
}
|
|
173
|
+
i = j;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return new TokenArrays(src, count, kind, start, end, matchBrackets(src, count, kind, start));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function matchBrackets(src: string, count: number, kind: Uint8Array, start: Int32Array): Int32Array {
|
|
180
|
+
const match = new Int32Array(count).fill(-1);
|
|
181
|
+
const open: number[] = [];
|
|
182
|
+
for (let t = 0; t < count; t++) {
|
|
183
|
+
if (kind[t] !== PUNCT) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const c = src.charCodeAt(start[t]);
|
|
187
|
+
if (c === 123 || c === 40 || c === 91) {
|
|
188
|
+
open.push(t);
|
|
189
|
+
} else if (c === 125) {
|
|
190
|
+
let k = open.length - 1;
|
|
191
|
+
while (k >= 0 && src.charCodeAt(start[open[k]]) !== 123) {
|
|
192
|
+
k--;
|
|
193
|
+
}
|
|
194
|
+
if (k >= 0) {
|
|
195
|
+
match[open[k]] = t;
|
|
196
|
+
match[t] = open[k];
|
|
197
|
+
open.length = k;
|
|
198
|
+
}
|
|
199
|
+
} else if (c === 41 || c === 93) {
|
|
200
|
+
const top = open[open.length - 1];
|
|
201
|
+
if (top !== undefined && src.charCodeAt(start[top]) === (c === 41 ? 40 : 91)) {
|
|
202
|
+
match[top] = t;
|
|
203
|
+
match[t] = top;
|
|
204
|
+
open.pop();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return match;
|
|
209
|
+
}
|