Skip to content

Instantly share code, notes, and snippets.

@igrep
Created July 21, 2018 03:51
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/521ee36b1e005c20bbdeedcb9de06ec7 to your computer and use it in GitHub Desktop.
Save igrep/521ee36b1e005c20bbdeedcb9de06ec7 to your computer and use it in GitHub Desktop.
class Parser
attr_reader :value
def initialize string, value = nil
@string = string
@value = value
end
def self.eval string
self.new(string).expr.value
end
def left_string
@string
end
# satify の略
def sat &block
c = @string[0]
if block.call(c)
::Parser.new(@string[1..-1], c)
end
end
def digit
sat {|c| c && '0' <= c && c <= '9' }
end
def space
sat {|c| c =~ /^\s$/ }
end
def nat
(successful_parser = many1_s {|new_parser| new_parser.digit }) &&
::Parser.new(successful_parser.left_string, successful_parser.value.to_i)
end
def spaces
many {|new_parser| new_parser.space }
end
def sym str
if @string.start_with? str
::Parser.new(@string[str.length..-1], str)
end
end
def token &block
spaces &&
(new_parser = block.call(self)) &&
(another_parser = new_parser.spaces) &&
::Parser.new(another_parser.left_string, new_parser.value)
end
def symbol str
token {|new_parser| new_parser.sym str }
end
def natural
token {|new_parser| new_parser.nat }
end
# TODO: rewrite with recursion
def many1_s &block
results = nil
new_parser = block.call(self)
successful_parser = nil
loop do
if new_parser
successful_parser = new_parser
results ||= ''
results << new_parser.value
else
break
end
new_parser = block.call(new_parser)
end
if results
::Parser.new(successful_parser.left_string, results)
end
end
# TODO: rewrite with recursion
def many &block
results = []
new_parser = block.call(self)
successful_parser = nil
loop do
if new_parser
successful_parser = new_parser
results << new_parser.value
else
break
end
new_parser = block.call(new_parser)
end
::Parser.new((successful_parser || self).left_string, results)
end
def expr
if t = term
if (s = t.symbol("+")) && (e = s.expr)
::Parser.new(e.left_string, t.value + e.value)
else
t
end
end
end
def term
if f = factor
if (s = f.symbol("*")) && (t = s.term)
::Parser.new(t.left_string, f.value * t.value)
else
f
end
end
end
def factor
result =
(s = self.symbol("(")) &&
(e = s.expr) &&
e.symbol(")")
if result
::Parser.new(result.left_string, e.value)
else
self.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