-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathexpression_parser.cpp
More file actions
147 lines (129 loc) · 5.39 KB
/
expression_parser.cpp
File metadata and controls
147 lines (129 loc) · 5.39 KB
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
/*
* Implementation of ExpressionParser.
*/
#include "expression_parser.h"
#include <iostream>
/* Seed the JavaScript interpreter with some useful math functions. */
static const char js_library[] = R"--(
/* Copy all the contents of the Math object (sqrt, sin, log, PI...) to the global object. */
Object.getOwnPropertyNames(Math).forEach(
function(name) { globalThis[name] = Math[name]; }
);
/* Shifted exp() and log(). */
function expm1(x) { return abs(x)<2e-8 ? x : exp(x) - 1; }
function log1p(x) { return abs(x)<2e-8 ? x : log(1 + x); }
/* Hyperbolic functions. */
function cosh(x) { return exp(x - LN2) + exp(-x - LN2); }
function sinh(x) { return abs(x)<1e-5 ? x : exp(x - LN2) - exp(-x - LN2); }
function tanh(x) { return abs(x)>20 ? sign(x) : expm1(2*x)/(exp(2*x) + 1); }
/* Inverse hyperbolic functions. */
function acosh(x) { return x>1e8 ? log(x) + LN2 : log(x + sqrt(x*x - 1)); }
function asinh(x) { return abs(x)<1e-5 ? x : sign(x) * (log(abs(x)/2 + hypot(1, x)/2) + LN2); }
function atanh(x) { return abs(x)<1e-5 ? x : log((1 + x)/(1 - x)) / 2; }
/* Simplified, one-argument version of Array.prototype.includes(). */
Array.prototype.includes = function(value) { return this.indexOf(value) != -1; };
)--";
// JavaScript print(): output debug messages from a user JavaScript function.
static duk_ret_t native_print(duk_context *ctx) {
std::cout << "SCRIPT:";
for (int i = 0; i < duk_get_top(ctx); ++i)
std::cout << ' ' << duk_to_string(ctx, i);
std::cout << '\n';
return 0; /* no return value (= undefined) */
}
ExpressionParser::ExpressionParser()
{
ctx = duk_create_heap_default();
duk_push_c_function(ctx, native_print, DUK_VARARGS);
duk_put_global_string(ctx, "print");
duk_int_t err = duk_peval_string(ctx, js_library);
die_if_error(err);
duk_pop(ctx); // drop the result of the evaluation
}
ExpressionParser::~ExpressionParser() { duk_destroy_heap(ctx); }
void ExpressionParser::die_if_error(duk_int_t err) const
{
if (err == DUK_ERR_NONE) return;
std::cerr << "Script " << duk_safe_to_string(ctx, -1) << '\n';
exit(EXIT_FAILURE);
}
// Compile a function expression and leave it as the sole value on the Duktape stack.
void ExpressionParser::set_function(const std::string &js_function) const
{
duk_set_top(ctx, 0); // clear the stack
duk_int_t err = duk_pcompile_string(ctx, DUK_COMPILE_FUNCTION, js_function.c_str());
die_if_error(err);
if (duk_get_top(ctx) != 1)
{
std::cerr << "Compilation of JavaScript function damaged the stack.\n";
exit(EXIT_FAILURE);
}
}
void ExpressionParser::set_expressions(const std::string ¶meters, const std::string &expr_x,
const std::string &expr_y, const std::string &expr_z)
{
set_function("function(" + parameters + ") { return [(" + expr_x + "), (" + expr_y + "), ("
+ expr_z + ")]; }");
}
double ExpressionParser::get_scalar(double arg) const
{
duk_dup(ctx, -1); // -> [ f f ]
duk_push_number(ctx, arg); // -> [ f f arg ]
duk_int_t err = duk_pcall(ctx, 1); // -> [ f result ]
die_if_error(err);
double result = duk_get_number(ctx, -1);
duk_pop(ctx); // -> [ f ]
return result;
}
double ExpressionParser::get_vector_component(int idx) const
{
duk_push_int(ctx, idx); // [ ... v ] -> [ ... v idx ]
duk_bool_t success = duk_get_prop(ctx, -2); // -> [ ... v v[idx] ]
if (!success)
{
std::cerr << "JavaScript function did not return a 3-vector.\n";
exit(EXIT_FAILURE);
}
double val = duk_get_number(ctx, -1);
duk_pop(ctx); // -> [ ... v ]
return val;
}
Eigen::Vector3d ExpressionParser::compute_vector(int argument_count) const
{
duk_int_t err = duk_pcall(ctx, argument_count); // [ f f args... ] -> [ f v ]
die_if_error(err);
double x = get_vector_component(0);
double y = get_vector_component(1);
double z = get_vector_component(2);
duk_pop(ctx); // -> [ f ]
return Eigen::Vector3d(x, y, z);
}
Eigen::Vector3d ExpressionParser::get_vector(double arg) const
{
duk_dup(ctx, -1); // -> [ f f ]
duk_push_number(ctx, arg); // -> [ f f arg ]
return compute_vector(1);
}
Eigen::Vector3d ExpressionParser::get_vector(const Eigen::Ref<Eigen::Vector3d> arg) const
{
duk_dup(ctx, -1); // -> [ f f ]
duk_push_number(ctx, arg.x()); // -> [ f f arg.x ]
duk_push_number(ctx, arg.y()); // -> [ f f arg.x arg.y ]
duk_push_number(ctx, arg.z()); // -> [ f f arg.x arg.y arg.z ]
return compute_vector(3);
}
Eigen::Vector3d ExpressionParser::get_vector(const Eigen::Ref<Eigen::Vector3d> position,
const std::vector<std::string> ®ions) const
{
duk_dup(ctx, -1); // -> [ f f ]
duk_push_number(ctx, position.x()); // -> [ f f x ]
duk_push_number(ctx, position.y()); // -> [ f f x y ]
duk_push_number(ctx, position.z()); // -> [ f f x y z ]
duk_push_array(ctx); // -> [ f f x y z [] ]
for (size_t i = 0; i < regions.size(); ++i)
{
duk_push_string(ctx, regions[i].c_str()); // -> [ f f x y z [] "reg_name" ]
duk_put_prop_index(ctx, -2, i); // -> [ f f x y z ["reg_name"] ]
}
return compute_vector(4);
}