feat: implement math in compiler

This commit is contained in:
samsonium
2026-10-11 02:10:54 +03:00
parent 981603105b
commit b9c43c6ad2
8 changed files with 506 additions and 83 deletions
+292 -54
View File
@@ -1,5 +1,7 @@
#include "compiler/fzl_token.h"
#include <compiler/fzl_parser.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -45,7 +47,7 @@ fzl_node_t *fzl_mknode(fzl_nodetype_t type, fzl_token_t *token)
// Get token at cursor
static fzl_token_t *peek(fzl_parser_t *parser)
{
if (!parser->cursor)
if (!parser || !parser->cursor)
return NULL;
return parser->cursor->token;
}
@@ -53,7 +55,7 @@ static fzl_token_t *peek(fzl_parser_t *parser)
// Get token at (cursor + 1)
static fzl_token_t *next(fzl_parser_t *parser)
{
if (!parser->cursor)
if (!parser || !parser->cursor)
return NULL;
fzl_reader_ll_t *next = parser->cursor->next;
@@ -66,105 +68,315 @@ static fzl_token_t *next(fzl_parser_t *parser)
// Consume token at cursor and move to the next
static fzl_token_t *consume(fzl_parser_t *parser)
{
if (!parser->cursor)
if (!parser || !parser->cursor)
return NULL;
fzl_token_t *tok = parser->cursor->token;
parser->cursor = parser->cursor->next;
return tok;
}
// Check if current token have the same type as specified
static bool is(fzl_parser_t *parser, fzl_toktype_t type)
{
if (!parser)
return false;
fzl_token_t *tok = peek(parser);
if (!tok)
return false;
return tok->type == type;
}
// Convert arithmetic operator token type to node type
static fzl_nodetype_t math_op_to_type(fzl_toktype_t type)
{
switch (type)
{
case FZL_TOKTYPE_PLUS:
return FZL_NODETYPE_ADD;
case FZL_TOKTYPE_MINUS:
return FZL_NODETYPE_SUB;
case FZL_TOKTYPE_STAR:
return FZL_NODETYPE_MUL;
case FZL_TOKTYPE_SLASH:
return FZL_NODETYPE_DIV;
case FZL_TOKTYPE_CARET:
return FZL_NODETYPE_POW;
case FZL_TOKTYPE_PERCENT:
return FZL_NODETYPE_MOD;
default:
return 0;
}
}
static fzl_nodetype_t unary_op_to_type(fzl_toktype_t type)
{
switch (type)
{
case FZL_TOKTYPE_PLUS:
return FZL_NODETYPE_UNARY_PLUS;
case FZL_TOKTYPE_MINUS:
return FZL_NODETYPE_UNARY_MINUS;
default:
return 0;
}
}
fzl_node_t *parse_expression(fzl_parser_t *parser);
// Parse primary
fzl_node_t *parse_primary(fzl_parser_t *parser)
{
if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL)
if (!parser || !peek(parser))
return NULL;
if (is(parser, FZL_TOKTYPE_STR_LITERAL))
{
fzl_token_t *tok = consume(parser);
return fzl_mknode(FZL_NODETYPE_LITERAL, tok);
}
else if (is(parser, FZL_TOKTYPE_NUM_LITERAL))
{
fzl_token_t *tok = consume(parser);
return fzl_mknode(FZL_NODETYPE_LITERAL, tok);
}
else if (is(parser, FZL_TOKTYPE_LPAREN))
{
consume(parser);
fzl_node_t *node = parse_expression(parser);
if (!is(parser, FZL_TOKTYPE_RPAREN))
{
snprintf(plast_err, sizeof(plast_err), "expected rparen at %d",
parser->cursor->token->i0);
return NULL;
}
consume(parser);
return node;
}
else if (is(parser, FZL_TOKTYPE_NAME))
{
fzl_token_t *name = consume(parser);
return fzl_mknode(FZL_NODETYPE_IDENTIFIER, name);
}
snprintf(plast_err, sizeof(plast_err), "failed to parse primary at %d",
peek(parser)->i0);
return NULL;
}
fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence);
// Parse call expression
fzl_node_t *parse_call(fzl_parser_t *parser)
// Parse postfix expressions (parens, index access, dots)
fzl_node_t *parse_postfix(fzl_parser_t *parser)
{
fzl_token_t *tok = consume(parser); // <NAME>
if (tok->type != FZL_TOKTYPE_NAME)
if (!parser)
return NULL;
fzl_token_t *lpair = consume(parser); // (
if (lpair->type != FZL_TOKTYPE_LPAIR)
fzl_node_t *left = parse_primary(parser);
fzl_node_t *right = NULL;
if (!left)
{
snprintf(plast_err, sizeof(plast_err), "failed to parse postfix at %d",
peek(parser)->i0);
return NULL;
}
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, tok);
node->list.entry_count = 0;
size_t capacity = 4;
node->list.entries = malloc(capacity * sizeof(fzl_node_t *));
node->list.entry_count = 0;
if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_RPAIR)
while (is(parser, FZL_TOKTYPE_LPAREN))
{
consume(parser);
return node;
}
while (peek(parser) && peek(parser)->type != FZL_TOKTYPE_RPAIR)
{
fzl_node_t *arg = parse_expression(parser, 0);
if (!arg)
return NULL;
size_t args_capacity = 2;
size_t args_count = 0;
fzl_node_t **args = malloc(args_capacity * sizeof(fzl_node_t *));
if (node->list.entry_count == capacity)
while (!is(parser, FZL_TOKTYPE_RPAREN))
{
capacity *= 2;
node->list.entries =
realloc(node->list.entries, capacity * sizeof(fzl_node_t *));
fzl_node_t *arg = parse_expression(parser);
if (!arg)
{
free(args);
return NULL;
}
if (args_capacity <= args_count)
{
args_capacity *= 2;
args = realloc(args, args_capacity * sizeof(fzl_node_t));
}
args[args_count] = arg;
args_count++;
if (is(parser, FZL_TOKTYPE_COMMA))
consume(parser);
}
node->list.entries[node->list.entry_count++] = arg;
if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_COMMA)
consume(parser);
else if (peek(parser) && peek(parser)->type != FZL_TOKTYPE_RPAIR)
fzl_token_t *rparen = consume(parser);
if (!rparen || rparen->type != FZL_TOKTYPE_RPAREN)
{
free(args);
snprintf(plast_err, sizeof(plast_err),
"expected ',' or ')' after arg list");
"expected ')' after argument list at %d", peek(parser)->i0);
return NULL;
}
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_CALL, left->token);
node->call.callee = left;
node->call.args = args;
node->call.args_count = args_count;
left = node;
}
fzl_token_t *rpair = consume(parser);
if (rpair->type != FZL_TOKTYPE_RPAIR)
return left;
}
// Parse unary operations
fzl_node_t *parse_unary(fzl_parser_t *parser)
{
if (!parser)
return NULL;
return node;
if (!is(parser, FZL_TOKTYPE_MINUS) && !is(parser, FZL_TOKTYPE_PLUS))
return parse_postfix(parser);
fzl_token_t *op = consume(parser);
fzl_node_t *operand = parse_unary(parser);
if (!operand)
return NULL;
fzl_node_t *result = fzl_mknode(unary_op_to_type(op->type), op);
result->unary_op.operand = operand;
return result;
}
fzl_node_t *parse_power(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = parse_unary(parser);
fzl_node_t *right = NULL;
if (!left)
{
snprintf(plast_err, sizeof(plast_err), "failed to parse power at %d",
peek(parser)->i0);
return NULL;
}
if (is(parser, FZL_TOKTYPE_CARET))
{
fzl_token_t *caret = consume(parser);
right = parse_unary(parser);
if (!right)
{
snprintf(plast_err, sizeof(plast_err),
"failed to parse right side of power at %d", peek(parser)->i0);
return NULL;
}
fzl_node_t *node = fzl_mknode(FZL_NODETYPE_POW, caret);
node->binary_op.left = left;
node->binary_op.right = right;
left = node;
}
return left;
}
// Parse FACTOR: multiplication and division
fzl_node_t *parse_factor(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = parse_power(parser);
fzl_node_t *right = NULL;
if (!left)
{
if (!plast_err[0])
snprintf(plast_err, sizeof(plast_err), "invalid expression at %u",
parser->cursor->token->i0);
return NULL;
}
while (is(parser, FZL_TOKTYPE_STAR) || is(parser, FZL_TOKTYPE_SLASH) ||
is(parser, FZL_TOKTYPE_PERCENT))
{
fzl_token_t *op = consume(parser);
right = parse_factor(parser);
if (!right)
{
snprintf(plast_err, sizeof(plast_err),
"unfinished factor (mul/div) expression at %u", left->token->i0);
return NULL;
}
fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op);
node->binary_op.left = left;
node->binary_op.right = right;
left = node;
}
return left;
}
// Parse TERM: addition and subtraction
fzl_node_t *parse_term(fzl_parser_t *parser)
{
if (!parser)
return NULL;
fzl_node_t *left = parse_factor(parser);
fzl_node_t *right = NULL;
if (!left)
{
snprintf(plast_err, sizeof(plast_err), "invalid expression at %u",
parser->cursor->token->i0);
return NULL;
}
while (is(parser, FZL_TOKTYPE_PLUS) || is(parser, FZL_TOKTYPE_MINUS))
{
fzl_token_t *op = consume(parser);
right = parse_factor(parser);
if (!right)
{
snprintf(plast_err, sizeof(plast_err),
"unfinished term (add/sub) expression at %u", left->token->i0);
return NULL;
}
fzl_node_t *node = fzl_mknode(math_op_to_type(op->type), op);
node->binary_op.left = left;
node->binary_op.right = right;
left = node;
}
return left;
}
// Expression parsing
fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence)
fzl_node_t *parse_expression(fzl_parser_t *parser)
{
if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_NAME)
{
if (next(parser) && next(parser)->type == FZL_TOKTYPE_LPAIR)
return parse_call(parser);
}
else if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL)
return parse_primary(parser);
snprintf(plast_err, sizeof(plast_err), "failed to parse expression at %d",
peek(parser)->i0);
return NULL;
return parse_term(parser);
}
// Statement parsing
fzl_node_t *parse_statement(fzl_parser_t *parser)
{
return parse_expression(parser, 0);
return parse_expression(parser);
}
// Internal parsing entry point
@@ -229,7 +441,7 @@ void fzl_freeast(fzl_node_t *root)
while (root)
{
if (root->type == FZL_NODETYPE_BLOCK || root->type == FZL_NODETYPE_CALL)
if (root->type == FZL_NODETYPE_BLOCK)
{
if (root->list.entries)
{
@@ -240,6 +452,32 @@ void fzl_freeast(fzl_node_t *root)
free(root->list.entries);
}
}
else if (root->type == FZL_NODETYPE_CALL)
{
if (root->call.args_count)
{
for (size_t i = 0; i < root->call.args_count; i++)
if (root->call.args[i])
fzl_freeast(root->call.args[i]);
free(root->call.args);
}
}
else if (root->type == FZL_NODETYPE_ADD || root->type == FZL_NODETYPE_SUB ||
root->type == FZL_NODETYPE_MUL || root->type == FZL_NODETYPE_DIV ||
root->type == FZL_NODETYPE_POW || root->type == FZL_NODETYPE_MOD)
{
if (root->binary_op.left)
fzl_freeast(root->binary_op.left);
if (root->binary_op.right)
fzl_freeast(root->binary_op.right);
}
else if (root->type == FZL_NODETYPE_UNARY_PLUS ||
root->type == FZL_NODETYPE_UNARY_MINUS)
{
if (root->unary_op.operand)
fzl_freeast(root->unary_op.operand);
}
fzl_freenode(root);
root = root->next;