Class: Calculus::Parser

Inherits:
StringScanner
  • Object
show all
Defined in:
lib/calculus/parser.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ Parser

Returns a new instance of Parser.



8
9
10
11
12
# File 'lib/calculus/parser.rb', line 8

def initialize(source)
  @operators = {:sqrt => 3, :exp => 3, :div => 2, :mul => 2, :plus => 1, :minus => 1, :eql => 0}

  super(source.dup)
end

Instance Attribute Details

#operatorsObject

Returns the value of attribute operators.



6
7
8
# File 'lib/calculus/parser.rb', line 6

def operators
  @operators
end

Instance Method Details

#fetch_tokenObject



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/calculus/parser.rb', line 40

def fetch_token
  skip(/\s+/)
  return nil if(eos?)

  token = nil
  scanning = true
  while(scanning)
    scanning = false
    token = case
            when scan(/=/)
              :eql
            when scan(/\*|\\times|\\cdot/)
              :mul
            when scan(/\\frac\s*(?<num>\{(?:(?>[^{}])|\g<num>)*\})\s*(?<denom>\{(?:(?>[^{}])|\g<denom>)*\})/)
              num, denom = [self[1], self[2]].map{|v| v.gsub(/^{|}$/, '')}
              string[pos, 0] = "(#{num}) / (#{denom}) "
              scanning = true
            when scan(/\//)
              :div
            when scan(/\+/)
              :plus
            when scan(/\^/)
              :exp
            when scan(/-/)
              :minus
            when scan(/sqrt/)
              :sqrt
            when scan(/\\sqrt\s*(?<deg>\[(?:(?>[^\[\]])|\g<deg>)*\])?\s*(?<rad>\{(?:(?>[^{}])|\g<rad>)*\})/)
              deg = (self[1] || "2").gsub(/^\[|\]$/, '')
              rad = self[2].gsub(/^{|}$/, '')
              string[pos, 0] = "(#{rad}) sqrt (#{deg}) "
              scanning = true
            when scan(/\(|\\left\(/)
              :open
            when scan(/\)|\\right\)/)
              :close
            when scan(/[\-\+]? [0-9]+ ((e[\-\+]?[0-9]+)| (\.[0-9]+(e[\-\+]?[0-9]+)?))/x)
              matched.to_f
            when scan(/[\-\+]?[0-9]+/)
              matched.to_i
            when scan(/([a-z0-9]+(?>_[a-z0-9]+)?)/i)
              matched
            else
              raise ParserError, "Invalid character at position #{pos} near '#{peek(20)}'."
            end
  end

  return token
end

#parseObject

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/calculus/parser.rb', line 14

def parse
  exp = []
  stack = []
  while true
    case token = fetch_token
    when :open
      stack.push(token)
    when :close
      exp << stack.pop while operators.keys.include?(stack.last)
      stack.pop if stack.last == :open
    when :plus, :minus, :mul, :div, :exp, :sqrt, :eql
      exp << stack.pop while operators.keys.include?(stack.last) && operators[stack.last] >= operators[token]
      stack.push(token)
    when Numeric, String
      exp << token
    when nil
      break
    else
      raise ArgumentError, "Unexpected symbol: #{token.inspect}"
    end
  end
  exp << stack.pop while stack.last && stack.last != :open
  raise ArgumentError, "Missing closing parentheses: #{stack.join(', ')}" unless stack.empty?
  exp
end