328 lines
8.3 KiB
C
328 lines
8.3 KiB
C
#include "parser.h"
|
|
#include "lexer.h"
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
Expr* error_expr(Position pos, const char* message)
|
|
{
|
|
Expr* node = malloc(sizeof(Expr));
|
|
*node = (Expr) {
|
|
.type = ExprTypeError,
|
|
.error = (ErrorExpr) {
|
|
.pos = pos,
|
|
.message = strndup(message, strlen(message)),
|
|
},
|
|
};
|
|
return node;
|
|
}
|
|
|
|
Expr* int_expr(int64_t value)
|
|
{
|
|
Expr* node = malloc(sizeof(Expr));
|
|
*node = (Expr) {
|
|
.type = ExprTypeInt,
|
|
.int_expr = (IntExpr) {
|
|
.value = value,
|
|
},
|
|
};
|
|
return node;
|
|
}
|
|
|
|
Expr* unary_expr(UnaryExprType type, Expr* subject)
|
|
{
|
|
Expr* node = malloc(sizeof(Expr));
|
|
*node = (Expr) {
|
|
.type = ExprTypeUnary,
|
|
.unary = (UnaryExpr) {
|
|
.type = type,
|
|
.subject = subject,
|
|
},
|
|
};
|
|
return node;
|
|
}
|
|
|
|
Expr* binary_expr(BinaryExprType type, Expr* left, Expr* right)
|
|
{
|
|
Expr* node = malloc(sizeof(Expr));
|
|
*node = (Expr) {
|
|
.type = ExprTypeBinary,
|
|
.binary = (BinaryExpr) {
|
|
.type = type,
|
|
.left = left,
|
|
.right = right,
|
|
},
|
|
};
|
|
return node;
|
|
}
|
|
|
|
typedef struct {
|
|
const char* text;
|
|
size_t length;
|
|
Lexer* lexer;
|
|
Token current;
|
|
} Parser;
|
|
|
|
bool parser_done(Parser* parser)
|
|
{
|
|
return parser->current.type == TokenTypeEof;
|
|
}
|
|
|
|
bool parser_current_is(Parser* parser, TokenType type)
|
|
{
|
|
return !parser_done(parser) && parser->current.type == type;
|
|
}
|
|
|
|
void parser_step(Parser* parser)
|
|
{
|
|
parser->current = lexer_next(parser->lexer);
|
|
}
|
|
|
|
Position parser_pos(Parser* parser)
|
|
{
|
|
return (Position) {
|
|
.index = parser->current.index,
|
|
.line = parser->current.line,
|
|
.col = parser->current.col,
|
|
};
|
|
}
|
|
|
|
Expr* parser_expr(Parser* parser);
|
|
|
|
Expr* parser_unknown_token_error(Parser* parser)
|
|
{
|
|
char buffer[128] = { 0 };
|
|
snprintf(
|
|
buffer, 128, "unknown char '%c'", parser->text[parser->current.index]
|
|
);
|
|
return error_expr(parser_pos(parser), buffer);
|
|
}
|
|
|
|
Expr* parser_operand(Parser* parser)
|
|
{
|
|
if (parser_current_is(parser, TokenTypeInt)) {
|
|
char buffer[24];
|
|
strncpy(
|
|
buffer, &parser->text[parser->current.index], parser->current.length
|
|
);
|
|
parser_step(parser);
|
|
return int_expr(atol(buffer));
|
|
} else if (parser_current_is(parser, TokenTypeEof)) {
|
|
Position pos = parser_pos(parser);
|
|
parser_step(parser);
|
|
return error_expr(pos, "unexpected end-of-file");
|
|
} else {
|
|
Expr* error = parser_unknown_token_error(parser);
|
|
parser_step(parser);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
Expr* parser_group(Parser* parser)
|
|
{
|
|
if (parser_current_is(parser, TokenTypeLParen)) {
|
|
parser_step(parser);
|
|
Expr* value = parser_expr(parser);
|
|
if (!parser_current_is(parser, TokenTypeRParen)) {
|
|
parser_step(parser);
|
|
return error_expr(parser_pos(parser), "expected ')'");
|
|
}
|
|
parser_step(parser);
|
|
return value;
|
|
} else {
|
|
return parser_operand(parser);
|
|
}
|
|
}
|
|
|
|
Expr* parser_unary(Parser* parser)
|
|
{
|
|
if (parser_current_is(parser, TokenTypeMinus)) {
|
|
parser_step(parser);
|
|
return unary_expr(UnaryExprTypeNegate, parser_group(parser));
|
|
} else {
|
|
return parser_group(parser);
|
|
}
|
|
}
|
|
|
|
Expr* parser_multiply_divide(Parser* parser)
|
|
{
|
|
Expr* left = parser_unary(parser);
|
|
while (!parser_done(parser)) {
|
|
if (parser_current_is(parser, TokenTypeAsterisk)) {
|
|
parser_step(parser);
|
|
Expr* right = parser_unary(parser);
|
|
left = binary_expr(BinaryExprTypeMultiply, left, right);
|
|
} else if (parser_current_is(parser, TokenTypeSlash)) {
|
|
parser_step(parser);
|
|
Expr* right = parser_unary(parser);
|
|
left = binary_expr(BinaryExprTypeDivide, left, right);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return left;
|
|
}
|
|
|
|
Expr* parser_add_subtract(Parser* parser)
|
|
{
|
|
Expr* left = parser_multiply_divide(parser);
|
|
while (!parser_done(parser)) {
|
|
if (parser_current_is(parser, TokenTypePlus)) {
|
|
parser_step(parser);
|
|
Expr* right = parser_multiply_divide(parser);
|
|
left = binary_expr(BinaryExprTypeAdd, left, right);
|
|
} else if (parser_current_is(parser, TokenTypeMinus)) {
|
|
parser_step(parser);
|
|
Expr* right = parser_multiply_divide(parser);
|
|
left = binary_expr(BinaryExprTypeSubtract, left, right);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return left;
|
|
}
|
|
|
|
Expr* parser_expr(Parser* parser) { return parser_add_subtract(parser); }
|
|
|
|
Expr* parse(Lexer* lexer, const char* text, size_t length)
|
|
{
|
|
Parser parser = {
|
|
.text = text,
|
|
.length = length,
|
|
.lexer = lexer,
|
|
.current = lexer_next(lexer),
|
|
};
|
|
return parser_expr(&parser);
|
|
}
|
|
|
|
void free_expr(Expr* expr)
|
|
{
|
|
switch (expr->type) {
|
|
case ExprTypeError:
|
|
free(expr->error.message);
|
|
break;
|
|
case ExprTypeInt:
|
|
break;
|
|
case ExprTypeUnary:
|
|
free_expr(expr->unary.subject);
|
|
break;
|
|
case ExprTypeBinary:
|
|
free_expr(expr->binary.left);
|
|
free_expr(expr->binary.right);
|
|
break;
|
|
default:
|
|
fprintf(
|
|
stderr,
|
|
"panic: unexhausted value ./%s:%d %s()\n",
|
|
__FILE__,
|
|
__LINE__,
|
|
__func__
|
|
);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
const char* unary_expr_type_to_string(UnaryExprType type)
|
|
{
|
|
switch (type) {
|
|
case UnaryExprTypeNegate:
|
|
return "Negate";
|
|
default:
|
|
fprintf(
|
|
stderr,
|
|
"panic: unexhausted value ./%s:%d %s()\n",
|
|
__FILE__,
|
|
__LINE__,
|
|
__func__
|
|
);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
const char* binary_expr_type_to_string(BinaryExprType type)
|
|
{
|
|
switch (type) {
|
|
case BinaryExprTypeAdd:
|
|
return "Add";
|
|
case BinaryExprTypeSubtract:
|
|
return "Subtract";
|
|
case BinaryExprTypeMultiply:
|
|
return "Multiply";
|
|
case BinaryExprTypeDivide:
|
|
return "Divide";
|
|
default:
|
|
fprintf(
|
|
stderr,
|
|
"panic: unexhausted value ./%s:%d %s()\n",
|
|
__FILE__,
|
|
__LINE__,
|
|
__func__
|
|
);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
char* expr_to_string(const Expr* expr)
|
|
{
|
|
switch (expr->type) {
|
|
case ExprTypeError: {
|
|
char formattet[65536];
|
|
snprintf(
|
|
formattet,
|
|
65536,
|
|
"Error { [%ld] %d:%d \"%s\" }",
|
|
expr->error.pos.index,
|
|
expr->error.pos.line,
|
|
expr->error.pos.col,
|
|
expr->error.message
|
|
);
|
|
return strndup(formattet, 65536);
|
|
}
|
|
case ExprTypeInt: {
|
|
char formattet[65536];
|
|
snprintf(formattet, 65536, "Int(%ld)", expr->int_expr.value);
|
|
return strndup(formattet, 65536);
|
|
}
|
|
case ExprTypeUnary: {
|
|
char* subject = expr_to_string(expr->unary.subject);
|
|
char formattet[65536];
|
|
snprintf(
|
|
formattet,
|
|
65536,
|
|
"Unary { type: %s, subject: %s }",
|
|
unary_expr_type_to_string(expr->unary.type),
|
|
subject
|
|
);
|
|
free(subject);
|
|
return strndup(formattet, 65536);
|
|
}
|
|
case ExprTypeBinary: {
|
|
char* left = expr_to_string(expr->binary.left);
|
|
char* right = expr_to_string(expr->binary.right);
|
|
char formattet[65536];
|
|
snprintf(
|
|
formattet,
|
|
65536,
|
|
"Binary { type: %s, left: %s, right: %s }",
|
|
binary_expr_type_to_string(expr->binary.type),
|
|
left,
|
|
right
|
|
);
|
|
free(left);
|
|
free(right);
|
|
return strndup(formattet, 65536);
|
|
}
|
|
default:
|
|
fprintf(
|
|
stderr,
|
|
"panic: unexhausted value ./%s:%d %s()\n",
|
|
__FILE__,
|
|
__LINE__,
|
|
__func__
|
|
);
|
|
exit(1);
|
|
}
|
|
}
|