Module: ActionAgent::AgentToolbox::Calculator
- Defined in:
- app/services/action_agent/agent_toolbox.rb
Overview
Minimal recursive-descent arithmetic evaluator (no eval). Grammar: expr := term (('+'|'-') term); term := factor ((''|'/'|'%') factor)*; factor := '-'? (number | '(' expr ')')
Defined Under Namespace
Classes: Error
Class Method Summary collapse
- .evaluate(expression) ⇒ Object
- .parse_expr(tokens) ⇒ Object
- .parse_factor(tokens) ⇒ Object
- .parse_term(tokens) ⇒ Object
- .tokenize(expression) ⇒ Object
Class Method Details
.evaluate(expression) ⇒ Object
439 440 441 442 443 444 445 446 |
# File 'app/services/action_agent/agent_toolbox.rb', line 439 def evaluate(expression) tokens = tokenize(expression) result, rest = parse_expr(tokens) raise Error, "Unexpected input: #{rest.join(' ')}" unless rest.empty? result = result.to_f result % 1 == 0 ? result.to_i : result.round(10) end |
.parse_expr(tokens) ⇒ Object
457 458 459 460 461 462 463 464 465 |
# File 'app/services/action_agent/agent_toolbox.rb', line 457 def parse_expr(tokens) value, tokens = parse_term(tokens) while tokens.first == "+" || tokens.first == "-" op = tokens.shift rhs, tokens = parse_term(tokens) value = op == "+" ? value + rhs : value - rhs end [ value, tokens ] end |
.parse_factor(tokens) ⇒ Object
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
# File 'app/services/action_agent/agent_toolbox.rb', line 482 def parse_factor(tokens) raise Error, "Unexpected end of expression" if tokens.empty? if tokens.first == "-" tokens.shift value, tokens = parse_factor(tokens) return [ -value, tokens ] end token = tokens.shift if token == "(" value, tokens = parse_expr(tokens) raise Error, "Missing closing parenthesis" unless tokens.shift == ")" [ value, tokens ] elsif token =~ /\A\d/ [ token.include?(".") ? token.to_f : Rational(token), tokens ] else raise Error, "Unexpected token: #{token}" end end |
.parse_term(tokens) ⇒ Object
467 468 469 470 471 472 473 474 475 476 477 478 479 480 |
# File 'app/services/action_agent/agent_toolbox.rb', line 467 def parse_term(tokens) value, tokens = parse_factor(tokens) while [ "*", "/", "%" ].include?(tokens.first) op = tokens.shift rhs, tokens = parse_factor(tokens) raise Error, "Division by zero" if rhs.zero? && op != "*" value = case op when "*" then value * rhs when "/" then value / rhs else value % rhs end end [ value, tokens ] end |
.tokenize(expression) ⇒ Object
448 449 450 451 452 453 454 455 |
# File 'app/services/action_agent/agent_toolbox.rb', line 448 def tokenize(expression) tokens = expression.scan(%r{\d+(?:\.\d+)?|[-+*/%()]|\S}) invalid = tokens.grep_v(%r{\A(?:\d+(?:\.\d+)?|[-+*/%()])\z}) raise Error, "Unsupported characters: #{invalid.uniq.join(' ')}" if invalid.any? raise Error, "Empty expression" if tokens.empty? tokens end |