aoc-2022/day_5/parser.y

177 lines
5.3 KiB
Text

/* Require bison minimal version */
%require "3.2"
/* write out a header file containing the token defines */
%header
// Code for the header file generated by bison
%code requires {
typedef void* yyscan_t;
#define NUM_PILES 9
// a stack of crates, implemented as linked list (b/c it makes movement easier)
struct stack_item {
struct stack_item* next;
char crate_id;
};
struct parser_state {
// points to the topmost item in the stack
struct stack_item** stacks;
unsigned stack_idx;
};
}
// Code for the c file
%{
#include <stdio.h>
#include <stdlib.h>
#include "parser.h"
#include "lexer.h"
void yyerror(struct parser_state* state, yyscan_t scanner, const char* msg) {
(void)state;
(void)scanner;
fprintf(stderr, "\033[93mSyntax Error: %s\033[0m\n", msg);
}
%}
// define a reentrant parser
%define api.pure
// enable parser tracing and detailed error messages (plus Lookahead Correction)
%define parse.trace
%define parse.error detailed
%define parse.lac full
%lex-param { yyscan_t scanner }
%parse-param { struct parser_state* state }
%parse-param { yyscan_t scanner }
%union {
unsigned long num;
char cval;
}
%start input
%token NEWLINE SPACE
%token CRATE_DELIM_OPEN CRATE_DELIM_CLOSE
%token <cval> CRATE_ID
%token <num> NUMBER
%token MOVE_OP ORIGIN DEST
%term END_OF_FILE
%%
input
: initialization NEWLINE movement END_OF_FILE { return 0; }
;
initialization
: line NEWLINE initialization
| line NEWLINE
;
line
: item SPACE line
| item
;
item
: CRATE_DELIM_OPEN CRATE_ID CRATE_DELIM_CLOSE {
// printf("Pushing %c onto stack #%lu\n", $2, state->stack_idx);
struct stack_item* it = calloc(1, sizeof(struct stack_item));
// TODO(feliix42): error handling on failed alloc
it->crate_id = $2;
// mind we build the stack top down, which means we have to traverse it when adding a new elem
if (!state->stacks[state->stack_idx]) {
state->stacks[state->stack_idx] = it;
} else {
struct stack_item* tail = state->stacks[state->stack_idx];
while (tail->next != NULL) {
tail = tail->next;
}
tail->next = it;
}
state->stack_idx = (state->stack_idx + 1) % NUM_PILES;
}
| SPACE NUMBER SPACE {
state->stack_idx = (state->stack_idx + 1) % NUM_PILES;
}
| SPACE SPACE SPACE {
state->stack_idx = (state->stack_idx + 1) % NUM_PILES;
}
;
movement
: expr NEWLINE movement
| expr NEWLINE
| NEWLINE
;
expr
: MOVE_OP NUMBER ORIGIN NUMBER DEST NUMBER {
// Source: $4, Dst: $6, repeat $2 times
// printf("move %lu from %lu to %lu", $2, $4, $6);
for (unsigned long i = 0; i < $2; i++) {
// get the current element
struct stack_item* it = state->stacks[$4 -1];
if (!it) {
// TODO(feliix42): use yyerror?
fprintf(stderr, "\033[91m[parser error] tried to pop from empty stack\n\033[0m");
break;
}
// delete old references
state->stacks[$4 -1] = it->next;
// insert on new stack
it->next = state->stacks[$6 -1];
state->stacks[$6 -1] = it;
}
}
;
%%
int main(void) {
struct parser_state parser_state;
parser_state.stack_idx = 0;
parser_state.stacks = calloc(NUM_PILES, sizeof(struct stack_item*));
if (!parser_state.stacks) {
fprintf(stderr, "\033[91m[error] Could not initialize parser state\033[0m\n");
return EXIT_FAILURE;
}
yyscan_t scanner;
if (yylex_init(&scanner)) {
fprintf(stderr, "\033[91m[error] Could not initialize lexer\033[0m\n");
return EXIT_FAILURE;
}
if (yyparse(&parser_state, scanner)) {
// TODO: Error handling https://www.gnu.org/software/bison/manual/html_node/Parser-Function.html
// error during parse occured
return EXIT_FAILURE;
}
yylex_destroy(scanner);
printf("Top Stack IDs: ");
struct stack_item* cur;
for (int i = 0; i < NUM_PILES; i++) {
cur = parser_state.stacks[i];
if (cur) {
printf("%c", cur->crate_id);
} else {
printf(" ");
}
}
printf("\n");
return 0;
}