feat: implement lexer+parser for example program

This commit is contained in:
samsonium
2026-10-09 15:30:50 +03:00
parent a46f79d538
commit 45206e3d07
12 changed files with 776 additions and 0 deletions
+214
View File
@@ -0,0 +1,214 @@
#include <compiler/fzl_parser.h>
#include <stdlib.h>
#include <stdio.h>
char last_err[256];
fzl_parser_t *fzl_mkparser(fzl_reader_ll_t *first)
{
if (!first)
{
snprintf(last_err, sizeof(last_err), "token is null");
return NULL;
}
fzl_parser_t *parser = malloc(sizeof(fzl_parser_t));
if (!parser) return NULL;
parser->root = NULL;
parser->cursor = first->next;
if (!parser->cursor) return NULL;
return parser;
}
// --- Parse zone ---
// Make a new node
fzl_node_t *fzl_mknode(fzl_nodetype_t type, fzl_token_t *token)
{
fzl_node_t *node = malloc(sizeof(fzl_node_t));
if (!node) return NULL;
node->type = type;
node->token = token;
node->parent = NULL;
node->next = NULL;
return node;
}
// Get token at cursor
static fzl_token_t *peek(fzl_parser_t *parser)
{
if (!parser->cursor) return NULL;
return parser->cursor->token;
}
// Get token at (cursor + 1)
static fzl_token_t *next(fzl_parser_t *parser)
{
if (!parser->cursor) return NULL;
fzl_reader_ll_t *next = parser->cursor->next;
if (!next) return NULL;
return next->token;
}
// Consume token at cursor and move to the next
static fzl_token_t *consume(fzl_parser_t *parser)
{
if (!parser->cursor) return NULL;
fzl_token_t *tok = parser->cursor->token;
parser->cursor = parser->cursor->next;
return tok;
}
// Parse primary
fzl_node_t *parse_primary(fzl_parser_t *parser)
{
if (peek(parser) && peek(parser)->type == FZL_TOKTYPE_STR_LITERAL)
{
fzl_token_t *tok = consume(parser);
return fzl_mknode(FZL_NODETYPE_LITERAL, tok);
}
snprintf(last_err, sizeof(last_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)
{
fzl_token_t *tok = consume(parser); // <NAME>
consume(parser); // (
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)
{
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;
if (node->list.entry_count == capacity)
{
capacity *= 2;
node->list.entries = realloc(node->list.entries, capacity * sizeof(fzl_node_t*));
}
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)
{
snprintf(last_err, sizeof(last_err), "expected ',' or ')' after arg list");
return NULL;
}
}
consume(parser);
return node;
}
// Expression parsing
fzl_node_t *parse_expression(fzl_parser_t *parser, int precedence)
{
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(last_err, sizeof(last_err),
"failed to parse expression at %d",
peek(parser)->i0);
return NULL;
}
// Statement parsing
fzl_node_t *parse_statement(fzl_parser_t *parser)
{
return parse_expression(parser, 0);
}
// Internal parsing entry point
fzl_node_t *parse_program(fzl_parser_t *parser)
{
fzl_node_t *first = NULL;
fzl_node_t *last = NULL;
fzl_node_t *node = NULL;
while (peek(parser) != NULL)
{
node = parse_statement(parser);
if (!node) return NULL;
if (!last)
{
first = node;
last = node;
}
else
{
last->next = node;
last = node;
}
}
if (!first)
{
snprintf(last_err, sizeof(last_err), "empty ;(");
return NULL;
}
return first;
}
// Parse tokens and build AST
int fzl_parse(fzl_parser_t *parser)
{
fzl_node_t *ast = parse_program(parser);
if (!ast) return -1;
parser->root = ast;
return 0;
}
// --- Parse zone end ---
char *fzl_parser_geterror()
{
return last_err;
}
void fzl_freenode(fzl_node_t *node)
{
if (!node) return;
if (node->token) fzl_freetoken(node->token);
free(node);
}
void fzl_freeparser(fzl_parser_t *parser)
{
if (!parser) return;
free(parser);
}