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
|
/*
* LLCalcParser.h
* SecondLife
*
* Created by Aimee Walton on 28/09/2008.
* Copyright 2008 Aimee Walton.
*
*/
#ifndef LL_CALCPARSER_H
#define LL_CALCPARSER_H
#include <boost/spirit/attribute.hpp>
#include <boost/spirit/core.hpp>
#include <boost/spirit/error_handling.hpp>
#include <boost/spirit/iterator/position_iterator.hpp>
#include <boost/spirit/phoenix/binders.hpp>
//#include <boost/spirit/symbols/symbols.hpp>
#include <map>
#include <string>
#include "llcalc.h"
#include "llmath.h"
struct LLCalcParser : boost::spirit::grammar<LLCalcParser>
{
LLCalcParser(F32& result, LLCalc::calc_map_t* constants, LLCalc::calc_map_t* vars) :
mResult(result), mConstants(constants), mVariables(vars) {};
struct value_closure : boost::spirit::closure<value_closure, F32>
{
member1 value;
};
template <typename ScannerT>
struct definition
{
// Rule declarations
boost::spirit::rule<ScannerT> statement, identifier;
boost::spirit::rule<ScannerT, value_closure::context_t> expression, term,
power,
unary_expr,
factor,
/*unary_func,
/binary_func,*/
group;
// start() should return the starting symbol
boost::spirit::rule<ScannerT> const& start() const { return statement; }
definition(LLCalcParser const& self)
{
using namespace boost::spirit;
using namespace phoenix;
assertion<std::string> assert_domain("Domain error");
// assertion<std::string> assert_symbol("Unknown symbol");
assertion<std::string> assert_syntax("Syntax error");
identifier =
lexeme_d[(alpha_p | '_') >> *(alnum_p | '_')]
;
group =
'(' >> expression[group.value = arg1] >> assert_syntax(ch_p(')'))
;
/*unary_func =
((str_p("SIN") >> '(' >> expression[unary_func.value = bind(&sin)(DEG_TO_RAD * arg1)]) |
(str_p("COS") >> '(' >> expression[unary_func.value = bind(&cos)(DEG_TO_RAD * arg1)]) |
(str_p("TAN") >> '(' >> expression[unary_func.value = bind(&tan)(DEG_TO_RAD * arg1)]) |
(str_p("ASIN") >> '(' >> expression[unary_func.value = (bind(&asin)(arg1)) * RAD_TO_DEG]) |
(str_p("ACOS") >> '(' >> expression[unary_func.value = bind(&acos)(arg1) * RAD_TO_DEG]) |
(str_p("ATAN") >> '(' >> expression[unary_func.value = bind(&atan)(arg1) * RAD_TO_DEG]) |
(str_p("SQRT") >> '(' >> expression[unary_func.value = bind(&sqrt)(arg1)]) |
(str_p("LOG") >> '(' >> expression[unary_func.value = bind(&log)(arg1)]) |
(str_p("EXP") >> '(' >> expression[unary_func.value = bind(&exp)(arg1)]) |
(str_p("ABS") >> '(' >> expression[unary_func.value = bind(&fabs)(arg1)])
) >> assert_syntax(ch_p(')'))
;
binary_func =
((str_p("ATAN2") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&atan2)(binary_func.value, arg1) * RAD_TO_DEG]) |
(str_p("MIN") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&LLCalcParser::min)(self, binary_func.value, arg1)]) |
(str_p("MAX") >> '(' >> expression[binary_func.value = arg1] >> ',' >>
expression[binary_func.value = bind(&LLCalcParser::max)(self, binary_func.value, arg1)])
) >> assert_syntax(ch_p(')'))
;*/
// *TODO: Localisation of the decimal point?
// Problem, LLLineEditor::postvalidateFloat accepts a comma when appropriate
// for the current locale. However to do that here could clash with using
// the comma as a separator when passing arguments to functions.
factor =
(ureal_p[factor.value = arg1] |
group[factor.value = arg1] |
/*unary_func[factor.value = arg1] |
binary_func[factor.value = arg1] |*/
// Lookup throws an Unknown Symbol error if it is unknown, while this works fine,
// would be "neater" to handle symbol lookup from here with an assertive parser.
// constants_p[factor.value = arg1]|
identifier[factor.value = bind(&LLCalcParser::lookup)(self, arg1, arg2)]
) >>
// Detect and throw math errors.
assert_domain(eps_p(bind(&LLCalcParser::checkNaN)(self, factor.value)))
;
unary_expr =
!ch_p('+') >> factor[unary_expr.value = arg1] |
'-' >> factor[unary_expr.value = -arg1]
;
power =
unary_expr[power.value = arg1] >>
*('^' >> assert_syntax(unary_expr[power.value = bind(&powf)(power.value, arg1)]))
;
term =
power[term.value = arg1] >>
*(('*' >> assert_syntax(power[term.value *= arg1])) |
('/' >> assert_syntax(power[term.value /= arg1]))
)
;
expression =
assert_syntax(term[expression.value = arg1]) >>
*(('+' >> assert_syntax(term[expression.value += arg1])) |
('-' >> assert_syntax(term[expression.value -= arg1]))
)
;
statement =
!ch_p('=') >> ( expression )[var(self.mResult) = arg1] >> (end_p)
;
}
};
private:
// Member functions for semantic actions
F32 lookup(const std::string::iterator&, const std::string::iterator&) const;
F32 min(const F32& a, const F32& b) const { return llmin(a, b); }
F32 max(const F32& a, const F32& b) const { return llmax(a, b); }
bool checkNaN(const F32& a) const { return !llisnan(a); }
LLCalc::calc_map_t* mConstants;
LLCalc::calc_map_t* mVariables;
// LLCalc::calc_map_t* mUserVariables;
F32& mResult;
};
#endif // LL_CALCPARSER_H
|