Skip to content

Commit

Permalink
add XOR operator and tests
Browse files Browse the repository at this point in the history
closes #246
  • Loading branch information
Dan Madere authored and rubysolo committed Jul 26, 2021
1 parent 21e1cb0 commit a397df0
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Also, all functions from Ruby's Math module, including `SIN`, `COS`, `TAN`, etc.

Comparison: `<`, `>`, `<=`, `>=`, `<>`, `!=`, `=`,

Logic: `IF`, `AND`, `OR`, `NOT`, `SWITCH`
Logic: `IF`, `AND`, `OR`, `XOR`, `NOT`, `SWITCH`

Numeric: `MIN`, `MAX`, `SUM`, `AVG`, `COUNT`, `ROUND`, `ROUNDDOWN`, `ROUNDUP`

Expand Down
1 change: 1 addition & 0 deletions lib/dentaku/ast.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
require_relative './ast/functions/string_functions'
require_relative './ast/functions/sum'
require_relative './ast/functions/switch'
require_relative './ast/functions/xor'
48 changes: 48 additions & 0 deletions lib/dentaku/ast/functions/xor.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
require_relative '../function'
require_relative '../../exceptions'

module Dentaku
module AST
class Xor < Function
def self.min_param_count
1
end

def self.max_param_count
Float::INFINITY
end

def deferred_args
[1, 2]
end

def value(context = {})
if @args.empty?
raise Dentaku::ArgumentError.for(
:too_few_arguments,
function_name: 'XOR()', at_least: 1, given: 0
), 'XOR() requires at least one argument'
end

true_arg_count = 0
@args.each do |arg|
case arg.value(context)
when TrueClass
true_arg_count += 1
break if true_arg_count > 1
when FalseClass, nil
next
else
raise Dentaku::ArgumentError.for(
:incompatible_type,
function_name: 'XOR()', expect: :logical, actual: arg.class
), 'XOR() requires arguments to be logical expressions'
end
end
true_arg_count == 1
end
end
end
end

Dentaku::AST::Function.register_class(:xor, Dentaku::AST::Xor)
1 change: 1 addition & 0 deletions lib/dentaku/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class Parser

and: AST::And,
or: AST::Or,
xor: AST::Xor,
}.freeze

attr_reader :input, :output, :operations, :arities, :case_sensitive
Expand Down
35 changes: 35 additions & 0 deletions spec/ast/xor_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
require 'spec_helper'
require 'dentaku'
require 'dentaku/ast/functions/or'

describe 'Dentaku::AST::Xor' do
let(:calculator) { Dentaku::Calculator.new }

it 'returns false if all of the arguments are false' do
result = Dentaku('XOR(false, false)')
expect(result).to eq(false)
end

it 'returns true if only one of the arguments is true' do
result = Dentaku('XOR(false, true)')
expect(result).to eq(true)
end

it 'returns false if more than one of the arguments is true' do
result = Dentaku('XOR(false, true, true)')
expect(result).to eq(false)
end

it 'supports nested expressions' do
result = Dentaku('XOR(y = 1, x = 1)', x: 1, y: 2)
expect(result).to eq(true)
end

it 'raises an error if no arguments are passed' do
expect { calculator.evaluate!('XOR()') }.to raise_error(Dentaku::ParseError)
end

it 'raises an error if a non logical argument is passed' do
expect { calculator.evaluate!('XOR("r")') }.to raise_error(Dentaku::ArgumentError)
end
end

0 comments on commit a397df0

Please sign in to comment.