Skip to content

Commit ad51f4f

Browse files
committed
change error messages
1 parent 0b5f846 commit ad51f4f

File tree

2 files changed

+54
-41
lines changed

2 files changed

+54
-41
lines changed

src/ast.ts

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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}'`);

src/lexer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function tokenizeSyx(source: string): Token[] {
8181

8282
while (src.length > 0) {
8383
if (src[0] === '/' && src[1] === '/') {
84-
while (src.length > 0 && src[0] !== '\n' as string) {
84+
while (src.length > 0 && src[0] as string !== '\n' ) {
8585
src.shift();
8686
}
8787
}
@@ -100,7 +100,7 @@ export function tokenizeSyx(source: string): Token[] {
100100
else if (src[0] === '|') tokens.push({ type: TokenType.VarSeperator, value: src.shift(), range: opr(curLine, curPos++) });
101101
else if (src[0] === '+' && chars.includes(src[1])) {
102102
if (src[1] === 's') tokens.push({ type: TokenType.WhitespaceIdentifier, value: '+s', range: tpr(pos(curLine, curPos), pos(curLine, curPos + 2)) });
103-
else throw new CompilerError(opr(curLine,curPos), `Unexpected identifier: '${src[1]}'`);
103+
else throw new CompilerError(opr(curLine,curPos), `Unexpected regex identifier after '+': ${src[1]}`);
104104
curPos += 2;
105105
src.shift(); src.shift();
106106
} else if (isInt(src[0])) {

0 commit comments

Comments
 (0)