Class: Pikuri::Tool::Calculator::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/pikuri/tool/calculator.rb

Overview

Recursive-descent parser-evaluator for Python’s arithmetic expression grammar:

additive       := multiplicative (('+' | '-') multiplicative)*
multiplicative := unary (('*' | '/' | '//' | '%') unary)*
unary          := ('+' | '-') unary | power
power          := atom ('**' unary)?
atom           := NUMBER | '(' additive ')'

The powerunary recursion on the right operand is what makes ** right-associative (+2**3**2+ is 512) and lets a sign follow it (+2**-3+); unary sitting above power on the left is what makes -2**2 evaluate to -4 — both exactly as Python parses them.

Semantics follow Python 3 where Ruby differs: / is always true (float) division, // floors, 2**-1 is the Float 0.5 (Ruby would return a Rational), and a negative base under a fractional exponent is rejected (Ruby would return a Complex).

Constant Summary collapse

TOKEN_RE =

One number or operator. ** / // listed before their single-character prefixes so the two-character operators win; number literals cover 42, 4.2, 5., .5, and e-notation on any of them. \G anchors each match at the scan position so nothing between tokens goes unnoticed.

%r{\G\s*(\*\*|//|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?|\.\d+(?:[eE][+-]?\d+)?|[-+*/%()])}

Instance Method Summary collapse

Constructor Details

#initialize(expression) ⇒ Parser

Returns a new instance of Parser.

Parameters:

  • expression (String)

    raw expression as the model wrote it

Raises:

  • (Error)

    when expression contains a character no token matches



93
94
95
96
# File 'lib/pikuri/tool/calculator.rb', line 93

def initialize(expression)
  @tokens = tokenize(expression)
  @pos = 0
end

Instance Method Details

#parseInteger, Float

Parse and evaluate the whole token stream.

Returns:

  • (Integer, Float)

    the value of the expression

Raises:

  • (Error)

    on syntax errors, division by zero, or a complex result



102
103
104
105
106
107
# File 'lib/pikuri/tool/calculator.rb', line 102

def parse
  value = additive
  raise Error, "unexpected #{peek.inspect} after expression" if peek

  value
end