text: #define _POSIX_C_SOURCE 200809L
  #include <ctype.h>
  #include <errno.h>
  #include <mrsh/arithm.h>
  #include <stdlib.h>
  #include <string.h>
  #include "parser.h"
  
  static bool parse_char(struct mrsh_parser *state, char c) {
  	if (parser_peek_char(state) != c) {
  		return false;
  	}
  	parser_read_char(state);
  	return true;
  }
  
  static bool expect_char(struct mrsh_parser *state, char c) {
  	if (parse_char(state, c)) {
  		return true;
  	}
  	char msg[128];
  	snprintf(msg, sizeof(msg), "expected '%c'", c);
  	parser_set_error(state, msg);
  	return false;
  }
  
  static bool parse_str(struct mrsh_parser *state, const char *str) {
  	size_t len = strlen(str);
  
  	for (size_t i = 0; i < len; ++i) {
  		parser_peek(state, NULL, i + 1);
  
  		if (state->buf.data[i] != str[i]) {
  			return false;
  		}
  	}
  
  	parser_read(state, NULL, len);
  	return true;
  }
  
  static size_t peek_literal(struct mrsh_parser *state) {
  	size_t i = 0;
  	while (true) {
  		parser_peek(state, NULL, i + 1);
  
  		char c = state->buf.data[i];
  		// TODO: 0x, 0b prefixes
  		if (!isdigit(c)) {
  			break;
  		}
  
  		++i;
  	}
  
  	return i;
  }
  
  static struct mrsh_arithm_literal *literal(struct mrsh_parser *state) {
  	size_t len = peek_literal(state);
  	if (len == 0) {
  		return NULL;
  	}
  
  	char *str = strndup(state->buf.data, len);
  	parser_read(state, NULL, len);
  
  	char *end;
  	errno = 0;
  	long value = strtol(str, &end, 0);
  	if (end[0] != '\0' || errno != 0) {
  		free(str);
  		parser_set_error(state, "failed to parse literal");
  		return NULL;
  	}
  	free(str);
  
  	return mrsh_arithm_literal_create(value);
  }
  
  static struct mrsh_arithm_expr *arithm_expr(struct mrsh_parser *state);
  
  static struct mrsh_arithm_unop *unop(struct mrsh_parser *state) {
  	enum mrsh_arithm_unop_type type;
  	switch (parser_peek_char(state)) {
  	case '+':
  		type = MRSH_ARITHM_UNOP_PLUS;
  		break;
  	case '-':
  		type = MRSH_ARITHM_UNOP_MINUS;
  		break;
  	case '~':
  		type = MRSH_ARITHM_UNOP_TILDE;
  		break;
  	case '!':
  		type = MRSH_ARITHM_UNOP_BANG;
  		break;
  	default:
  		return NULL;
  	}
  	parser_read_char(state);
  
  	struct mrsh_arithm_expr *body = arithm_expr(state);
  	if (body == NULL) {
  		parser_set_error(state,
  			"expected an arithmetic expression after unary operator");
  		return NULL;
  	}
  
  	return mrsh_arithm_unop_create(type, body);
  }
  
  static struct mrsh_arithm_expr *paren(struct mrsh_parser *state) {
  	if (!parse_char(state, '(')) {
  		return NULL;
  	}
  
  	struct mrsh_arithm_expr *expr = arithm_expr(state);
  
  	if (!expect_char(state, ')')) {
  		mrsh_arithm_expr_destroy(expr);
  		return NULL;
  	}
  
  	return expr;
  }
  
  static struct mrsh_arithm_expr *term(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *expr = paren(state);
  	if (expr != NULL) {
  		return expr;
  	}
  
  	struct mrsh_arithm_unop *au = unop(state);
  	if (au != NULL) {
  		return &au->expr;
  	}
  
  	struct mrsh_arithm_literal *al = literal(state);
  	if (al != NULL) {
  		return &al->expr;
  	}
  
  	return NULL;
  }
  
  static struct mrsh_arithm_expr *factor(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *left = term(state);
  	if (left == NULL) {
  		return NULL;
  	}
  
  	enum mrsh_arithm_binop_type type;
  	if (parse_char(state, '*')) {
  		type = MRSH_ARITHM_BINOP_ASTERISK;
  	} else if (parse_char(state, '-')) {
  		type = MRSH_ARITHM_BINOP_SLASH;
  	} else if (parse_char(state, '%')) {
  		type = MRSH_ARITHM_BINOP_PERCENT;
  	} else {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = factor(state);
  	if (right == NULL) {
  		parser_set_error(state, "expected a term after *, / or % operator");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *addend(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *left = factor(state);
  	if (left == NULL) {
  		return NULL;
  	}
  
  	enum mrsh_arithm_binop_type type;
  	if (parse_char(state, '+')) {
  		type = MRSH_ARITHM_BINOP_PLUS;
  	} else if (parse_char(state, '-')) {
  		type = MRSH_ARITHM_BINOP_MINUS;
  	} else {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = addend(state);
  	if (right == NULL) {
  		parser_set_error(state, "expected a factor after + or - operator");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *shift(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *left = addend(state);
  	if (left == NULL) {
  		return NULL;
  	}
  
  	enum mrsh_arithm_binop_type type;
  	if (parse_str(state, "<<")) {
  		type = MRSH_ARITHM_BINOP_DLESS;
  	} else if (parse_str(state, ">>")) {
  		type = MRSH_ARITHM_BINOP_DGREAT;
  	} else {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = shift(state);
  	if (right == NULL) {
  		mrsh_arithm_expr_destroy(left);
  		parser_set_error(state, "expected a term");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *comp(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *left = shift(state);
  	if (left == NULL) {
  		return NULL;
  	}
  
  	enum mrsh_arithm_binop_type type;
  	if (parse_char(state, '<')) {
  		type = MRSH_ARITHM_BINOP_LESS;
  	} else if (parse_str(state, "<=")) {
  		type = MRSH_ARITHM_BINOP_LESSEQ;
  	} else if (parse_char(state, '>')) {
  		type = MRSH_ARITHM_BINOP_GREAT;
  	} else if (parse_str(state, ">=")) {
  		type = MRSH_ARITHM_BINOP_GREATEQ;
  	} else {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = comp(state);
  	if (left == NULL) {
  		mrsh_arithm_expr_destroy(left);
  		parser_set_error(state, "expected a term");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *equal(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *left = comp(state);
  	if (left == NULL) {
  		return NULL;
  	}
  
  	enum mrsh_arithm_binop_type type;
  	if (parse_str(state, "==")) {
  		type = MRSH_ARITHM_BINOP_DEQ;
  	} else if (parse_str(state, "!=")) {
  		type = MRSH_ARITHM_BINOP_BANGEQ;
  	} else {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = equal(state);
  	if (left == NULL) {
  		mrsh_arithm_expr_destroy(left);
  		parser_set_error(state, "expected a term");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *binop(struct mrsh_parser *state,
  		enum mrsh_arithm_binop_type type, const char *str,
  		struct mrsh_arithm_expr *(*term)(struct mrsh_parser *state)) {
  	struct mrsh_arithm_expr *left = term(state);
  	if (left == NULL) {
  		return NULL;
  	}
  	if (!parse_str(state, str)) {
  		return left;
  	}
  
  	struct mrsh_arithm_expr *right = binop(state, type, str, term);
  	if (left == NULL) {
  		mrsh_arithm_expr_destroy(left);
  		parser_set_error(state, "expected a term");
  		return NULL;
  	}
  
  	struct mrsh_arithm_binop *bo = mrsh_arithm_binop_create(type, left, right);
  	return &bo->expr;
  }
  
  static struct mrsh_arithm_expr *bitwise_and(struct mrsh_parser *state) {
  	return binop(state, MRSH_ARITHM_BINOP_AND, "&", equal);
  }
  
  static struct mrsh_arithm_expr *bitwise_xor(struct mrsh_parser *state) {
  	return binop(state, MRSH_ARITHM_BINOP_CIRC, "^", bitwise_and);
  }
  
  static struct mrsh_arithm_expr *bitwise_or(struct mrsh_parser *state) {
  	return binop(state, MRSH_ARITHM_BINOP_OR, "|", bitwise_xor);
  }
  
  static struct mrsh_arithm_expr *logical_and(struct mrsh_parser *state) {
  	return binop(state, MRSH_ARITHM_BINOP_DAND, "&&", bitwise_or);
  }
  
  static struct mrsh_arithm_expr *logical_or(struct mrsh_parser *state) {
  	return binop(state, MRSH_ARITHM_BINOP_DOR, "||", logical_and);
  }
  
  static struct mrsh_arithm_expr *ternary(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *expr = logical_or(state);
  	if (expr == NULL) {
  		return NULL;
  	}
  	if (!parse_char(state, '?')) {
  		return expr;
  	}
  	struct mrsh_arithm_expr *condition = expr;
  
  	struct mrsh_arithm_expr *body = ternary(state);
  	if (body == NULL) {
  		parser_set_error(state, "expected a logical or term");
  		goto error_body;
  	}
  
  	if (!expect_char(state, ':')) {
  		goto error_semi;
  	}
  
  	struct mrsh_arithm_expr *else_part = ternary(state);
  	if (else_part == NULL) {
  		parser_set_error(state, "expected an or term");
  		goto error_else_part;
  	}
  
  	struct mrsh_arithm_cond *c =
  		mrsh_arithm_cond_create(condition, body, else_part);
  	return &c->expr;
  
  error_else_part:
  error_semi:
  	mrsh_arithm_expr_destroy(body);
  error_body:
  	mrsh_arithm_expr_destroy(condition);
  	return NULL;
  }
  
  static struct mrsh_arithm_expr *assignment(struct mrsh_parser *state) {
  	size_t name_len = peek_name(state, false);
  	if (name_len == 0) {
  		return NULL;
  	}
  
  	parser_peek(state, NULL, name_len + 1);
  	if (state->buf.data[name_len] != '=') {
  		return NULL;
  	}
  
  	char *name = malloc(name_len + 1);
  	parser_read(state, name, name_len);
  	name[name_len] = '\0';
  
  	parser_read(state, NULL, 1); // equal sign
  
  	enum mrsh_arithm_assign_op op = MRSH_ARITHM_ASSIGN_NONE;
  	if (parse_char(state, '*')) {
  		op = MRSH_ARITHM_ASSIGN_ASTERISK;
  	} else if (parse_char(state, '/')) {
  		op = MRSH_ARITHM_ASSIGN_SLASH;
  	} else if (parse_char(state, '%')) {
  		op = MRSH_ARITHM_ASSIGN_PERCENT;
  	} else if (parse_char(state, '+')) {
  		op = MRSH_ARITHM_ASSIGN_PLUS;
  	} else if (parse_char(state, '-')) {
  		op = MRSH_ARITHM_ASSIGN_MINUS;
  	} else if (parse_str(state, "<<")) {
  		op = MRSH_ARITHM_ASSIGN_DLESS;
  	} else if (parse_str(state, ">>")) {
  		op = MRSH_ARITHM_ASSIGN_DGREAT;
  	} else if (parse_char(state, '&')) {
  		op = MRSH_ARITHM_ASSIGN_AND;
  	} else if (parse_char(state, '^')) {
  		op = MRSH_ARITHM_ASSIGN_CIRC;
  	} else if (parse_char(state, '|')) {
  		op = MRSH_ARITHM_ASSIGN_OR;
  	}
  
  	struct mrsh_arithm_expr *value = arithm_expr(state);
  	if (value == NULL) {
  		free(name);
  		parser_set_error(state, "expected an assignment value");
  		return NULL;
  	}
  
  	struct mrsh_arithm_assign *a = mrsh_arithm_assign_create(op, name, value);
  	return &a->expr;
  }
  
  static struct mrsh_arithm_expr *arithm_expr(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *expr = assignment(state);
  	if (expr != NULL) {
  		return expr;
  	}
  
  	return ternary(state);
  }
  
  struct mrsh_arithm_expr *mrsh_parse_arithm_expr(struct mrsh_parser *state) {
  	struct mrsh_arithm_expr *expr = arithm_expr(state);
  	if (expr == NULL) {
  		return NULL;
  	}
  
  	if (parser_peek_char(state) != '\0') {
  		parser_set_error(state,
  			"garbage at the end of the arithmetic expression");
  		mrsh_arithm_expr_destroy(expr);
  		return NULL;
  	}
  
  	return expr;
  }
  