ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

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);
}