Skip to content

Instantly share code, notes, and snippets.

@mrcsparker
Created April 12, 2012 03:16
Show Gist options
  • Save mrcsparker/2364424 to your computer and use it in GitHub Desktop.
Save mrcsparker/2364424 to your computer and use it in GitHub Desktop.
Parslet with proper LR support
# Evaluates expressions left to right, with presedence
# using a PEG
require "parslet"
class Parser < Parslet::Parser
root :expression
rule(:expression) { additive | block | number }
rule(:block) {
str("(") >> space? >> expression >> space? >> str(")")
}
rule(:additive) {
multitive.as(:left) >> (space? >> match('[+-]').as(:op) >> space? >> multitive.as(:right)).repeat.as(:binary_operation)
}
rule(:multitive) {
primary.as(:left) >> (space? >> match('[*/]').as(:op) >> space? >> primary.as(:right)).repeat.as(:binary_operation)
}
rule(:space) { match('[ \t\v\n\f]') }
rule(:spaces) { space.repeat(1) }
rule(:space?) { space.maybe }
rule(:spaces?) { space.repeat }
rule(:primary) { block | number }
rule(:digit) { match('[0-9]') }
rule(:digits) { digit.repeat(1) }
rule(:digits?) { digit.repeat }
rule(:number) {
(match('[+-]').maybe >> digits >> (str('.') >> digits).maybe).as(:number) >> spaces?
}
rule(:digit) { match("[0-9]") }
rule(:space?) { space.maybe }
rule(:space) { match("\s").repeat(1) }
end
class Number < Struct.new(:int)
def eval
Float(int)
end
end
class BinaryOperation < Struct.new(:left, :binary_operation)
def eval
binary_operation.inject(left.eval) do |value, element|
p element
a = Arithmetic.new(value, element[:right].eval, element[:op])
a.eval
end
end
end
class Arithmetic < Struct.new(:left, :right, :op)
def eval
if op == "+"
left + right
elsif op == "-"
left - right
elsif op == "*"
left * right
elsif op == "/"
left / right
end
end
end
class Transformer < Parslet::Transform
rule(:left => simple(:left), :binary_operation => []) { left }
rule(:left => simple(:left), :binary_operation => subtree(:binary_operation)) {
BinaryOperation.new(left, binary_operation)
}
rule(:number => simple(:number)) { Number.new(number) }
rule(:left => simple(:left), :right => simple(:right), :op => '+') { Arithmetic.new(left, right, "+") }
rule(:left => simple(:left), :right => simple(:right), :op => '-') { Arithmetic.new(left, right, "-") }
rule(:left => simple(:left), :right => simple(:right), :op => '*') { Arithmetic.new(left, right, "*") }
rule(:left => simple(:left), :right => simple(:right), :op => '/') { Arithmetic.new(left, right, "/") }
end
ps = Parser.new
tf = Transformer.new
#p tf.apply(ps.parse("2+3-4-1")).eval
#p tf.apply(ps.parse("2+3-(4-1)")).eval
#p tf.apply(ps.parse("2+3-4*8")).eval
p tf.apply(ps.parse("5/4/2")).eval
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment