Skip to content

Commit 1e2d30d

Browse files
committed
Add line numbers to errors
1 parent 09ef821 commit 1e2d30d

File tree

2 files changed

+100
-59
lines changed

2 files changed

+100
-59
lines changed

src/parser.hpp

+35-41
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ class Parser {
110110
{
111111
}
112112

113+
void error_expected(const std::string& msg) const
114+
{
115+
std::cerr << "[Parse Error] Expected " << msg << " on line " << peek(-1).value().line << std::endl;
116+
exit(EXIT_FAILURE);
117+
}
118+
113119
std::optional<NodeTerm*> parse_term() // NOLINT(*-no-recursion)
114120
{
115121
if (auto int_lit = try_consume(TokenType::int_lit)) {
@@ -122,13 +128,12 @@ class Parser {
122128
auto term = m_allocator.emplace<NodeTerm>(expr_ident);
123129
return term;
124130
}
125-
if (auto open_paren = try_consume(TokenType::open_paren)) {
131+
if (const auto open_paren = try_consume(TokenType::open_paren)) {
126132
auto expr = parse_expr();
127133
if (!expr.has_value()) {
128-
std::cerr << "Expected expression" << std::endl;
129-
exit(EXIT_FAILURE);
134+
error_expected("expression");
130135
}
131-
try_consume(TokenType::close_paren, "Expected `)`");
136+
try_consume_err(TokenType::close_paren);
132137
auto term_paren = m_allocator.emplace<NodeTermParen>(expr.value());
133138
auto term = m_allocator.emplace<NodeTerm>(term_paren);
134139
return term;
@@ -156,12 +161,11 @@ class Parser {
156161
else {
157162
break;
158163
}
159-
const auto [type, value] = consume();
164+
const auto [type, line, value] = consume();
160165
const int next_min_prec = prec.value() + 1;
161166
auto expr_rhs = parse_expr(next_min_prec);
162167
if (!expr_rhs.has_value()) {
163-
std::cerr << "Unable to parse expression" << std::endl;
164-
exit(EXIT_FAILURE);
168+
error_expected("expression");
165169
}
166170
auto expr = m_allocator.emplace<NodeBinExpr>();
167171
auto expr_lhs2 = m_allocator.emplace<NodeExpr>();
@@ -202,29 +206,27 @@ class Parser {
202206
while (auto stmt = parse_stmt()) {
203207
scope->stmts.push_back(stmt.value());
204208
}
205-
try_consume(TokenType::close_curly, "Expected `}`");
209+
try_consume_err(TokenType::close_curly);
206210
return scope;
207211
}
208212

209213
std::optional<NodeIfPred*> parse_if_pred() // NOLINT(*-no-recursion)
210214
{
211215
if (try_consume(TokenType::elif)) {
212-
try_consume(TokenType::open_paren, "Expected `(`");
216+
try_consume_err(TokenType::open_paren);
213217
const auto elif = m_allocator.alloc<NodeIfPredElif>();
214218
if (const auto expr = parse_expr()) {
215219
elif->expr = expr.value();
216220
}
217221
else {
218-
std::cerr << "Expected expression" << std::endl;
219-
exit(EXIT_FAILURE);
222+
error_expected("expression");
220223
}
221-
try_consume(TokenType::close_paren, "Expected `)`");
224+
try_consume_err(TokenType::close_paren);
222225
if (const auto scope = parse_scope()) {
223226
elif->scope = scope.value();
224227
}
225228
else {
226-
std::cerr << "Expected scope" << std::endl;
227-
exit(EXIT_FAILURE);
229+
error_expected("scope");
228230
}
229231
elif->pred = parse_if_pred();
230232
auto pred = m_allocator.emplace<NodeIfPred>(elif);
@@ -236,8 +238,7 @@ class Parser {
236238
else_->scope = scope.value();
237239
}
238240
else {
239-
std::cerr << "Expected scope" << std::endl;
240-
exit(EXIT_FAILURE);
241+
error_expected("scope");
241242
}
242243
auto pred = m_allocator.emplace<NodeIfPred>(else_);
243244
return pred;
@@ -247,7 +248,7 @@ class Parser {
247248

248249
std::optional<NodeStmt*> parse_stmt() // NOLINT(*-no-recursion)
249250
{
250-
if (peek().value().type == TokenType::exit && peek(1).has_value()
251+
if (peek().has_value() && peek().value().type == TokenType::exit && peek(1).has_value()
251252
&& peek(1).value().type == TokenType::open_paren) {
252253
consume();
253254
consume();
@@ -256,11 +257,10 @@ class Parser {
256257
stmt_exit->expr = node_expr.value();
257258
}
258259
else {
259-
std::cerr << "Invalid expression" << std::endl;
260-
exit(EXIT_FAILURE);
260+
error_expected("expression");
261261
}
262-
try_consume(TokenType::close_paren, "Expected `)`");
263-
try_consume(TokenType::semi, "Expected `;`");
262+
try_consume_err(TokenType::close_paren);
263+
try_consume_err(TokenType::semi);
264264
auto stmt = m_allocator.emplace<NodeStmt>();
265265
stmt->var = stmt_exit;
266266
return stmt;
@@ -276,10 +276,9 @@ class Parser {
276276
stmt_let->expr = expr.value();
277277
}
278278
else {
279-
std::cerr << "Invalid expression" << std::endl;
280-
exit(EXIT_FAILURE);
279+
error_expected("expression");
281280
}
282-
try_consume(TokenType::semi, "Expected `;`");
281+
try_consume_err(TokenType::semi);
283282
auto stmt = m_allocator.emplace<NodeStmt>();
284283
stmt->var = stmt_let;
285284
return stmt;
@@ -293,10 +292,9 @@ class Parser {
293292
assign->expr = expr.value();
294293
}
295294
else {
296-
std::cerr << "Expected expression" << std::endl;
297-
exit(EXIT_FAILURE);
295+
error_expected("expression");
298296
}
299-
try_consume(TokenType::semi, "Expected `;`");
297+
try_consume_err(TokenType::semi);
300298
auto stmt = m_allocator.emplace<NodeStmt>(assign);
301299
return stmt;
302300
}
@@ -305,26 +303,23 @@ class Parser {
305303
auto stmt = m_allocator.emplace<NodeStmt>(scope.value());
306304
return stmt;
307305
}
308-
std::cerr << "Invalid scope" << std::endl;
309-
exit(EXIT_FAILURE);
306+
error_expected("scope");
310307
}
311308
if (auto if_ = try_consume(TokenType::if_)) {
312-
try_consume(TokenType::open_paren, "Expected `(`");
309+
try_consume_err(TokenType::open_paren);
313310
auto stmt_if = m_allocator.emplace<NodeStmtIf>();
314311
if (const auto expr = parse_expr()) {
315312
stmt_if->expr = expr.value();
316313
}
317314
else {
318-
std::cerr << "Invalid expression" << std::endl;
319-
exit(EXIT_FAILURE);
315+
error_expected("expression");
320316
}
321-
try_consume(TokenType::close_paren, "Expected `)`");
317+
try_consume_err(TokenType::close_paren);
322318
if (const auto scope = parse_scope()) {
323319
stmt_if->scope = scope.value();
324320
}
325321
else {
326-
std::cerr << "Invalid scope" << std::endl;
327-
exit(EXIT_FAILURE);
322+
error_expected("scope");
328323
}
329324
stmt_if->pred = parse_if_pred();
330325
auto stmt = m_allocator.emplace<NodeStmt>(stmt_if);
@@ -341,15 +336,14 @@ class Parser {
341336
prog.stmts.push_back(stmt.value());
342337
}
343338
else {
344-
std::cerr << "Invalid statement" << std::endl;
345-
exit(EXIT_FAILURE);
339+
error_expected("statement");
346340
}
347341
}
348342
return prog;
349343
}
350344

351345
private:
352-
[[nodiscard]] std::optional<Token> peek(const size_t offset = 0) const
346+
[[nodiscard]] std::optional<Token> peek(const int offset = 0) const
353347
{
354348
if (m_index + offset >= m_tokens.size()) {
355349
return {};
@@ -362,13 +356,13 @@ class Parser {
362356
return m_tokens.at(m_index++);
363357
}
364358

365-
Token try_consume(const TokenType type, const std::string& err_msg)
359+
Token try_consume_err(const TokenType type)
366360
{
367361
if (peek().has_value() && peek().value().type == type) {
368362
return consume();
369363
}
370-
std::cerr << err_msg << std::endl;
371-
exit(EXIT_FAILURE);
364+
error_expected(to_string(type));
365+
return {};
372366
}
373367

374368
std::optional<Token> try_consume(const TokenType type)

src/tokenization.hpp

+65-18
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,47 @@ enum class TokenType {
2323
else_,
2424
};
2525

26+
inline std::string to_string(const TokenType type)
27+
{
28+
switch (type) {
29+
case TokenType::exit:
30+
return "`exit`";
31+
case TokenType::int_lit:
32+
return "int literal";
33+
case TokenType::semi:
34+
return "`;`";
35+
case TokenType::open_paren:
36+
return "`(`";
37+
case TokenType::close_paren:
38+
return "`)`";
39+
case TokenType::ident:
40+
return "identifier";
41+
case TokenType::let:
42+
return "`let`";
43+
case TokenType::eq:
44+
return "`=`";
45+
case TokenType::plus:
46+
return "`+`";
47+
case TokenType::star:
48+
return "`*`";
49+
case TokenType::minus:
50+
return "`-`";
51+
case TokenType::fslash:
52+
return "`/`";
53+
case TokenType::open_curly:
54+
return "`{`";
55+
case TokenType::close_curly:
56+
return "`}`";
57+
case TokenType::if_:
58+
return "`if`";
59+
case TokenType::elif:
60+
return "`elif`";
61+
case TokenType::else_:
62+
return "`else`";
63+
}
64+
assert(false);
65+
}
66+
2667
inline std::optional<int> bin_prec(const TokenType type)
2768
{
2869
switch (type) {
@@ -39,6 +80,7 @@ inline std::optional<int> bin_prec(const TokenType type)
3980

4081
struct Token {
4182
TokenType type;
83+
int line;
4284
std::optional<std::string> value {};
4385
};
4486

@@ -53,34 +95,35 @@ class Tokenizer {
5395
{
5496
std::vector<Token> tokens;
5597
std::string buf;
98+
int line_count = 1;
5699
while (peek().has_value()) {
57100
if (std::isalpha(peek().value())) {
58101
buf.push_back(consume());
59102
while (peek().has_value() && std::isalnum(peek().value())) {
60103
buf.push_back(consume());
61104
}
62105
if (buf == "exit") {
63-
tokens.push_back({ .type = TokenType::exit });
106+
tokens.push_back({ TokenType::exit, line_count });
64107
buf.clear();
65108
}
66109
else if (buf == "let") {
67-
tokens.push_back({ .type = TokenType::let });
110+
tokens.push_back({ TokenType::let, line_count });
68111
buf.clear();
69112
}
70113
else if (buf == "if") {
71-
tokens.push_back({ .type = TokenType::if_ });
114+
tokens.push_back({ TokenType::if_, line_count });
72115
buf.clear();
73116
}
74117
else if (buf == "elif") {
75-
tokens.push_back({ .type = TokenType::elif });
118+
tokens.push_back({ TokenType::elif, line_count });
76119
buf.clear();
77120
}
78121
else if (buf == "else") {
79-
tokens.push_back({ .type = TokenType::else_ });
122+
tokens.push_back({ TokenType::else_, line_count });
80123
buf.clear();
81124
}
82125
else {
83-
tokens.push_back({ .type = TokenType::ident, .value = buf });
126+
tokens.push_back({ TokenType::ident, line_count, buf });
84127
buf.clear();
85128
}
86129
}
@@ -89,7 +132,7 @@ class Tokenizer {
89132
while (peek().has_value() && std::isdigit(peek().value())) {
90133
buf.push_back(consume());
91134
}
92-
tokens.push_back({ .type = TokenType::int_lit, .value = buf });
135+
tokens.push_back({ TokenType::int_lit, line_count, buf });
93136
buf.clear();
94137
}
95138
else if (peek().value() == '/' && peek(1).has_value() && peek(1).value() == '/') {
@@ -117,49 +160,53 @@ class Tokenizer {
117160
}
118161
else if (peek().value() == '(') {
119162
consume();
120-
tokens.push_back({ .type = TokenType::open_paren });
163+
tokens.push_back({ TokenType::open_paren, line_count });
121164
}
122165
else if (peek().value() == ')') {
123166
consume();
124-
tokens.push_back({ .type = TokenType::close_paren });
167+
tokens.push_back({ TokenType::close_paren, line_count });
125168
}
126169
else if (peek().value() == ';') {
127170
consume();
128-
tokens.push_back({ .type = TokenType::semi });
171+
tokens.push_back({ TokenType::semi, line_count });
129172
}
130173
else if (peek().value() == '=') {
131174
consume();
132-
tokens.push_back({ .type = TokenType::eq });
175+
tokens.push_back({ TokenType::eq, line_count });
133176
}
134177
else if (peek().value() == '+') {
135178
consume();
136-
tokens.push_back({ .type = TokenType::plus });
179+
tokens.push_back({ TokenType::plus, line_count });
137180
}
138181
else if (peek().value() == '*') {
139182
consume();
140-
tokens.push_back({ .type = TokenType::star });
183+
tokens.push_back({ TokenType::star, line_count });
141184
}
142185
else if (peek().value() == '-') {
143186
consume();
144-
tokens.push_back({ .type = TokenType::minus });
187+
tokens.push_back({ TokenType::minus, line_count });
145188
}
146189
else if (peek().value() == '/') {
147190
consume();
148-
tokens.push_back({ .type = TokenType::fslash });
191+
tokens.push_back({ TokenType::fslash, line_count });
149192
}
150193
else if (peek().value() == '{') {
151194
consume();
152-
tokens.push_back({ .type = TokenType::open_curly });
195+
tokens.push_back({ TokenType::open_curly, line_count });
153196
}
154197
else if (peek().value() == '}') {
155198
consume();
156-
tokens.push_back({ .type = TokenType::close_curly });
199+
tokens.push_back({ TokenType::close_curly, line_count });
200+
}
201+
else if (peek().value() == '\n') {
202+
consume();
203+
line_count++;
157204
}
158205
else if (std::isspace(peek().value())) {
159206
consume();
160207
}
161208
else {
162-
std::cerr << "You messed up!" << std::endl;
209+
std::cerr << "Invalid token" << std::endl;
163210
exit(EXIT_FAILURE);
164211
}
165212
}

0 commit comments

Comments
 (0)