Skip to content

Commit 27e85fa

Browse files
committed
New ME_AUTO dtype. New debug mode for builds.
1 parent 2570606 commit 27e85fa

4 files changed

Lines changed: 259 additions & 14 deletions

File tree

Makefile

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
# Organized structure: src/, bench/, tests/
33

44
CC = gcc
5-
CFLAGS = -Wall -Wshadow -Wno-unknown-pragmas -Wno-unused-function -O2
5+
CFLAGS = -Wall -Wshadow -Wno-unknown-pragmas -Wno-unused-function -O2 -DNDEBUG
6+
DEBUG_CFLAGS = -Wall -Wshadow -Wno-unknown-pragmas -Wno-unused-function -O0 -g
67
LDFLAGS = -lm
78

89
# Directories
@@ -24,7 +25,7 @@ BENCH_BINS = $(patsubst $(BENCHDIR)/%.c,$(BUILDDIR)/%,$(BENCH_SRCS))
2425
TEST_SRCS = $(wildcard $(TESTDIR)/*.c)
2526
TEST_BINS = $(patsubst $(TESTDIR)/%.c,$(BUILDDIR)/%,$(TEST_SRCS))
2627

27-
.PHONY: all lib bench test clean help
28+
.PHONY: all lib bench test clean help debug debug-test
2829

2930
# Default target
3031
all: lib bench
@@ -35,11 +36,13 @@ help:
3536
@echo "====================="
3637
@echo ""
3738
@echo "Targets:"
38-
@echo " make lib - Build library object file"
39-
@echo " make bench - Build all benchmarks"
40-
@echo " make test - Build and run all tests"
41-
@echo " make clean - Remove all build artifacts"
42-
@echo " make help - Show this help"
39+
@echo " make lib - Build library object file (release mode)"
40+
@echo " make bench - Build all benchmarks"
41+
@echo " make test - Build and run all tests"
42+
@echo " make debug - Build library in debug mode (-g -O0, asserts enabled)"
43+
@echo " make debug-test - Build and run tests in debug mode"
44+
@echo " make clean - Remove all build artifacts"
45+
@echo " make help - Show this help"
4346
@echo ""
4447
@echo "Individual benchmarks:"
4548
@echo " make $(BUILDDIR)/benchmark_all_types"
@@ -61,6 +64,16 @@ $(LIB_OBJ): $(LIB_SRC) $(LIB_HDR) | $(BUILDDIR)
6164
$(CC) $(CFLAGS) -c $(LIB_SRC) -o $@
6265
@echo "✓ Library built: $@"
6366

67+
# Build library in debug mode
68+
debug: CFLAGS = $(DEBUG_CFLAGS)
69+
debug: clean lib
70+
@echo "✓ Debug build complete (asserts enabled, -g -O0)"
71+
72+
# Build and run tests in debug mode
73+
debug-test: CFLAGS = $(DEBUG_CFLAGS)
74+
debug-test: clean test
75+
@echo "✓ Debug tests complete"
76+
6477
# Build all benchmarks
6578
bench: $(BENCH_BINS)
6679

src/miniexpr.c

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ For log = natural log uncomment the next line. */
5656
#include <stdint.h>
5757
#include <stdbool.h>
5858
#include <complex.h>
59+
#include <assert.h>
5960

6061
#ifndef NAN
6162
#define NAN (0.0/0.0)
@@ -76,6 +77,7 @@ enum {
7677

7778

7879
/* Type promotion table following NumPy rules */
80+
/* Note: ME_AUTO (0) should never appear in type promotion, so we index from 1 */
7981
static const me_dtype type_promotion_table[13][13] = {
8082
/* Rows: left operand, Columns: right operand */
8183
/* BOOL, INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FLOAT32, FLOAT64, COMPLEX64, COMPLEX128 */
@@ -135,10 +137,24 @@ static const me_dtype type_promotion_table[13][13] = {
135137

136138
/* Promote two types according to NumPy rules */
137139
static me_dtype promome_types(me_dtype a, me_dtype b) {
138-
if (a >= 0 && a < 13 && b >= 0 && b < 13) {
139-
return type_promotion_table[a][b];
140+
// ME_AUTO should have been resolved during compilation
141+
if (a == ME_AUTO || b == ME_AUTO) {
142+
fprintf(stderr, "FATAL: ME_AUTO in type promotion (a=%d, b=%d). This is a bug.\n", a, b);
143+
#ifdef NDEBUG
144+
abort(); // Release build: terminate immediately
145+
#else
146+
assert(0 && "ME_AUTO should be resolved during compilation"); // Debug: trigger debugger
147+
#endif
148+
}
149+
150+
// Adjust indices since table starts at ME_BOOL (index 1), not ME_AUTO (index 0)
151+
int a_idx = a - 1;
152+
int b_idx = b - 1;
153+
if (a_idx >= 0 && a_idx < 13 && b_idx >= 0 && b_idx < 13) {
154+
return type_promotion_table[a_idx][b_idx];
140155
}
141-
return ME_FLOAT64; // Fallback
156+
fprintf(stderr, "WARNING: Invalid dtype in type promotion (a=%d, b=%d). Falling back to FLOAT64.\n", a, b);
157+
return ME_FLOAT64; // Fallback for out-of-range types
142158
}
143159

144160
/* Get size of a type in bytes */
@@ -2358,6 +2374,14 @@ void me_eval(const me_expr *n) {
23582374
bool all_match = all_variables_match_type(n, result_type);
23592375
if (result_type == n->dtype && all_match) {
23602376
// Fast path: no promotion needed
2377+
if (n->dtype == ME_AUTO) {
2378+
fprintf(stderr, "FATAL: ME_AUTO dtype in evaluation. This is a bug.\n");
2379+
#ifdef NDEBUG
2380+
abort(); // Release build: terminate immediately
2381+
#else
2382+
assert(0 && "ME_AUTO should be resolved during compilation"); // Debug: trigger debugger
2383+
#endif
2384+
}
23612385
switch (n->dtype) {
23622386
case ME_BOOL: me_eval_i8(n);
23632387
break;
@@ -2385,6 +2409,13 @@ void me_eval(const me_expr *n) {
23852409
break;
23862410
case ME_COMPLEX128: me_eval_c128(n);
23872411
break;
2412+
default:
2413+
fprintf(stderr, "FATAL: Invalid dtype %d in evaluation.\n", n->dtype);
2414+
#ifdef NDEBUG
2415+
abort(); // Release build: terminate immediately
2416+
#else
2417+
assert(0 && "Invalid dtype"); // Debug: trigger debugger
2418+
#endif
23882419
}
23892420
return;
23902421
}
@@ -2409,6 +2440,14 @@ void me_eval(const me_expr *n) {
24092440
((me_expr *) n)->dtype = result_type;
24102441

24112442
// Evaluate with promoted types
2443+
if (result_type == ME_AUTO) {
2444+
fprintf(stderr, "FATAL: ME_AUTO result type in evaluation. This is a bug.\n");
2445+
#ifdef NDEBUG
2446+
abort(); // Release build: terminate immediately
2447+
#else
2448+
assert(0 && "ME_AUTO should be resolved during compilation"); // Debug: trigger debugger
2449+
#endif
2450+
}
24122451
switch (result_type) {
24132452
case ME_BOOL: me_eval_i8(n);
24142453
break;
@@ -2436,6 +2475,13 @@ void me_eval(const me_expr *n) {
24362475
break;
24372476
case ME_COMPLEX128: me_eval_c128(n);
24382477
break;
2478+
default:
2479+
fprintf(stderr, "FATAL: Invalid result type %d in evaluation.\n", result_type);
2480+
#ifdef NDEBUG
2481+
abort(); // Release build: terminate immediately
2482+
#else
2483+
assert(0 && "Invalid dtype"); // Debug: trigger debugger
2484+
#endif
24392485
}
24402486

24412487
// Restore original variable bindings
@@ -2842,9 +2888,8 @@ me_expr *me_compile(const char *expression, const me_variable *variables, int va
28422888
}
28432889
for (int i = 0; i < var_count; i++) {
28442890
vars_copy[i] = variables[i];
2845-
// If dtype not set (0 = ME_BOOL, which is unlikely for user variables),
2846-
// use the expression's dtype
2847-
if (vars_copy[i].dtype == 0 && vars_copy[i].type == 0) {
2891+
// If dtype not set (ME_AUTO), use the expression's dtype
2892+
if (vars_copy[i].dtype == ME_AUTO && vars_copy[i].type == 0) {
28482893
vars_copy[i].dtype = dtype;
28492894
vars_copy[i].type = ME_VARIABLE;
28502895
}

src/miniexpr.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ extern "C" {
4646

4747
/* Data type enumeration - Full C99 support */
4848
typedef enum {
49+
/* Automatic type inference */
50+
ME_AUTO,
51+
4952
/* Boolean */
5053
ME_BOOL,
5154

@@ -105,7 +108,7 @@ enum {
105108

106109
typedef struct me_variable {
107110
const char *name;
108-
me_dtype dtype; // Data type of this variable (0 = use output dtype)
111+
me_dtype dtype; // Data type of this variable (ME_AUTO = use output dtype)
109112
const void *address; // Pointer to data (NULL for me_compile_chunk)
110113
int type; // ME_VARIABLE for user variables (0 = auto-set to ME_VARIABLE)
111114
void *context; // For closures/functions (NULL for normal variables)

tests/test_auto_dtype.c

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/* Test ME_AUTO dtype and ME_BOOL to ensure no interference */
2+
3+
#include <stdio.h>
4+
#include <stdlib.h>
5+
#include <stdint.h>
6+
#include <stdbool.h>
7+
#include "miniexpr.h"
8+
9+
#define VECTOR_SIZE 10
10+
11+
int main() {
12+
printf("Testing ME_AUTO and ME_BOOL\n");
13+
printf("===========================\n\n");
14+
15+
int tests_passed = 0;
16+
int tests_total = 0;
17+
18+
/* Test 1: ME_AUTO (value 0) for automatic type inference */
19+
{
20+
tests_total++;
21+
printf("Test 1: ME_AUTO for automatic type inference\n");
22+
23+
int32_t a[VECTOR_SIZE];
24+
int32_t b[VECTOR_SIZE];
25+
int32_t result[VECTOR_SIZE];
26+
27+
for (int i = 0; i < VECTOR_SIZE; i++) {
28+
a[i] = i;
29+
b[i] = i * 2;
30+
}
31+
32+
/* Use ME_AUTO (0) to let the compiler infer the type from variables */
33+
me_variable vars[] = {
34+
{"a", ME_AUTO, a}, // Explicitly use ME_AUTO
35+
{"b", ME_AUTO, b}
36+
};
37+
38+
int err;
39+
me_expr *expr = me_compile("a + b", vars, 2, result, VECTOR_SIZE, ME_INT32, &err);
40+
41+
if (!expr) {
42+
printf(" ❌ FAIL: Compilation error at position %d\n", err);
43+
} else {
44+
me_eval(expr);
45+
46+
bool passed = true;
47+
for (int i = 0; i < VECTOR_SIZE && passed; i++) {
48+
if (result[i] != a[i] + b[i]) {
49+
printf(" ❌ FAIL: Mismatch at [%d]: got %d, expected %d\n",
50+
i, result[i], a[i] + b[i]);
51+
passed = false;
52+
}
53+
}
54+
55+
if (passed) {
56+
printf(" ✅ PASS: ME_AUTO works correctly\n");
57+
tests_passed++;
58+
}
59+
60+
me_free(expr);
61+
}
62+
}
63+
64+
/* Test 2: ME_BOOL (value 1 after ME_AUTO) */
65+
{
66+
tests_total++;
67+
printf("\nTest 2: ME_BOOL operations\n");
68+
69+
bool a[VECTOR_SIZE] = {true, false, true, false, true, false, true, false, true, false};
70+
bool b[VECTOR_SIZE] = {true, true, false, false, true, true, false, false, true, true};
71+
bool result[VECTOR_SIZE] = {0};
72+
73+
me_variable vars[] = {
74+
{"a", ME_BOOL, a},
75+
{"b", ME_BOOL, b}
76+
};
77+
78+
int err;
79+
me_expr *expr = me_compile("a & b", vars, 2, result, VECTOR_SIZE, ME_BOOL, &err);
80+
81+
if (!expr) {
82+
printf(" ❌ FAIL: Compilation error at position %d\n", err);
83+
} else {
84+
me_eval(expr);
85+
86+
bool passed = true;
87+
for (int i = 0; i < VECTOR_SIZE && passed; i++) {
88+
bool expected = a[i] && b[i];
89+
if (result[i] != expected) {
90+
printf(" ❌ FAIL: Mismatch at [%d]: got %d, expected %d\n",
91+
i, result[i], expected);
92+
passed = false;
93+
}
94+
}
95+
96+
if (passed) {
97+
printf(" ✅ PASS: ME_BOOL works correctly\n");
98+
tests_passed++;
99+
}
100+
101+
me_free(expr);
102+
}
103+
}
104+
105+
/* Test 3: Verify ME_AUTO and ME_BOOL have different values */
106+
{
107+
tests_total++;
108+
printf("\nTest 3: ME_AUTO != ME_BOOL\n");
109+
110+
if (ME_AUTO != ME_BOOL) {
111+
printf(" ✅ PASS: ME_AUTO (%d) != ME_BOOL (%d)\n", ME_AUTO, ME_BOOL);
112+
tests_passed++;
113+
} else {
114+
printf(" ❌ FAIL: ME_AUTO (%d) == ME_BOOL (%d) - conflict!\n", ME_AUTO, ME_BOOL);
115+
}
116+
}
117+
118+
/* Test 4: Verify ME_AUTO is 0 */
119+
{
120+
tests_total++;
121+
printf("\nTest 4: ME_AUTO value is 0\n");
122+
123+
if (ME_AUTO == 0) {
124+
printf(" ✅ PASS: ME_AUTO == 0\n");
125+
tests_passed++;
126+
} else {
127+
printf(" ❌ FAIL: ME_AUTO == %d (expected 0)\n", ME_AUTO);
128+
}
129+
}
130+
131+
/* Test 5: Mixed - explicit ME_BOOL with ME_AUTO inference */
132+
{
133+
tests_total++;
134+
printf("\nTest 5: Mixed ME_BOOL variable with ME_AUTO for other var\n");
135+
136+
bool a[VECTOR_SIZE];
137+
int32_t b[VECTOR_SIZE];
138+
int32_t result[VECTOR_SIZE];
139+
140+
for (int i = 0; i < VECTOR_SIZE; i++) {
141+
a[i] = (i % 2 == 0);
142+
b[i] = i * 10;
143+
}
144+
145+
me_variable vars[] = {
146+
{"a", ME_BOOL, a}, // Explicit ME_BOOL
147+
{"b", ME_AUTO, b} // Let it infer from output dtype
148+
};
149+
150+
int err;
151+
// Simple expression: cast bool to int and add
152+
me_expr *expr = me_compile("a + b", vars, 2, result, VECTOR_SIZE, ME_INT32, &err);
153+
154+
if (!expr) {
155+
printf(" ❌ FAIL: Compilation error at position %d\n", err);
156+
} else {
157+
me_eval(expr);
158+
159+
bool passed = true;
160+
for (int i = 0; i < VECTOR_SIZE && passed; i++) {
161+
int32_t expected = a[i] + b[i];
162+
if (result[i] != expected) {
163+
printf(" ❌ FAIL: Mismatch at [%d]: got %d, expected %d\n",
164+
i, result[i], expected);
165+
passed = false;
166+
}
167+
}
168+
169+
if (passed) {
170+
printf(" ✅ PASS: Mixed ME_BOOL and ME_AUTO work together\n");
171+
tests_passed++;
172+
}
173+
174+
me_free(expr);
175+
}
176+
}
177+
178+
printf("\n");
179+
printf("===============================\n");
180+
printf("Results: %d/%d tests passed\n", tests_passed, tests_total);
181+
printf("===============================\n");
182+
183+
return (tests_passed == tests_total) ? 0 : 1;
184+
}

0 commit comments

Comments
 (0)