/
rpn.cxx
164 lines (134 loc) · 3.8 KB
/
rpn.cxx
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <cmath>
#include <cstring>
#include <memory>
#include <iostream>
#include <sstream>
#include <stack>
namespace rpn {
enum class kind_t {
var, // A variable or number
op, // + - * /
f1, // unary function
f2, // binary function
};
struct token_t {
kind_t kind;
std::string text;
};
struct token_name_t {
kind_t kind;
const char* string;
};
const token_name_t token_names[] {
// Supported with @op
{ kind_t::op, "+" },
{ kind_t::op, "-" },
{ kind_t::op, "*" },
{ kind_t::op, "/" },
// Unary functions
{ kind_t::f1, "abs" },
{ kind_t::f1, "exp" },
{ kind_t::f1, "log" },
{ kind_t::f1, "sqrt" },
{ kind_t::f1, "sin" },
{ kind_t::f1, "cos" },
{ kind_t::f1, "tan" },
{ kind_t::f1, "asin" },
{ kind_t::f1, "acos" },
{ kind_t::f1, "atan" },
// Binary functions
{ kind_t::f2, "atan2" },
{ kind_t::f2, "pow" },
};
inline kind_t find_token_kind(const char* text) {
for(token_name_t name : token_names) {
if(!strcmp(text, name.string))
return name.kind;
}
return kind_t::var;
}
struct node_t;
typedef std::unique_ptr<node_t> node_ptr_t;
struct node_t {
kind_t kind;
std::string text;
node_ptr_t a, b;
};
inline node_ptr_t parse(const char* text) {
std::istringstream iss(text);
std::string token;
std::stack<node_ptr_t> stack;
while(iss>> token) {
// Make operator ^ call pow.
if("^" == token)
token = "pow";
// Match against any of the known functions.
kind_t kind = find_token_kind(token.c_str());
node_ptr_t node = std::make_unique<node_t>();
node->kind = kind;
node->text = token;
switch(kind) {
case kind_t::var:
// Do nothing before pushing the node.
break;
case kind_t::f1:
if(stack.size() < 1)
throw std::range_error("RPN formula is invalid");
node->a = std::move(stack.top()); stack.pop();
break;
case kind_t::op:
case kind_t::f2:
// Binary operators and functions pop two items from the stack,
// combine them, and push the result.
if(stack.size() < 2)
throw std::range_error("RPN formula is invalid");
node->b = std::move(stack.top()); stack.pop();
node->a = std::move(stack.top()); stack.pop();
break;
}
stack.push(std::move(node));
}
if(1 != stack.size())
throw std::range_error("RPN formula is invalid");
return std::move(stack.top());
}
@macro auto eval_node(const node_t* __node) {
@meta+ if(rpn::kind_t::var == __node->kind) {
@emit return @expression(__node->text);
} else if(rpn::kind_t::op == __node->kind) {
@emit return @op(
__node->text,
rpn::eval_node(__node->a.get()),
rpn::eval_node(__node->b.get())
);
} else if(rpn::kind_t::f1 == __node->kind) {
// Call a unary function.
@emit return @(__node->text)(
rpn::eval_node(__node->a.get())
);
} else if(rpn::kind_t::f2 == __node->kind) {
// Call a binary function.
@emit return @(__node->text)(
rpn::eval_node(__node->a.get()),
rpn::eval_node(__node->b.get())
);
}
}
// Define an expression macro for evaluating the RPN expression. This
// expands into the expression context from which it is called. It can have
// any number of meta statements, but only one real return statement, and no
// real declarations (because such declarations are prohibited inside
// expressions).
@macro auto eval(const char* __text) {
@meta rpn::node_ptr_t __node = rpn::parse(__text);
return rpn::eval_node(__node.get());
}
} // namespace rpn
double my_function(double x, double y, double z) {
return rpn::eval("z 1 x / sin y * ^");
}
int main() {
double result = my_function(.3, .6, .9);
printf("%f\n", result);
return 0;
}