SHiNKiROU

ExprParser

ExprParser is a simple command line calculator written in Ruby.

Command Line Usage

$ ruby ExprParser.rb
>> a = 6
=> 6.0
>> a + 12 * (5 + 3)
=> 102.0
>> a ^ 2
=> 36.0
>>

$ ruby ExprParser.rb 'a = 6' 'a ^ 2'
36.0

$ ruby ExprParser.rb --tokens --tree 'a = 6' 'a ^ 2'
>> a = 6
 Tokens: [[:IDENT, "a"], [:EQUAL, "="], [:NUMBER, "6"]]
 Syntax Tree: [:ASSIGN, "a", "6"]
>> a ^ 2
 Tokens: [[:IDENT, "a"], [:UP, "^"], [:NUMBER, "2"]]
 Syntax Tree: [:POWER, "a", "2"]
36.0

Command Line Arguments

ruby ExprParser.rb [--help] [--tokens] [--tree] [EXPR ...]
 * --help: show the help message.
 * --tokens: show the tokens parsed.
 * --tree: show the syntax tree.
 * EXPR ...: expression(s) to be evaluated.

Code Usage

e = ExprParser.new { :x => 10 }
e.evaluate "x ^ 2" # => 100
e.prepare "1 + sqrt(12 * 5) * (5 * 8)"
e.tokens # => [[:NUMBER, "1"], [:PLUS, "+"], [:IDENT, "sqrt"],
               [:LEFT_PAREN, "("], [:NUMBER, "12"], [:MULTIPLY, "*"],
               [:NUMBER, "5"], [:RIGHT_PAREN, ")"], [:MULTIPLY, "*"],
               [:LEFT_PAREN, "("], [:NUMBER, "5"], [:MULTIPLY, "*"],
               [:NUMBER, "8"], [:RIGHT_PAREN, ")"]]
e.tree # => [:ADD, "1", [:MULTIPLY, [:CALL, "sqrt", [:MULTIPLY, "12", "5"]],
                         [:MULTIPLY, "5", "8"]]]
e.evaluate # => 310.838667696593

Syntax

BNF

           Expression ::= AssignmentExpression
                        | MainExpression

 AssignmentExpression ::= <IDENT> "=" MainExpression

       MainExpression ::= Term
                        [ ( "+" | "-" ) Term ]

                 Term ::= "-" Term
                        | ( Exponent
                          [ ( "*" | "/" )
                            Exponent ] )

             Exponent ::= Primary "^" [ Exponent ]

              Primary ::= "(" MainExpression ")"
                        | ( <IDENT> "("
                          ( MainExpression "," )*
                          [ MainExpression ]
                            ")" )
                        | <IDENT>
                        | <NUMBER>

Tokens

Numbers

A number consists of sequence of digits, an optional decimal part and optional e-notation. For example:

  • 1258
  • 1.258
  • 1.258e10
  • 1.258e+10
  • 1.258e-10

A number matches the regexp /([1-9][0-9]*|[0-9])(\.[0-9]+)?(e[+-]?[1-9][0-9]*)?/.

Identifiers

An identifier consists of sequence of chararacters capital A-Z, lowercase a-z, underscore ("_") or numbers 0-9. An identifier cannot start with a number. The regexp used for identifiers is /[A-Za-z_][A-Za-z0-9_]*/. abc, x1, theNumber are valid while 4chan is not a valid identifier.

Operator Precedence

  • a = B (bin, n-assoc)
  • A + B, A - B (bin, l-assoc)
  • A * B, A / B (bin, l-assoc), -A (unary)
  • A ^ B (bin, r-assoc)
  • (A), f(A)

Functions

sqrt exp log log10 cos sin tan cosh sinh tanh acos asin atan atan2 acosh asinh atanh

They are mapped from Ruby Math module.

Version History

0.1

Changed the operator associativity of +-*/ to left-associative. 5 - 3 - 3 now equals -1.

0.0

Initial release.