From e48bbdeedfba78ff41b4a3492b512dfba25f91e4 Mon Sep 17 00:00:00 2001 From: Christopher Locke Date: Thu, 20 May 2021 22:46:50 +0900 Subject: [PATCH 1/4] Add is_constant? method This method can be used to determine if an AST represents a constant value. For instance, constants like 5, 'foo', and nil are obviously constant, but also things like [1, 2], -'foo', etc are constants. --- lib/keisan/ast/constant_literal.rb | 4 ++++ lib/keisan/ast/function.rb | 7 +++++++ lib/keisan/ast/hash.rb | 4 ++++ lib/keisan/ast/node.rb | 4 ++++ lib/keisan/ast/parent.rb | 4 ++++ spec/keisan/ast/boolean_spec.rb | 6 ++++++ spec/keisan/ast/date_spec.rb | 7 +++++++ spec/keisan/ast/function_spec.rb | 10 ++++++++++ spec/keisan/ast/hash_spec.rb | 16 ++++++++++++++++ spec/keisan/ast/list_spec.rb | 14 ++++++++++++++ spec/keisan/ast/null_spec.rb | 6 ++++++ spec/keisan/ast/number_spec.rb | 6 ++++++ spec/keisan/ast/string_spec.rb | 6 ++++++ spec/keisan/ast/time_spec.rb | 7 +++++++ 14 files changed, 101 insertions(+) create mode 100644 spec/keisan/ast/function_spec.rb diff --git a/lib/keisan/ast/constant_literal.rb b/lib/keisan/ast/constant_literal.rb index 3604894..2465e1a 100644 --- a/lib/keisan/ast/constant_literal.rb +++ b/lib/keisan/ast/constant_literal.rb @@ -22,6 +22,10 @@ def to_s value.to_s end end + + def is_constant? + true + end end end end diff --git a/lib/keisan/ast/function.rb b/lib/keisan/ast/function.rb index 3afb5d9..9792c61 100644 --- a/lib/keisan/ast/function.rb +++ b/lib/keisan/ast/function.rb @@ -91,6 +91,13 @@ def differentiate(variable, context = nil) self.class.new([self, variable], "diff") end + + # Functions cannot be guaranteed to be constant even if the arguments + # are constants, because there might be randomness involved in the + # outputs. + def is_constant? + false + end end end end diff --git a/lib/keisan/ast/hash.rb b/lib/keisan/ast/hash.rb index f98c6da..357e7d9 100644 --- a/lib/keisan/ast/hash.rb +++ b/lib/keisan/ast/hash.rb @@ -90,6 +90,10 @@ def to_cell ]) AST::Cell.new(h) end + + def is_constant? + @hash.all? {|k,v| v.is_constant?} + end end end end diff --git a/lib/keisan/ast/node.rb b/lib/keisan/ast/node.rb index 4924db9..586f775 100644 --- a/lib/keisan/ast/node.rb +++ b/lib/keisan/ast/node.rb @@ -202,6 +202,10 @@ def and(other) def or(other) LogicalOr.new([self, other.to_node]) end + + def is_constant? + false + end end end end diff --git a/lib/keisan/ast/parent.rb b/lib/keisan/ast/parent.rb index fd34f29..aa5eee2 100644 --- a/lib/keisan/ast/parent.rb +++ b/lib/keisan/ast/parent.rb @@ -75,6 +75,10 @@ def replace(variable, replacement) @children = children.map {|child| child.replace(variable, replacement)} self end + + def is_constant? + @children.all?(&:is_constant?) + end end end end diff --git a/spec/keisan/ast/boolean_spec.rb b/spec/keisan/ast/boolean_spec.rb index 8493ce5..5a2c7a1 100644 --- a/spec/keisan/ast/boolean_spec.rb +++ b/spec/keisan/ast/boolean_spec.rb @@ -9,6 +9,12 @@ end end + describe "is_constant?" do + it "is true" do + expect(described_class.new(true).is_constant?).to eq true + end + end + describe "operations" do it "should reduce to the answer right away" do res = !Keisan::AST::Boolean.new(true) diff --git a/spec/keisan/ast/date_spec.rb b/spec/keisan/ast/date_spec.rb index d5ced38..43ee873 100644 --- a/spec/keisan/ast/date_spec.rb +++ b/spec/keisan/ast/date_spec.rb @@ -1,6 +1,13 @@ require "spec_helper" RSpec.describe Keisan::AST::Date do + describe "is_constant?" do + it "is true" do + date = Keisan::AST.parse("date(2018, 11, 20)").evaluate + expect(date.is_constant?).to eq true + end + end + describe "evaluate" do it "reduces to a date when adding numbers" do ast = Keisan::AST.parse("date(2018, 11, 20) + 1") diff --git a/spec/keisan/ast/function_spec.rb b/spec/keisan/ast/function_spec.rb new file mode 100644 index 0000000..8663f62 --- /dev/null +++ b/spec/keisan/ast/function_spec.rb @@ -0,0 +1,10 @@ +require "spec_helper" + +RSpec.describe Keisan::AST::Function do + describe "is_constant?" do + it "is false" do + ast = Keisan::Calculator.new.ast("f(1)") + expect(ast.is_constant?).to eq false + end + end +end diff --git a/spec/keisan/ast/hash_spec.rb b/spec/keisan/ast/hash_spec.rb index edc7409..c713a89 100644 --- a/spec/keisan/ast/hash_spec.rb +++ b/spec/keisan/ast/hash_spec.rb @@ -1,6 +1,22 @@ require "spec_helper" RSpec.describe Keisan::AST::Hash do + describe "is_constant?" do + it "is true when all elements are constant" do + hash = {"foo" => {"a" => 1, "b" => 2}, "bar" => {"c" => 3, "d" => 4}}.to_node + expect(hash.is_constant?).to eq true + end + + it "is false if one element is not constant" do + hash = {"foo" => {"a" => 1, "b" => 2}, "bar" => {"c" => 3, "d" => 4}}.to_node + hash = described_class.new([ + ["a".to_node, 1.to_node], + ["b".to_node, Keisan::AST::Variable.new("x")] + ]) + expect(hash.is_constant?).to eq false + end + end + describe "to_node" do it "can created nested hashes" do hash = {"foo" => {"a" => 1, "b" => 2}, "bar" => {"c" => 3, "d" => 4}}.to_node diff --git a/spec/keisan/ast/list_spec.rb b/spec/keisan/ast/list_spec.rb index b570960..f912db4 100644 --- a/spec/keisan/ast/list_spec.rb +++ b/spec/keisan/ast/list_spec.rb @@ -1,6 +1,20 @@ require "spec_helper" RSpec.describe Keisan::AST::List do + describe "is_constant?" do + it "is true when all elements are constant" do + list = [[1,"x"],[2,"y",true]].to_node + expect(list.is_constant?).to eq true + end + + it "is false if one element is not constant" do + list = described_class.new([ + "a".to_node, Keisan::AST::Variable.new("x") + ]) + expect(list.is_constant?).to eq false + end + end + describe "to_node" do it "can created nested lists" do node = [[1,"x"],[2,"y",true]].to_node diff --git a/spec/keisan/ast/null_spec.rb b/spec/keisan/ast/null_spec.rb index 20deabb..418e8b5 100644 --- a/spec/keisan/ast/null_spec.rb +++ b/spec/keisan/ast/null_spec.rb @@ -1,6 +1,12 @@ require "spec_helper" RSpec.describe Keisan::AST::Null do + describe "is_constant?" do + it "is true" do + expect(Keisan::AST::Null.new.is_constant?).to eq true + end + end + describe "logical operations" do it "can do == and != checks" do positive_equal = described_class.new.equal described_class.new diff --git a/spec/keisan/ast/number_spec.rb b/spec/keisan/ast/number_spec.rb index f20f089..51a0a52 100644 --- a/spec/keisan/ast/number_spec.rb +++ b/spec/keisan/ast/number_spec.rb @@ -1,6 +1,12 @@ require "spec_helper" RSpec.describe Keisan::AST::Number do + describe "is_constant?" do + it "is true" do + expect(Keisan::AST::Number.new(1).is_constant?).to eq true + end + end + describe "evaluate" do it "reduces to a single number when using arithmetic operators" do ast = Keisan::AST.parse("1+2") diff --git a/spec/keisan/ast/string_spec.rb b/spec/keisan/ast/string_spec.rb index 20923c5..2006060 100644 --- a/spec/keisan/ast/string_spec.rb +++ b/spec/keisan/ast/string_spec.rb @@ -1,6 +1,12 @@ require "spec_helper" RSpec.describe Keisan::AST::String do + describe "is_constant?" do + it "is true" do + expect(Keisan::AST::String.new('foo').is_constant?).to eq true + end + end + describe "evaluate" do it "reduces to a single string when using plus to concatenate" do ast = Keisan::AST.parse("'hello ' + 'world'") diff --git a/spec/keisan/ast/time_spec.rb b/spec/keisan/ast/time_spec.rb index ff4353e..c55c7d9 100644 --- a/spec/keisan/ast/time_spec.rb +++ b/spec/keisan/ast/time_spec.rb @@ -1,6 +1,13 @@ require "spec_helper" RSpec.describe Keisan::AST::Time do + describe "is_constant?" do + it "is true" do + time = Keisan::AST.parse("time(2018)").evaluate + expect(time.is_constant?).to eq true + end + end + describe "evaluate" do it "reduces to a time when adding numbers" do ast = Keisan::AST.parse("time(2018, 11, 20) + 1") From 44ec19aaab760d0677efa1f0190a7c2ddcfa8a1e Mon Sep 17 00:00:00 2001 From: Christopher Locke Date: Thu, 20 May 2021 22:55:23 +0900 Subject: [PATCH 2/4] Update behavior of operations on constants Operations between constants that are not predefined (e.g. adding numbers or comparing strings) should raise an InvalidExpression error. However, logical equal and not-equal can evaluate definitely to false and true respectively, because the types are different. --- lib/keisan/ast/constant_literal.rb | 160 +++++++++++++++++++++++++++++ spec/keisan/ast/boolean_spec.rb | 18 +++- spec/keisan/ast/date_spec.rb | 3 +- spec/keisan/ast/null_spec.rb | 6 +- spec/keisan/ast/number_spec.rb | 55 ++++++++++ spec/keisan/ast/string_spec.rb | 6 +- spec/keisan/ast/time_spec.rb | 3 +- spec/keisan/calculator_spec.rb | 20 ++++ 8 files changed, 259 insertions(+), 12 deletions(-) diff --git a/lib/keisan/ast/constant_literal.rb b/lib/keisan/ast/constant_literal.rb index 2465e1a..333ee2b 100644 --- a/lib/keisan/ast/constant_literal.rb +++ b/lib/keisan/ast/constant_literal.rb @@ -26,6 +26,166 @@ def to_s def is_constant? true end + + def +(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot add #{self.class} to #{other.class}") + else + super + end + end + + def -(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot subtract #{self.class} from #{other.class}") + else + super + end + end + + def *(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot multiply #{self.class} and #{other.class}") + else + super + end + end + + def /(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot divide #{self.class} and #{other.class}") + else + super + end + end + + def %(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot modulo #{self.class} and #{other.class}") + else + super + end + end + + def ! + raise Keisan::Exceptions::InvalidExpression.new("Cannot take logical not of #{self.class}") + end + + def ~ + raise Keisan::Exceptions::InvalidExpression.new("Cannot take bitwise not of #{self.class}") + end + + def +@ + raise Keisan::Exceptions::InvalidExpression.new("Cannot take unary plus of #{self.class}") + end + + def -@ + raise Keisan::Exceptions::InvalidExpression.new("Cannot take unary minus of #{self.class}") + end + + def **(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot exponentiate #{self.class} and #{other.class}") + else + super + end + end + + def &(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot bitwise and #{self.class} and #{other.class}") + else + super + end + end + + def ^(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot bitwise xor #{self.class} and #{other.class}") + else + super + end + end + + def |(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot bitwise or #{self.class} and #{other.class}") + else + super + end + end + + def <<(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot bitwise left shift #{self.class} and #{other.class}") + else + super + end + end + + def >>(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot bitwise right shift #{self.class} and #{other.class}") + else + super + end + end + + def >(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot compute #{self.class} > #{other.class}") + else + super + end + end + + def >=(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot compute #{self.class} >= #{other.class}") + else + super + end + end + + def <(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot compute #{self.class} < #{other.class}") + else + super + end + end + + def <=(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot compute #{self.class} <= #{other.class}") + else + super + end + end + + def equal(other) + other.is_constant? ? Boolean.new(false) : super + end + + def not_equal(other) + other.is_constant? ? Boolean.new(true) : super + end + + def and(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot logical and #{self.class} and #{other.class}") + else + super + end + end + + def or(other) + if other.is_constant? + raise Keisan::Exceptions::InvalidExpression.new("Cannot logical or #{self.class} and #{other.class}") + else + super + end + end end end end diff --git a/spec/keisan/ast/boolean_spec.rb b/spec/keisan/ast/boolean_spec.rb index 5a2c7a1..b4067de 100644 --- a/spec/keisan/ast/boolean_spec.rb +++ b/spec/keisan/ast/boolean_spec.rb @@ -47,11 +47,14 @@ expect(negative_or).to be_a(Keisan::AST::Boolean) expect(negative_or.value).to eq false - and_other = described_class.new(true).and Keisan::AST::Number.new(1) - or_other = described_class.new(true).or Keisan::AST::Number.new(1) + and_other = described_class.new(true).and Keisan::AST::Variable.new("x") + or_other = described_class.new(true).or Keisan::AST::Variable.new("x") expect(and_other).to be_a(Keisan::AST::LogicalAnd) expect(or_other).to be_a(Keisan::AST::LogicalOr) + + expect{described_class.new(true).and Keisan::AST::Number.new(1)}.to raise_error(Keisan::Exceptions::InvalidExpression) + expect{described_class.new(true).or Keisan::AST::Number.new(1)}.to raise_error(Keisan::Exceptions::InvalidExpression) end it "can do == and != checks" do @@ -69,11 +72,18 @@ expect(negative_not_equal).to be_a(Keisan::AST::Boolean) expect(negative_not_equal.value).to eq false - equal_other = described_class.new(true).equal Keisan::AST::Number.new(1) - not_equal_other = described_class.new(true).not_equal Keisan::AST::Number.new(1) + equal_other = described_class.new(true).equal Keisan::AST::Variable.new("x") + not_equal_other = described_class.new(true).not_equal Keisan::AST::Variable.new("x") expect(equal_other).to be_a(Keisan::AST::LogicalEqual) expect(not_equal_other).to be_a(Keisan::AST::LogicalNotEqual) + + equal_number = described_class.new(true).equal Keisan::AST::Number.new(1) + expect(equal_number).to be_a(Keisan::AST::Boolean) + expect(equal_number.value).to eq false + not_equal_number = described_class.new(true).not_equal Keisan::AST::Number.new(1) + expect(not_equal_number).to be_a(Keisan::AST::Boolean) + expect(not_equal_number.value).to eq true end end end diff --git a/spec/keisan/ast/date_spec.rb b/spec/keisan/ast/date_spec.rb index 43ee873..493ea88 100644 --- a/spec/keisan/ast/date_spec.rb +++ b/spec/keisan/ast/date_spec.rb @@ -47,8 +47,7 @@ expect(ast.evaluate.value).to eq false ast = Keisan::AST.parse("date(2000) + date(2000)") - expect(ast.evaluate).to be_a(Keisan::AST::Plus) - expect{ast.evaluate.value}.to raise_error(TypeError) + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) end it "works in arrays" do diff --git a/spec/keisan/ast/null_spec.rb b/spec/keisan/ast/null_spec.rb index 418e8b5..98ba3c2 100644 --- a/spec/keisan/ast/null_spec.rb +++ b/spec/keisan/ast/null_spec.rb @@ -20,8 +20,10 @@ expect(negative_not_equal).to be_a(Keisan::AST::Boolean) expect(negative_not_equal.value).to eq false - expect(other_equal).to be_a(Keisan::AST::LogicalEqual) - expect(other_not_equal).to be_a(Keisan::AST::LogicalNotEqual) + expect(other_equal).to be_a(Keisan::AST::Boolean) + expect(other_equal.value).to eq false + expect(other_not_equal).to be_a(Keisan::AST::Boolean) + expect(other_not_equal.value).to eq true end end end diff --git a/spec/keisan/ast/number_spec.rb b/spec/keisan/ast/number_spec.rb index 51a0a52..0769c88 100644 --- a/spec/keisan/ast/number_spec.rb +++ b/spec/keisan/ast/number_spec.rb @@ -155,6 +155,61 @@ ast = Keisan::AST.parse("1!=x") expect(ast.evaluate).to be_a(Keisan::AST::LogicalNotEqual) end + + it "has definite behavior for other constants" do + ast = Keisan::AST.parse("1+'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1-'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1*'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1/'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1%'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("2**'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1&'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1|'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1^'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1<<'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1>>'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1>'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1>='a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1<'a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1<='a'") + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) + + ast = Keisan::AST.parse("1=='a'") + expect(ast.evaluate).to be_a(Keisan::AST::Boolean) + expect(ast.evaluate.value).to eq false + + ast = Keisan::AST.parse("1!='a'") + expect(ast.evaluate).to be_a(Keisan::AST::Boolean) + expect(ast.evaluate.value).to eq true + end end describe "operations" do diff --git a/spec/keisan/ast/string_spec.rb b/spec/keisan/ast/string_spec.rb index 2006060..b712408 100644 --- a/spec/keisan/ast/string_spec.rb +++ b/spec/keisan/ast/string_spec.rb @@ -71,8 +71,10 @@ equal_other = described_class.new("a").equal Keisan::AST::Number.new(1) not_equal_other = described_class.new("a").not_equal Keisan::AST::Number.new(1) - expect(equal_other).to be_a(Keisan::AST::LogicalEqual) - expect(not_equal_other).to be_a(Keisan::AST::LogicalNotEqual) + expect(equal_other).to be_a(Keisan::AST::Boolean) + expect(equal_other.value).to eq false + expect(not_equal_other).to be_a(Keisan::AST::Boolean) + expect(not_equal_other.value).to eq true end end end diff --git a/spec/keisan/ast/time_spec.rb b/spec/keisan/ast/time_spec.rb index c55c7d9..0394bbd 100644 --- a/spec/keisan/ast/time_spec.rb +++ b/spec/keisan/ast/time_spec.rb @@ -47,8 +47,7 @@ expect(ast.evaluate.value).to eq false ast = Keisan::AST.parse("time(2000) + time(2000)") - expect(ast.evaluate).to be_a(Keisan::AST::Plus) - expect{ast.evaluate.value}.to raise_error(TypeError) + expect{ast.evaluate}.to raise_error(Keisan::Exceptions::InvalidExpression) end it "works in arrays" do diff --git a/spec/keisan/calculator_spec.rb b/spec/keisan/calculator_spec.rb index 0e9e0f1..73abce0 100644 --- a/spec/keisan/calculator_spec.rb +++ b/spec/keisan/calculator_spec.rb @@ -9,6 +9,26 @@ expect(calculator.evaluate("2 / 3 ** 2")).to eq Rational(2,9) end + it "can do complex nested operations" do + calculator.evaluate("get_rand() = sample([1, 2, 3, 4, 5])") + calculator.evaluate("includes(a, element) = a.reduce(false, found, x, found || (x == element))") + calculator.evaluate("""f(n) = { + let values = [] + let i = 0 + while (i < n, + let value = nil + while (value == nil || includes(values, value), + value = get_rand() + ) + values += [value] + i += 1 + ) + values + } """) + res = calculator.evaluate("f(5)") + expect(res).to match_array([1, 2, 3, 4, 5]) + end + it "reduces Rational with 1 denominator to Integer" do one = calculator.evaluate("1/1") expect(one).to be_a(Integer) From 89a5b0b2e4a4846a99034f580becc72e9bbfda5d Mon Sep 17 00:00:00 2001 From: Christopher Locke Date: Thu, 20 May 2021 22:58:57 +0900 Subject: [PATCH 3/4] Fix typo --- lib/keisan/functions/enumerable_function.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/keisan/functions/enumerable_function.rb b/lib/keisan/functions/enumerable_function.rb index d8e85ba..0f914ce 100644 --- a/lib/keisan/functions/enumerable_function.rb +++ b/lib/keisan/functions/enumerable_function.rb @@ -21,7 +21,7 @@ def evaluate(ast_function, context = nil) context ||= Context.new operand, arguments, expression = operand_arguments_expression_for(ast_function, context) - + # Extract underlying operand for cells real_operand = operand.is_a?(AST::Cell) ? operand.node : operand From 70c30093680a5d44ce5f1bcdaaf6e469df7b0e20 Mon Sep 17 00:00:00 2001 From: Christopher Locke Date: Thu, 20 May 2021 23:05:48 +0900 Subject: [PATCH 4/4] Update version to 0.8.12 --- lib/keisan/version.rb | 2 +- spec/version_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/keisan/version.rb b/lib/keisan/version.rb index 39a23d1..d1107e4 100644 --- a/lib/keisan/version.rb +++ b/lib/keisan/version.rb @@ -1,3 +1,3 @@ module Keisan - VERSION = "0.8.11" + VERSION = "0.8.12" end diff --git a/spec/version_spec.rb b/spec/version_spec.rb index b699e24..df4a065 100644 --- a/spec/version_spec.rb +++ b/spec/version_spec.rb @@ -2,6 +2,6 @@ RSpec.describe Keisan do it "has the expected version number" do - expect(Keisan::VERSION).to eq "0.8.11" + expect(Keisan::VERSION).to eq "0.8.12" end end