Skip to content

Commit

Permalink
Merge 99b88ef into cf8a38d
Browse files Browse the repository at this point in the history
  • Loading branch information
project-eutopia committed Nov 22, 2018
2 parents cf8a38d + 99b88ef commit f6dc455
Show file tree
Hide file tree
Showing 12 changed files with 167 additions and 27 deletions.
2 changes: 1 addition & 1 deletion lib/keisan/ast/boolean.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def value(context = nil)
end

def true?
false
bool
end

def !
Expand Down
24 changes: 24 additions & 0 deletions lib/keisan/ast/cell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ def to_s
def to_node
node
end

def <(other)
node < other.to_node
end

def <=(other)
node <= other.to_node
end

def >(other)
node > other.to_node
end

def >=(other)
node >= other.to_node
end

def equal(other)
node.equal(other.to_node)
end

def not_equal(other)
node.not_equal(other.to_node)
end
end
end
end
23 changes: 20 additions & 3 deletions lib/keisan/ast/logical_and.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,35 @@ def self.symbol
:"&&"
end

def blank_value
true
end

def evaluate(context = nil)
children[0].evaluate(context).and(children[1].evaluate(context))
short_circuit_do(:evaluate, context)
end

def blank_value
true
def simplify(context = nil)
short_circuit_do(:simplify, context)
end

def value(context = nil)
context ||= Context.new
children[0].value(context) && children[1].value(context)
end

private

def short_circuit_do(method, context)
context ||= Context.new
lhs = children[0].send(method, context)
case lhs
when AST::Boolean
lhs.false? ? AST::Boolean.new(false) : children[1].send(method, context)
else
lhs.and(children[1].send(method, context))
end
end
end
end
end
11 changes: 6 additions & 5 deletions lib/keisan/ast/logical_equal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ def self.symbol
:"=="
end

def evaluate(context = nil)
children[0].evaluate(context).equal(children[1].evaluate(context))
protected

def value_operator
:==
end

def value(context=nil)
context ||= Context.new
children[0].value(context) == children[1].value(context)
def operator
:equal
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/keisan/ast/logical_greater_than.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ def self.symbol
:">"
end

def evaluate(context = nil)
children[0].evaluate(context) > children[1].evaluate(context)
protected

def value_operator
:>
end

def value(context = nil)
children.first.value(context) > children.last.value(context)
def operator
:>
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/keisan/ast/logical_greater_than_or_equal_to.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ def self.symbol
:">="
end

def evaluate(context = nil)
children[0].evaluate(context) >= children[1].evaluate(context)
protected

def value_operator
:>=
end

def value(context = nil)
children.first.value(context) >= children.last.value(context)
def operator
:>=
end
end
end
Expand Down
10 changes: 6 additions & 4 deletions lib/keisan/ast/logical_less_than_or_equal_to.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ def self.symbol
:"<="
end

def evaluate(context = nil)
children[0].evaluate(context) <= children[1].evaluate(context)
protected

def value_operator
:<=
end

def value(context = nil)
children.first.value(context) <= children.last.value(context)
def operator
:<=
end
end
end
Expand Down
11 changes: 6 additions & 5 deletions lib/keisan/ast/logical_not_equal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ def self.symbol
:"!="
end

def evaluate(context = nil)
children[0].evaluate(context).not_equal(children[1].evaluate(context))
protected

def value_operator
:!=
end

def value(context=nil)
context ||= Context.new
children[0].value(context) != children[1].value(context)
def operator
:not_equal
end
end
end
Expand Down
24 changes: 24 additions & 0 deletions lib/keisan/ast/logical_operator.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
module Keisan
module AST
class LogicalOperator < Operator
def evaluate(context = nil)
context ||= Context.new
children[0].evaluate(context).send(operator, children[1].evaluate(context))
end

def simplify(context = nil)
context ||= Context.new
children[0].simplify(context).send(operator, children[1].simplify(context))
end

def value(context=nil)
context ||= Context.new
children[0].value(context).send(value_operator, children[1].value(context))
end

protected

def value_operator
raise Exceptions::NotImplementedError.new
end

def operator
raise Exceptions::NotImplementedError.new
end
end
end
end
19 changes: 18 additions & 1 deletion lib/keisan/ast/logical_or.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,30 @@ def blank_value
end

def evaluate(context = nil)
children[0].evaluate(context).or(children[1].evaluate(context))
short_circuit_do(:evaluate, context)
end

def simplify(context = nil)
short_circuit_do(:simplify, context)
end

def value(context = nil)
context ||= Context.new
children[0].value(context) || children[1].value(context)
end

private

def short_circuit_do(method, context)
context ||= Context.new
lhs = children[0].send(method, context)
case lhs
when AST::Boolean
lhs.true? ? AST::Boolean.new(true) : children[1].send(method, context)
else
lhs.or(children[1].send(method, context))
end
end
end
end
end
31 changes: 31 additions & 0 deletions spec/keisan/ast/logical_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require "spec_helper"

RSpec.describe "Logical operators" do
let(:calculator) { Keisan::Calculator.new }

describe "evaluate" do
it "leaves as logical comparison when cannot fully evaluate operands" do
expect(Keisan::AST.parse("1 == x").evaluate).to be_a(Keisan::AST::LogicalEqual)
end

it "evaluates to a boolean for complex operands" do
calculator.evaluate("a = [1, 2, 3]")
calculator.evaluate("i = 1")
calculator.evaluate("x = 2")
expect(calculator.evaluate("a[i] == x")).to be true
end
end

describe "simplify" do
it "short-circuits if possible" do
expect(calculator.simplify("true || (x = 1)").value).to be true
expect(calculator.simplify("x")).to be_a(Keisan::AST::Variable)

expect(calculator.simplify("false && (x = 1)").value).to be false
expect(calculator.simplify("x")).to be_a(Keisan::AST::Variable)

expect(calculator.simplify("false || (x = 1)").value).to be 1
expect(calculator.simplify("x").value).to eq 1
end
end
end
19 changes: 19 additions & 0 deletions spec/keisan/functions/while_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,27 @@
RSpec.describe Keisan::Functions::While do
it "does loops" do
calculator = Keisan::Calculator.new

calculator.evaluate("x = 0")
calculator.evaluate("while(x < 10, x = x + 1)")
expect(calculator.evaluate("x")).to eq 10

calculator.evaluate(<<-KEISAN
includes(a, element) = {
let i = 0;
let found = false;
while (i < a.size,
if (a[i] == element,
found = true;
i = a.size
)
i += 1
);
found
}
KEISAN
)
expect(calculator.evaluate("[1,2,3].includes(2)")).to eq true
expect(calculator.evaluate("[1,2,3].includes(4)")).to eq false
end
end

0 comments on commit f6dc455

Please sign in to comment.