expr.c (6872B)
#include "../parser.h"
#include "../utils.h"
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
Node*
parse_func_call(Parser* par)
{
Token tok = expect(par, TOKEN_IDENT);
Span callee = { .start = tok.start, .end = tok.end };
Node* ident = make_ident_node(callee);
const char* name = span_str(par->src, ident->data.ident.name, (char[IDENTSZ]) { 0 });
printf("parse_func_call: %s\n", name);
expect(par, TOKEN_LPAREN);
Node* call = (Node*)calloc(1, sizeof(Node));
if (call == NULL) panic("parse_func_call: alloc failed");
// start parse arguments
if (peek(par).type != TOKEN_RPAREN) {
call->data.call_expr.args = (Node**)calloc(4, sizeof(Node*));
if (call->data.call_expr.args == NULL) panic("parse_func_call: args: could not alloc");
call->data.call_expr.cap = 4;
call->data.call_expr.len = 0;
for (;;) {
Node* arg = parse_expression(par);
if (call->data.call_expr.len == call->data.call_expr.cap) {
call->data.call_expr.cap *= 2;
call->data.call_expr.args
= (Node**)realloc(call->data.call_expr.args, call->data.call_expr.cap * sizeof(Node*));
}
call->data.call_expr.args[call->data.call_expr.len++] = arg;
if (!match(par, TOKEN_COMMA)) break; // found `)` instead of `,`
}
}
// ends parse arguments
expect(par, TOKEN_RPAREN);
call->type = NODE_CALL_EXPR;
call->scope = NULL;
call->data.call_expr.callee = ident;
return call;
}
Node*
parse_number(Parser* par)
{
Token tok = consume(par);
assert(tok.type == TOKEN_INT_LITERAL || tok.type == TOKEN_FLOAT_LITERAL);
size_t len = tok.end - tok.start;
char buf[len + 1]; // strtod needs a \0 terminated string
for (size_t i = 0; i < len; i++)
buf[i] = par->src[tok.start + i];
buf[len] = '\0';
double value = strtod(buf, NULL);
Node* num_node = (Node*)calloc(1, sizeof(Node));
if (num_node == NULL) panic("parse_number: alloc failed");
num_node->type = tok.type == TOKEN_INT_LITERAL ? NODE_INT_LITERAL : NODE_FLOAT_LITERAL;
num_node->scope = NULL;
num_node->data.number.value = value;
return num_node;
}
Node*
parse_ident(Parser* par)
{
Token tok = consume(par);
bool comp_time = (tok.type == TOKEN_COMP_TIME);
bool variadic = (tok.type == TOKEN_VARIADIC);
if (comp_time || variadic) { tok = consume(par); }
assert(tok.type == TOKEN_IDENT);
Node* ident_node = (Node*)calloc(1, sizeof(Node));
if (ident_node == NULL) panic("parse_ident: alloc failed");
ident_node->type = NODE_IDENT;
ident_node->scope = NULL;
ident_node->data.ident_type.name = (Span) { .start = tok.start, .end = tok.end };
ident_node->data.ident_type.is_comp_time = comp_time;
ident_node->data.ident_type.is_variadic = variadic;
// const char* name = span_str(
// par->src, (Span) { .start = tok.start, .end = tok.end }, (char[IDENTSZ]) { 0 });
// printf("parse_ident: %s\n", name);
return ident_node;
}
NodeVec
parse_func_arguments(Parser* par)
{
NodeVec v = { 0 };
if (peek(par).type == TOKEN_RPAREN) return v; // found `)` no arguments
v.cap = 4;
v.items = (Node**)calloc(v.cap, sizeof(Node*));
if (v.items == NULL) panic("parse_func_arguments: could not alloc");
for (;;) {
Node* arg = parse_expression(par);
if (v.len == v.cap) {
v.cap *= 2;
v.items = (Node**)realloc(v.items, v.cap * sizeof(Node*));
}
v.items[v.len++] = arg;
if (!match(par, TOKEN_COMMA)) break; // found `)` instead of `,`
}
return v;
}
Node*
parse_postfix(Parser* par)
{
Node* node = parse_primary(par);
for (;;) {
if (match(par, TOKEN_PLUSPLUS)) {
node = make_postfix_node(OPER_POSTINC, node);
} else if (match(par, TOKEN_MINUSMINUS)) {
node = make_postfix_node(OPER_POSTDEC, node);
} else if (match(par, TOKEN_LBRACKET)) {
Node* index = parse_expression(par); // parse inside brackets
expect(par, TOKEN_RBRACKET);
node = make_subscript_node(node, index);
// } else if (match(par, TOKEN_DOT)) { // TODO dot members and arrows
// Token id = expect(par, TOKEN_IDENTIFIER);
// expr = make_member_node(expr, id);
// } else if (match(par, TOKEN_ARROW)) {
// Token id = expect(par, TOKEN_IDENTIFIER);
// expr = make_ptrmember_node(expr, id);
} else if (match(par, TOKEN_LPAREN)) {
NodeVec args = parse_func_arguments(par); // parse func call args
expect(par, TOKEN_RPAREN);
node = make_call_node(node, args);
} else {
break;
}
}
return node;
}
#define STARTING_ROOT_NODES 32
Node*
make_program_node(Parser* par)
{
Node* node = (Node*)calloc(1, sizeof(Node));
if (node == NULL) panic("make_program_node: alloc failed");
node->type = NODE_PROGRAM;
node->filename = par->filename;
node->scope = NULL;
node->next = NULL;
node->data.program.cap = STARTING_ROOT_NODES;
node->data.program.len = 0;
node->data.program.decl = (Node**)calloc(STARTING_ROOT_NODES, sizeof(Node));
if (node->data.program.decl == NULL) panic("make_program_node: decls: alloc failed");
return node;
}
Node*
make_ident_node(Span name)
{
Node* node = (Node*)calloc(1, sizeof(Node));
if (node == NULL) panic("make_ident_node: alloc failed");
node->type = NODE_IDENT;
node->scope = NULL;
node->next = NULL;
node->data.ident.name = name;
return node;
}
Node*
parse_primary(Parser* par)
{
Token tok = peek(par);
if (tok.type == TOKEN_BOOL_TRUE_LITERAL || tok.type == TOKEN_BOOL_FALSE_LITERAL) {
return make_boolean_node(par, tok.type == TOKEN_BOOL_TRUE_LITERAL);
}
if (tok.type == TOKEN_STRING_LITERAL) { return make_string_node(par); }
if (tok.type == TOKEN_INT_LITERAL || tok.type == TOKEN_FLOAT_LITERAL) { return make_number_node(par); }
if (tok.type == TOKEN_IDENT || tok.type == TOKEN_VARIADIC || tok.type == TOKEN_COMP_TIME) { return parse_ident(par); }
if (tok.type == TOKEN_LPAREN) {
consume(par); // consume '('
Node* node = parse_expression(par);
expect(par, TOKEN_RPAREN);
return node;
}
const char* name = span_str(par->src, (Span) { .start = tok.start, .end = tok.end }, (char[IDENTSZ]) { 0 });
panic("Expected Primary Expr, but found '%s' (%s at %s:%zu:%zu",
name,
token_type_str(tok.type),
par->filename,
tok.line,
tok.col);
return NULL;
}
Node*
parse_unary(Parser* par)
{
Node* inner = NULL;
switch (peek(par).type) {
case TOKEN_MINUS:
consume(par);
inner = parse_unary(par);
return make_unary_node(OPER_MINUS, inner);
case TOKEN_MINUSMINUS:
consume(par);
inner = parse_unary(par);
return make_unary_node(OPER_PREDEC, inner);
case TOKEN_BANG:
consume(par);
inner = parse_unary(par);
return make_unary_node(OPER_BANG, inner);
// TODO add others '~a' '$a' '*a' '^a' '@a' '&a'
case TOKEN_PLUSPLUS:
consume(par);
inner = parse_unary(par);
return make_unary_node(OPER_PREINC, inner);
case TOKEN_PLUS:
consume(par);
return parse_unary(par);
default:
return parse_postfix(par);
}
}
// called by parse_multiplicative
Node*
parse_term(Parser* par)
{
return parse_unary(par);
}