Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
d11wtq committed Nov 4, 2011
1 parent 071ec89 commit 50d5389
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 31 deletions.
29 changes: 1 addition & 28 deletions lib/veritas-td/interpreter.rb
Expand Up @@ -3,30 +3,8 @@
module Veritas
module TD
class Interpreter
class AssociationTransformer < Parslet::Transform
# FIXME: Make this generic and mix it into the parser, something like:
# left(:multiply)
# left(:divide)
rule(:multiply => { :left => subtree(:left), :right => { :sum => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :sum => { :left => { :multiply => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:multiply => { :left => subtree(:left), :right => { :subtract => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :subtract => { :left => { :multiply => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:divide => { :left => subtree(:left), :right => { :sum => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :sum => { :left => { :divide => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:divide => { :left => subtree(:left), :right => { :subtract => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :subtract => { :left => { :divide => { :left => left, :right => sub_left } }, :right => sub_right } }
end
end

def eval(expr)
p reassociate(parser.parse(expr))
transform.apply(reassociate(parser.parse(expr)))
transform.apply(parser.parse(expr))
end

private
Expand All @@ -35,11 +13,6 @@ def parser
@parser ||= Parser.new
end

def reassociate(tree)
@associations ||= AssociationTransformer.new
@associations.apply(tree)
end

def transform
@transform ||= Transform.new
end
Expand Down
20 changes: 18 additions & 2 deletions lib/veritas-td/parser.rb
Expand Up @@ -35,20 +35,27 @@ class Parser < Parslet::Parser
rule(:subtract) { (operand.as(:left) >> padded("-") >> expr.as(:right)).as(:subtract) }
rule(:multiply) { (operand.as(:left) >> padded("*") >> expr.as(:right)).as(:multiply) }
rule(:divide) { (operand.as(:left) >> padded("/") >> expr.as(:right)).as(:divide) }

rule(:binary_expr) { multiply | divide | sum | subtract }

rule(:operand) { scalar | expr }

# Complex expressions
rule(:expr) { padded(binary_expr | unary_expr | scalar) }
rule(:expr) { parenthesized(expr) | padded(binary_expr | unary_expr | scalar) }

# Full user input (currently single expressions only)
rule(:prog) { expr | noop }

# Top-level element is any possible expression
root(:prog)

# The parse method is overloaded to transform the tree and left-associate
# branches that are incorrectly right-associated by default
#
# Note that Parslet does not support left-associative grammars
def parse(input)
reassociate(super)
end

private

def ci_str(s)
Expand All @@ -59,6 +66,15 @@ def padded(other)
other = str(other) unless Parslet::Atoms::Base === other
wsp? >> other >> wsp?
end

def parenthesized(other)
(padded("(") >> other >> padded(")")).as(:parenthesized)
end

def reassociate(tree)
@associativity ||= Associativity.new
@associativity.apply(tree)
end
end
end
end
26 changes: 26 additions & 0 deletions lib/veritas-td/parser/associativity.rb
@@ -0,0 +1,26 @@
require "parslet"

module Veritas
module TD
class Associativity < Parslet::Transform
# FIXME: Make this generic and mix it into the parser, something like:
# left(:multiply)
# left(:divide)
rule(:multiply => { :left => subtree(:left), :right => { :sum => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :sum => { :left => { :multiply => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:multiply => { :left => subtree(:left), :right => { :subtract => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :subtract => { :left => { :multiply => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:divide => { :left => subtree(:left), :right => { :sum => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :sum => { :left => { :divide => { :left => left, :right => sub_left } }, :right => sub_right } }
end

rule(:divide => { :left => subtree(:left), :right => { :subtract => { :left => subtree(:sub_left), :right => subtree(:sub_right) } } }) do
{ :subtract => { :left => { :divide => { :left => left, :right => sub_left } }, :right => sub_right } }
end
end
end
end
3 changes: 3 additions & 0 deletions lib/veritas-td/transform.rb
Expand Up @@ -12,6 +12,9 @@ class Transform < Parslet::Transform
rule(:chars => simple(:chars)) { chars }
rule(:string => sequence(:chunks)) { chunks.join }

# Logical grouping
rule(:parenthesized => subtree(:expr)) { expr }

# +expr and -expr
rule(:minus => simple(:minus), :value => simple(:value)) { -value }
rule(:plus => simple(:minus), :value => simple(:value)) { value }
Expand Down
3 changes: 2 additions & 1 deletion lib/veritas-tutorial-d.rb
@@ -1,6 +1,7 @@
require "veritas"
require "veritas-td/version"
require "veritas-td/atoms/case_insensitive_str"
require "veritas-td/parser/case_insensitive_str"
require "veritas-td/parser/associativity"
require "veritas-td/parser"
require "veritas-td/transform"
require "veritas-td/interpreter"
17 changes: 17 additions & 0 deletions spec/unit/arithmetic_spec.rb
Expand Up @@ -108,4 +108,21 @@
end
end
end

describe "logical grouping" do
let(:expr) { "3 * (2 + 2)" }

it "evaluates the parenthesized expression first" do
result.should == 12
end
end

describe "mixed grouping" do
let(:expr) { "3 * (2 + 2) - 1" }

it "can be parsed" do
pending "doesn't parse; will come back to this"
result.should == 11
end
end
end

0 comments on commit 50d5389

Please sign in to comment.