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
+12 -3
View File
@@ -4,9 +4,18 @@
// Available opcodes
typedef enum
{
FZL_OPCODE_LOADCONST, // Load const into T (temp) reg
FZL_OPCODE_PUSHARG, // Push argument from T into args stack
FZL_OPCODE_CALL_BUILTIN // Call builtin function
FZL_OPCODE_LOADCONST, // Load const into T (temp) reg
FZL_OPCODE_PUSHARG, // Push argument from T into args stack
FZL_OPCODE_CALL_BUILTIN, // Call builtin function
FZL_OPCODE_PUSHNUM, // Push number into stack
FZL_OPCODE_ADD, // Sum two numbers in arg strack
FZL_OPCODE_SUB, // Subract two numbers in arg stack
FZL_OPCODE_MUL, // Multiply two numbers in arg stack
FZL_OPCODE_DIV, // Divide two numbers in arg stack
FZL_OPCODE_POW, // Power first number in arg stack by second
FZL_OPCODE_MOD, // Get module of two numbers in arg stack
FZL_OPCODE_NEG, // Negotiate number in arg stack
} fzl_opcode_t;
#endif // FZL_OPCODES_H
+34
View File
@@ -11,6 +11,17 @@ typedef enum
FZL_NODETYPE_BLOCK,
FZL_NODETYPE_CALL,
FZL_NODETYPE_LITERAL,
FZL_NODETYPE_IDENTIFIER,
FZL_NODETYPE_ADD,
FZL_NODETYPE_SUB,
FZL_NODETYPE_MUL,
FZL_NODETYPE_DIV,
FZL_NODETYPE_POW,
FZL_NODETYPE_MOD,
FZL_NODETYPE_UNARY_PLUS,
FZL_NODETYPE_UNARY_MINUS
} fzl_nodetype_t;
// AST node descriptor
@@ -23,11 +34,34 @@ typedef struct fzl_node
union
{
// Array of nested nodes (for blocks, etc)
struct
{
struct fzl_node **entries;
size_t entry_count;
} list;
// Call data
struct
{
struct fzl_node *callee;
struct fzl_node **args;
size_t args_count;
} call;
// Binary operations
struct
{
struct fzl_node *left;
struct fzl_node *right;
} binary_op;
// Unary operations
struct
{
struct fzl_node *operand;
} unary_op;
};
} fzl_node_t;
+16 -5
View File
@@ -8,11 +8,22 @@
// Token type
typedef enum
{
FZL_TOKTYPE_NAME,
FZL_TOKTYPE_LPAIR,
FZL_TOKTYPE_RPAIR,
FZL_TOKTYPE_COMMA,
FZL_TOKTYPE_STR_LITERAL
FZL_TOKTYPE_NAME, // abc
FZL_TOKTYPE_LPAREN, // (
FZL_TOKTYPE_RPAREN, // )
FZL_TOKTYPE_COMMA, // ,
FZL_TOKTYPE_STR_LITERAL, // ""
FZL_TOKTYPE_NUM_LITERAL, // 123
FZL_TOKTYPE_STAR, // *
FZL_TOKTYPE_SLASH, // /
FZL_TOKTYPE_PLUS, // +
FZL_TOKTYPE_MINUS, // -
FZL_TOKTYPE_CARET, // ^
FZL_TOKTYPE_PERCENT, // %
} fzl_toktype_t;
// Token descriptor
+20 -3
View File
@@ -1,3 +1,4 @@
#include "compiler/fzl_parser.h"
#include <compiler/fzl_debug.h>
#include <stddef.h>
#include <stdio.h>
@@ -32,12 +33,12 @@ void print_node(fzl_node_t *node, int level)
switch (node->type)
{
case FZL_NODETYPE_CALL:
if (node->list.entry_count > 0)
if (node->call.args_count > 0)
{
printf("%s Call fn '%s' with args:\n", levelbuf,
(char *)node->token->value);
for (size_t i = 0; i < node->list.entry_count; i++)
print_node(node->list.entries[i], level + 1);
for (size_t i = 0; i < node->call.args_count; i++)
print_node(node->call.args[i], level + 1);
}
else
printf("%s Call fn '%s' w/o args\n", levelbuf,
@@ -62,6 +63,22 @@ void print_node(fzl_node_t *node, int level)
print_node(node->list.entries[i], level + 1);
break;
case FZL_NODETYPE_ADD:
case FZL_NODETYPE_SUB:
case FZL_NODETYPE_MUL:
case FZL_NODETYPE_DIV:
case FZL_NODETYPE_POW:
case FZL_NODETYPE_MOD:
printf("%s Binary op (%d)\n", levelbuf, node->type);
printf("-%s LEFT:\n", levelbuf);
print_node(node->binary_op.left, level + 2);
printf("-%s RIGHT:\n", levelbuf);
print_node(node->binary_op.right, level + 2);
break;
case FZL_NODETYPE_IDENTIFIER:
printf("%s Identifier ('%s')\n", levelbuf, (char *)node->token->value);
break;
default:
printf("%s Secure node\n", levelbuf);
+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;
+58 -8
View File
@@ -1,5 +1,7 @@
#include "compiler/fzl_token.h"
#include <compiler/fzl_reader.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -97,6 +99,44 @@ fzl_token_t *parse_string_literal(fzl_reader_t *reader)
return tok;
}
// Parse numeric literal
fzl_token_t *parse_number(fzl_reader_t *reader)
{
if (!reader)
return NULL;
size_t size = 0;
size_t lpos = reader->position;
do
{
size++;
lpos++;
} while ((reader->source[lpos] >= '0' && reader->source[lpos] <= '9') &&
lpos < reader->size);
char *value = malloc(size * sizeof(char) + 1);
memcpy(value, reader->source + reader->position, size);
value[size] = '\0';
fzl_token_t *tok =
fzl_mktoken(FZL_TOKTYPE_NUM_LITERAL, reader->position, lpos, value, size);
free(value);
if (!tok)
return NULL;
reader->position += size;
return tok;
}
// Add single-char token
fzl_token_t *add_single(fzl_reader_t *reader, fzl_toktype_t type)
{
size_t p = reader->position++;
return fzl_mktoken(type, p, p, NULL, 0);
}
int fzl_read(fzl_reader_t *reader)
{
if (reader->position >= reader->size)
@@ -117,17 +157,27 @@ int fzl_read(fzl_reader_t *reader)
if (curr == '\'' || curr == '"')
token = parse_string_literal(reader);
else if (curr == '(')
{
token = fzl_mktoken(FZL_TOKTYPE_LPAIR, p, p, NULL, 0);
reader->position++;
}
token = add_single(reader, FZL_TOKTYPE_LPAREN);
else if (curr == ')')
{
token = fzl_mktoken(FZL_TOKTYPE_RPAIR, p, p, NULL, 0);
reader->position++;
}
token = add_single(reader, FZL_TOKTYPE_RPAREN);
else if (curr == ',')
token = add_single(reader, FZL_TOKTYPE_COMMA);
else if (curr == '*')
token = add_single(reader, FZL_TOKTYPE_STAR);
else if (curr == '/')
token = add_single(reader, FZL_TOKTYPE_SLASH);
else if (curr == '+')
token = add_single(reader, FZL_TOKTYPE_PLUS);
else if (curr == '-')
token = add_single(reader, FZL_TOKTYPE_MINUS);
else if (curr == '^')
token = add_single(reader, FZL_TOKTYPE_CARET);
else if (curr == '%')
token = add_single(reader, FZL_TOKTYPE_PERCENT);
else if (curr >= 'A' && curr <= 'Z' || curr >= 'a' && curr <= 'z')
token = parse_name(reader);
else if (curr >= '0' && curr <= '9')
token = parse_number(reader);
else
{
sprintf(last_err, "undefined token '%c'", curr);
+66 -8
View File
@@ -1,6 +1,7 @@
#include <common/fzl_builtins.h>
#include <common/fzl_opcodes.h>
#include <compiler/fzl_parser.h>
#include <compiler/fzl_token.h>
#include <compiler/fzl_walker.h>
#include <compiler/utils/fzl_const_pool.h>
#include <stdbool.h>
@@ -108,9 +109,9 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node)
case FZL_NODETYPE_CALL:
if (node->list.entry_count > 0)
{
for (size_t i = 0; i < node->list.entry_count; i++)
for (size_t i = 0; i < node->call.args_count; i++)
{
if (!walk(walker, node->list.entries[i]))
if (!walk(walker, node->call.args[i]))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
}
@@ -129,13 +130,70 @@ bool walk(fzl_walker_t *walker, fzl_node_t *node)
break;
case FZL_NODETYPE_LITERAL:
size_t n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
.as.string.chars = node->token->value,
.as.string.length = node->token->value_sz});
if (node->token->type == FZL_TOKTYPE_STR_LITERAL)
{
size_t n = add_const(
walker, (fzl_const_t){.type = FZL_CONSTTYPE_STRING,
.as.string.chars = node->token->value,
.as.string.length = node->token->value_sz});
push_op(walker, FZL_OPCODE_LOADCONST);
push_word(walker, n);
push_op(walker, FZL_OPCODE_LOADCONST);
push_word(walker, n);
}
else if (node->token->type == FZL_TOKTYPE_NUM_LITERAL)
{
push_op(walker, FZL_OPCODE_PUSHNUM);
uint64_t num = strtol(node->token->value, NULL, 10);
push_word(walker, num);
}
break;
case FZL_NODETYPE_ADD:
case FZL_NODETYPE_SUB:
case FZL_NODETYPE_MUL:
case FZL_NODETYPE_DIV:
case FZL_NODETYPE_POW:
case FZL_NODETYPE_MOD:
if (!walk(walker, node->binary_op.left))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
if (!walk(walker, node->binary_op.right))
return false;
push_op(walker, FZL_OPCODE_PUSHARG);
switch (node->type)
{
case FZL_NODETYPE_ADD:
push_op(walker, FZL_OPCODE_ADD);
break;
case FZL_NODETYPE_SUB:
push_op(walker, FZL_OPCODE_SUB);
break;
case FZL_NODETYPE_MUL:
push_op(walker, FZL_OPCODE_MUL);
break;
case FZL_NODETYPE_DIV:
push_op(walker, FZL_OPCODE_DIV);
break;
case FZL_NODETYPE_POW:
push_op(walker, FZL_OPCODE_POW);
break;
case FZL_NODETYPE_MOD:
push_op(walker, FZL_OPCODE_MOD);
break;
default:
return false;
}
break;
case FZL_NODETYPE_UNARY_MINUS:
if (!walk(walker, node->unary_op.operand))
return false;
push_op(walker, FZL_OPCODE_NEG);
break;
}
+8 -2
View File
@@ -193,8 +193,14 @@ int main(int argc, char **argv)
printf("---> %s\n", walker->consts->array[i].as.string.chars);
printf("--> Ops:\n");
printf("---> ");
for (size_t i = 0; i < walker->op_count; i++)
printf("---> %lu\n", walker->ops[i]);
{
if (i > 0 && i < walker->op_count)
printf("->");
printf("%lu", walker->ops[i]);
}
printf("\n");
printf("-> preparing final write\n");
}
@@ -207,7 +213,7 @@ int main(int argc, char **argv)
if (args->verbose)
{
printf("-> output file path is: %s", outpath);
printf("-> output file path is: %s\n", outpath);
printf("-> writing file...");
}