@@ -76,7 +76,7 @@ export namespace syxparser {
76
76
* @param {boolean } put Whether the result should be added to the program statement.
77
77
* @returns A node that is either a statement or an expression if a statement wasn't present.
78
78
* @author efekos
79
- * @version 1.0.6
79
+ * @version 1.0.7
80
80
* @since 0.0.1-alpha
81
81
*/
82
82
export function parseStatement ( put : boolean = true ) : Node {
@@ -87,7 +87,7 @@ export namespace syxparser {
87
87
if ( token . type === TokenType . ImportKeyword ) {
88
88
89
89
const ex = parseExpression ( false , false ) ;
90
- if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , 'Expected string after import statement.' ) ;
90
+ if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , 'Expected file path after import statement.' ) ;
91
91
return node ( { type : NodeType . Import , path : ( ex as Expression ) . value , range : combineTwo ( token , ex . range ) } , put ) ;
92
92
93
93
} else if ( token . type === TokenType . OperatorKeyword ) {
@@ -100,8 +100,8 @@ export namespace syxparser {
100
100
}
101
101
102
102
const braceExpr = parseExpression ( false ) ;
103
- if ( braceExpr . type !== NodeType . Brace ) throw new CompilerError ( braceExpr . range , 'Expected braces after \' operator\' .' ) ;
104
- braceExpr . body . forEach ( s => { if ( ! ( [ NodeType . Compile , NodeType . Imports ] . includes ( s . type ) ) ) throw new CompilerError ( s . range , 'Statement not allowed.' ) ; } ) ;
103
+ if ( braceExpr . type !== NodeType . Brace ) throw new CompilerError ( braceExpr . range , 'Expected braces after operator regex .' ) ;
104
+ braceExpr . body . forEach ( s => { if ( ! ( [ NodeType . Compile , NodeType . Imports ] . includes ( s . type ) ) ) throw new CompilerError ( s . range , 'Statement not allowed inside of operator statement .' ) ; } ) ;
105
105
106
106
statement . body = braceExpr . body ;
107
107
statement . range = combineTwo ( token , braceExpr . range ) ;
@@ -110,7 +110,7 @@ export namespace syxparser {
110
110
} else if ( token . type === TokenType . CompileKeyword ) {
111
111
const statement : CompileStatement = { type : NodeType . Compile , formats : [ ] , body : [ ] , range : defaultRange } ;
112
112
113
- if ( at ( ) . type !== TokenType . OpenParen ) throw new CompilerError ( at ( ) . range , 'Expected parens after \'compile\' statement .' ) ;
113
+ if ( at ( ) . type !== TokenType . OpenParen ) throw new CompilerError ( at ( ) . range , 'Compile statement require parens .' ) ;
114
114
115
115
tokens . shift ( ) ; // skip OpenParen
116
116
while ( at ( ) . type !== TokenType . CloseParen ) {
@@ -120,10 +120,12 @@ export namespace syxparser {
120
120
else if ( t . type === TokenType . Comma && statement . formats . length === 0 ) throw new CompilerError ( t . range , 'Can\'t start with comma.' ) ;
121
121
else if ( t . type === TokenType . Comma ) { }
122
122
else if ( t . type === TokenType . Identifier ) statement . formats . push ( t . value ) ;
123
- else throw new CompilerError ( t . range , `Expected comma or identifier but found ${ t . type } .` ) ;
123
+ else throw new CompilerError ( t . range , `Expected comma or identifier, found ' ${ t . value } ' .` ) ;
124
124
}
125
125
tokens . shift ( ) ; // skip CloseParen
126
126
127
+ if ( statement . formats . length === 0 ) throw new CompilerError ( token . range , 'At least one file type is required.' ) ;
128
+
127
129
while ( at ( ) . type !== TokenType . Semicolon ) {
128
130
const expr = parseExpression ( false , false ) ;
129
131
statement . body . push ( expr as Expression ) ;
@@ -133,12 +135,12 @@ export namespace syxparser {
133
135
return node ( statement , put ) ;
134
136
} else if ( token . type === TokenType . ExportKeyword ) {
135
137
const stmt = parseStatement ( false ) ;
136
- if ( ! exportable . includes ( stmt . type ) ) throw new CompilerError ( stmt . range , 'Expected exportable statement after export.' ) ;
138
+ if ( ! exportable . includes ( stmt . type ) ) throw new CompilerError ( stmt . range , 'Expected exportable statement after \' export\' .' ) ;
137
139
return node ( { type : NodeType . Export , body : stmt , range : combineTwo ( token , stmt . range ) } , put ) ;
138
140
} else if ( token . type === TokenType . ImportsKeyword ) {
139
141
const statement : ImportsStatement = { type : NodeType . Imports , formats : [ ] , module : '' , range : defaultRange } ;
140
142
141
- if ( at ( ) . type !== TokenType . OpenParen ) throw new CompilerError ( at ( ) . range , 'Expected parens after \'imports\' statement .' ) ;
143
+ if ( at ( ) . type !== TokenType . OpenParen ) throw new CompilerError ( at ( ) . range , 'Imports statement require parens .' ) ;
142
144
143
145
tokens . shift ( ) ; // skip OpenParen
144
146
while ( at ( ) . type !== TokenType . CloseParen ) {
@@ -148,52 +150,55 @@ export namespace syxparser {
148
150
else if ( t . type === TokenType . Comma && statement . formats . length === 0 ) throw new CompilerError ( t . range , 'Can\'t start with comma.' ) ;
149
151
else if ( t . type === TokenType . Comma ) { }
150
152
else if ( t . type === TokenType . Identifier ) statement . formats . push ( t . value ) ;
151
- else throw new CompilerError ( t . range , 'Unexpected token.' ) ;
153
+ else throw new CompilerError ( t . range , `Expected comma or identifier, found ' ${ t . value } '.` ) ;
152
154
}
153
155
tokens . shift ( ) ; // skip CloseParen
154
156
155
- const moduleExpr = parseExpression ( false , false ) ;
157
+ if ( statement . formats . length === 0 ) throw new CompilerError ( token . range , 'At least one file type is required.' ) ;
158
+
159
+
160
+ const moduleExpr = parseExpression ( false , false ) as Expression ;
156
161
157
- if ( moduleExpr . type !== NodeType . String ) { throw new CompilerError ( moduleExpr . range , ' Expected string after parens of imports statement.' ) ; }
162
+ if ( moduleExpr . type !== NodeType . String ) { throw new CompilerError ( moduleExpr . range , ` Expected string after parens of imports statement, found ' ${ moduleExpr . value } '.` ) ; }
158
163
159
164
statement . module = moduleExpr . value ;
160
165
statement . range = combineTwo ( token , moduleExpr . range ) ;
161
166
162
- if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ' Expected \';\ ' after imports statement.' ) ;
167
+ if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ` Expected '; ' after imports statement, found ' ${ at ( ) . value } '.` ) ;
163
168
tokens . shift ( ) ;
164
169
165
170
return node ( statement , put ) ;
166
171
} else if ( token . type === TokenType . FunctionKeyword ) {
167
172
const statement : FunctionStatement = { type : NodeType . Function , arguments : [ ] , name : '' , body : [ ] , range : defaultRange } ;
168
173
169
- if ( at ( ) . type !== TokenType . Identifier ) throw new CompilerError ( at ( ) . range , ' Expected identifier after function statement.' ) ;
174
+ if ( at ( ) . type !== TokenType . Identifier ) throw new CompilerError ( at ( ) . range , ` Expected identifier after function statement, found ' ${ at ( ) . value } '.` ) ;
170
175
statement . name = at ( ) . value ;
171
176
tokens . shift ( ) ;
172
177
173
178
while ( at ( ) . type !== TokenType . OpenBrace ) {
174
- const expr = parseExpression ( false , false ) ;
175
- if ( expr . type !== NodeType . PrimitiveType ) throw new CompilerError ( expr . range , ' Expected argument types after function name.' ) ;
179
+ const expr = parseExpression ( false , false ) as Expression ;
180
+ if ( expr . type !== NodeType . PrimitiveType ) throw new CompilerError ( expr . range , ` Expected argument types after function name, found ${ expr . value } .` ) ;
176
181
statement . arguments . push ( ( expr as PrimitiveTypeExpression ) . value ) ;
177
182
}
178
183
179
184
const braceExpr = parseExpression ( false ) ;
180
- if ( braceExpr . type !== NodeType . Brace ) throw new CompilerError ( braceExpr . range , 'Expected braces after \'function\' .' ) ;
181
- braceExpr . body . forEach ( s => { if ( ! ( [ NodeType . Compile , NodeType . Imports ] . includes ( s . type ) ) ) throw new CompilerError ( s . range , 'Statement not allowed' ) ; } ) ;
185
+ if ( braceExpr . type !== NodeType . Brace ) throw new CompilerError ( braceExpr . range , 'Function statement requires braces .' ) ;
186
+ braceExpr . body . forEach ( s => { if ( ! ( [ NodeType . Compile , NodeType . Imports ] . includes ( s . type ) ) ) throw new CompilerError ( s . range , 'Statement not allowed inside a function statement. ' ) ; } ) ;
182
187
183
188
statement . body = braceExpr . body ;
184
189
statement . range = combineTwo ( token , braceExpr . range ) ;
185
190
186
191
return node ( statement , put ) ;
187
192
} else if ( token . type === TokenType . KeywordKeyword ) {
188
- const ex = parseExpression ( false , false , true ) ;
189
- if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , ' Expected identifier after keyword statement.' ) ;
190
- if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ' Expected semicolon after statement.' ) ;
193
+ const ex = parseExpression ( false , false , true ) as Expression ;
194
+ if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , ` Expected identifier after keyword statement, found ' ${ ex . value } '.` ) ;
195
+ if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ` Expected ';' after statement, found ' ${ at ( ) . value } '.` ) ;
191
196
tokens . shift ( ) ; // skip semicolon
192
197
return node ( { type : NodeType . Keyword , word : ex . value , range : combineTwo ( token , ex . range ) } , put ) ;
193
198
} else if ( token . type === TokenType . RuleKeyword ) {
194
- const ruleExpr = parseExpression ( false , false ) ;
195
- if ( ruleExpr . type !== NodeType . String ) { throw new CompilerError ( ruleExpr . range , ' Expected rule name as string after \ 'rule\'.' ) ; }
196
- if ( at ( ) . value !== ':' ) throw new CompilerError ( at ( ) . range , ' Expected \':\' after rule name.' ) ;
199
+ const ruleExpr = parseExpression ( false , false ) as Expression ;
200
+ if ( ruleExpr . type !== NodeType . String ) { throw new CompilerError ( ruleExpr . range , ` Expected rule name as string after 'rule', found ${ ruleExpr . value } .` ) ; }
201
+ if ( at ( ) . value !== ':' ) throw new CompilerError ( at ( ) . range , ` Expected \':\' after rule name, found ${ at ( ) . value } .` ) ;
197
202
tokens . shift ( ) ;
198
203
if ( ! dictionary . Rules . find ( r => r . name === ruleExpr . value ) ) throw new CompilerError ( ruleExpr . range , `Unknown rule '${ ruleExpr . value } '.` ) ;
199
204
const rule = dictionary . Rules . find ( r => r . name === ruleExpr . value ) ;
@@ -203,7 +208,7 @@ export namespace syxparser {
203
208
if ( ! ( boolEx . type === NodeType . String && dictionary . RuleTypeRegexes . boolean . test ( boolEx . value ) ) ) { throw new CompilerError ( boolEx . range , `Rule '${ rule . name } ' requires a boolean value, found '${ boolEx . value } '.` ) ; }
204
209
205
210
206
- if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ' Expected semicolon after rule statement.' ) ;
211
+ if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ` Expected semicolon after rule statement, found ' ${ at ( ) . value } '.` ) ;
207
212
return node ( { type : NodeType . Rule , rule : ruleExpr . value , value : boolEx . value , range : combineTwo ( token , tokens . shift ( ) ) } , put ) ;
208
213
} else if ( rule . type === 'keyword' ) {
209
214
const keyEx = parseExpression ( false , false , true ) as Expression ;
@@ -213,9 +218,9 @@ export namespace syxparser {
213
218
( s . type === NodeType . Keyword && ( s as KeywordStatement ) . word === keyEx . value ) ||
214
219
( s . type === NodeType . Export && ( s as ExportStatement ) . body . type === NodeType . Keyword && ( ( s as ExportStatement ) . body as KeywordStatement ) . word === keyEx . value )
215
220
)
216
- ) ) throw new CompilerError ( keyEx . range , `Can't find keyword ${ keyEx . value } .` ) ;
221
+ ) ) throw new CompilerError ( keyEx . range , `Can't find keyword ' ${ keyEx . value } ' .` ) ;
217
222
218
- if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ' Expected semicolon after rule statement.' ) ;
223
+ if ( at ( ) . type !== TokenType . Semicolon ) throw new CompilerError ( at ( ) . range , ` Expected semicolon after rule statement, found ${ at ( ) . value } .` ) ;
219
224
return node ( { type : NodeType . Rule , rule : ruleExpr . value , value : keyEx . value , range : combineTwo ( token , tokens . shift ( ) ) } , put ) ;
220
225
}
221
226
}
@@ -245,7 +250,7 @@ export namespace syxparser {
245
250
* @param {boolean } expectIdentifier Whether identifiers should be allowed. Unknown identifiers will stop the function with this value set to `false`, returning the identifier as a {@link StringExpression} otherwise.
246
251
* @returns The parsed node.
247
252
* @author efekos
248
- * @version 1.0.6
253
+ * @version 1.0.7
249
254
* @since 0.0.1-alpha
250
255
*/
251
256
export function parseExpression ( put : boolean = true , statements : boolean = true , expectIdentifier : boolean = false ) : Node {
@@ -258,6 +263,8 @@ export namespace syxparser {
258
263
tokens . shift ( ) ;
259
264
while ( at ( ) . type !== TokenType . SingleQuote ) {
260
265
const _t = tokens . shift ( ) ;
266
+ if ( _t . type === TokenType . EndOfFile ) throw new CompilerError ( combineTwo ( range , { start :{ line :0 , character :0 } , end :{ character :range . end . character + s . length , line :range . end . line } } ) , 'Strings must be closed.' ) ;
267
+
261
268
s += _t . value ;
262
269
}
263
270
@@ -268,8 +275,10 @@ export namespace syxparser {
268
275
const { range } = at ( ) ;
269
276
270
277
tokens . shift ( ) ;
271
- while ( at ( ) . type !== TokenType . SingleQuote ) {
278
+ while ( at ( ) . type !== TokenType . DoubleQuote ) {
272
279
const _t = tokens . shift ( ) ;
280
+ if ( _t . type === TokenType . EndOfFile ) throw new CompilerError ( combineTwo ( range , { start :{ line :0 , character :0 } , end :{ character :range . end . character + s . length , line :range . end . line } } ) , 'Strings must be closed.' ) ;
281
+
273
282
s += _t . value ;
274
283
}
275
284
@@ -278,9 +287,9 @@ export namespace syxparser {
278
287
} else if ( tt === TokenType . OpenDiamond ) {
279
288
280
289
const newToken = at ( 1 ) ;
281
- if ( newToken . type !== TokenType . Identifier ) throw new CompilerError ( newToken . range , ' Expected identifier after \'<\'.' ) ;
282
- if ( ! newToken . value . match ( primitiveTypes ) ) throw new CompilerError ( newToken . range , `Expected primitive type, found '${ newToken . value } '` ) ;
283
- if ( at ( 2 ) . type !== TokenType . CloseDiamond ) throw new CompilerError ( at ( 2 ) . range , `Expected '>' after primitive type, found '${ at ( 2 ) . value } '` ) ;
290
+ if ( newToken . type !== TokenType . Identifier ) throw new CompilerError ( newToken . range , ` Expected identifier after '<', found ' ${ newToken . value } '.` ) ;
291
+ if ( ! newToken . value . match ( primitiveTypes ) ) throw new CompilerError ( newToken . range , `Expected primitive type identifier after '<' , found '${ newToken . value } '` ) ;
292
+ if ( at ( 2 ) . type !== TokenType . CloseDiamond ) throw new CompilerError ( at ( 2 ) . range , `Expected '>' after primitive type identifier , found '${ at ( 2 ) . value } '` ) ;
284
293
const t = tokens . shift ( ) ;
285
294
tokens . shift ( ) ;
286
295
@@ -328,7 +337,7 @@ export namespace syxparser {
328
337
329
338
} else if ( tt === TokenType . Identifier && at ( 1 ) . type === TokenType . VarSeperator ) {
330
339
331
- if ( at ( 2 ) . type !== TokenType . IntNumber ) throw new CompilerError ( at ( 2 ) . range , `Expected index after ${ at ( ) . value } variable` ) ;
340
+ if ( at ( 2 ) . type !== TokenType . IntNumber ) throw new CompilerError ( at ( 2 ) . range , `Expected index after ${ at ( ) . value } variable, found ${ at ( 2 ) . value } . ` ) ;
332
341
333
342
const id = tokens . shift ( ) ; // id
334
343
tokens . shift ( ) ; // sep
@@ -337,7 +346,7 @@ export namespace syxparser {
337
346
338
347
return node ( expr , put ) ;
339
348
} else if ( keywords . includes ( tt ) ) {
340
- if ( ! statements ) throw new CompilerError ( at ( ) . range , 'Unexpected statement .' ) ;
349
+ if ( ! statements ) throw new CompilerError ( at ( ) . range , 'Statement not allowed here .' ) ;
341
350
return parseStatement ( ) ;
342
351
} else if ( tt === TokenType . Identifier && expectIdentifier ) {
343
352
const { value, range } = tokens . shift ( ) ;
@@ -426,7 +435,7 @@ export namespace sysparser {
426
435
* @param {boolean } put Whether the result should be added to the program statement.
427
436
* @returns A node that is either a statement or an expression if a statement wasn't present.
428
437
* @author efekos
429
- * @version 1.0.4
438
+ * @version 1.0.5
430
439
* @since 0.0.1-alpha
431
440
*/
432
441
export function parseStatement ( put : boolean = true ) : Node {
@@ -436,8 +445,8 @@ export namespace sysparser {
436
445
437
446
if ( token . type === TokenType . ImportKeyword ) {
438
447
439
- const ex = parseExpression ( false , false ) ;
440
- if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , ' Expected string after import statement.' ) ;
448
+ const ex = parseExpression ( false , false ) as Expression ;
449
+ if ( ex . type !== NodeType . String ) throw new CompilerError ( ex . range , ` Expected string after import statement, found ${ ex . value } .` ) ;
441
450
return node ( { type : NodeType . Import , path : ( ex as Expression ) . value , range : combineTwo ( token , ex . range ) } , put ) ;
442
451
443
452
}
@@ -467,7 +476,7 @@ export namespace sysparser {
467
476
* @param {boolean } expectIdentifier Whether identifiers should be allowed. Unknown identifiers will stop the function with this value set to `false`, returning the identifier as a {@link StringExpression} otherwise.
468
477
* @returns The parsed node.
469
478
* @author efekos
470
- * @version 1.0.4
479
+ * @version 1.0.5
471
480
* @since 0.0.1-alpha
472
481
*/
473
482
export function parseExpression ( put : boolean = true , statements : boolean = true ) : Node {
@@ -480,6 +489,8 @@ export namespace sysparser {
480
489
tokens . shift ( ) ;
481
490
while ( at ( ) . type !== TokenType . SingleQuote ) {
482
491
const _t = tokens . shift ( ) ;
492
+ if ( _t . type === TokenType . EndOfFile ) throw new CompilerError ( combineTwo ( range , { start :{ line :0 , character :0 } , end :{ character :range . end . character + s . length , line :range . end . line } } ) , 'Strings must be closed.' ) ;
493
+
483
494
s += _t . value ;
484
495
}
485
496
@@ -490,15 +501,17 @@ export namespace sysparser {
490
501
const { range } = at ( ) ;
491
502
492
503
tokens . shift ( ) ;
493
- while ( at ( ) . type !== TokenType . SingleQuote ) {
504
+ while ( at ( ) . type !== TokenType . DoubleQuote ) {
494
505
const _t = tokens . shift ( ) ;
506
+ if ( _t . type === TokenType . EndOfFile ) throw new CompilerError ( combineTwo ( range , { start :{ line :0 , character :0 } , end :{ character :range . end . character + s . length , line :range . end . line } } ) , 'Strings must be closed.' ) ;
507
+
495
508
s += _t . value ;
496
509
}
497
510
498
511
return node ( { type : NodeType . String , value : s , range : combineTwo ( range , tokens . shift ( ) ) } , put ) ;
499
512
500
513
} else if ( keywords . includes ( tt ) ) {
501
- if ( ! statements ) throw new CompilerError ( at ( ) . range , 'Unexpected statement .' ) ;
514
+ if ( ! statements ) throw new CompilerError ( at ( ) . range , 'Statements are not allowed here .' ) ;
502
515
return parseStatement ( ) ;
503
516
}
504
517
else throw new CompilerError ( at ( ) . range , `Unexpected expression: '${ at ( ) . value } '` ) ;
0 commit comments