@@ -76,7 +76,7 @@ export namespace syxparser {
7676 * @param {boolean } put Whether the result should be added to the program statement.
7777 * @returns A node that is either a statement or an expression if a statement wasn't present.
7878 * @author efekos
79- * @version 1.0.6
79+ * @version 1.0.7
8080 * @since 0.0.1-alpha
8181 */
8282 export function parseStatement ( put : boolean = true ) : Node {
@@ -87,7 +87,7 @@ export namespace syxparser {
8787 if ( token . type === TokenType . ImportKeyword ) {
8888
8989 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.' ) ;
9191 return node ( { type : NodeType . Import , path : ( ex as Expression ) . value , range : combineTwo ( token , ex . range ) } , put ) ;
9292
9393 } else if ( token . type === TokenType . OperatorKeyword ) {
@@ -100,8 +100,8 @@ export namespace syxparser {
100100 }
101101
102102 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 .' ) ; } ) ;
105105
106106 statement . body = braceExpr . body ;
107107 statement . range = combineTwo ( token , braceExpr . range ) ;
@@ -110,7 +110,7 @@ export namespace syxparser {
110110 } else if ( token . type === TokenType . CompileKeyword ) {
111111 const statement : CompileStatement = { type : NodeType . Compile , formats : [ ] , body : [ ] , range : defaultRange } ;
112112
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 .' ) ;
114114
115115 tokens . shift ( ) ; // skip OpenParen
116116 while ( at ( ) . type !== TokenType . CloseParen ) {
@@ -120,10 +120,12 @@ export namespace syxparser {
120120 else if ( t . type === TokenType . Comma && statement . formats . length === 0 ) throw new CompilerError ( t . range , 'Can\'t start with comma.' ) ;
121121 else if ( t . type === TokenType . Comma ) { }
122122 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 } ' .` ) ;
124124 }
125125 tokens . shift ( ) ; // skip CloseParen
126126
127+ if ( statement . formats . length === 0 ) throw new CompilerError ( token . range , 'At least one file type is required.' ) ;
128+
127129 while ( at ( ) . type !== TokenType . Semicolon ) {
128130 const expr = parseExpression ( false , false ) ;
129131 statement . body . push ( expr as Expression ) ;
@@ -133,12 +135,12 @@ export namespace syxparser {
133135 return node ( statement , put ) ;
134136 } else if ( token . type === TokenType . ExportKeyword ) {
135137 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\' .' ) ;
137139 return node ( { type : NodeType . Export , body : stmt , range : combineTwo ( token , stmt . range ) } , put ) ;
138140 } else if ( token . type === TokenType . ImportsKeyword ) {
139141 const statement : ImportsStatement = { type : NodeType . Imports , formats : [ ] , module : '' , range : defaultRange } ;
140142
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 .' ) ;
142144
143145 tokens . shift ( ) ; // skip OpenParen
144146 while ( at ( ) . type !== TokenType . CloseParen ) {
@@ -148,52 +150,55 @@ export namespace syxparser {
148150 else if ( t . type === TokenType . Comma && statement . formats . length === 0 ) throw new CompilerError ( t . range , 'Can\'t start with comma.' ) ;
149151 else if ( t . type === TokenType . Comma ) { }
150152 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 } '.` ) ;
152154 }
153155 tokens . shift ( ) ; // skip CloseParen
154156
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 ;
156161
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 } '.` ) ; }
158163
159164 statement . module = moduleExpr . value ;
160165 statement . range = combineTwo ( token , moduleExpr . range ) ;
161166
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 } '.` ) ;
163168 tokens . shift ( ) ;
164169
165170 return node ( statement , put ) ;
166171 } else if ( token . type === TokenType . FunctionKeyword ) {
167172 const statement : FunctionStatement = { type : NodeType . Function , arguments : [ ] , name : '' , body : [ ] , range : defaultRange } ;
168173
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 } '.` ) ;
170175 statement . name = at ( ) . value ;
171176 tokens . shift ( ) ;
172177
173178 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 } .` ) ;
176181 statement . arguments . push ( ( expr as PrimitiveTypeExpression ) . value ) ;
177182 }
178183
179184 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. ' ) ; } ) ;
182187
183188 statement . body = braceExpr . body ;
184189 statement . range = combineTwo ( token , braceExpr . range ) ;
185190
186191 return node ( statement , put ) ;
187192 } 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 } '.` ) ;
191196 tokens . shift ( ) ; // skip semicolon
192197 return node ( { type : NodeType . Keyword , word : ex . value , range : combineTwo ( token , ex . range ) } , put ) ;
193198 } 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 } .` ) ;
197202 tokens . shift ( ) ;
198203 if ( ! dictionary . Rules . find ( r => r . name === ruleExpr . value ) ) throw new CompilerError ( ruleExpr . range , `Unknown rule '${ ruleExpr . value } '.` ) ;
199204 const rule = dictionary . Rules . find ( r => r . name === ruleExpr . value ) ;
@@ -203,7 +208,7 @@ export namespace syxparser {
203208 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 } '.` ) ; }
204209
205210
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 } '.` ) ;
207212 return node ( { type : NodeType . Rule , rule : ruleExpr . value , value : boolEx . value , range : combineTwo ( token , tokens . shift ( ) ) } , put ) ;
208213 } else if ( rule . type === 'keyword' ) {
209214 const keyEx = parseExpression ( false , false , true ) as Expression ;
@@ -213,9 +218,9 @@ export namespace syxparser {
213218 ( s . type === NodeType . Keyword && ( s as KeywordStatement ) . word === keyEx . value ) ||
214219 ( s . type === NodeType . Export && ( s as ExportStatement ) . body . type === NodeType . Keyword && ( ( s as ExportStatement ) . body as KeywordStatement ) . word === keyEx . value )
215220 )
216- ) ) throw new CompilerError ( keyEx . range , `Can't find keyword ${ keyEx . value } .` ) ;
221+ ) ) throw new CompilerError ( keyEx . range , `Can't find keyword ' ${ keyEx . value } ' .` ) ;
217222
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 } .` ) ;
219224 return node ( { type : NodeType . Rule , rule : ruleExpr . value , value : keyEx . value , range : combineTwo ( token , tokens . shift ( ) ) } , put ) ;
220225 }
221226 }
@@ -245,7 +250,7 @@ export namespace syxparser {
245250 * @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.
246251 * @returns The parsed node.
247252 * @author efekos
248- * @version 1.0.6
253+ * @version 1.0.7
249254 * @since 0.0.1-alpha
250255 */
251256 export function parseExpression ( put : boolean = true , statements : boolean = true , expectIdentifier : boolean = false ) : Node {
@@ -258,6 +263,8 @@ export namespace syxparser {
258263 tokens . shift ( ) ;
259264 while ( at ( ) . type !== TokenType . SingleQuote ) {
260265 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+
261268 s += _t . value ;
262269 }
263270
@@ -268,8 +275,10 @@ export namespace syxparser {
268275 const { range } = at ( ) ;
269276
270277 tokens . shift ( ) ;
271- while ( at ( ) . type !== TokenType . SingleQuote ) {
278+ while ( at ( ) . type !== TokenType . DoubleQuote ) {
272279 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+
273282 s += _t . value ;
274283 }
275284
@@ -278,9 +287,9 @@ export namespace syxparser {
278287 } else if ( tt === TokenType . OpenDiamond ) {
279288
280289 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 } '` ) ;
284293 const t = tokens . shift ( ) ;
285294 tokens . shift ( ) ;
286295
@@ -328,7 +337,7 @@ export namespace syxparser {
328337
329338 } else if ( tt === TokenType . Identifier && at ( 1 ) . type === TokenType . VarSeperator ) {
330339
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 } . ` ) ;
332341
333342 const id = tokens . shift ( ) ; // id
334343 tokens . shift ( ) ; // sep
@@ -337,7 +346,7 @@ export namespace syxparser {
337346
338347 return node ( expr , put ) ;
339348 } 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 .' ) ;
341350 return parseStatement ( ) ;
342351 } else if ( tt === TokenType . Identifier && expectIdentifier ) {
343352 const { value, range } = tokens . shift ( ) ;
@@ -426,7 +435,7 @@ export namespace sysparser {
426435 * @param {boolean } put Whether the result should be added to the program statement.
427436 * @returns A node that is either a statement or an expression if a statement wasn't present.
428437 * @author efekos
429- * @version 1.0.4
438+ * @version 1.0.5
430439 * @since 0.0.1-alpha
431440 */
432441 export function parseStatement ( put : boolean = true ) : Node {
@@ -436,8 +445,8 @@ export namespace sysparser {
436445
437446 if ( token . type === TokenType . ImportKeyword ) {
438447
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 } .` ) ;
441450 return node ( { type : NodeType . Import , path : ( ex as Expression ) . value , range : combineTwo ( token , ex . range ) } , put ) ;
442451
443452 }
@@ -467,7 +476,7 @@ export namespace sysparser {
467476 * @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.
468477 * @returns The parsed node.
469478 * @author efekos
470- * @version 1.0.4
479+ * @version 1.0.5
471480 * @since 0.0.1-alpha
472481 */
473482 export function parseExpression ( put : boolean = true , statements : boolean = true ) : Node {
@@ -480,6 +489,8 @@ export namespace sysparser {
480489 tokens . shift ( ) ;
481490 while ( at ( ) . type !== TokenType . SingleQuote ) {
482491 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+
483494 s += _t . value ;
484495 }
485496
@@ -490,15 +501,17 @@ export namespace sysparser {
490501 const { range } = at ( ) ;
491502
492503 tokens . shift ( ) ;
493- while ( at ( ) . type !== TokenType . SingleQuote ) {
504+ while ( at ( ) . type !== TokenType . DoubleQuote ) {
494505 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+
495508 s += _t . value ;
496509 }
497510
498511 return node ( { type : NodeType . String , value : s , range : combineTwo ( range , tokens . shift ( ) ) } , put ) ;
499512
500513 } 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 .' ) ;
502515 return parseStatement ( ) ;
503516 }
504517 else throw new CompilerError ( at ( ) . range , `Unexpected expression: '${ at ( ) . value } '` ) ;
0 commit comments