Skip to content

Commit

Permalink
Merge pull request #174 from KennyOliver/issue-173
Browse files Browse the repository at this point in the history
Issue 173: (Parser Error) Expected `:` as `TOKEN_DELIMITER` but Lexer Classifies it as `TOKEN_OPERATOR` in `check` Statements
  • Loading branch information
KennyOliver authored Jan 3, 2025
2 parents 5dfcecb + e404600 commit 5244e27
Show file tree
Hide file tree
Showing 5 changed files with 38 additions and 101 deletions.
18 changes: 13 additions & 5 deletions src/lexer/scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ void scan_operator(ScannerState *state, Token **tokens, size_t *token_count,
char third_char =
state->pos < state->length - 2 ? state->source[state->pos + 2] : '\0';

if (first_char == ':') {
append_token(tokens, token_count, capacity, TOKEN_COLON, ":",
state->line);
state->pos++;
return;
}

// Check for two-character & three-character operators
if (second_char != '\0') {
if ((first_char == '=' && second_char == '=') || // ==
Expand All @@ -159,15 +166,16 @@ void scan_operator(ScannerState *state, Token **tokens, size_t *token_count,
}

// Handle single-character operators
if (strchr("=<>!+-*/%.?:", first_char)) {
char *lexeme = strndup(&state->source[state->pos], 1);
if (strchr("=<>!+-*/%.?", first_char)) {
char lexeme[2] = {first_char, '\0'};
append_token(tokens, token_count, capacity, TOKEN_OPERATOR, lexeme,
state->line);
free(lexeme);
state->pos++;
} else {
fprintf(stderr, "Error: Unknown operator or invalid character `%d`\n",
state->line);
fprintf(
stderr,
"Error: Unknown operator or invalid character `%c` on line %d\n",
first_char, state->line);
exit(1);
}
}
110 changes: 20 additions & 90 deletions src/parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ ASTNode *parse_case_body(ParserState *state) {
while (1) {
Token *current = get_current_token(state);

// Stop parsing the body if we encounter `is`, `else`, or `}`
// Stop parsing the body if any of `is`, `else`, or `}` get encountered
if ((current->type == TOKEN_KEYWORD &&
(strcmp(current->lexeme, "is") == 0 ||
strcmp(current->lexeme, "else") == 0)) ||
Expand Down Expand Up @@ -593,6 +593,9 @@ ASTNode *parse_switch_block(ParserState *state) {
}

node->type = AST_SWITCH;
node->switch_case.expression = NULL;
node->switch_case.cases = NULL;
node->next = NULL;

// Handle `check` keyword
expect_token(state, TOKEN_KEYWORD, "Expected `check` keyword");
Expand All @@ -605,7 +608,6 @@ ASTNode *parse_switch_block(ParserState *state) {
"Expected `{` after check expression");

// Initialize cases list
node->switch_case.cases = NULL;
ASTCaseNode *last_case = NULL;

while (1) {
Expand All @@ -624,116 +626,44 @@ ASTNode *parse_switch_block(ParserState *state) {

// Parse condition expression
ASTNode *condition = parse_expression(state);
expect_token(state, TOKEN_DELIMITER,
"Expected `:` after case value");
expect_token(state, TOKEN_COLON, "Expected `:` after case value");

// Collect all consecutive `is` clauses
// allowing multiple `is` conditions to share the same body
ASTCaseNode *temp_head = NULL;
ASTCaseNode *temp_tail = NULL;

// Add the first condition
// Create a new case node
ASTCaseNode *case_node = malloc(sizeof(ASTCaseNode));
if (!case_node) {
parser_error("Memory allocation failed", current);
parser_error("Memory allocation failed for ASTCaseNode",
current);
}
case_node->condition = condition;
case_node->body = NULL; // To be assigned after parsing the body
case_node->body = parse_case_body(state); // Parse unique body
case_node->next = NULL;

temp_head = temp_tail = case_node;

// Check for additional `is` clauses
while (1) {
Token *next = get_current_token(state);
if (next->type == TOKEN_KEYWORD &&
strcmp(next->lexeme, "is") == 0) {
advance_token(state); // consume `is`

// Parse next condition
ASTNode *next_condition = parse_expression(state);
expect_token(state, TOKEN_DELIMITER,
"Expected `:` after case value");

// Create new case node
ASTCaseNode *new_case = malloc(sizeof(ASTCaseNode));
if (!new_case) {
parser_error("Memory allocation failed", next);
}
new_case->condition = next_condition;
new_case->body = NULL;
new_case->next = NULL;

// Add to temporary list
temp_tail->next = new_case;
temp_tail = new_case;
} else {
break; // No more `is` clauses
}
}

// Now, parse the shared body
ASTNode *body = NULL;
Token *body_start = get_current_token(state);
if (body_start->type == TOKEN_BRACE_OPEN) {
advance_token(state); // Consume `{`
body = parse_block(state);
expect_token(state, TOKEN_BRACE_CLOSE,
"Expected `}` after case body");
// Append to the cases list
if (!node->switch_case.cases) {
node->switch_case.cases = case_node;
} else {
// Parse multiple statements as the shared body
body = parse_case_body(state);
// No need to expect a delimiter here since parse_case_body
// stops appropriately
}

// Assign the same body to all collected `is` cases
ASTCaseNode *temp_case = temp_head;
while (temp_case) {
temp_case->body = body;
// Link to the main cases list
if (!node->switch_case.cases) {
node->switch_case.cases = temp_case;
} else {
last_case->next = temp_case;
}
last_case = temp_case;
temp_case = temp_case->next;
last_case->next = case_node;
}
last_case = case_node;
}
// Handle `else` clause
else if (current->type == TOKEN_KEYWORD &&
strcmp(current->lexeme, "else") == 0) {
advance_token(state); // consume `else`

// Parse `:` delimiter
expect_token(state, TOKEN_DELIMITER, "Expected `:` after `else`");

// Parse the body of `else`
ASTNode *body = NULL;
Token *body_start = get_current_token(state);
if (body_start->type == TOKEN_BRACE_OPEN) {
advance_token(state); // consume `{`
body = parse_block(state);
expect_token(state, TOKEN_BRACE_CLOSE,
"Expected `}` after else body");
} else {
// Parse multiple statements as the body
body = parse_case_body(state);
// No need to expect a delimiter here since `parse_case_body`
// stops appropriately
}
expect_token(state, TOKEN_COLON, "Expected `:` after `else`");

// Create the default case node
ASTCaseNode *default_case = malloc(sizeof(ASTCaseNode));
if (!default_case) {
parser_error("Memory allocation failed", current);
parser_error("Memory allocation failed for default ASTCaseNode",
current);
}
default_case->condition = NULL; // no condition for `else`
default_case->body = body;
default_case->condition = NULL; // No condition for `else`
default_case->body = parse_case_body(state); // Parse unique body
default_case->next = NULL;

// Add to cases list
// Append to the cases list
if (!node->switch_case.cases) {
node->switch_case.cases = default_case;
} else {
Expand Down
8 changes: 3 additions & 5 deletions src/parser/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ void free_ast(ASTNode *node) {

case AST_UNARY_OP:
free(node->unary_op.operator);
free(node->unary_op.operand);
free_ast(node->unary_op.operand);
break;

case AST_BINARY_OP:
Expand Down Expand Up @@ -54,10 +54,8 @@ void free_ast(ASTNode *node) {

case AST_FUNCTION_DECLARATION:
case AST_FUNCTION_CALL:
free(node->function_call.parameters);
free(node->function_call.arguments);
free(node->function_call.body);
free(node->function_call.return_data);
free(node->function_call.name);
free_ast(node->function_call.arguments);
break;

case AST_BREAK:
Expand Down
1 change: 1 addition & 0 deletions src/shared/token_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ typedef enum {
TOKEN_PAREN_CLOSE,
TOKEN_BRACE_OPEN,
TOKEN_BRACE_CLOSE,
TOKEN_COLON,
TOKEN_EOF
} TokenType;

Expand Down
2 changes: 1 addition & 1 deletion src/tests/8_switch_case.flv
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ check dessert {
break;
else:
serve("Dessert not on the menu.");
}
}

0 comments on commit 5244e27

Please sign in to comment.