@alex-dbs/coderuler 0.0.0-stage → 1.3.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/LICENSE +175 -0
- package/README.md +218 -2
- package/dist/adapter/cli/commands/check.d.ts +3 -0
- package/dist/adapter/cli/commands/check.js +93 -0
- package/dist/adapter/cli/commands/deps.d.ts +3 -0
- package/dist/adapter/cli/commands/deps.js +4 -0
- package/dist/adapter/cli/commands/inspect.d.ts +3 -0
- package/dist/adapter/cli/commands/inspect.js +59 -0
- package/dist/adapter/cli/commands/rules.d.ts +10 -0
- package/dist/adapter/cli/commands/rules.js +17 -0
- package/dist/adapter/cli/commands/skill.d.ts +4 -0
- package/dist/adapter/cli/commands/skill.js +7 -0
- package/dist/adapter/cli/commands/test.d.ts +3 -0
- package/dist/adapter/cli/commands/test.js +78 -0
- package/dist/adapter/cli/commands/update.d.ts +3 -0
- package/dist/adapter/cli/commands/update.js +7 -0
- package/dist/adapter/cli/commands/validate.d.ts +3 -0
- package/dist/adapter/cli/commands/validate.js +12 -0
- package/dist/adapter/cli/exit.d.ts +7 -0
- package/dist/adapter/cli/exit.js +10 -0
- package/dist/adapter/cli/inspect-json.d.ts +9 -0
- package/dist/adapter/cli/inspect-json.js +160 -0
- package/dist/adapter/cli/inspect-usage.d.ts +9 -0
- package/dist/adapter/cli/inspect-usage.js +14 -0
- package/dist/adapter/cli/program.d.ts +4 -0
- package/dist/adapter/cli/program.js +216 -0
- package/dist/adapter/cli/progress.d.ts +22 -0
- package/dist/adapter/cli/progress.js +79 -0
- package/dist/adapter/cli/reports.d.ts +47 -0
- package/dist/adapter/cli/reports.js +735 -0
- package/dist/adapter/cli/self-update.d.ts +1 -0
- package/dist/adapter/cli/self-update.js +89 -0
- package/dist/adapter/cli/services.d.ts +101 -0
- package/dist/adapter/cli/services.js +1 -0
- package/dist/adapter/cli/text.d.ts +5 -0
- package/dist/adapter/cli/text.js +36 -0
- package/dist/adapter/cli/version.d.ts +1 -0
- package/dist/adapter/cli/version.js +3 -0
- package/dist/adapter/decision/jev.d.ts +11 -0
- package/dist/adapter/decision/jev.js +138 -0
- package/dist/adapter/filesystem/dependencies.d.ts +26 -0
- package/dist/adapter/filesystem/dependencies.js +577 -0
- package/dist/adapter/filesystem/dependency-installation.d.ts +11 -0
- package/dist/adapter/filesystem/dependency-installation.js +373 -0
- package/dist/adapter/filesystem/project.d.ts +14 -0
- package/dist/adapter/filesystem/project.js +387 -0
- package/dist/adapter/filesystem/rule-resolution.d.ts +4 -0
- package/dist/adapter/filesystem/rule-resolution.js +516 -0
- package/dist/adapter/filesystem/rule-test-worker.d.ts +17 -0
- package/dist/adapter/filesystem/rule-test-worker.js +47 -0
- package/dist/adapter/filesystem/rule-tests.d.ts +3 -0
- package/dist/adapter/filesystem/rule-tests.js +264 -0
- package/dist/adapter/filesystem/rules.d.ts +13 -0
- package/dist/adapter/filesystem/rules.js +242 -0
- package/dist/adapter/git/changes.d.ts +1 -0
- package/dist/adapter/git/changes.js +54 -0
- package/dist/adapter/git/environment.d.ts +1 -0
- package/dist/adapter/git/environment.js +10 -0
- package/dist/adapter/github/api.d.ts +5 -0
- package/dist/adapter/github/api.js +40 -0
- package/dist/adapter/github/comments.d.ts +3 -0
- package/dist/adapter/github/comments.js +235 -0
- package/dist/adapter/github/pull-request.d.ts +16 -0
- package/dist/adapter/github/pull-request.js +139 -0
- package/dist/adapter/scope/astgrep-languages.d.ts +5 -0
- package/dist/adapter/scope/astgrep-languages.js +119 -0
- package/dist/adapter/scope/astgrep.d.ts +14 -0
- package/dist/adapter/scope/astgrep.js +183 -0
- package/dist/adapter/scope/csv.d.ts +4 -0
- package/dist/adapter/scope/csv.js +158 -0
- package/dist/adapter/scope/file.d.ts +2 -0
- package/dist/adapter/scope/file.js +4 -0
- package/dist/adapter/scope/json.d.ts +7 -0
- package/dist/adapter/scope/json.js +132 -0
- package/dist/adapter/scope/lines.d.ts +10 -0
- package/dist/adapter/scope/lines.js +42 -0
- package/dist/adapter/scope/protobuf-options.d.ts +29 -0
- package/dist/adapter/scope/protobuf-options.js +64 -0
- package/dist/adapter/scope/protobuf-parser.d.ts +30 -0
- package/dist/adapter/scope/protobuf-parser.js +772 -0
- package/dist/adapter/scope/protobuf.d.ts +5 -0
- package/dist/adapter/scope/protobuf.js +53 -0
- package/dist/adapter/scope/structured.d.ts +10 -0
- package/dist/adapter/scope/structured.js +84 -0
- package/dist/adapter/scope/yaml.d.ts +7 -0
- package/dist/adapter/scope/yaml.js +224 -0
- package/dist/adapter/skills/cli.d.ts +10 -0
- package/dist/adapter/skills/cli.js +77 -0
- package/dist/app/concurrency.d.ts +1 -0
- package/dist/app/concurrency.js +44 -0
- package/dist/app/evaluators.d.ts +14 -0
- package/dist/app/evaluators.js +175 -0
- package/dist/app/execute.d.ts +2 -0
- package/dist/app/execute.js +427 -0
- package/dist/app/jsonpath.d.ts +1 -0
- package/dist/app/jsonpath.js +8 -0
- package/dist/app/ports.d.ts +165 -0
- package/dist/app/ports.js +1 -0
- package/dist/app/rule-tests.d.ts +20 -0
- package/dist/app/rule-tests.js +11 -0
- package/dist/app/rule.d.ts +121 -0
- package/dist/app/rule.js +24 -0
- package/dist/app/selection.d.ts +2 -0
- package/dist/app/selection.js +23 -0
- package/dist/app/usage.d.ts +2 -0
- package/dist/app/usage.js +13 -0
- package/dist/app/validate.d.ts +11 -0
- package/dist/app/validate.js +415 -0
- package/dist/app/violation-comments.d.ts +10 -0
- package/dist/app/violation-comments.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +3 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +11 -0
- package/dist/infra/runtime.d.ts +3 -0
- package/dist/infra/runtime.js +168 -0
- package/dist/infra/settings.d.ts +5 -0
- package/dist/infra/settings.js +91 -0
- package/package.json +86 -4
- package/skills/coderuler-new-rule/SKILL.md +141 -0
- package/skills/coderuler-new-rule/reference.md +447 -0
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
import { assignOption, extendSpan, optionMessage, optionsJson } from './protobuf-options.js';
|
|
3
|
+
import { pointerSegment } from './structured.js';
|
|
4
|
+
export const DECLARATION_LISTS = ['services', 'rpcs', 'messages', 'fields', 'oneofs', 'enums', 'enumValues', 'extensions'];
|
|
5
|
+
const CLOSERS = { '{': '}', '(': ')', '[': ']', '<': '>' };
|
|
6
|
+
function isIdentStart(ch) {
|
|
7
|
+
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || ch === '_';
|
|
8
|
+
}
|
|
9
|
+
function isIdentPart(ch) {
|
|
10
|
+
return isIdentStart(ch) || (ch >= '0' && ch <= '9');
|
|
11
|
+
}
|
|
12
|
+
function isWhitespace(ch) {
|
|
13
|
+
return ch === ' ' || ch === '\t' || ch === '\r' || ch === '\n' || ch === '\f' || ch === '\v' || ch === '\uFEFF';
|
|
14
|
+
}
|
|
15
|
+
function hexValue(ch) {
|
|
16
|
+
if (ch >= '0' && ch <= '9')
|
|
17
|
+
return ch.charCodeAt(0) - 48;
|
|
18
|
+
if (ch >= 'a' && ch <= 'f')
|
|
19
|
+
return ch.charCodeAt(0) - 87;
|
|
20
|
+
if (ch >= 'A' && ch <= 'F')
|
|
21
|
+
return ch.charCodeAt(0) - 55;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function readEscape(source, index) {
|
|
25
|
+
const ch = source[index + 1];
|
|
26
|
+
if (ch === undefined)
|
|
27
|
+
throw new Error('Unterminated escape sequence in string literal');
|
|
28
|
+
switch (ch) {
|
|
29
|
+
case 'n': return { next: index + 2, text: '\n' };
|
|
30
|
+
case 'r': return { next: index + 2, text: '\r' };
|
|
31
|
+
case 't': return { next: index + 2, text: '\t' };
|
|
32
|
+
case 'a': return { next: index + 2, text: '\u0007' };
|
|
33
|
+
case 'b': return { next: index + 2, text: '\b' };
|
|
34
|
+
case 'f': return { next: index + 2, text: '\f' };
|
|
35
|
+
case 'v': return { next: index + 2, text: '\v' };
|
|
36
|
+
case '\\': return { next: index + 2, text: '\\' };
|
|
37
|
+
case '"': return { next: index + 2, text: '"' };
|
|
38
|
+
case "'": return { next: index + 2, text: "'" };
|
|
39
|
+
case '/': return { next: index + 2, text: '/' };
|
|
40
|
+
case '?': return { next: index + 2, text: '?' };
|
|
41
|
+
case 'x':
|
|
42
|
+
case 'X': {
|
|
43
|
+
let value = 0;
|
|
44
|
+
let count = 0;
|
|
45
|
+
let cursor = index + 2;
|
|
46
|
+
while (count < 2) {
|
|
47
|
+
const digit = source[cursor];
|
|
48
|
+
if (digit === undefined)
|
|
49
|
+
break;
|
|
50
|
+
const parsed = hexValue(digit);
|
|
51
|
+
if (parsed === undefined)
|
|
52
|
+
break;
|
|
53
|
+
value = value * 16 + parsed;
|
|
54
|
+
cursor++;
|
|
55
|
+
count++;
|
|
56
|
+
}
|
|
57
|
+
if (count === 0)
|
|
58
|
+
throw new Error('Invalid hex escape in string literal');
|
|
59
|
+
return { next: cursor, byte: value };
|
|
60
|
+
}
|
|
61
|
+
case 'u':
|
|
62
|
+
case 'U': {
|
|
63
|
+
const width = ch === 'u' ? 4 : 8;
|
|
64
|
+
let value = 0;
|
|
65
|
+
let cursor = index + 2;
|
|
66
|
+
for (let offset = 0; offset < width; offset++) {
|
|
67
|
+
const digit = source[cursor];
|
|
68
|
+
const parsed = digit === undefined ? undefined : hexValue(digit);
|
|
69
|
+
if (parsed === undefined)
|
|
70
|
+
throw new Error('Invalid unicode escape in string literal');
|
|
71
|
+
value = value * 16 + parsed;
|
|
72
|
+
cursor++;
|
|
73
|
+
}
|
|
74
|
+
if (value > 0x10ffff)
|
|
75
|
+
throw new Error('Unicode escape out of range in string literal');
|
|
76
|
+
return { next: cursor, text: String.fromCodePoint(value) };
|
|
77
|
+
}
|
|
78
|
+
default: {
|
|
79
|
+
if (ch >= '0' && ch <= '7') {
|
|
80
|
+
let value = 0;
|
|
81
|
+
let count = 0;
|
|
82
|
+
let cursor = index + 1;
|
|
83
|
+
while (count < 3) {
|
|
84
|
+
const digit = source[cursor];
|
|
85
|
+
if (digit === undefined || digit < '0' || digit > '7')
|
|
86
|
+
break;
|
|
87
|
+
value = value * 8 + digit.charCodeAt(0) - 48;
|
|
88
|
+
cursor++;
|
|
89
|
+
count++;
|
|
90
|
+
}
|
|
91
|
+
return { next: cursor, byte: value & 0xff };
|
|
92
|
+
}
|
|
93
|
+
return { next: index + 2, text: ch };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function flushByteRun(bytes, out) {
|
|
98
|
+
if (bytes.length === 0)
|
|
99
|
+
return;
|
|
100
|
+
out.push(Buffer.from(bytes).toString('utf8'));
|
|
101
|
+
bytes.length = 0;
|
|
102
|
+
}
|
|
103
|
+
function decodeStringLiteral(raw, bytes, out) {
|
|
104
|
+
const end = raw.length - 1;
|
|
105
|
+
let index = 1;
|
|
106
|
+
let literalStart = index;
|
|
107
|
+
while (index < end) {
|
|
108
|
+
if (raw[index] !== '\\') {
|
|
109
|
+
index++;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (literalStart < index) {
|
|
113
|
+
flushByteRun(bytes, out);
|
|
114
|
+
out.push(raw.slice(literalStart, index));
|
|
115
|
+
}
|
|
116
|
+
const escape = readEscape(raw, index);
|
|
117
|
+
if (escape.byte === undefined) {
|
|
118
|
+
flushByteRun(bytes, out);
|
|
119
|
+
out.push(escape.text);
|
|
120
|
+
}
|
|
121
|
+
else
|
|
122
|
+
bytes.push(escape.byte);
|
|
123
|
+
index = escape.next;
|
|
124
|
+
literalStart = index;
|
|
125
|
+
}
|
|
126
|
+
if (literalStart < end) {
|
|
127
|
+
flushByteRun(bytes, out);
|
|
128
|
+
out.push(raw.slice(literalStart, end));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
function readStringToken(source, start) {
|
|
132
|
+
const quote = source[start];
|
|
133
|
+
let index = start + 1;
|
|
134
|
+
for (;;) {
|
|
135
|
+
if (index >= source.length)
|
|
136
|
+
throw new Error('Unterminated string literal in protobuf source');
|
|
137
|
+
const ch = source[index];
|
|
138
|
+
if (ch === quote) {
|
|
139
|
+
const end = index + 1;
|
|
140
|
+
return [{ kind: 'string', text: source.slice(start, end), start, end }, end];
|
|
141
|
+
}
|
|
142
|
+
if (ch === '\n' || ch === '\r')
|
|
143
|
+
throw new Error('Unterminated string literal in protobuf source');
|
|
144
|
+
if (ch === '\\') {
|
|
145
|
+
index = readEscape(source, index).next;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
index++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function tokenizeProto(source) {
|
|
152
|
+
const tokens = [];
|
|
153
|
+
let index = 0;
|
|
154
|
+
const length = source.length;
|
|
155
|
+
while (index < length) {
|
|
156
|
+
const ch = source[index];
|
|
157
|
+
if (isWhitespace(ch)) {
|
|
158
|
+
index++;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (ch === '/' && source[index + 1] === '/') {
|
|
162
|
+
index += 2;
|
|
163
|
+
while (index < length && source[index] !== '\n')
|
|
164
|
+
index++;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (ch === '/' && source[index + 1] === '*') {
|
|
168
|
+
index += 2;
|
|
169
|
+
let closed = false;
|
|
170
|
+
while (index < length) {
|
|
171
|
+
if (source[index] === '*' && source[index + 1] === '/') {
|
|
172
|
+
index += 2;
|
|
173
|
+
closed = true;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
index++;
|
|
177
|
+
}
|
|
178
|
+
if (!closed)
|
|
179
|
+
throw new Error('Unterminated block comment in protobuf source');
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (ch === '#') {
|
|
183
|
+
index++;
|
|
184
|
+
while (index < length && source[index] !== '\n')
|
|
185
|
+
index++;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (ch === '"' || ch === "'") {
|
|
189
|
+
const [token, next] = readStringToken(source, index);
|
|
190
|
+
tokens.push(token);
|
|
191
|
+
index = next;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (isIdentStart(ch)) {
|
|
195
|
+
const start = index++;
|
|
196
|
+
while (index < length && isIdentPart(source[index]))
|
|
197
|
+
index++;
|
|
198
|
+
tokens.push({ kind: 'ident', text: source.slice(start, index), start, end: index });
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if ((ch >= '0' && ch <= '9') || (ch === '.' && source[index + 1] !== undefined && source[index + 1] >= '0' && source[index + 1] <= '9')) {
|
|
202
|
+
const start = index++;
|
|
203
|
+
const hexadecimal = ch === '0' && (source[index] === 'x' || source[index] === 'X');
|
|
204
|
+
while (index < length) {
|
|
205
|
+
const next = source[index];
|
|
206
|
+
if (isIdentPart(next) || next === '.') {
|
|
207
|
+
index++;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (!hexadecimal && (next === '+' || next === '-') && (source[index - 1] === 'e' || source[index - 1] === 'E')) {
|
|
211
|
+
index++;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
tokens.push({ kind: 'number', text: source.slice(start, index), start, end: index });
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
tokens.push({ kind: 'punct', text: ch, start: index, end: index + 1 });
|
|
220
|
+
index++;
|
|
221
|
+
}
|
|
222
|
+
return tokens;
|
|
223
|
+
}
|
|
224
|
+
function skipBalanced(tokens, index) {
|
|
225
|
+
const open = tokens[index].text;
|
|
226
|
+
const close = CLOSERS[open];
|
|
227
|
+
let depth = 0;
|
|
228
|
+
let cursor = index;
|
|
229
|
+
while (cursor < tokens.length) {
|
|
230
|
+
const token = tokens[cursor];
|
|
231
|
+
if (token.kind === 'punct' && token.text === open)
|
|
232
|
+
depth++;
|
|
233
|
+
else if (token.kind === 'punct' && token.text === close) {
|
|
234
|
+
depth--;
|
|
235
|
+
if (depth === 0)
|
|
236
|
+
return cursor + 1;
|
|
237
|
+
}
|
|
238
|
+
cursor++;
|
|
239
|
+
}
|
|
240
|
+
throw new Error(`Unbalanced '${open}' in protobuf source`);
|
|
241
|
+
}
|
|
242
|
+
function skipStatement(tokens, index) {
|
|
243
|
+
let cursor = index;
|
|
244
|
+
let depth = 0;
|
|
245
|
+
while (cursor < tokens.length) {
|
|
246
|
+
const token = tokens[cursor];
|
|
247
|
+
if (token.kind !== 'punct') {
|
|
248
|
+
cursor++;
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const text = token.text;
|
|
252
|
+
if (text === '{') {
|
|
253
|
+
depth++;
|
|
254
|
+
cursor++;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (text === '}') {
|
|
258
|
+
if (depth === 0)
|
|
259
|
+
return cursor;
|
|
260
|
+
depth--;
|
|
261
|
+
cursor++;
|
|
262
|
+
if (depth === 0)
|
|
263
|
+
return cursor;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (text === '(' || text === '[' || text === '<') {
|
|
267
|
+
cursor = skipBalanced(tokens, cursor);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (text === ';' && depth === 0)
|
|
271
|
+
return cursor + 1;
|
|
272
|
+
cursor++;
|
|
273
|
+
}
|
|
274
|
+
return cursor;
|
|
275
|
+
}
|
|
276
|
+
class ProtoReader {
|
|
277
|
+
tokens;
|
|
278
|
+
source;
|
|
279
|
+
index = 0;
|
|
280
|
+
packageName = '';
|
|
281
|
+
fileOptions = optionMessage();
|
|
282
|
+
drafts = [];
|
|
283
|
+
constructor(source) {
|
|
284
|
+
this.source = source;
|
|
285
|
+
this.tokens = tokenizeProto(source);
|
|
286
|
+
}
|
|
287
|
+
read() {
|
|
288
|
+
while (this.index < this.tokens.length) {
|
|
289
|
+
const token = this.tokens[this.index];
|
|
290
|
+
let next;
|
|
291
|
+
if (token.kind === 'ident' && token.text === 'package')
|
|
292
|
+
next = this.readPackage(this.index);
|
|
293
|
+
else if (token.kind === 'ident' && token.text === 'option')
|
|
294
|
+
next = this.readOption(this.index, this.fileOptions);
|
|
295
|
+
else if (token.kind === 'ident' && token.text === 'message')
|
|
296
|
+
next = this.readNamedBody(this.index, 'message', 'messages', undefined);
|
|
297
|
+
else if (token.kind === 'ident' && token.text === 'enum')
|
|
298
|
+
next = this.readNamedBody(this.index, 'enum', 'enums', undefined);
|
|
299
|
+
else if (token.kind === 'ident' && token.text === 'service')
|
|
300
|
+
next = this.readNamedBody(this.index, 'service', 'services', undefined);
|
|
301
|
+
else if (token.kind === 'ident' && token.text === 'extend')
|
|
302
|
+
next = this.readExtend(this.index, undefined);
|
|
303
|
+
else
|
|
304
|
+
next = skipStatement(this.tokens, this.index);
|
|
305
|
+
this.index = next > this.index ? next : this.index + 1;
|
|
306
|
+
}
|
|
307
|
+
const spans = new Map();
|
|
308
|
+
spans.set('', { start: 0, end: this.source.length });
|
|
309
|
+
const output = new Map();
|
|
310
|
+
for (const list of DECLARATION_LISTS)
|
|
311
|
+
output.set(list, []);
|
|
312
|
+
const indexes = new Map();
|
|
313
|
+
for (const draft of this.drafts) {
|
|
314
|
+
const index = indexes.get(draft.list) ?? 0;
|
|
315
|
+
indexes.set(draft.list, index + 1);
|
|
316
|
+
const pointer = `/${pointerSegment(draft.list)}/${index}`;
|
|
317
|
+
if (draft.span !== undefined)
|
|
318
|
+
spans.set(pointer, draft.span);
|
|
319
|
+
spans.set(`${pointer}/name`, draft.nameSpan);
|
|
320
|
+
const options = optionsJson(draft.options, `${pointer}/options`, spans);
|
|
321
|
+
const id = this.qualify(draft.localId);
|
|
322
|
+
const parent = draft.parent === undefined ? null : this.qualify(draft.parent.localId);
|
|
323
|
+
output.get(draft.list).push({ id, name: draft.name, parent, options });
|
|
324
|
+
}
|
|
325
|
+
const options = optionsJson(this.fileOptions, '/options', spans);
|
|
326
|
+
const data = {
|
|
327
|
+
options,
|
|
328
|
+
services: output.get('services'),
|
|
329
|
+
rpcs: output.get('rpcs'),
|
|
330
|
+
messages: output.get('messages'),
|
|
331
|
+
fields: output.get('fields'),
|
|
332
|
+
oneofs: output.get('oneofs'),
|
|
333
|
+
enums: output.get('enums'),
|
|
334
|
+
enumValues: output.get('enumValues'),
|
|
335
|
+
extensions: output.get('extensions'),
|
|
336
|
+
};
|
|
337
|
+
return { data, spans };
|
|
338
|
+
}
|
|
339
|
+
qualify(localId) {
|
|
340
|
+
return this.packageName === '' ? localId : `${this.packageName}.${localId}`;
|
|
341
|
+
}
|
|
342
|
+
readPackage(start) {
|
|
343
|
+
let cursor = start + 1;
|
|
344
|
+
const parts = [];
|
|
345
|
+
while (cursor < this.tokens.length && this.tokens[cursor].text !== ';') {
|
|
346
|
+
const token = this.tokens[cursor];
|
|
347
|
+
if (token.kind === 'ident')
|
|
348
|
+
parts.push(token.text);
|
|
349
|
+
else if (token.text !== '.')
|
|
350
|
+
throw new Error('Malformed package declaration in protobuf source');
|
|
351
|
+
cursor++;
|
|
352
|
+
}
|
|
353
|
+
if (this.tokens[cursor]?.text !== ';')
|
|
354
|
+
throw new Error('Unterminated package declaration in protobuf source');
|
|
355
|
+
this.packageName = parts.join('.');
|
|
356
|
+
return cursor + 1;
|
|
357
|
+
}
|
|
358
|
+
createDraft(list, name, localId, parent, nameToken) {
|
|
359
|
+
const draft = {
|
|
360
|
+
list,
|
|
361
|
+
localId,
|
|
362
|
+
name,
|
|
363
|
+
...(parent === undefined ? {} : { parent }),
|
|
364
|
+
options: optionMessage(),
|
|
365
|
+
nameSpan: { start: nameToken.start, end: nameToken.end },
|
|
366
|
+
};
|
|
367
|
+
this.drafts.push(draft);
|
|
368
|
+
return draft;
|
|
369
|
+
}
|
|
370
|
+
readNamedBody(start, kind, list, parent) {
|
|
371
|
+
const nameToken = this.tokens[start + 1];
|
|
372
|
+
if (nameToken === undefined || nameToken.kind !== 'ident' || this.tokens[start + 2]?.text !== '{') {
|
|
373
|
+
throw new Error(`Malformed ${kind} declaration in protobuf source`);
|
|
374
|
+
}
|
|
375
|
+
const localId = parent === undefined ? nameToken.text : `${parent.localId}.${nameToken.text}`;
|
|
376
|
+
const draft = this.createDraft(list, nameToken.text, localId, parent, nameToken);
|
|
377
|
+
return this.readBody(start + 3, kind, draft, this.tokens[start].start);
|
|
378
|
+
}
|
|
379
|
+
readExtend(start, parent) {
|
|
380
|
+
let cursor = start + 1;
|
|
381
|
+
let sawName = false;
|
|
382
|
+
while (cursor < this.tokens.length && this.tokens[cursor].text !== '{') {
|
|
383
|
+
const token = this.tokens[cursor];
|
|
384
|
+
if (token.kind === 'ident' || token.text === '.')
|
|
385
|
+
sawName = true;
|
|
386
|
+
else
|
|
387
|
+
throw new Error('Malformed extend declaration in protobuf source');
|
|
388
|
+
cursor++;
|
|
389
|
+
}
|
|
390
|
+
if (!sawName || this.tokens[cursor]?.text !== '{')
|
|
391
|
+
throw new Error('Malformed extend declaration in protobuf source');
|
|
392
|
+
return this.readBody(cursor + 1, 'extend', parent, this.tokens[start].start);
|
|
393
|
+
}
|
|
394
|
+
readBody(start, kind, owner, declarationStart) {
|
|
395
|
+
let cursor = start;
|
|
396
|
+
while (cursor < this.tokens.length) {
|
|
397
|
+
const token = this.tokens[cursor];
|
|
398
|
+
if (token.text === '}') {
|
|
399
|
+
if (kind === 'extend')
|
|
400
|
+
return cursor + 1;
|
|
401
|
+
const hasSemicolon = kind === 'rpc' && this.tokens[cursor + 1]?.text === ';';
|
|
402
|
+
const end = hasSemicolon ? this.tokens[cursor + 1].end : token.end;
|
|
403
|
+
if (owner !== undefined)
|
|
404
|
+
owner.span = extendSpan(owner.span, { start: declarationStart, end });
|
|
405
|
+
return cursor + (hasSemicolon ? 2 : 1);
|
|
406
|
+
}
|
|
407
|
+
if (token.text === ';') {
|
|
408
|
+
cursor++;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
if (kind === 'message') {
|
|
412
|
+
if (token.kind === 'ident' && token.text === 'message')
|
|
413
|
+
cursor = this.readNamedBody(cursor, 'message', 'messages', owner);
|
|
414
|
+
else if (token.kind === 'ident' && token.text === 'enum')
|
|
415
|
+
cursor = this.readNamedBody(cursor, 'enum', 'enums', owner);
|
|
416
|
+
else if (token.kind === 'ident' && token.text === 'extend')
|
|
417
|
+
cursor = this.readExtend(cursor, owner);
|
|
418
|
+
else if (token.kind === 'ident' && token.text === 'oneof')
|
|
419
|
+
cursor = this.readNamedBody(cursor, 'oneof', 'oneofs', owner);
|
|
420
|
+
else if (token.kind === 'ident' && token.text === 'option')
|
|
421
|
+
cursor = this.readOption(cursor, owner.options);
|
|
422
|
+
else if (token.kind === 'ident' && (token.text === 'reserved' || token.text === 'extensions'))
|
|
423
|
+
cursor = skipStatement(this.tokens, cursor);
|
|
424
|
+
else
|
|
425
|
+
cursor = this.readField(cursor, owner, false);
|
|
426
|
+
}
|
|
427
|
+
else if (kind === 'oneof') {
|
|
428
|
+
if (token.kind === 'ident' && token.text === 'option')
|
|
429
|
+
cursor = this.readOption(cursor, owner.options);
|
|
430
|
+
else
|
|
431
|
+
cursor = this.readField(cursor, owner.parent, false);
|
|
432
|
+
}
|
|
433
|
+
else if (kind === 'enum') {
|
|
434
|
+
if (token.kind === 'ident' && token.text === 'option')
|
|
435
|
+
cursor = this.readOption(cursor, owner.options);
|
|
436
|
+
else if (token.kind === 'ident' && token.text === 'reserved')
|
|
437
|
+
cursor = skipStatement(this.tokens, cursor);
|
|
438
|
+
else
|
|
439
|
+
cursor = this.readEnumValue(cursor, owner);
|
|
440
|
+
}
|
|
441
|
+
else if (kind === 'service') {
|
|
442
|
+
if (token.kind === 'ident' && token.text === 'option')
|
|
443
|
+
cursor = this.readOption(cursor, owner.options);
|
|
444
|
+
else if (token.kind === 'ident' && token.text === 'rpc')
|
|
445
|
+
cursor = this.readRpc(cursor, owner);
|
|
446
|
+
else
|
|
447
|
+
cursor = skipStatement(this.tokens, cursor);
|
|
448
|
+
}
|
|
449
|
+
else if (kind === 'rpc') {
|
|
450
|
+
if (token.kind === 'ident' && token.text === 'option')
|
|
451
|
+
cursor = this.readOption(cursor, owner.options);
|
|
452
|
+
else
|
|
453
|
+
cursor = skipStatement(this.tokens, cursor);
|
|
454
|
+
}
|
|
455
|
+
else if (kind === 'extend')
|
|
456
|
+
cursor = this.readField(cursor, owner, true);
|
|
457
|
+
if (cursor <= start)
|
|
458
|
+
cursor++;
|
|
459
|
+
start = cursor;
|
|
460
|
+
}
|
|
461
|
+
throw new Error(`Unterminated ${kind} body in protobuf source`);
|
|
462
|
+
}
|
|
463
|
+
readRpc(start, service) {
|
|
464
|
+
const nameToken = this.tokens[start + 1];
|
|
465
|
+
if (nameToken === undefined || nameToken.kind !== 'ident')
|
|
466
|
+
throw new Error('Malformed rpc declaration in protobuf source');
|
|
467
|
+
let cursor = start + 2;
|
|
468
|
+
if (this.tokens[cursor]?.text !== '(')
|
|
469
|
+
throw new Error('Malformed rpc signature in protobuf source');
|
|
470
|
+
cursor = skipBalanced(this.tokens, cursor);
|
|
471
|
+
if (this.tokens[cursor]?.text !== 'returns')
|
|
472
|
+
throw new Error('Malformed rpc signature in protobuf source');
|
|
473
|
+
cursor++;
|
|
474
|
+
if (this.tokens[cursor]?.text !== '(')
|
|
475
|
+
throw new Error('Malformed rpc signature in protobuf source');
|
|
476
|
+
cursor = skipBalanced(this.tokens, cursor);
|
|
477
|
+
if (this.tokens[cursor]?.kind === 'ident' && this.tokens[cursor].text === 'stream')
|
|
478
|
+
cursor++;
|
|
479
|
+
const rpcLocalId = `${service.localId}.${nameToken.text}`;
|
|
480
|
+
const draft = this.createDraft('rpcs', nameToken.text, rpcLocalId, service, nameToken);
|
|
481
|
+
const body = this.tokens[cursor];
|
|
482
|
+
if (body?.text === ';') {
|
|
483
|
+
draft.span = { start: this.tokens[start].start, end: body.end };
|
|
484
|
+
return cursor + 1;
|
|
485
|
+
}
|
|
486
|
+
if (body?.text !== '{')
|
|
487
|
+
throw new Error('Malformed rpc declaration in protobuf source');
|
|
488
|
+
return this.readBody(cursor + 1, 'rpc', draft, this.tokens[start].start);
|
|
489
|
+
}
|
|
490
|
+
readField(start, parent, extension) {
|
|
491
|
+
let cursor = start;
|
|
492
|
+
let equal = -1;
|
|
493
|
+
while (cursor < this.tokens.length) {
|
|
494
|
+
const token = this.tokens[cursor];
|
|
495
|
+
if (token.text === '=') {
|
|
496
|
+
equal = cursor;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
if (token.text === '<') {
|
|
500
|
+
cursor = skipBalanced(this.tokens, cursor);
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
if (token.text === ';' || token.text === '{' || token.text === '}')
|
|
504
|
+
throw new Error('Malformed field declaration in protobuf source');
|
|
505
|
+
cursor++;
|
|
506
|
+
}
|
|
507
|
+
if (equal < 0)
|
|
508
|
+
throw new Error('Malformed field declaration in protobuf source');
|
|
509
|
+
const nameToken = this.tokens[equal - 1];
|
|
510
|
+
const number = this.tokens[equal + 1];
|
|
511
|
+
if (nameToken?.kind !== 'ident' || number?.kind !== 'number')
|
|
512
|
+
throw new Error('Malformed field declaration in protobuf source');
|
|
513
|
+
const list = extension ? 'extensions' : 'fields';
|
|
514
|
+
const localId = `${parent?.localId ?? ''}${parent === undefined ? '' : '.'}${nameToken.text}`;
|
|
515
|
+
const draft = this.createDraft(list, nameToken.text, localId, parent, nameToken);
|
|
516
|
+
cursor = equal + 2;
|
|
517
|
+
if (this.tokens[cursor]?.text === '[') {
|
|
518
|
+
const parsed = this.readInlineOptions(cursor, draft.options);
|
|
519
|
+
cursor = parsed.next;
|
|
520
|
+
}
|
|
521
|
+
const token = this.tokens[cursor];
|
|
522
|
+
if (token?.text === ';') {
|
|
523
|
+
draft.span = { start: this.tokens[start].start, end: token.end };
|
|
524
|
+
return cursor + 1;
|
|
525
|
+
}
|
|
526
|
+
if (token?.text === '{') {
|
|
527
|
+
const groupName = nameToken.text;
|
|
528
|
+
draft.name = groupName.toLowerCase();
|
|
529
|
+
draft.localId = `${parent?.localId ?? ''}${parent === undefined ? '' : '.'}${draft.name}`;
|
|
530
|
+
const messageLocalId = `${parent?.localId ?? ''}${parent === undefined ? '' : '.'}${groupName}`;
|
|
531
|
+
const group = this.createDraft('messages', groupName, messageLocalId, parent, nameToken);
|
|
532
|
+
const after = this.readBody(cursor + 1, 'message', group, this.tokens[start].start);
|
|
533
|
+
const close = this.tokens[after - 1];
|
|
534
|
+
draft.span = { start: this.tokens[start].start, end: close.end };
|
|
535
|
+
group.span = { start: this.tokens[start].start, end: close.end };
|
|
536
|
+
return after;
|
|
537
|
+
}
|
|
538
|
+
throw new Error('Malformed field declaration in protobuf source');
|
|
539
|
+
}
|
|
540
|
+
readEnumValue(start, parent) {
|
|
541
|
+
const name = this.tokens[start];
|
|
542
|
+
if (name?.kind !== 'ident' || this.tokens[start + 1]?.text !== '=')
|
|
543
|
+
throw new Error('Malformed enum value declaration in protobuf source');
|
|
544
|
+
let cursor = start + 2;
|
|
545
|
+
if (this.tokens[cursor]?.text === '-')
|
|
546
|
+
cursor++;
|
|
547
|
+
if (this.tokens[cursor]?.kind !== 'number')
|
|
548
|
+
throw new Error('Malformed enum value declaration in protobuf source');
|
|
549
|
+
const localId = `${parent.localId}.${name.text}`;
|
|
550
|
+
const draft = this.createDraft('enumValues', name.text, localId, parent, name);
|
|
551
|
+
cursor++;
|
|
552
|
+
if (this.tokens[cursor]?.text === '[')
|
|
553
|
+
cursor = this.readInlineOptions(cursor, draft.options).next;
|
|
554
|
+
if (this.tokens[cursor]?.text !== ';')
|
|
555
|
+
throw new Error('Malformed enum value declaration in protobuf source');
|
|
556
|
+
draft.span = { start: name.start, end: this.tokens[cursor].end };
|
|
557
|
+
return cursor + 1;
|
|
558
|
+
}
|
|
559
|
+
readOption(start, message) {
|
|
560
|
+
const name = this.readOptionName(start + 1);
|
|
561
|
+
if (this.tokens[name.next]?.text !== '=')
|
|
562
|
+
throw new Error('Expected "=" after option name in protobuf source');
|
|
563
|
+
const value = this.readValue(name.next + 1);
|
|
564
|
+
if (this.tokens[value.next]?.text !== ';')
|
|
565
|
+
throw new Error('Expected ";" after option in protobuf source');
|
|
566
|
+
const end = this.tokens[value.next];
|
|
567
|
+
assignOption(message, name.path, value.values, value.span, value.list, { start: this.tokens[start].start, end: end.end });
|
|
568
|
+
return value.next + 1;
|
|
569
|
+
}
|
|
570
|
+
readOptionName(start) {
|
|
571
|
+
let cursor = start;
|
|
572
|
+
const path = [];
|
|
573
|
+
let needSegment = true;
|
|
574
|
+
while (cursor < this.tokens.length) {
|
|
575
|
+
const token = this.tokens[cursor];
|
|
576
|
+
if (needSegment) {
|
|
577
|
+
if (token.text === '(') {
|
|
578
|
+
cursor++;
|
|
579
|
+
if (this.tokens[cursor]?.text === '.')
|
|
580
|
+
cursor++;
|
|
581
|
+
const parts = [];
|
|
582
|
+
if (this.tokens[cursor]?.kind !== 'ident')
|
|
583
|
+
throw new Error(this.tokens[cursor] === undefined ? 'Unterminated option name in protobuf source' : 'Malformed option name in protobuf source');
|
|
584
|
+
parts.push(this.tokens[cursor++].text);
|
|
585
|
+
while (this.tokens[cursor]?.text === '.' && this.tokens[cursor + 1]?.kind === 'ident') {
|
|
586
|
+
parts.push(this.tokens[cursor + 1].text);
|
|
587
|
+
cursor += 2;
|
|
588
|
+
}
|
|
589
|
+
if (this.tokens[cursor]?.text !== ')')
|
|
590
|
+
throw new Error(this.tokens[cursor] === undefined ? 'Unterminated option name in protobuf source' : 'Malformed option name in protobuf source');
|
|
591
|
+
path.push(parts.join('.'));
|
|
592
|
+
cursor++;
|
|
593
|
+
}
|
|
594
|
+
else if (token.kind === 'ident') {
|
|
595
|
+
path.push(token.text);
|
|
596
|
+
cursor++;
|
|
597
|
+
}
|
|
598
|
+
else
|
|
599
|
+
throw new Error('Malformed option name in protobuf source');
|
|
600
|
+
needSegment = false;
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
if (token.text !== '.')
|
|
604
|
+
return { path, next: cursor };
|
|
605
|
+
cursor++;
|
|
606
|
+
if (cursor >= this.tokens.length)
|
|
607
|
+
throw new Error('Unterminated option name in protobuf source');
|
|
608
|
+
needSegment = true;
|
|
609
|
+
}
|
|
610
|
+
if (needSegment)
|
|
611
|
+
throw new Error('Unterminated option name in protobuf source');
|
|
612
|
+
return { path, next: cursor };
|
|
613
|
+
}
|
|
614
|
+
readInlineOptions(start, message) {
|
|
615
|
+
let cursor = start + 1;
|
|
616
|
+
while (cursor <= this.tokens.length) {
|
|
617
|
+
if (this.tokens[cursor] === undefined)
|
|
618
|
+
throw new Error('Unterminated inline options in protobuf source');
|
|
619
|
+
if (this.tokens[cursor].text === ']') {
|
|
620
|
+
cursor++;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
const name = this.readOptionName(cursor);
|
|
624
|
+
if (this.tokens[name.next]?.text !== '=')
|
|
625
|
+
throw new Error('Malformed inline options in protobuf source');
|
|
626
|
+
const value = this.readValue(name.next + 1);
|
|
627
|
+
assignOption(message, name.path, value.values, value.span, value.list, { start: this.tokens[cursor].start, end: value.span.end });
|
|
628
|
+
cursor = value.next;
|
|
629
|
+
if (this.tokens[cursor]?.text === ',') {
|
|
630
|
+
cursor++;
|
|
631
|
+
if (this.tokens[cursor]?.text === ']')
|
|
632
|
+
throw new Error('Malformed inline options in protobuf source');
|
|
633
|
+
}
|
|
634
|
+
else if (this.tokens[cursor] === undefined)
|
|
635
|
+
throw new Error('Unterminated inline options in protobuf source');
|
|
636
|
+
else if (this.tokens[cursor]?.text !== ']')
|
|
637
|
+
throw new Error('Malformed inline options in protobuf source');
|
|
638
|
+
}
|
|
639
|
+
return { next: cursor };
|
|
640
|
+
}
|
|
641
|
+
readValue(start) {
|
|
642
|
+
const first = this.tokens[start];
|
|
643
|
+
if (first === undefined || first.text === ';' || first.text === ']' || first.text === ',' || first.text === '}')
|
|
644
|
+
throw new Error('Expected an option value in protobuf source');
|
|
645
|
+
if (first.text === '-' && (this.tokens[start + 1]?.kind === 'number' || (this.tokens[start + 1]?.kind === 'ident' && !['true', 'false'].includes(this.tokens[start + 1].text))))
|
|
646
|
+
return this.scalarValue(start, start + 1, true);
|
|
647
|
+
if (first.kind === 'number' || first.kind === 'string' || first.kind === 'ident')
|
|
648
|
+
return this.scalarValue(start, start, false);
|
|
649
|
+
if (first.text === '{' || first.text === '<')
|
|
650
|
+
return this.messageValue(start);
|
|
651
|
+
if (first.text === '[')
|
|
652
|
+
return this.listValue(start);
|
|
653
|
+
throw new Error('Malformed option value in protobuf source');
|
|
654
|
+
}
|
|
655
|
+
scalarValue(start, tokenIndex, negative) {
|
|
656
|
+
const first = this.tokens[start];
|
|
657
|
+
const token = this.tokens[tokenIndex];
|
|
658
|
+
let value;
|
|
659
|
+
let cursor = tokenIndex + 1;
|
|
660
|
+
let end = token.end;
|
|
661
|
+
if (token.kind === 'string') {
|
|
662
|
+
const parts = [];
|
|
663
|
+
const bytes = [];
|
|
664
|
+
cursor = tokenIndex;
|
|
665
|
+
while (this.tokens[cursor]?.kind === 'string') {
|
|
666
|
+
const stringToken = this.tokens[cursor];
|
|
667
|
+
decodeStringLiteral(stringToken.text, bytes, parts);
|
|
668
|
+
end = stringToken.end;
|
|
669
|
+
cursor++;
|
|
670
|
+
}
|
|
671
|
+
flushByteRun(bytes, parts);
|
|
672
|
+
value = parts.join('');
|
|
673
|
+
}
|
|
674
|
+
else if (token.kind === 'ident')
|
|
675
|
+
value = !negative && (token.text === 'true' || token.text === 'false') ? token.text === 'true' : `${negative ? '-' : ''}${token.text}`;
|
|
676
|
+
else
|
|
677
|
+
value = parseNumber(token.text, negative);
|
|
678
|
+
const startOffset = negative ? first.start : token.start;
|
|
679
|
+
const span = { start: startOffset, end };
|
|
680
|
+
const scalar = { kind: 'scalar', value, span };
|
|
681
|
+
return { values: [scalar], list: false, span, next: cursor };
|
|
682
|
+
}
|
|
683
|
+
messageValue(start) {
|
|
684
|
+
const open = this.tokens[start];
|
|
685
|
+
const closeText = open.text === '{' ? '}' : '>';
|
|
686
|
+
const message = optionMessage();
|
|
687
|
+
let cursor = start + 1;
|
|
688
|
+
while (cursor < this.tokens.length) {
|
|
689
|
+
const token = this.tokens[cursor];
|
|
690
|
+
if (token.text === closeText) {
|
|
691
|
+
const span = { start: open.start, end: token.end };
|
|
692
|
+
message.span = span;
|
|
693
|
+
return { values: [message], list: false, span, next: cursor + 1 };
|
|
694
|
+
}
|
|
695
|
+
if (token.text === '}' || token.text === '>')
|
|
696
|
+
throw new Error('Malformed option value in protobuf source');
|
|
697
|
+
if (token.text === ',' || token.text === ';') {
|
|
698
|
+
cursor++;
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
if (token.kind !== 'ident' && token.text !== '[')
|
|
702
|
+
throw new Error('Malformed option value in protobuf source');
|
|
703
|
+
const key = this.readAggregateKey(cursor);
|
|
704
|
+
cursor = key.next;
|
|
705
|
+
if (this.tokens[cursor]?.text === ':')
|
|
706
|
+
cursor++;
|
|
707
|
+
const valueToken = this.tokens[cursor];
|
|
708
|
+
if (valueToken === undefined)
|
|
709
|
+
throw new Error('Unterminated option value in protobuf source');
|
|
710
|
+
if (valueToken.kind !== 'ident' && valueToken.kind !== 'number' && valueToken.kind !== 'string' && valueToken.text !== '-' && valueToken.text !== '{' && valueToken.text !== '<' && valueToken.text !== '[') {
|
|
711
|
+
throw new Error('Malformed option value in protobuf source');
|
|
712
|
+
}
|
|
713
|
+
const value = this.readValue(cursor);
|
|
714
|
+
assignOption(message, [key.key], value.values, value.span, value.list, { start: key.start, end: value.span.end });
|
|
715
|
+
cursor = value.next;
|
|
716
|
+
}
|
|
717
|
+
throw new Error('Unterminated option value in protobuf source');
|
|
718
|
+
}
|
|
719
|
+
readAggregateKey(start) {
|
|
720
|
+
const token = this.tokens[start];
|
|
721
|
+
if (token.kind === 'ident')
|
|
722
|
+
return { key: token.text, next: start + 1, start: token.start };
|
|
723
|
+
if (token.text !== '[')
|
|
724
|
+
throw new Error('Malformed option value in protobuf source');
|
|
725
|
+
let cursor = start + 1;
|
|
726
|
+
let key = '';
|
|
727
|
+
while (cursor < this.tokens.length && this.tokens[cursor].text !== ']')
|
|
728
|
+
key += this.tokens[cursor++].text;
|
|
729
|
+
if (this.tokens[cursor]?.text !== ']')
|
|
730
|
+
throw new Error('Unterminated option value in protobuf source');
|
|
731
|
+
if (cursor === start + 1)
|
|
732
|
+
throw new Error('Malformed option value in protobuf source');
|
|
733
|
+
return { key, next: cursor + 1, start: token.start };
|
|
734
|
+
}
|
|
735
|
+
listValue(start) {
|
|
736
|
+
const open = this.tokens[start];
|
|
737
|
+
const values = [];
|
|
738
|
+
let cursor = start + 1;
|
|
739
|
+
while (cursor < this.tokens.length) {
|
|
740
|
+
const token = this.tokens[cursor];
|
|
741
|
+
if (token.text === ']')
|
|
742
|
+
return { values, list: true, span: { start: open.start, end: token.end }, next: cursor + 1 };
|
|
743
|
+
if (token.text === ',') {
|
|
744
|
+
cursor++;
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
if (token.text === ';' || token.text === '}')
|
|
748
|
+
throw new Error('Malformed option value in protobuf source');
|
|
749
|
+
const value = this.readValue(cursor);
|
|
750
|
+
if (value.list || value.values.length !== 1)
|
|
751
|
+
throw new Error('Malformed option value in protobuf source');
|
|
752
|
+
values.push(value.values[0]);
|
|
753
|
+
cursor = value.next;
|
|
754
|
+
}
|
|
755
|
+
throw new Error('Unterminated option value in protobuf source');
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function parseNumber(text, negative) {
|
|
759
|
+
const signed = `${negative ? '-' : ''}${text}`;
|
|
760
|
+
if (/^(?:0|[1-9][0-9]*)$/.test(text)) {
|
|
761
|
+
const value = Number(text);
|
|
762
|
+
return Number.isSafeInteger(value) ? (negative ? -value : value) : signed;
|
|
763
|
+
}
|
|
764
|
+
if (/^(?:[0-9]+\.[0-9]*|\.[0-9]+|[0-9]+(?=[eE]))(?:[eE][+-]?[0-9]+)?$/.test(text)) {
|
|
765
|
+
const value = Number(signed);
|
|
766
|
+
return Number.isFinite(value) ? value : signed;
|
|
767
|
+
}
|
|
768
|
+
return signed;
|
|
769
|
+
}
|
|
770
|
+
export function readProtoFile(source) {
|
|
771
|
+
return new ProtoReader(source).read();
|
|
772
|
+
}
|