aboutsummaryrefslogtreecommitdiff
path: root/std/int.c
blob: dfc436cfdf126fb6a5af22c7b68f46c918bbadf7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <stdlib.h>
#include "../src/err.h"
#include "../api/vm.h"
#include "../api/int.h"
#include "../api/bool.h"

typedef enum
{
	BOP_SUB,
	BOP_DIV,
	BOP_MOD,
	BOP_SML,
	BOP_GRT,
	BOP_EQU,
} BinaryOP;

static int binary(const char *fnname, UwUVMArgs *args, BinaryOP op)
{
	if (args->num != 2)
		error("error: %s requires exactly 2 arguments\n", fnname);

	UwUVMValue value0 = uwuvm_get_arg(args, 0);

	if (value0.type != VT_INT)
		error("error: %s requires an integer as $0\n", fnname);

	UwUVMValue value1 = uwuvm_get_arg(args, 1);

	if (value1.type != VT_INT)
		error("error: %s requires an integer as $1\n", fnname);

	int a = value0.value.int_value;
	int b = value1.value.int_value;

	switch (op) {
		case BOP_SUB: return a - b;
		case BOP_DIV: return a / b;
		case BOP_MOD: return a % b;
		case BOP_SML: return a < b;
		case BOP_GRT: return a > b;
		case BOP_EQU: return a == b;
	}
}

typedef enum
{
	ROP_ADD,
	ROP_MUL,
	ROP_EQU,
} ReduceOP;

static int reduce(const char *fnname, UwUVMArgs *args, ReduceOP op, int result)
{
	int first;

	for (size_t i = 0; i < args->num; i++) {
		UwUVMValue value = uwuvm_get_arg(args, i);

		if (value.type != VT_INT)
			error("error: %s only accepts integers as arguments (invalid argument: $%lu)\n", fnname, i);

		int this = value.value.int_value;

		switch (op) {
			case ROP_ADD: result += this; break;
			case ROP_MUL: result *= this; break;
			case ROP_EQU:
				if (i == 0)
					first = this;
				else if (this != first)
					return 0;

				break;
		}
	}

	return result;
}

UwUVMValue uwu_add(UwUVMArgs *args)
{
	return uwuint_create(reduce(":int:add", args, ROP_ADD, 0));
}

UwUVMValue uwu_sub(UwUVMArgs *args)
{
	return uwuint_create(binary(":int:sub", args, BOP_SUB));
}

UwUVMValue uwu_mul(UwUVMArgs *args)
{
	return uwuint_create(reduce(":int:mul", args, ROP_MUL, 1));
}

UwUVMValue uwu_div(UwUVMArgs *args)
{
	return uwuint_create(binary(":int:div", args, BOP_DIV));
}

UwUVMValue uwu_mod(UwUVMArgs *args)
{
	return uwuint_create(binary(":int:mod", args, BOP_MOD));
}

UwUVMValue uwu_smaller(UwUVMArgs *args)
{
	return uwubool_create(binary(":int:smaller", args, BOP_SML) == 1);
}

UwUVMValue uwu_greater(UwUVMArgs *args)
{
	return uwubool_create(binary(":int:greater", args, BOP_GRT) == 1);
}

UwUVMValue uwu_equal(UwUVMArgs *args)
{
	if (args->num < 2)
		error("error: :int:equal requires at least 2 arguments\n");

	return uwubool_create(reduce(":int:equal", args, ROP_EQU, 1) == 1);
}

UwUVMValue uwu_is(UwUVMArgs *args)
{
	if (args->num < 1)
		error("error: :int:is requires at least 1 argument\n");

	for (size_t i = 0; i < args->num; i++)
		if (uwuvm_get_arg(args, i).type != VT_INT)
			return uwubool_create(false);

	return uwubool_create(true);
}