1 #include "mathexpr.hpp"
6 mathexpr_operinfo::mathexpr_operinfo(std::string funcname
)
7 : fnname(funcname
), is_operator(false), operands(0), precedence(0), rtl(false)
10 mathexpr_operinfo::mathexpr_operinfo(std::string opername
, unsigned _operands
, int _percedence
, bool _rtl
)
11 : fnname(opername
), is_operator(true), operands(_operands
), precedence(_percedence
), rtl(_rtl
)
14 mathexpr_operinfo::~mathexpr_operinfo()
18 mathexpr_typeinfo::~mathexpr_typeinfo()
22 mathexpr::mathexpr(mathexpr_typeinfo
* _type
)
25 owns_operator
= false;
28 fn
= (mathexpr_operinfo
*)0xDEADBEEF;
31 mathexpr::mathexpr(mathexpr_typeinfo
* _type
, gcroot_pointer
<mathexpr
> fwd
)
34 owns_operator
= false;
36 value
= type
.allocate();
37 arguments
.push_back(&*fwd
);
41 mathexpr::mathexpr(mathexpr_value _value
)
44 owns_operator
= false;
46 value
= type
.copy_allocate(_value
.value
);
50 mathexpr::mathexpr(mathexpr_typeinfo
* _type
, const std::string
& _value
, bool string
)
53 owns_operator
= false;
55 value
= type
.parse(_value
, string
);
59 mathexpr::mathexpr(mathexpr_typeinfo
* _type
, mathexpr_operinfo
* _fn
, std::vector
<gcroot_pointer
<mathexpr
>> _args
,
61 : type(*_type
), fn(_fn
), owns_operator(_owns_operator
)
65 arguments
.push_back(&*i
);
66 value
= type
.allocate();
67 state
= TO_BE_EVALUATED
;
77 if(owns_operator
&& fn
)
79 type
.deallocate(value
);
82 void mathexpr::reset()
84 if(state
== TO_BE_EVALUATED
|| state
== FIXED
|| state
== UNDEFINED
|| state
== FORWARD
)
86 if(state
== FORWARD_EVALD
|| state
== FORWARD_EVALING
) {
90 state
= TO_BE_EVALUATED
;
91 for(auto i
: arguments
)
95 mathexpr::mathexpr(const mathexpr
& m
)
96 : state(m
.state
), type(m
.type
), fn(m
.fn
), error(m
.error
), arguments(m
.arguments
)
98 value
= m
.value
? type
.copy_allocate(m
.value
) : NULL
;
99 if(state
== EVALUATING
) state
= TO_BE_EVALUATED
;
102 mathexpr
& mathexpr::operator=(const mathexpr
& m
)
106 std::string _error
= m
.error
;
107 std::vector
<mathexpr
*> _arguments
= m
.arguments
;
110 value
= m
.type
.copy_allocate(m
.value
);
112 m
.type
.copy(value
, m
.value
);
114 m
.type
.deallocate(value
);
121 owns_operator
= m
.owns_operator
;
122 m
.owns_operator
= false;
123 std::swap(arguments
, _arguments
);
124 std::swap(error
, _error
);
128 mathexpr_value
mathexpr::evaluate()
133 case TO_BE_EVALUATED
:
136 for(auto i
: arguments
) {
137 if(&i
->type
!= &type
) {
138 throw mathexpr_error(mathexpr_error::TYPE_MISMATCH
,
139 "Types for function mismatch");
143 std::vector
<std::function
<mathexpr_value()>> promises
;
144 for(auto i
: arguments
) {
146 promises
.push_back([m
]() { return m
->evaluate(); });
151 fn
->evaluate(tmp
, promises
);
153 } catch(mathexpr_error
& e
) {
155 errcode
= e
.get_code();
158 } catch(std::exception
& e
) {
160 errcode
= mathexpr_error::UNKNOWN
;
165 errcode
= mathexpr_error::UNKNOWN
;
166 error
= "Unknown error";
172 case FORWARD_EVALING
:
173 //Circular dependency.
174 mark_error_and_throw(mathexpr_error::CIRCULAR
, "Circular dependency");
181 throw mathexpr_error(mathexpr_error::UNDEFINED
, "Undefined variable");
183 throw mathexpr_error(errcode
, error
);
186 state
= FORWARD_EVALING
;
187 mathexpr_value v
= arguments
[0]->evaluate();
188 type
.copy(value
, v
.value
);
189 state
= FORWARD_EVALD
;
196 throw mathexpr_error(mathexpr_error::INTERNAL
, "Internal error (shouldn't be here)");
199 void mathexpr::trace()
201 for(auto i
: arguments
)
205 void mathexpr::mark_error_and_throw(mathexpr_error::errorcode _errcode
, const std::string
& _error
)
207 if(state
== EVALUATING
) {
212 if(state
== FORWARD_EVALING
) {
215 throw mathexpr_error(_errcode
, _error
);
224 X_EXPR -> FUNCTION X_ARGS
225 X_EXPR -> UNARY-OP X_EXPR
226 X_EXPR -> X_LAMBDA X_EXPR
227 X_LAMBDA -> X_EXPR BINARY-OP
228 X_ARGS -> OPEN-PAREN CLOSE_PAREN
229 X_ARGS -> OPEN-PAREN X_TAIL
230 X_TAIL -> X_PAIR X_TAIL
231 X_TAIL -> X_EXPR CLOSE-PAREN
232 X_PAIR -> X_EXPR COMMA
235 //SUBEXPRESSION -> VALUE
236 //SUBEXPRESSION -> STRING
237 //SUBEXPRESSION -> FUNCTION OPEN-PAREN CLOSE-PAREN
238 //SUBEXPRESSION -> FUNCTION OPEN-PAREN (SUBEXPRESSION COMMA)* SUBEXPRESSION CLOSE-PAREN
239 //SUBEXPRESSION -> OPEN-PAREN SUBEXPRESSION CLOSE-PAREN
240 //SUBEXPRESSION -> SUBEXPRESSION BINARY-OP SUBEXPRESSION
241 //SUBEXPRESSION -> UNARY-OP SUBEXPRESSION
242 //SUBEXPRESSION -> NONARY-OP
244 bool is_alphanumeric(char ch
)
246 if(ch
>= '0' && ch
<= '9') return true;
247 if(ch
>= 'a' && ch
<= 'z') return true;
248 if(ch
>= 'A' && ch
<= 'Z') return true;
249 if(ch
== '_') return true;
250 if(ch
== '.') return true;
266 struct operations_set
268 operations_set(std::set
<mathexpr_operinfo
*>& ops
)
272 mathexpr_operinfo
* find_function(const std::string
& name
)
274 mathexpr_operinfo
* fn
= NULL
;
275 for(auto j
: operations
) {
276 if(name
== j
->fnname
&& !j
->is_operator
)
279 if(!fn
) throw std::runtime_error("No such function '" + name
+ "'");
282 mathexpr_operinfo
* find_operator(const std::string
& name
, unsigned arity
)
284 for(auto j
: operations
) {
285 if(name
== j
->fnname
&& j
->is_operator
&& j
->operands
== arity
)
291 std::set
<mathexpr_operinfo
*>& operations
;
296 subexpression(token_kind k
) : kind(k
) {}
297 subexpression(token_kind k
, const std::string
& str
) : kind(k
), string(str
) {}
302 size_t find_last_in_sub(std::vector
<subexpression
>& ss
, size_t first
)
305 switch(ss
[first
].kind
) {
307 if(first
+ 1 == ss
.size() || ss
[first
+ 1].kind
!= TT_OPEN_PAREN
)
308 throw std::runtime_error("Function requires argument list");
312 while(first
< ss
.size()) {
313 if(ss
[first
].kind
== TT_OPEN_PAREN
)
315 if(ss
[first
].kind
== TT_CLOSE_PAREN
)
319 if(first
== ss
.size())
320 throw std::runtime_error("Unmatched '('");
323 throw std::runtime_error("Unmatched ')'");
325 throw std::runtime_error("',' only allowed in function arguments");
332 throw std::runtime_error("Internal error (shouldn't be here)");
335 size_t find_end_of_arg(std::vector
<subexpression
>& ss
, size_t first
)
338 while(first
< ss
.size()) {
339 if(depth
== 0 && ss
[first
].kind
== TT_COMMA
)
341 if(ss
[first
].kind
== TT_OPEN_PAREN
)
343 if(ss
[first
].kind
== TT_CLOSE_PAREN
) {
355 expr_or_op(gcroot_pointer
<mathexpr
> e
) : expr(e
), typei(NULL
) {}
356 expr_or_op(std::string o
) : op(o
), typei(NULL
) {}
357 gcroot_pointer
<mathexpr
> expr
;
359 mathexpr_operinfo
* typei
;
362 gcroot_pointer
<mathexpr
> parse_rec(mathexpr_typeinfo
& _type
, std::vector
<expr_or_op
>& operands
,
363 size_t first
, size_t last
)
366 return gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
);
367 if(last
- first
> 1) {
368 //Find the highest percedence operator.
370 for(size_t i
= first
; i
< last
; i
++) {
371 if(operands
[i
].typei
) {
374 else if(operands
[i
].typei
->precedence
< operands
[best
].typei
->precedence
) {
376 } else if(!operands
[best
].typei
->rtl
&&
377 operands
[i
].typei
->precedence
== operands
[best
].typei
->precedence
) {
382 if(best
== last
) throw std::runtime_error("Internal error: No operands?");
383 if(operands
[best
].typei
->operands
== 1) {
384 //The operator is unary, collect up all following unary operators.
386 while(operands
[j
].typei
)
388 std::vector
<gcroot_pointer
<mathexpr
>> args
;
389 args
.push_back(parse_rec(_type
, operands
, first
+ 1, j
+ 1));
390 return gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
,
391 operands
[best
].typei
, args
);
394 std::vector
<gcroot_pointer
<mathexpr
>> args
;
395 args
.push_back(parse_rec(_type
, operands
, first
, best
));
396 args
.push_back(parse_rec(_type
, operands
, best
+ 1, last
));
397 return gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
,
398 operands
[best
].typei
, args
);
401 return operands
[first
].expr
;
404 gcroot_pointer
<mathexpr
> parse_rec(mathexpr_typeinfo
& _type
, std::vector
<subexpression
>& ss
,
405 std::set
<mathexpr_operinfo
*>& operations
,
406 std::function
<gcroot_pointer
<mathexpr
>(const std::string
&)> vars
, size_t first
, size_t last
)
408 operations_set
opset(operations
);
409 std::vector
<expr_or_op
> operands
;
410 std::vector
<gcroot_pointer
<mathexpr
>> args
;
411 mathexpr_operinfo
* fn
;
412 for(size_t i
= first
; i
< last
; i
++) {
413 size_t l
= find_last_in_sub(ss
, i
);
414 if(l
>= last
) throw std::runtime_error("Internal error: Improper nesting");
417 operands
.push_back(parse_rec(_type
, ss
, operations
, vars
, i
+ 1, l
));
420 operands
.push_back(gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
,
421 ss
[i
].string
, false));
424 operands
.push_back(gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
,
425 ss
[i
].string
, true));
428 //We have to warp this is identify transform to make the evaluation lazy.
429 operands
.push_back(gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
,
430 vars(ss
[i
].string
)));
433 fn
= opset
.find_function(ss
[i
].string
);
435 while(ss
[i
].kind
!= TT_CLOSE_PAREN
) {
436 size_t k
= find_end_of_arg(ss
, i
);
437 args
.push_back(parse_rec(_type
, ss
, operations
, vars
, i
, k
));
438 if(k
< ss
.size() && ss
[k
].kind
== TT_COMMA
)
443 operands
.push_back(gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
, fn
,
448 operands
.push_back(ss
[i
].string
);
457 throw std::runtime_error("Empty subexpression");
458 //Translate nonary operators to values.
459 for(auto& i
: operands
) {
461 auto fn
= opset
.find_operator(i
.op
, 0);
463 i
.expr
= gcroot_pointer
<mathexpr
>(gcroot_pointer_object_tag(), &_type
, fn
,
464 std::vector
<gcroot_pointer
<mathexpr
>>());
467 //Check that there aren't two consequtive subexpressions and mark operators.
468 bool was_operand
= false;
469 for(auto& i
: operands
) {
470 bool is_operand
= (bool)i
.expr
;
471 if(!is_operand
&& !was_operand
)
472 if(!(i
.typei
= opset
.find_operator(i
.op
, 1)))
473 throw std::runtime_error("'" + i
.op
+ "' is not an unary operator");
474 if(!is_operand
&& was_operand
)
475 if(!(i
.typei
= opset
.find_operator(i
.op
, 2)))
476 throw std::runtime_error("'" + i
.op
+ "' is not a binary operator");
477 if(was_operand
&& is_operand
)
478 throw std::runtime_error("Expected operator, got operand");
479 was_operand
= is_operand
;
482 throw std::runtime_error("Expected operand, got end of subexpression");
483 //Okay, now the expression has been reduced into series of operators and subexpressions.
484 //If there are multiple consequtive operators, the first (except as first item) is binary,
485 //and the others are unary.
486 return parse_rec(_type
, operands
, 0, operands
.size());
489 void tokenize(const std::string
& expr
, std::set
<mathexpr_operinfo
*>& operations
,
490 std::vector
<subexpression
>& tokenization
)
492 for(size_t i
= 0; i
< expr
.length();) {
494 tokenization
.push_back(subexpression(TT_OPEN_PAREN
));
496 } else if(expr
[i
] == ')') {
497 tokenization
.push_back(subexpression(TT_CLOSE_PAREN
));
499 } else if(expr
[i
] == ',') {
500 tokenization
.push_back(subexpression(TT_COMMA
));
502 } else if(expr
[i
] == ' ') {
504 } else if(expr
[i
] == '$') {
505 //Variable. If the next character is {, parse until }, otherwise parse until
507 std::string varname
= "";
508 if(i
+ 1 < expr
.length() && expr
[i
+ 1] == '{') {
511 while(i
+ 1 < expr
.length() && expr
[i
+ 1] != '}')
512 varname
+= std::string(1, expr
[++i
]);
513 if(i
+ 1 >= expr
.length() || expr
[i
+ 1] != '}')
514 throw std::runtime_error("${ without matching }");
517 //Terminate by non-alphanum.
518 while(i
+ 1 < expr
.length() && is_alphanumeric(expr
[i
+ 1]))
519 varname
+= std::string(1, expr
[++i
]);
521 tokenization
.push_back(subexpression(TT_VARIABLE
, varname
));
523 } else if(expr
[i
] == '"') {
527 while(endpos
< expr
.length() && (escape
|| expr
[endpos
] != '"')) {
529 if(expr
[endpos
] == '\\')
537 if(endpos
== expr
.length())
538 throw std::runtime_error("Unmatched \"");
539 //Copy (i,endpos-1) and descape.
542 for(size_t j
= i
+ 1; j
< endpos
; j
++) {
545 tmp
+= std::string(1, expr
[j
]);
549 tmp
+= std::string(1, expr
[j
]);
553 tokenization
.push_back(subexpression(TT_STRING
, tmp
));
557 //Function names are only recognized if it begins here and is followed by '('.
558 for(auto j
: operations
) {
559 if(j
->is_operator
) continue; //Not a function.
560 if(i
+ j
->fnname
.length() + 1 > expr
.length()) continue; //Too long.
561 if(expr
[i
+ j
->fnname
.length()] != '(') continue; //Not followed by '('.
562 for(size_t k
= 0; k
< j
->fnname
.length(); k
++)
563 if(expr
[i
+ k
] != j
->fnname
[k
]) goto nomatch
; //No match.
564 tokenization
.push_back(subexpression(TT_FUNCTION
, j
->fnname
));
565 i
+= j
->fnname
.length();
571 //Operators. These use longest match rule.
572 size_t longest_match
= 0;
574 for(auto j
: operations
) {
575 if(!j
->is_operator
) continue; //Not an operator.
576 if(i
+ j
->fnname
.length() > expr
.length()) continue; //Too long.
577 for(size_t k
= 0; k
< j
->fnname
.length(); k
++)
578 if(expr
[i
+ k
] != j
->fnname
[k
]) goto next
; //No match.
579 if(j
->fnname
.length() <= longest_match
) continue; //Not longest.
582 longest_match
= op
.length();
586 tokenization
.push_back(subexpression(TT_OPERATOR
, op
));
590 //Okay, token until next non-alphanum.
592 while(i
< expr
.length() && is_alphanumeric(expr
[i
]))
593 tmp
+= std::string(1, expr
[i
++]);
595 tokenization
.push_back(subexpression(TT_VALUE
, tmp
));
601 for(j
= i
; j
< expr
.length() && (j
< i
+ 20 || utfcount
); j
++) {
602 if(utfcount
) utfcount
--;
603 summary
+= std::string(1, expr
[j
]);
604 if((uint8_t)expr
[j
] >= 0xF0) utfcount
= 3;
605 if((uint8_t)expr
[j
] >= 0xE0) utfcount
= 2;
606 if((uint8_t)expr
[j
] >= 0xC0) utfcount
= 1;
607 if((uint8_t)expr
[j
] < 0x80) utfcount
= 0;
609 if(j
< expr
.length()) summary
+= "[...]";
610 throw std::runtime_error("Expression parse error, at '" + summary
+ "'");
616 gcroot_pointer
<mathexpr
> mathexpr::parse(mathexpr_typeinfo
& _type
, const std::string
& expr
,
617 std::function
<gcroot_pointer
<mathexpr
>(const std::string
&)> vars
)
620 throw std::runtime_error("Empty expression");
621 auto operations
= _type
.operations();
622 std::vector
<subexpression
> tokenization
;
623 tokenize(expr
, operations
, tokenization
);
624 return parse_rec(_type
, tokenization
, operations
, vars
, 0, tokenization
.size());