Skip to content

Instantly share code, notes, and snippets.

@igrep
Created May 19, 2018 05:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igrep/526ba501f89f83ba68b5058e830478b8 to your computer and use it in GitHub Desktop.
Save igrep/526ba501f89f83ba68b5058e830478b8 to your computer and use it in GitHub Desktop.
Arithmetic expression parser *without* StringScanner in Ruby, inspired by parser combinators.
class Parser
def initialize string
@string = string
# To improve performance, increments position instead of modifying @string.
@position = 0
end
def self.eval string
self.new(string).expr
end
def left_string
@string[@position..-1]
end
# satify の略
def sat &block
c = @string[@position]
if block.call(c)
@position += 1
c
end
end
def digit
sat {|c| c && '0' <= c && c <= '9' }
end
def space
sat {|c| c =~ /^\s$/ }
end
def nat
many1_s { digit }&.to_i
end
def spaces
many { space }
end
def sym str
if @string.index(str, @position) == @position
@position += str.length
str
end
end
def token &block
spaces && (result = block.call) && spaces
result
end
def symbol str
token { sym str }
end
def natural
token { nat }
end
def many1_s &block
results = nil
loop do
result = block.call
if result
results ||= ''
results << result
else
break
end
end
results
end
def many &block
results = []
loop do
result = block.call
if result
results << result
else
break
end
end
results
end
def expr
if t = term
if symbol("+") && (e = expr)
t + e
else
t
end
end
end
def term
if f = factor
if symbol("*") && (t = term)
f * t
else
f
end
end
end
def factor
result =
symbol("(") &&
(e = expr) &&
symbol(")")
if result
e
else
natural
end
end
end
if __FILE__ == $PROGRAM_NAME
puts "Expected: 9876543210"
p Parser.eval("9876543210")
puts "\nExpected: 9"
p Parser.eval("1+ 2* 4") #=> 9
puts "\nExpected: 12"
p Parser.eval("(1 +2) *4 ") #=> 12
puts "\nExpected: 9"
p Parser.eval("(1 +(2 *4))") #=> 9
puts "\nExpected: 6"
p Parser.eval("1 + 2 + 3") #=> 6
puts "\nExpected: 24"
p Parser.eval("2*3*4") #=> 24
puts "\nExpected: 42"
p Parser.eval("2*((3+4) * 3)") #=> 42
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment