cddl 0.16.0 → 0.18.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 +1 -1
- package/package.json +5 -1
- package/.release-it.ts +0 -10
- package/docs/README.md +0 -47
- package/docs/arrays.md +0 -356
- package/docs/ast-structure.md +0 -166
- package/docs/basic-types.md +0 -243
- package/docs/cddl-cheatsheet.md +0 -231
- package/docs/comments.md +0 -307
- package/docs/groups.md +0 -351
- package/docs/operators.md +0 -466
- package/docs/properties.md +0 -334
- package/docs/ranges.md +0 -339
- package/docs/references.md +0 -340
- package/docs/variables.md +0 -335
- package/src/ast.ts +0 -180
- package/src/cli/commands/repl.ts +0 -33
- package/src/cli/commands/validate.ts +0 -44
- package/src/cli/constants.ts +0 -1
- package/src/cli/index.ts +0 -18
- package/src/constants.ts +0 -16
- package/src/index.ts +0 -12
- package/src/lexer.ts +0 -238
- package/src/parser.ts +0 -993
- package/src/tokens.ts +0 -50
- package/src/utils.ts +0 -96
- package/tests/__snapshots__/complex_types.test.ts.snap +0 -80
- package/tests/__snapshots__/examples.test.ts.snap +0 -1077
- package/tests/__snapshots__/group-choices.test.ts.snap +0 -403
- package/tests/__snapshots__/parser.test.ts.snap +0 -3045
- package/tests/__snapshots__/webdriver-local.test.ts.snap +0 -11192
- package/tests/__snapshots__/webdriver-remote.test.ts.snap +0 -16350
- package/tests/commands/repl.test.ts +0 -52
- package/tests/commands/validate.test.ts +0 -66
- package/tests/complex_types.test.ts +0 -18
- package/tests/examples.test.ts +0 -25
- package/tests/group-choices.test.ts +0 -132
- package/tests/lexer.test.ts +0 -63
- package/tests/module.test.ts +0 -21
- package/tests/parser.test.ts +0 -63
- package/tests/utils.test.ts +0 -211
- package/tests/webdriver-local.test.ts +0 -15
- package/tests/webdriver-remote.test.ts +0 -15
- package/tsconfig.json +0 -11
package/src/parser.ts
DELETED
|
@@ -1,993 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs'
|
|
2
|
-
|
|
3
|
-
import Lexer from './lexer.js'
|
|
4
|
-
import { Property } from './ast.js'
|
|
5
|
-
import { Token, Tokens } from './tokens.js';
|
|
6
|
-
import { PREDEFINED_IDENTIFIER, BOOLEAN_LITERALS } from './constants.js'
|
|
7
|
-
import { parseNumberValue } from './utils.js'
|
|
8
|
-
import {
|
|
9
|
-
Type, PropertyName, PropertyType, PropertyReferenceType,
|
|
10
|
-
Variable, RangePropertyReference, Occurrence, Assignment,
|
|
11
|
-
Comment, Group, OperatorType, NativeTypeWithOperator, Operator
|
|
12
|
-
} from './ast.js'
|
|
13
|
-
|
|
14
|
-
const NIL_TOKEN: Token = { Type: Tokens.ILLEGAL, Literal: '' }
|
|
15
|
-
const DEFAULT_OCCURRENCE: Occurrence = { n: 1, m: 1 } // exactly one time
|
|
16
|
-
const OPERATORS: OperatorType[] = ['default', 'size', 'regexp', 'bits', 'and', 'within', 'eq', 'ne', 'lt', 'le', 'gt', 'ge']
|
|
17
|
-
const OPERATORS_EXPECTING_VALUES: Record<OperatorType, PropertyReferenceType[] | undefined> = {
|
|
18
|
-
default: undefined,
|
|
19
|
-
size: ['literal', 'range'],
|
|
20
|
-
regexp: ['literal'],
|
|
21
|
-
bits: ['group'],
|
|
22
|
-
and: ['group'],
|
|
23
|
-
within: ['group'],
|
|
24
|
-
eq: ['group'],
|
|
25
|
-
ne: ['group'],
|
|
26
|
-
lt: ['group'],
|
|
27
|
-
le: ['group'],
|
|
28
|
-
gt: ['group'],
|
|
29
|
-
ge: ['group'],
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export default class Parser {
|
|
33
|
-
#filePath: string
|
|
34
|
-
l: Lexer;
|
|
35
|
-
|
|
36
|
-
curToken: Token = NIL_TOKEN;
|
|
37
|
-
peekToken: Token = NIL_TOKEN;
|
|
38
|
-
peekBelowToken: Token = NIL_TOKEN;
|
|
39
|
-
|
|
40
|
-
constructor (filePath: string) {
|
|
41
|
-
this.#filePath = filePath
|
|
42
|
-
this.l = new Lexer(fs.readFileSync(filePath, 'utf-8'))
|
|
43
|
-
|
|
44
|
-
this.nextToken()
|
|
45
|
-
this.nextToken()
|
|
46
|
-
this.nextToken()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private nextToken () {
|
|
50
|
-
this.curToken = this.peekToken
|
|
51
|
-
this.peekToken = this.peekBelowToken
|
|
52
|
-
this.peekBelowToken = this.l.nextToken()
|
|
53
|
-
return true
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private parseAssignments (): Assignment {
|
|
57
|
-
const comments: Comment[] = []
|
|
58
|
-
while (this.curToken.Type === Tokens.COMMENT) {
|
|
59
|
-
const comment = this.parseComment()
|
|
60
|
-
if (comment) {
|
|
61
|
-
comments.push(comment)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* expect group identifier, e.g.
|
|
67
|
-
* groupName =
|
|
68
|
-
* groupName /=
|
|
69
|
-
* groupName //=
|
|
70
|
-
*/
|
|
71
|
-
if (this.curToken.Type !== Tokens.IDENT || !(this.peekToken.Type === Tokens.ASSIGN || this.peekToken.Type === Tokens.SLASH)) {
|
|
72
|
-
throw this.parserError(`group identifier expected, received "${JSON.stringify(this.curToken)}"`)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
let isChoiceAddition = false
|
|
76
|
-
const groupName = this.curToken.Literal
|
|
77
|
-
this.nextToken() // eat group identifier
|
|
78
|
-
|
|
79
|
-
// @ts-ignore
|
|
80
|
-
if (this.curToken.Type === Tokens.SLASH) {
|
|
81
|
-
isChoiceAddition = true
|
|
82
|
-
this.nextToken() // eat `/`
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// @ts-ignore
|
|
86
|
-
if (this.curToken.Type === Tokens.SLASH) {
|
|
87
|
-
this.nextToken() // eat `/`
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.nextToken() // eat `=`
|
|
91
|
-
const assignmentValue = this.parseAssignmentValue(groupName, isChoiceAddition) as Assignment
|
|
92
|
-
|
|
93
|
-
// @ts-expect-error curToken can be changed by now but TS doesn't understand this
|
|
94
|
-
while (this.curToken.Type === Tokens.COMMENT) {
|
|
95
|
-
const comment = this.parseComment()
|
|
96
|
-
comment && comments.push(comment)
|
|
97
|
-
}
|
|
98
|
-
assignmentValue.Comments = comments
|
|
99
|
-
return assignmentValue
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private parseAssignmentValue (groupName?: string, isChoiceAddition = false): Assignment | PropertyType[] {
|
|
103
|
-
let isChoice = false
|
|
104
|
-
const valuesOrProperties: (Property | Property[])[] = []
|
|
105
|
-
const closingTokens = this.openSegment()
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* if no group segment was opened we have a variable assignment
|
|
109
|
-
* and can return immediatelly, e.g.
|
|
110
|
-
*
|
|
111
|
-
* attire = "bow tie" / "necktie" / "Internet attire"
|
|
112
|
-
*
|
|
113
|
-
*/
|
|
114
|
-
if (closingTokens.length === 0) {
|
|
115
|
-
if (groupName) {
|
|
116
|
-
const variable: Variable = {
|
|
117
|
-
Type: 'variable',
|
|
118
|
-
Name: groupName,
|
|
119
|
-
IsChoiceAddition: isChoiceAddition,
|
|
120
|
-
PropertyType: this.parsePropertyTypes(),
|
|
121
|
-
Comments: []
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
return variable
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return this.parsePropertyTypes()
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* type or group choices can be wrapped within `(` and `)`, e.g.
|
|
132
|
-
*
|
|
133
|
-
* attireBlock = (
|
|
134
|
-
* "bow tie" /
|
|
135
|
-
* "necktie" /
|
|
136
|
-
* "Internet attire"
|
|
137
|
-
* )
|
|
138
|
-
* attireGroup = (
|
|
139
|
-
* attire //
|
|
140
|
-
* attireBlock
|
|
141
|
-
* )
|
|
142
|
-
*/
|
|
143
|
-
if (
|
|
144
|
-
closingTokens.includes(Tokens.RPAREN) &&
|
|
145
|
-
this.peekToken.Type === Tokens.SLASH &&
|
|
146
|
-
this.peekBelowToken.Type !== Tokens.SLASH &&
|
|
147
|
-
!(this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH)
|
|
148
|
-
) {
|
|
149
|
-
const propertyType: PropertyType[] = []
|
|
150
|
-
while (!closingTokens.includes(this.curToken.Type)) {
|
|
151
|
-
propertyType.push(...this.parsePropertyTypes())
|
|
152
|
-
if (closingTokens.includes(this.curToken.Type)) {
|
|
153
|
-
this.nextToken()
|
|
154
|
-
break
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
this.nextToken()
|
|
158
|
-
|
|
159
|
-
if (this.curToken.Type === Tokens.SLASH) {
|
|
160
|
-
this.nextToken()
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
if (this.curToken.Type === Tokens.RPAREN) {
|
|
164
|
-
this.nextToken();
|
|
165
|
-
}
|
|
166
|
-
if (groupName) {
|
|
167
|
-
const variable: Variable = {
|
|
168
|
-
Type: 'variable',
|
|
169
|
-
Name: groupName,
|
|
170
|
-
IsChoiceAddition: isChoiceAddition,
|
|
171
|
-
PropertyType: propertyType,
|
|
172
|
-
Comments: []
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (this.isOperator()) {
|
|
176
|
-
variable.Operator = this.parseOperator()
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return variable
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return propertyType
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* parse operator assignments, e.g. `ip4 = (float .ge 0.0) .default 1.0`
|
|
187
|
-
*/
|
|
188
|
-
if (closingTokens.length === 1 && this.peekToken.Type === Tokens.DOT) {
|
|
189
|
-
const propertyType = this.parsePropertyType()
|
|
190
|
-
const operator = this.isOperator() ? this.parseOperator() : undefined
|
|
191
|
-
const prop: PropertyType = {
|
|
192
|
-
Type: propertyType,
|
|
193
|
-
...(operator ? { Operator: operator } : {})
|
|
194
|
-
} as NativeTypeWithOperator
|
|
195
|
-
|
|
196
|
-
this.nextToken() // eat closing token
|
|
197
|
-
if (groupName) {
|
|
198
|
-
const variable: Variable = {
|
|
199
|
-
Type: 'variable',
|
|
200
|
-
Name: groupName,
|
|
201
|
-
IsChoiceAddition: isChoiceAddition,
|
|
202
|
-
PropertyType: prop,
|
|
203
|
-
Operator: this.parseOperator(),
|
|
204
|
-
Comments: []
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return variable
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
return [prop]
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
while (!closingTokens.includes(this.curToken.Type)) {
|
|
214
|
-
/**
|
|
215
|
-
* check if we have a group choice instead of an assignment
|
|
216
|
-
*/
|
|
217
|
-
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
218
|
-
if (valuesOrProperties.length === 0) {
|
|
219
|
-
throw this.parserError('Unexpected group choice operator "//" at start of group')
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (!isChoice) {
|
|
223
|
-
const last = valuesOrProperties.pop() as Property
|
|
224
|
-
valuesOrProperties.push([last])
|
|
225
|
-
isChoice = true
|
|
226
|
-
}
|
|
227
|
-
this.nextToken()
|
|
228
|
-
this.nextToken()
|
|
229
|
-
continue
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const propertyType: PropertyType[] = []
|
|
233
|
-
const comments: Comment[] = []
|
|
234
|
-
let isUnwrapped = false
|
|
235
|
-
let hasCut = false
|
|
236
|
-
let propertyName = ''
|
|
237
|
-
|
|
238
|
-
const leadingComment = this.parseComment(true)
|
|
239
|
-
leadingComment && comments.push(leadingComment)
|
|
240
|
-
|
|
241
|
-
const occurrence = this.parseOccurrences()
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* check if variable name is unwrapped
|
|
245
|
-
*/
|
|
246
|
-
if (this.curToken.Literal === Tokens.TILDE) {
|
|
247
|
-
isUnwrapped = true
|
|
248
|
-
this.nextToken() // eat ~
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* parse assignment within array, e.g.
|
|
253
|
-
* ```
|
|
254
|
-
* ActionsPerformActionsParameters = [1* {
|
|
255
|
-
* type: "key",
|
|
256
|
-
* id: text,
|
|
257
|
-
* actions: ActionItems,
|
|
258
|
-
* *text => any
|
|
259
|
-
* }]
|
|
260
|
-
* ```
|
|
261
|
-
* or
|
|
262
|
-
* ```
|
|
263
|
-
* script.MappingRemoteValue = [*[(script.RemoteValue / text), script.RemoteValue]];
|
|
264
|
-
* ```
|
|
265
|
-
*/
|
|
266
|
-
if (
|
|
267
|
-
this.curToken.Literal === Tokens.LBRACE ||
|
|
268
|
-
this.curToken.Literal === Tokens.LBRACK ||
|
|
269
|
-
this.curToken.Literal === Tokens.LPAREN
|
|
270
|
-
) {
|
|
271
|
-
const innerGroup = this.parseAssignmentValue() as Group
|
|
272
|
-
const prop = {
|
|
273
|
-
HasCut: false,
|
|
274
|
-
Occurrence: occurrence,
|
|
275
|
-
Name: '',
|
|
276
|
-
Type: innerGroup,
|
|
277
|
-
Comments: []
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (isChoice) {
|
|
281
|
-
(valuesOrProperties[valuesOrProperties.length - 1] as Property[]).push(prop)
|
|
282
|
-
} else {
|
|
283
|
-
valuesOrProperties.push(prop)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (this.curToken.Type === Tokens.COMMA) {
|
|
287
|
-
this.nextToken()
|
|
288
|
-
isChoice = false
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type !== Tokens.SLASH) {
|
|
292
|
-
if (!isChoice) {
|
|
293
|
-
const last = valuesOrProperties.pop() as Property
|
|
294
|
-
valuesOrProperties.push([last])
|
|
295
|
-
isChoice = true
|
|
296
|
-
}
|
|
297
|
-
this.nextToken()
|
|
298
|
-
}
|
|
299
|
-
continue
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* check if we are in an array and a new item is indicated
|
|
304
|
-
*/
|
|
305
|
-
if (this.curToken.Literal === Tokens.COMMA && closingTokens[0] === Tokens.RBRACK) {
|
|
306
|
-
this.nextToken()
|
|
307
|
-
continue
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
propertyName = this.parsePropertyName()
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* if `,` is found we have a group reference and jump to the next line
|
|
314
|
-
*/
|
|
315
|
-
if (this.curToken.Type === Tokens.COMMA || closingTokens.includes(this.curToken.Type)) {
|
|
316
|
-
const tokenType = this.curToken.Type
|
|
317
|
-
let parsedComments = false
|
|
318
|
-
let comment: Comment | undefined
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* check if line has a comment
|
|
322
|
-
*/
|
|
323
|
-
if (this.curToken.Type === Tokens.COMMA && this.peekToken.Type === Tokens.COMMENT) {
|
|
324
|
-
this.nextToken()
|
|
325
|
-
comment = this.parseComment()
|
|
326
|
-
parsedComments = true
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
valuesOrProperties.push({
|
|
330
|
-
HasCut: hasCut,
|
|
331
|
-
Occurrence: occurrence,
|
|
332
|
-
Name: '',
|
|
333
|
-
Type: PREDEFINED_IDENTIFIER.includes(propertyName)
|
|
334
|
-
? propertyName
|
|
335
|
-
: [{
|
|
336
|
-
Type: 'group' as PropertyReferenceType,
|
|
337
|
-
Value: propertyName,
|
|
338
|
-
Unwrapped: isUnwrapped
|
|
339
|
-
}],
|
|
340
|
-
Comments: comment ? [comment] : []
|
|
341
|
-
})
|
|
342
|
-
|
|
343
|
-
if (this.curToken.Literal === Tokens.COMMA || this.curToken.Literal === closingTokens[0]) {
|
|
344
|
-
if (this.curToken.Literal === Tokens.COMMA) {
|
|
345
|
-
this.nextToken()
|
|
346
|
-
}
|
|
347
|
-
continue
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
if (!parsedComments) {
|
|
351
|
-
this.nextToken()
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* only continue if next token contains a comma
|
|
356
|
-
*/
|
|
357
|
-
if (tokenType === Tokens.COMMA) {
|
|
358
|
-
continue
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* otherwise break
|
|
363
|
-
*/
|
|
364
|
-
break
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
* check if property has cut, which happens if a property is described as
|
|
369
|
-
* - `? "optional-key" ^ => int,`
|
|
370
|
-
* - `? optional-key: int,` - since the colon shortcut includes cuts
|
|
371
|
-
*/
|
|
372
|
-
if (this.curToken.Type === Tokens.CARET || this.curToken.Type === Tokens.COLON) {
|
|
373
|
-
hasCut = true
|
|
374
|
-
|
|
375
|
-
if (this.curToken.Type === Tokens.CARET) {
|
|
376
|
-
this.nextToken() // eat ^
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
/**
|
|
381
|
-
* check if we have a group choice instead of an assignment
|
|
382
|
-
*/
|
|
383
|
-
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
384
|
-
const prop: Property = {
|
|
385
|
-
HasCut: hasCut,
|
|
386
|
-
Occurrence: occurrence,
|
|
387
|
-
Name: '',
|
|
388
|
-
Type: {
|
|
389
|
-
Type: 'group',
|
|
390
|
-
Value: propertyName,
|
|
391
|
-
Unwrapped: isUnwrapped
|
|
392
|
-
},
|
|
393
|
-
Comments: comments
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (isChoice) {
|
|
397
|
-
/**
|
|
398
|
-
* if we already in a choice just push into it
|
|
399
|
-
*/
|
|
400
|
-
(valuesOrProperties[valuesOrProperties.length - 1] as Property[]).push(prop)
|
|
401
|
-
} else {
|
|
402
|
-
/**
|
|
403
|
-
* otherwise create a new one
|
|
404
|
-
*/
|
|
405
|
-
isChoice = true
|
|
406
|
-
valuesOrProperties.push([prop])
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
this.nextToken() // eat /
|
|
410
|
-
this.nextToken() // eat /
|
|
411
|
-
continue
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* else if no colon was found, throw
|
|
416
|
-
*/
|
|
417
|
-
if (!this.isPropertyValueSeparator()) {
|
|
418
|
-
throw this.parserError('Expected ":" or "=>"')
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
this.nextToken() // eat :
|
|
422
|
-
|
|
423
|
-
/**
|
|
424
|
-
* parse property value
|
|
425
|
-
*/
|
|
426
|
-
const props = this.parseAssignmentValue()
|
|
427
|
-
let operator = this.isOperator() ? this.parseOperator() : undefined
|
|
428
|
-
if (!isChoice && this.curToken.Type === Tokens.SLASH && this.peekToken.Type !== Tokens.SLASH) {
|
|
429
|
-
this.nextToken()
|
|
430
|
-
const nextType = this.parsePropertyType()
|
|
431
|
-
if (Array.isArray(props)) {
|
|
432
|
-
/**
|
|
433
|
-
* property has not yet been flagged as a choice, but is part
|
|
434
|
-
* of one, e.g. `(float .ge 1.0) / null`
|
|
435
|
-
*/
|
|
436
|
-
props.push(nextType)
|
|
437
|
-
if (!this.isOperator()) {
|
|
438
|
-
this.nextToken()
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
if (this.isOperator()) {
|
|
443
|
-
operator = this.parseOperator()
|
|
444
|
-
}
|
|
445
|
-
if (Array.isArray(props)) {
|
|
446
|
-
/**
|
|
447
|
-
* property has multiple types (e.g. `float / tstr / int`)
|
|
448
|
-
*/
|
|
449
|
-
propertyType.push(...props)
|
|
450
|
-
} else {
|
|
451
|
-
propertyType.push(props)
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/**
|
|
455
|
-
* advance comma
|
|
456
|
-
*/
|
|
457
|
-
let flipIsChoice = false
|
|
458
|
-
if (this.curToken.Type === Tokens.COMMA) {
|
|
459
|
-
/**
|
|
460
|
-
* if we are in a choice, we leave it here
|
|
461
|
-
*/
|
|
462
|
-
flipIsChoice = true
|
|
463
|
-
|
|
464
|
-
this.nextToken() // eat ,
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const trailingComment = this.parseComment()
|
|
468
|
-
trailingComment && comments.push(trailingComment)
|
|
469
|
-
|
|
470
|
-
const prop = {
|
|
471
|
-
HasCut: hasCut,
|
|
472
|
-
Occurrence: occurrence,
|
|
473
|
-
Name: propertyName,
|
|
474
|
-
Type: propertyType,
|
|
475
|
-
Comments: comments,
|
|
476
|
-
...(operator ? { Operator: operator } : {})
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
if (isChoice) {
|
|
480
|
-
(valuesOrProperties[valuesOrProperties.length - 1] as Property[]).push(prop)
|
|
481
|
-
} else {
|
|
482
|
-
valuesOrProperties.push(prop)
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (flipIsChoice) {
|
|
486
|
-
isChoice = false
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* if `}` is found we are at the end of the group
|
|
491
|
-
*/
|
|
492
|
-
if (closingTokens.includes(this.curToken.Type)) {
|
|
493
|
-
/**
|
|
494
|
-
* Handle the case where a group is followed by an inclusion operator, e.g.
|
|
495
|
-
*
|
|
496
|
-
* group1 = {
|
|
497
|
-
* name: tstr,
|
|
498
|
-
* age: number,
|
|
499
|
-
* }
|
|
500
|
-
*
|
|
501
|
-
* group2 = {
|
|
502
|
-
* handle: tstr
|
|
503
|
-
* } .and group1
|
|
504
|
-
*
|
|
505
|
-
*/
|
|
506
|
-
while (this.peekToken.Type === Tokens.DOT) {
|
|
507
|
-
this.nextToken()
|
|
508
|
-
if (this.isOperator()) {
|
|
509
|
-
valuesOrProperties.push({
|
|
510
|
-
Name: '',
|
|
511
|
-
Type: 'group',
|
|
512
|
-
Occurrence: DEFAULT_OCCURRENCE,
|
|
513
|
-
Operator: this.parseOperator(),
|
|
514
|
-
Comments: [],
|
|
515
|
-
HasCut: false,
|
|
516
|
-
})
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
break
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* eat // if we are in a choice
|
|
524
|
-
*/
|
|
525
|
-
if (isChoice) {
|
|
526
|
-
this.nextToken() // eat /
|
|
527
|
-
this.nextToken() // eat /
|
|
528
|
-
continue
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* close segment
|
|
534
|
-
*/
|
|
535
|
-
if (this.curToken.Type === [...closingTokens].shift()) {
|
|
536
|
-
this.nextToken()
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* if last closing token is "]" we have an array
|
|
541
|
-
*/
|
|
542
|
-
if (closingTokens[closingTokens.length - 1] === Tokens.RBRACK) {
|
|
543
|
-
return {
|
|
544
|
-
Type: 'array',
|
|
545
|
-
Name: groupName || '',
|
|
546
|
-
Values: valuesOrProperties,
|
|
547
|
-
Comments: []
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
/**
|
|
552
|
-
* simplify wrapped types, e.g. from
|
|
553
|
-
* {
|
|
554
|
-
* "Type": "group",
|
|
555
|
-
* "Name": "",
|
|
556
|
-
* "Properties": [
|
|
557
|
-
* {
|
|
558
|
-
* "HasCut": false,
|
|
559
|
-
* "Occurrence": {
|
|
560
|
-
* "n": 1,
|
|
561
|
-
* "m": 1
|
|
562
|
-
* },
|
|
563
|
-
* "Name": "",
|
|
564
|
-
* "Type": "bool",
|
|
565
|
-
* "Comment": ""
|
|
566
|
-
* }
|
|
567
|
-
* ],
|
|
568
|
-
* "IsChoiceAddition": false
|
|
569
|
-
* }
|
|
570
|
-
* back to:
|
|
571
|
-
* bool
|
|
572
|
-
*/
|
|
573
|
-
if (!groupName && valuesOrProperties.length === 1 && PREDEFINED_IDENTIFIER.includes((valuesOrProperties[0] as Property).Type as string)) {
|
|
574
|
-
return (valuesOrProperties[0] as Property).Type as Assignment
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* otherwise a group
|
|
579
|
-
*/
|
|
580
|
-
return {
|
|
581
|
-
Type: 'group',
|
|
582
|
-
Name: groupName || '',
|
|
583
|
-
Properties: valuesOrProperties,
|
|
584
|
-
IsChoiceAddition: isChoiceAddition,
|
|
585
|
-
Comments: []
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
private isPropertyValueSeparator () {
|
|
590
|
-
if (this.curToken.Type === Tokens.COLON) {
|
|
591
|
-
return true
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
if (this.curToken.Type === Tokens.ASSIGN && this.peekToken.Type === Tokens.GT) {
|
|
595
|
-
this.nextToken() // eat <
|
|
596
|
-
return true
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
return false
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/**
|
|
603
|
-
* checks if group segment is opened and forwards to beginning of
|
|
604
|
-
* first property declaration
|
|
605
|
-
* @returns {String[]} closing tokens for group (either `}`, `)` or both)
|
|
606
|
-
*/
|
|
607
|
-
private openSegment (): string[] {
|
|
608
|
-
if (this.curToken.Type === Tokens.LBRACE) {
|
|
609
|
-
this.nextToken()
|
|
610
|
-
return [Tokens.RBRACE]
|
|
611
|
-
} else if (this.curToken.Type === Tokens.LPAREN) {
|
|
612
|
-
this.nextToken()
|
|
613
|
-
return [Tokens.RPAREN]
|
|
614
|
-
} else if (this.curToken.Type === Tokens.LBRACK) {
|
|
615
|
-
this.nextToken()
|
|
616
|
-
return [Tokens.RBRACK]
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return []
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
private parsePropertyName (): PropertyName {
|
|
623
|
-
/**
|
|
624
|
-
* property name without quotes
|
|
625
|
-
*/
|
|
626
|
-
if (this.curToken.Type === Tokens.IDENT || this.curToken.Type === Tokens.STRING) {
|
|
627
|
-
const name = this.curToken.Literal
|
|
628
|
-
this.nextToken()
|
|
629
|
-
return name
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
throw this.parserError(`Expected property name, received ${this.curToken.Type}(${this.curToken.Literal}), ${this.peekToken.Type}(${this.peekToken.Literal})`)
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
private parsePropertyType (): PropertyType {
|
|
636
|
-
let type: PropertyType | undefined = undefined
|
|
637
|
-
let isUnwrapped = false
|
|
638
|
-
let isGroupedRange = false
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* check if variable name is unwrapped
|
|
642
|
-
*/
|
|
643
|
-
if (this.curToken.Literal === Tokens.TILDE) {
|
|
644
|
-
isUnwrapped = true
|
|
645
|
-
this.nextToken() // eat ~
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
switch (this.curToken.Literal) {
|
|
649
|
-
case Type.ANY:
|
|
650
|
-
case Type.BOOL:
|
|
651
|
-
case Type.INT:
|
|
652
|
-
case Type.UINT:
|
|
653
|
-
case Type.NINT:
|
|
654
|
-
case Type.FLOAT:
|
|
655
|
-
case Type.FLOAT16:
|
|
656
|
-
case Type.FLOAT32:
|
|
657
|
-
case Type.FLOAT64:
|
|
658
|
-
case Type.BSTR:
|
|
659
|
-
case Type.BYTES:
|
|
660
|
-
case Type.TSTR:
|
|
661
|
-
case Type.TEXT:
|
|
662
|
-
case Type.NIL:
|
|
663
|
-
case Type.NULL:
|
|
664
|
-
type = this.curToken.Literal
|
|
665
|
-
break
|
|
666
|
-
default: {
|
|
667
|
-
if (BOOLEAN_LITERALS.includes(this.curToken.Literal)) {
|
|
668
|
-
type = {
|
|
669
|
-
Type: 'literal' as PropertyReferenceType,
|
|
670
|
-
Value: this.curToken.Literal === 'true',
|
|
671
|
-
Unwrapped: isUnwrapped
|
|
672
|
-
}
|
|
673
|
-
} else if (this.curToken.Literal === Tokens.LBRACE || this.curToken.Literal === Tokens.LBRACK) {
|
|
674
|
-
const val = this.parseAssignmentValue()
|
|
675
|
-
if (Array.isArray(val)) {
|
|
676
|
-
throw new Error('Unexpected array in property type parsing')
|
|
677
|
-
}
|
|
678
|
-
type = val
|
|
679
|
-
} else if (this.curToken.Type === Tokens.IDENT) {
|
|
680
|
-
type = {
|
|
681
|
-
Type: 'group' as PropertyReferenceType,
|
|
682
|
-
Value: this.curToken.Literal,
|
|
683
|
-
Unwrapped: isUnwrapped
|
|
684
|
-
}
|
|
685
|
-
} else if (this.curToken.Type === Tokens.STRING) {
|
|
686
|
-
type = {
|
|
687
|
-
Type: 'literal' as PropertyReferenceType,
|
|
688
|
-
Value: this.curToken.Literal,
|
|
689
|
-
Unwrapped: isUnwrapped
|
|
690
|
-
}
|
|
691
|
-
} else if (this.curToken.Type === Tokens.NUMBER || this.curToken.Type === Tokens.FLOAT) {
|
|
692
|
-
type = {
|
|
693
|
-
Type: 'literal' as PropertyReferenceType,
|
|
694
|
-
Value: parseNumberValue(this.curToken),
|
|
695
|
-
Unwrapped: isUnwrapped
|
|
696
|
-
}
|
|
697
|
-
} else if (this.curToken.Type === Tokens.HASH) {
|
|
698
|
-
this.nextToken()
|
|
699
|
-
const n = parseNumberValue(this.curToken)
|
|
700
|
-
this.nextToken() // eat numeric value
|
|
701
|
-
this.nextToken() // eat (
|
|
702
|
-
const t = this.parsePropertyType()
|
|
703
|
-
this.nextToken() // eat )
|
|
704
|
-
type = {
|
|
705
|
-
Type: 'tag',
|
|
706
|
-
Value: {
|
|
707
|
-
NumericPart: n as number,
|
|
708
|
-
TypePart: t as string
|
|
709
|
-
},
|
|
710
|
-
Unwrapped: isUnwrapped
|
|
711
|
-
}
|
|
712
|
-
} else if (this.curToken.Literal === Tokens.LPAREN && this.peekToken.Type === Tokens.NUMBER) {
|
|
713
|
-
this.nextToken()
|
|
714
|
-
type = {
|
|
715
|
-
Type: 'literal' as PropertyReferenceType,
|
|
716
|
-
Value: parseNumberValue(this.curToken),
|
|
717
|
-
Unwrapped: isUnwrapped
|
|
718
|
-
}
|
|
719
|
-
isGroupedRange = true
|
|
720
|
-
} else {
|
|
721
|
-
throw this.parserError(`Invalid property type "${this.curToken.Literal}"`)
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
/**
|
|
727
|
-
* check if type continue as a range
|
|
728
|
-
*/
|
|
729
|
-
if (
|
|
730
|
-
this.peekToken.Type === Tokens.DOT &&
|
|
731
|
-
this.nextToken() &&
|
|
732
|
-
this.peekToken.Type === Tokens.DOT
|
|
733
|
-
) {
|
|
734
|
-
this.nextToken()
|
|
735
|
-
let Inclusive = true
|
|
736
|
-
|
|
737
|
-
/**
|
|
738
|
-
* check if range excludes upper bound
|
|
739
|
-
*/
|
|
740
|
-
if (this.peekToken.Type === Tokens.DOT) {
|
|
741
|
-
Inclusive = false
|
|
742
|
-
this.nextToken()
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
this.nextToken()
|
|
746
|
-
|
|
747
|
-
if (!type || (typeof type === 'object' && !('Value' in type))) {
|
|
748
|
-
throw new Error('Invalid type for range definition')
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
const Min: RangePropertyReference = typeof type === 'string' || typeof type.Value === 'number'
|
|
752
|
-
? type as string
|
|
753
|
-
: type.Value as (number | string)
|
|
754
|
-
type = {
|
|
755
|
-
Type: 'range' as PropertyReferenceType,
|
|
756
|
-
Value: {
|
|
757
|
-
Inclusive,
|
|
758
|
-
Min,
|
|
759
|
-
Max: this.parsePropertyType() as RangePropertyReference
|
|
760
|
-
},
|
|
761
|
-
Unwrapped: isUnwrapped
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
if (!isGroupedRange && this.peekToken.Literal === Tokens.RPAREN) {
|
|
765
|
-
/**
|
|
766
|
-
* If we are at the end of a grouped range, and this was called
|
|
767
|
-
* on the first item of the range as opposed to the opening
|
|
768
|
-
* parenthesis, isGroupedRange will not be set to true at this
|
|
769
|
-
* point. We need to advance to the closing parenthesis, and if
|
|
770
|
-
* the next token is an operator, we need to advance to the dot
|
|
771
|
-
* so that parseOperator will work properly.
|
|
772
|
-
* e.g.
|
|
773
|
-
*
|
|
774
|
-
* ```
|
|
775
|
-
* (1.0..2.0) .default 1.5
|
|
776
|
-
* ```
|
|
777
|
-
*
|
|
778
|
-
* This will be called on the `1.0` and then the `2.0` will be parsed
|
|
779
|
-
* as a grouped range.
|
|
780
|
-
*/
|
|
781
|
-
this.nextToken()
|
|
782
|
-
if (this.isOperator()) {
|
|
783
|
-
isGroupedRange = true
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
if (isGroupedRange) {
|
|
788
|
-
this.nextToken() // eat ")"
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if (!type) {
|
|
793
|
-
const { line, position: column } = this.l.getLocation()
|
|
794
|
-
throw new Error(`Unexpected type: ${this.curToken.Type} at line ${line} column ${column}`)
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
return type
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
private parseOperator (): Operator {
|
|
801
|
-
const type = this.peekToken.Literal as OperatorType
|
|
802
|
-
if (this.curToken.Literal !== Tokens.DOT || !OPERATORS.includes(this.peekToken.Literal as OperatorType)) {
|
|
803
|
-
throw new Error(`Operator ".${type}", expects a ${OPERATORS_EXPECTING_VALUES[type]!.join(' or ')} property, but found ${this.peekToken.Literal}!`)
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
this.nextToken() // eat "."
|
|
807
|
-
this.nextToken() // eat operator type
|
|
808
|
-
const value = this.parsePropertyType() as PropertyReferenceType
|
|
809
|
-
this.nextToken() // eat operator value
|
|
810
|
-
return {
|
|
811
|
-
Type: type,
|
|
812
|
-
Value: value
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
private isOperator () {
|
|
817
|
-
return this.curToken.Literal === Tokens.DOT && OPERATORS.includes(this.peekToken.Literal as OperatorType)
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
private parsePropertyTypes (): PropertyType[] {
|
|
821
|
-
const propertyTypes: PropertyType[] = []
|
|
822
|
-
|
|
823
|
-
let prop: PropertyType = this.parsePropertyType()
|
|
824
|
-
if (this.isOperator()) {
|
|
825
|
-
prop = {
|
|
826
|
-
Type: prop,
|
|
827
|
-
Operator: this.parseOperator()
|
|
828
|
-
} as NativeTypeWithOperator
|
|
829
|
-
} else if (this.curToken.Type !== Tokens.SLASH) {
|
|
830
|
-
this.nextToken() // eat Property if not already consumed (e.g. by Group parsing)
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
propertyTypes.push(prop)
|
|
834
|
-
|
|
835
|
-
/**
|
|
836
|
-
* ignore comments between type choice members, e.g.
|
|
837
|
-
* ```
|
|
838
|
-
* Foo = int ; comment
|
|
839
|
-
* / text
|
|
840
|
-
* ```
|
|
841
|
-
* or
|
|
842
|
-
* ```
|
|
843
|
-
* Foo = int / ; comment
|
|
844
|
-
* text
|
|
845
|
-
* ```
|
|
846
|
-
*/
|
|
847
|
-
while (this.curToken.Type === Tokens.COMMENT && this.peekToken.Type === Tokens.SLASH) {
|
|
848
|
-
this.parseComment()
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* ensure we don't go into the next choice, e.g.:
|
|
853
|
-
* ```
|
|
854
|
-
* delivery = (
|
|
855
|
-
* city // lala: tstr / bool // per-pickup: true,
|
|
856
|
-
* )
|
|
857
|
-
*/
|
|
858
|
-
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
859
|
-
return propertyTypes
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
* capture more if available (e.g. `tstr / float / boolean`)
|
|
864
|
-
*/
|
|
865
|
-
while (this.curToken.Type === Tokens.SLASH) {
|
|
866
|
-
this.nextToken() // eat `/`
|
|
867
|
-
while ([Tokens.COMMENT].includes(this.curToken.Type)) {
|
|
868
|
-
this.parseComment()
|
|
869
|
-
}
|
|
870
|
-
propertyTypes.push(this.parsePropertyType())
|
|
871
|
-
if (!this.isOperator() && this.curToken.Type !== Tokens.SLASH) {
|
|
872
|
-
/**
|
|
873
|
-
* If we are not parsing an operator, we need to eat the next token;
|
|
874
|
-
* otherwise, the operator will be parsed by the caller
|
|
875
|
-
*/
|
|
876
|
-
this.nextToken()
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
while ([Tokens.COMMENT].includes(this.curToken.Type) && this.peekToken.Type === Tokens.SLASH) {
|
|
880
|
-
this.parseComment()
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/**
|
|
884
|
-
* ensure we don't go into the next choice, e.g.:
|
|
885
|
-
* ```
|
|
886
|
-
* delivery = (
|
|
887
|
-
* city // lala: tstr / bool // per-pickup: true,
|
|
888
|
-
* )
|
|
889
|
-
*/
|
|
890
|
-
if (this.curToken.Type === Tokens.SLASH && this.peekToken.Type === Tokens.SLASH) {
|
|
891
|
-
break
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
return propertyTypes
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
private parseOccurrences () {
|
|
899
|
-
let occurrence = DEFAULT_OCCURRENCE
|
|
900
|
-
|
|
901
|
-
/**
|
|
902
|
-
* check for non-numbered occurrence indicator, e.g.
|
|
903
|
-
* ```
|
|
904
|
-
* * bedroom: size,
|
|
905
|
-
* ```
|
|
906
|
-
* which is the same as:
|
|
907
|
-
* ```
|
|
908
|
-
* ? bedroom: size,
|
|
909
|
-
* ```
|
|
910
|
-
* or have miniumum of 1 occurrence
|
|
911
|
-
* ```
|
|
912
|
-
* + bedroom: size,
|
|
913
|
-
* ```
|
|
914
|
-
*/
|
|
915
|
-
if (this.curToken.Type === Tokens.QUEST || this.curToken.Type === Tokens.ASTERISK || this.curToken.Type === Tokens.PLUS) {
|
|
916
|
-
const n = this.curToken.Type === Tokens.PLUS ? 1 : 0
|
|
917
|
-
let m = Infinity
|
|
918
|
-
|
|
919
|
-
/**
|
|
920
|
-
* check if there is a max definition
|
|
921
|
-
*/
|
|
922
|
-
if (this.peekToken.Type === Tokens.NUMBER) {
|
|
923
|
-
m = parseInt(this.peekToken.Literal, 10)
|
|
924
|
-
this.nextToken()
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
occurrence = { n, m }
|
|
928
|
-
this.nextToken()
|
|
929
|
-
/**
|
|
930
|
-
* numbered occurrence indicator, e.g.
|
|
931
|
-
* ```
|
|
932
|
-
* 1*10 bedroom: size,
|
|
933
|
-
* ```
|
|
934
|
-
*/
|
|
935
|
-
} else if (
|
|
936
|
-
this.curToken.Type === Tokens.NUMBER &&
|
|
937
|
-
this.peekToken.Type === Tokens.ASTERISK
|
|
938
|
-
) {
|
|
939
|
-
const n = parseInt(this.curToken.Literal, 10)
|
|
940
|
-
let m = Infinity
|
|
941
|
-
this.nextToken() // eat "n"
|
|
942
|
-
this.nextToken() // eat "*"
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* check if there is a max definition
|
|
946
|
-
*/
|
|
947
|
-
if (this.curToken.Type === Tokens.NUMBER) {
|
|
948
|
-
m = parseInt(this.curToken.Literal, 10)
|
|
949
|
-
this.nextToken()
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
occurrence = { n, m }
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
return occurrence
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
/**
|
|
959
|
-
* check if line has a comment
|
|
960
|
-
*/
|
|
961
|
-
private parseComment (isLeading?: boolean): Comment | undefined {
|
|
962
|
-
if (this.curToken.Type !== Tokens.COMMENT) {
|
|
963
|
-
return
|
|
964
|
-
}
|
|
965
|
-
const comment = this.curToken.Literal.replace(/^;(\s*)/, '')
|
|
966
|
-
this.nextToken()
|
|
967
|
-
|
|
968
|
-
if (comment.trim().length === 0) {
|
|
969
|
-
return
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
return { Type: 'comment', Content: comment, Leading: Boolean(isLeading) }
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
parse () {
|
|
976
|
-
const definition: Assignment[] = []
|
|
977
|
-
|
|
978
|
-
while (this.curToken.Type !== Tokens.EOF) {
|
|
979
|
-
const group = this.parseAssignments()
|
|
980
|
-
if (group) {
|
|
981
|
-
definition.push(group)
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
return definition
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
private parserError (message: string) {
|
|
989
|
-
const location = this.l.getLocation()
|
|
990
|
-
const locInfo = this.l.getLocationInfo()
|
|
991
|
-
return new Error(`${this.#filePath.replace(process.cwd(), '')}:${location.line + 1}:${location.position} - error: ${message}\n\n${locInfo}`)
|
|
992
|
-
}
|
|
993
|
-
}
|