Newer
Older
Tardis / lang / number.c
// SPDX-License-Identifier: MIT
// Copyright (c) 2023 John Watts and the LuminaSensum contributors

#include "number.h"
#include "boolean.h"
#include "object.h"
#include "vm.h"
#include <mini-gmp.h>
#include <mini-mpq.h>
#include <stddef.h>

static struct object_class num_class;

struct number {
	mpq_t value;
};

Object number_create(VmState state, int value) {
	Object obj = object_create(state, &num_class, sizeof(struct number));
	vm_abort_if(state, !obj, "unable to allocate number");
	struct number *num =
		(struct number *)object_priv(state, obj, &num_class);
	mpq_init(num->value);
	mpq_set_ui(num->value, value, 1);
	return obj;
}

static void number_cleanup(VmState state, Object obj) {
	struct number *num =
		(struct number *)object_priv(state, obj, &num_class);
	mpq_clear(num->value);
}

int number_value(VmState state, Object obj) {
	struct number *num =
		(struct number *)object_priv(state, obj, &num_class);
	mpz_t out;
	mpz_init(out);
	mpq_get_num(out, num->value);
	int out_int = (int)mpz_get_si(out);
	mpz_clear(out);
	return out_int;
}

#define OP_ADD (void *)1
#define OP_SUBTRACT (void *)2

static void number_math(VmState state, Object obj, void *priv) {
	int arg_count = vm_stack_depth(state);
	vm_abort_if(state, arg_count != 2,
		"number_math called without 2 arguments");
	Object arg1 = vm_stack_get(state, 1);
	int result = 0;
	if (priv == OP_ADD) {
		result = number_value(state, obj) + number_value(state, arg1);
	} else if (priv == OP_SUBTRACT) {
		result = number_value(state, obj) - number_value(state, arg1);
	} else {
		vm_abort_msg(state, "number_math called with invalid priv");
	}
	vm_stack_set(state, 0, number_create(state, result));
	vm_stack_drop(state, 1);
	object_drop(state, &arg1);
}

static void number_equals(VmState state, Object obj, void *priv) {
	(void)priv;
	int arg_count = vm_stack_depth(state);
	vm_abort_if(state, arg_count != 2,
		"number_equals called without 2 arguments");
	Object arg1 = vm_stack_get(state, 1);
	bool equals = number_value(state, obj) == number_value(state, arg1);
	vm_stack_set(state, 0, boolean_create(state, equals));
	vm_stack_drop(state, 1);
	object_drop(state, &arg1);
}

static struct object_call calls[] = {
	{.name = "Add", .handler = number_math, .priv = OP_ADD},
	{.name = "Subtract", .handler = number_math, .priv = OP_SUBTRACT},
	{.name = "Equals", .handler = number_equals, .priv = NULL},
	{.name = NULL, /* end */}};

static struct object_class num_class = {
	.cleanup = number_cleanup,
	.calls = &calls[0],
};