Skip to content

Commit 7970ac0

Browse files
committed
Add a new test: fcalc
fcalc is a basic, fully self-contained one-function calculator. Source: https://github.com/tomolt/fcalc
1 parent 17c399a commit 7970ac0

File tree

4 files changed

+151
-0
lines changed

4 files changed

+151
-0
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ include mk/tests.mk
169169
CHECK_ELF_FILES := \
170170
hello \
171171
puzzle \
172+
fcalc
172173

173174
ifeq ($(call has, EXT_M), 1)
174175
CHECK_ELF_FILES += \
@@ -177,6 +178,7 @@ endif
177178

178179
EXPECTED_hello = Hello World!
179180
EXPECTED_puzzle = success in 2005 trials
181+
EXPECTED_fcalc = Performed 12 tests, 0 failures, 100% success rate.
180182
EXPECTED_pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086
181183

182184
check: $(BIN)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ In `rv32emu` repository, there are some prebuilt ELF files for testing purpose.
244244
* `dhrystone.elf` : See [rv8-bench](https://github.com/michaeljclark/rv8-bench)
245245
* `donut.elf` : See [donut](tests/donut.c)
246246
* `doom.elf` : See [sysprog21/doom_riscv](https://github.com/sysprog21/doom_riscv) [RV32M]
247+
* `fcalc.elf` : See [fcalc](tests/fcalc.c)
247248
* `ieee754.elf` : See [tests/ieee754.c](tests/ieee754.c) [RV32F]
248249
* `jit-bf.elf` : See [ezaki-k/xkon_beta](https://github.com/ezaki-k/xkon_beta)
249250
* `lena.elf`: See [tests/lena.c](tests/lena.c)

build/fcalc.elf

83.1 KB
Binary file not shown.

tests/fcalc.c

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/* fcalc - a basic, fully self-contained one-function calculator,
2+
* released into the public domain. written by Thomas Oltmann.
3+
*
4+
* Supported operations:
5+
* addition (+)
6+
* subtraction (-)
7+
* multiplication (*)
8+
* division (/)
9+
* negation (-)
10+
* Has a builtin 'pi' constant
11+
*
12+
* Source: https://github.com/tomolt/fcalc
13+
*/
14+
double fcalc(const char **str, int *err, int _)
15+
{
16+
switch (_) {
17+
case 0:; /* Parse full expression */
18+
const char *str1 = *str;
19+
int err1 = 0;
20+
double ret = fcalc(&str1, &err1, 1);
21+
while (*str1 != 0) {
22+
if (*str1 != ' ' && *str1 != '\n')
23+
err1 = 1;
24+
str1++;
25+
}
26+
if (err)
27+
*err = err1;
28+
return ret;
29+
case -1: /* Parse primitive */
30+
while (**str == ' ')
31+
(*str)++; /* Skip whitespace */
32+
switch (**str) {
33+
case '-': /* Parse unary minus (negation) */
34+
(*str)++;
35+
return -fcalc(str, err, -1);
36+
case '(': /* Parse subexpression in parentheses */
37+
(*str)++;
38+
double ret = fcalc(str, err, 1);
39+
if (**str != ')') {
40+
*err = -1;
41+
return 0.0;
42+
}
43+
(*str)++;
44+
return ret;
45+
case '0':
46+
case '1':
47+
case '2':
48+
case '3':
49+
case '4': /* Parse number */
50+
case '5':
51+
case '6':
52+
case '7':
53+
case '8':
54+
case '9':
55+
case '.':
56+
_ = 0;
57+
double dig = 0.0, bak = 1.0;
58+
for (;;) {
59+
if (**str == '.') {
60+
_ = 1;
61+
(*str)++;
62+
}
63+
if (!(**str >= '0' && **str <= '9'))
64+
break;
65+
if (_ == 1)
66+
bak /= 10.0;
67+
dig = dig * 10.0 + (**str - '0');
68+
(*str)++;
69+
}
70+
return dig * bak;
71+
case 'p':
72+
case 'P': /* Parse 'pi' */
73+
(*str)++;
74+
if (**str != 'i' && **str != 'I') {
75+
*err = -1;
76+
return 0.0;
77+
}
78+
(*str)++;
79+
return 3.14159265358979;
80+
default:
81+
*err = -1;
82+
return 0.0;
83+
}
84+
default:; /* Parse binary operation */
85+
double lhs = fcalc(str, err, -1);
86+
if (*err)
87+
return 0.0;
88+
for (;;) {
89+
while (**str == ' ')
90+
(*str)++; /* Skip whitespace */
91+
#define BINOP(sym, prec, op) \
92+
if (prec >= _ && **str == sym) { \
93+
(*str)++; \
94+
double rhs = fcalc(str, err, (prec + 1)); \
95+
if (*err) \
96+
return 0.0; \
97+
op; \
98+
}
99+
/* clang-format off */
100+
BINOP('+', 1, lhs += rhs)
101+
else BINOP('-', 1, lhs -= rhs)
102+
else BINOP('*', 2, lhs *= rhs)
103+
else BINOP('/', 2, lhs /= rhs)
104+
else return lhs;
105+
/* clang-format on */
106+
#undef BINOP
107+
}
108+
}
109+
}
110+
111+
#include <stdio.h>
112+
113+
static const double EPSILON = 0.00001;
114+
115+
static unsigned int fails, total;
116+
117+
static void test(const char *name, const char *str, double er, int ee)
118+
{
119+
total++;
120+
int e;
121+
double r = fcalc(&str, &e, 0);
122+
double dr = r - er;
123+
if ((dr > 0.0 ? dr : -dr) > EPSILON || e != ee) {
124+
printf("Test '%s' failed; Expected %f/%d, got %f/%d\n", name, er, ee, r,
125+
e);
126+
fails++;
127+
}
128+
}
129+
130+
int main()
131+
{
132+
test("Integer", "11", 11, 0);
133+
test("Real number", "11.32", 11.32, 0);
134+
test("Sub-0-Real", ".32", 0.32, 0);
135+
test("Negation", "-42", -42, 0);
136+
test("Addition", "1.2 + 5", 6.2, 0);
137+
test("Subtraction", "3 - 1.4", 1.6, 0);
138+
test("Multiplication", "10 * 5", 50, 0);
139+
test("Division", "50 / 10", 5, 0);
140+
test("Precedence", "3 - 2 * 5", -7, 0);
141+
test("Pi", "pi", 3.14159265358979, 0);
142+
test("Parentheses", "3 * (1 + 2)", 9, 0);
143+
test("Unmatched (", "(1 + 2", 0.0, -1);
144+
printf("Performed %d tests, %d failures, %d%% success rate.\n", total,
145+
fails, 100 - fails * 100 / total);
146+
147+
return fails == 0 ? 0 : -1;
148+
}

0 commit comments

Comments
 (0)