1 #include "mathexpr.hpp"
8 operinfo::operinfo(std::string funcname
)
9 : fnname(funcname
), is_operator(false), operands(0), precedence(0), rtl(false)
12 operinfo::operinfo(std::string opername
, unsigned _operands
, int _percedence
, bool _rtl
)
13 : fnname(opername
), is_operator(true), operands(_operands
), precedence(_percedence
), rtl(_rtl
)
24 mathexpr::mathexpr(typeinfo
* _type
)
27 owns_operator
= false;
30 fn
= (operinfo
*)0xDEADBEEF;
33 mathexpr::mathexpr(typeinfo
* _type
, GC::pointer
<mathexpr
> fwd
)
36 owns_operator
= false;
38 _value
= type
.allocate();
39 arguments
.push_back(&*fwd
);
43 mathexpr::mathexpr(value _val
)
46 owns_operator
= false;
48 _value
= type
.copy_allocate(_val
._value
);
52 mathexpr::mathexpr(typeinfo
* _type
, const std::string
& _val
, bool string
)
55 owns_operator
= false;
57 _value
= type
.parse(_val
, string
);
61 mathexpr::mathexpr(typeinfo
* _type
, operinfo
* _fn
, std::vector
<GC::pointer
<mathexpr
>> _args
, bool _owns_operator
)
62 : type(*_type
), fn(_fn
), owns_operator(_owns_operator
)
66 arguments
.push_back(&*i
);
67 _value
= type
.allocate();
68 state
= TO_BE_EVALUATED
;
78 if(owns_operator
&& fn
)
80 type
.deallocate(_value
);
83 void mathexpr::reset()
85 if(state
== TO_BE_EVALUATED
|| state
== FIXED
|| state
== UNDEFINED
|| state
== FORWARD
)
87 if(state
== FORWARD_EVALD
|| state
== FORWARD_EVALING
) {
91 state
= TO_BE_EVALUATED
;
92 for(auto i
: arguments
)
96 mathexpr::mathexpr(const mathexpr
& m
)
97 : state(m
.state
), type(m
.type
), fn(m
.fn
), _error(m
._error
), arguments(m
.arguments
)
99 _value
= m
._value
? type
.copy_allocate(m
._value
) : NULL
;
100 if(state
== EVALUATING
) state
= TO_BE_EVALUATED
;
103 mathexpr
& mathexpr::operator=(const mathexpr
& m
)
107 std::string _xerror
= m
._error
;
108 std::vector
<mathexpr
*> _arguments
= m
.arguments
;
111 _value
= m
.type
.copy_allocate(m
._value
);
113 m
.type
.copy(_value
, m
._value
);
115 m
.type
.deallocate(_value
);
122 owns_operator
= m
.owns_operator
;
123 m
.owns_operator
= false;
124 std::swap(arguments
, _arguments
);
125 std::swap(_error
, _xerror
);
129 value
mathexpr::evaluate()
134 case TO_BE_EVALUATED
:
137 for(auto i
: arguments
) {
138 if(&i
->type
!= &type
) {
139 throw error(error::TYPE_MISMATCH
,
140 "Types for function mismatch");
144 std::vector
<std::function
<value()>> promises
;
145 for(auto i
: arguments
) {
147 promises
.push_back([m
]() { return m
->evaluate(); });
152 fn
->evaluate(tmp
, promises
);
156 errcode
= e
.get_code();
159 } catch(std::exception
& e
) {
161 errcode
= error::UNKNOWN
;
166 errcode
= error::UNKNOWN
;
167 _error
= "Unknown error";
173 case FORWARD_EVALING
:
174 //Circular dependency.
175 mark_error_and_throw(error::CIRCULAR
, "Circular dependency");
182 throw error(error::UNDEFINED
, "Undefined variable");
184 throw error(errcode
, _error
);
187 state
= FORWARD_EVALING
;
188 value v
= arguments
[0]->evaluate();
189 type
.copy(_value
, v
._value
);
190 state
= FORWARD_EVALD
;
197 throw error(error::INTERNAL
, "Internal error (shouldn't be here)");
200 void mathexpr::trace()
202 for(auto i
: arguments
)
206 void mathexpr::mark_error_and_throw(error::errorcode _errcode
, const std::string
& _xerror
)
208 if(state
== EVALUATING
) {
213 if(state
== FORWARD_EVALING
) {
216 throw error(_errcode
, _error
);
225 X_EXPR -> FUNCTION X_ARGS
226 X_EXPR -> UNARY-OP X_EXPR
227 X_EXPR -> X_LAMBDA X_EXPR
228 X_LAMBDA -> X_EXPR BINARY-OP
229 X_ARGS -> OPEN-PAREN CLOSE_PAREN
230 X_ARGS -> OPEN-PAREN X_TAIL
231 X_TAIL -> X_PAIR X_TAIL
232 X_TAIL -> X_EXPR CLOSE-PAREN
233 X_PAIR -> X_EXPR COMMA
236 //SUBEXPRESSION -> VALUE
237 //SUBEXPRESSION -> STRING
238 //SUBEXPRESSION -> FUNCTION OPEN-PAREN CLOSE-PAREN
239 //SUBEXPRESSION -> FUNCTION OPEN-PAREN (SUBEXPRESSION COMMA)* SUBEXPRESSION CLOSE-PAREN
240 //SUBEXPRESSION -> OPEN-PAREN SUBEXPRESSION CLOSE-PAREN
241 //SUBEXPRESSION -> SUBEXPRESSION BINARY-OP SUBEXPRESSION
242 //SUBEXPRESSION -> UNARY-OP SUBEXPRESSION
243 //SUBEXPRESSION -> NONARY-OP
245 bool is_alphanumeric(char ch
)
247 if(ch
>= '0' && ch
<= '9') return true;
248 if(ch
>= 'a' && ch
<= 'z') return true;
249 if(ch
>= 'A' && ch
<= 'Z') return true;
250 if(ch
== '_') return true;
251 if(ch
== '.') return true;
267 struct operations_set
269 operations_set(std::set
<operinfo
*>& ops
)
273 operinfo
* find_function(const std::string
& name
)
276 for(auto j
: operations
) {
277 if(name
== j
->fnname
&& !j
->is_operator
)
280 if(!fn
) throw std::runtime_error("No such function '" + name
+ "'");
283 operinfo
* find_operator(const std::string
& name
, unsigned arity
)
285 for(auto j
: operations
) {
286 if(name
== j
->fnname
&& j
->is_operator
&& j
->operands
== arity
)
292 std::set
<operinfo
*>& operations
;
297 subexpression(token_kind k
) : kind(k
) {}
298 subexpression(token_kind k
, const std::string
& str
) : kind(k
), string(str
) {}
303 size_t find_last_in_sub(std::vector
<subexpression
>& ss
, size_t first
)
306 switch(ss
[first
].kind
) {
308 if(first
+ 1 == ss
.size() || ss
[first
+ 1].kind
!= TT_OPEN_PAREN
)
309 throw std::runtime_error("Function requires argument list");
313 while(first
< ss
.size()) {
314 if(ss
[first
].kind
== TT_OPEN_PAREN
)
316 if(ss
[first
].kind
== TT_CLOSE_PAREN
)
320 if(first
== ss
.size())
321 throw std::runtime_error("Unmatched '('");
324 throw std::runtime_error("Unmatched ')'");
326 throw std::runtime_error("',' only allowed in function arguments");
333 throw std::runtime_error("Internal error (shouldn't be here)");
336 size_t find_end_of_arg(std::vector
<subexpression
>& ss
, size_t first
)
339 while(first
< ss
.size()) {
340 if(depth
== 0 && ss
[first
].kind
== TT_COMMA
)
342 if(ss
[first
].kind
== TT_OPEN_PAREN
)
344 if(ss
[first
].kind
== TT_CLOSE_PAREN
) {
356 expr_or_op(GC::pointer
<mathexpr
> e
) : expr(e
), typei(NULL
) {}
357 expr_or_op(std::string o
) : op(o
), typei(NULL
) {}
358 GC::pointer
<mathexpr
> expr
;
363 GC::pointer
<mathexpr
> parse_rec(typeinfo
& _type
, std::vector
<expr_or_op
>& operands
,
364 size_t first
, size_t last
)
367 return GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
);
368 if(last
- first
> 1) {
369 //Find the highest percedence operator.
371 for(size_t i
= first
; i
< last
; i
++) {
372 if(operands
[i
].typei
) {
375 else if(operands
[i
].typei
->precedence
< operands
[best
].typei
->precedence
) {
377 } else if(!operands
[best
].typei
->rtl
&&
378 operands
[i
].typei
->precedence
== operands
[best
].typei
->precedence
) {
383 if(best
== last
) throw std::runtime_error("Internal error: No operands?");
384 if(operands
[best
].typei
->operands
== 1) {
385 //The operator is unary, collect up all following unary operators.
387 while(operands
[j
].typei
)
389 std::vector
<GC::pointer
<mathexpr
>> args
;
390 args
.push_back(parse_rec(_type
, operands
, first
+ 1, j
+ 1));
391 return GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
,
392 operands
[best
].typei
, args
);
395 std::vector
<GC::pointer
<mathexpr
>> args
;
396 args
.push_back(parse_rec(_type
, operands
, first
, best
));
397 args
.push_back(parse_rec(_type
, operands
, best
+ 1, last
));
398 return GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
,
399 operands
[best
].typei
, args
);
402 return operands
[first
].expr
;
405 GC::pointer
<mathexpr
> parse_rec(typeinfo
& _type
, std::vector
<subexpression
>& ss
,
406 std::set
<operinfo
*>& operations
,
407 std::function
<GC::pointer
<mathexpr
>(const std::string
&)> vars
, size_t first
, size_t last
)
409 operations_set
opset(operations
);
410 std::vector
<expr_or_op
> operands
;
411 std::vector
<GC::pointer
<mathexpr
>> args
;
413 for(size_t i
= first
; i
< last
; i
++) {
414 size_t l
= find_last_in_sub(ss
, i
);
415 if(l
>= last
) throw std::runtime_error("Internal error: Improper nesting");
418 operands
.push_back(parse_rec(_type
, ss
, operations
, vars
, i
+ 1, l
));
421 operands
.push_back(GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
,
422 ss
[i
].string
, false));
425 operands
.push_back(GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
,
426 ss
[i
].string
, true));
429 //We have to warp this is identify transform to make the evaluation lazy.
430 operands
.push_back(GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
,
431 vars(ss
[i
].string
)));
434 fn
= opset
.find_function(ss
[i
].string
);
436 while(ss
[i
].kind
!= TT_CLOSE_PAREN
) {
437 size_t k
= find_end_of_arg(ss
, i
);
438 args
.push_back(parse_rec(_type
, ss
, operations
, vars
, i
, k
));
439 if(k
< ss
.size() && ss
[k
].kind
== TT_COMMA
)
444 operands
.push_back(GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
, fn
,
449 operands
.push_back(ss
[i
].string
);
458 throw std::runtime_error("Empty subexpression");
459 //Translate nonary operators to values.
460 for(auto& i
: operands
) {
462 auto fn
= opset
.find_operator(i
.op
, 0);
464 i
.expr
= GC::pointer
<mathexpr
>(GC::obj_tag(), &_type
, fn
,
465 std::vector
<GC::pointer
<mathexpr
>>());
468 //Check that there aren't two consequtive subexpressions and mark operators.
469 bool was_operand
= false;
470 for(auto& i
: operands
) {
471 bool is_operand
= (bool)i
.expr
;
472 if(!is_operand
&& !was_operand
)
473 if(!(i
.typei
= opset
.find_operator(i
.op
, 1)))
474 throw std::runtime_error("'" + i
.op
+ "' is not an unary operator");
475 if(!is_operand
&& was_operand
)
476 if(!(i
.typei
= opset
.find_operator(i
.op
, 2)))
477 throw std::runtime_error("'" + i
.op
+ "' is not a binary operator");
478 if(was_operand
&& is_operand
)
479 throw std::runtime_error("Expected operator, got operand");
480 was_operand
= is_operand
;
483 throw std::runtime_error("Expected operand, got end of subexpression");
484 //Okay, now the expression has been reduced into series of operators and subexpressions.
485 //If there are multiple consequtive operators, the first (except as first item) is binary,
486 //and the others are unary.
487 return parse_rec(_type
, operands
, 0, operands
.size());
490 void tokenize(const std::string
& expr
, std::set
<operinfo
*>& operations
,
491 std::vector
<subexpression
>& tokenization
)
493 for(size_t i
= 0; i
< expr
.length();) {
495 tokenization
.push_back(subexpression(TT_OPEN_PAREN
));
497 } else if(expr
[i
] == ')') {
498 tokenization
.push_back(subexpression(TT_CLOSE_PAREN
));
500 } else if(expr
[i
] == ',') {
501 tokenization
.push_back(subexpression(TT_COMMA
));
503 } else if(expr
[i
] == ' ') {
505 } else if(expr
[i
] == '$') {
506 //Variable. If the next character is {, parse until }, otherwise parse until
508 std::string varname
= "";
509 if(i
+ 1 < expr
.length() && expr
[i
+ 1] == '{') {
512 while(i
+ 1 < expr
.length() && expr
[i
+ 1] != '}')
513 varname
+= std::string(1, expr
[++i
]);
514 if(i
+ 1 >= expr
.length() || expr
[i
+ 1] != '}')
515 throw std::runtime_error("${ without matching }");
518 //Terminate by non-alphanum.
519 while(i
+ 1 < expr
.length() && is_alphanumeric(expr
[i
+ 1]))
520 varname
+= std::string(1, expr
[++i
]);
522 tokenization
.push_back(subexpression(TT_VARIABLE
, varname
));
524 } else if(expr
[i
] == '"') {
528 while(endpos
< expr
.length() && (escape
|| expr
[endpos
] != '"')) {
530 if(expr
[endpos
] == '\\')
538 if(endpos
== expr
.length())
539 throw std::runtime_error("Unmatched \"");
540 //Copy (i,endpos-1) and descape.
543 for(size_t j
= i
+ 1; j
< endpos
; j
++) {
546 tmp
+= std::string(1, expr
[j
]);
550 tmp
+= std::string(1, expr
[j
]);
554 tokenization
.push_back(subexpression(TT_STRING
, tmp
));
558 //Function names are only recognized if it begins here and is followed by '('.
559 for(auto j
: operations
) {
560 if(j
->is_operator
) continue; //Not a function.
561 if(i
+ j
->fnname
.length() + 1 > expr
.length()) continue; //Too long.
562 if(expr
[i
+ j
->fnname
.length()] != '(') continue; //Not followed by '('.
563 for(size_t k
= 0; k
< j
->fnname
.length(); k
++)
564 if(expr
[i
+ k
] != j
->fnname
[k
]) goto nomatch
; //No match.
565 tokenization
.push_back(subexpression(TT_FUNCTION
, j
->fnname
));
566 i
+= j
->fnname
.length();
572 //Operators. These use longest match rule.
573 size_t longest_match
= 0;
575 for(auto j
: operations
) {
576 if(!j
->is_operator
) continue; //Not an operator.
577 if(i
+ j
->fnname
.length() > expr
.length()) continue; //Too long.
578 for(size_t k
= 0; k
< j
->fnname
.length(); k
++)
579 if(expr
[i
+ k
] != j
->fnname
[k
]) goto next
; //No match.
580 if(j
->fnname
.length() <= longest_match
) continue; //Not longest.
583 longest_match
= op
.length();
587 tokenization
.push_back(subexpression(TT_OPERATOR
, op
));
591 //Okay, token until next non-alphanum.
593 while(i
< expr
.length() && is_alphanumeric(expr
[i
]))
594 tmp
+= std::string(1, expr
[i
++]);
596 tokenization
.push_back(subexpression(TT_VALUE
, tmp
));
602 for(j
= i
; j
< expr
.length() && (j
< i
+ 20 || utfcount
); j
++) {
603 if(utfcount
) utfcount
--;
604 summary
+= std::string(1, expr
[j
]);
605 if((uint8_t)expr
[j
] >= 0xF0) utfcount
= 3;
606 if((uint8_t)expr
[j
] >= 0xE0) utfcount
= 2;
607 if((uint8_t)expr
[j
] >= 0xC0) utfcount
= 1;
608 if((uint8_t)expr
[j
] < 0x80) utfcount
= 0;
610 if(j
< expr
.length()) summary
+= "[...]";
611 throw std::runtime_error("Expression parse error, at '" + summary
+ "'");
617 GC::pointer
<mathexpr
> mathexpr::parse(typeinfo
& _type
, const std::string
& expr
,
618 std::function
<GC::pointer
<mathexpr
>(const std::string
&)> vars
)
621 throw std::runtime_error("Empty expression");
622 auto operations
= _type
.operations();
623 std::vector
<subexpression
> tokenization
;
624 tokenize(expr
, operations
, tokenization
);
625 return parse_rec(_type
, tokenization
, operations
, vars
, 0, tokenization
.size());