Skip to content

Instantly share code, notes, and snippets.

@igrep
Last active June 16, 2023 21:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igrep/702cad632782c9dd88b3c115f7d9f40a to your computer and use it in GitHub Desktop.
Save igrep/702cad632782c9dd88b3c115f7d9f40a to your computer and use it in GitHub Desktop.
Arithmetic expression parser with StringScanner in Ruby, inspired by parser combinators.
require 'strscan'
class Parser
def initialize string
@scanner = StringScanner.new string
end
def self.eval string
self.new(string).expr
end
def nat
@scanner.scan(/\d+/)&.to_i
end
def spaces
@scanner.scan(/\s*/)
end
def sym str
@scanner.scan(Regexp.compile(Regexp.escape(str)))
end
def token &block
spaces && (result = block.call) && spaces
result
end
def symbol str
token { sym str }
end
def natural
token { nat }
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