diff --git a/lib/dentaku/ast/arithmetic.rb b/lib/dentaku/ast/arithmetic.rb index 62d54835..81eb9f56 100644 --- a/lib/dentaku/ast/arithmetic.rb +++ b/lib/dentaku/ast/arithmetic.rb @@ -7,7 +7,9 @@ module AST class Arithmetic < Operation def initialize(*) super - fail "#{ self.class } requires numeric operands" unless valid_node?(left) && valid_node?(right) + unless valid_node?(left) && valid_node?(right) + fail ParseError, "#{ self.class } requires numeric operands" + end end def type @@ -29,7 +31,7 @@ def cast(value, prefer_integer=true) end def valid_node?(node) - node.dependencies.any? || node.type == :numeric + node && (node.dependencies.any? || node.type == :numeric) end end diff --git a/lib/dentaku/ast/combinators.rb b/lib/dentaku/ast/combinators.rb index 07b0ec7b..a02fd581 100644 --- a/lib/dentaku/ast/combinators.rb +++ b/lib/dentaku/ast/combinators.rb @@ -5,7 +5,9 @@ module AST class Combinator < Operation def initialize(*) super - fail "#{ self.class } requires logical operands" unless valid_node?(left) && valid_node?(right) + unless valid_node?(left) && valid_node?(right) + fail ParseError, "#{ self.class } requires logical operands" + end end def type diff --git a/lib/dentaku/ast/function.rb b/lib/dentaku/ast/function.rb index b37c011d..2f1b82a2 100644 --- a/lib/dentaku/ast/function.rb +++ b/lib/dentaku/ast/function.rb @@ -12,7 +12,9 @@ def dependencies(context={}) end def self.get(name) - registry.fetch(function_name(name)) { fail "Undefined function #{ name } "} + registry.fetch(function_name(name)) { + fail ParseError, "Undefined function #{ name }" + } end def self.register(name, type, implementation) diff --git a/lib/dentaku/ast/negation.rb b/lib/dentaku/ast/negation.rb index 39d7010c..bb281b20 100644 --- a/lib/dentaku/ast/negation.rb +++ b/lib/dentaku/ast/negation.rb @@ -3,7 +3,7 @@ module AST class Negation < Operation def initialize(node) @node = node - fail "Negation requires numeric operand" unless valid_node?(node) + fail ParseError, "Negation requires numeric operand" unless valid_node?(node) end def value(context={}) @@ -33,7 +33,7 @@ def dependencies(context={}) private def valid_node?(node) - node.dependencies.any? || node.type == :numeric + node && (node.dependencies.any? || node.type == :numeric) end end end diff --git a/lib/dentaku/exceptions.rb b/lib/dentaku/exceptions.rb index 5d1d4643..bcb875e0 100644 --- a/lib/dentaku/exceptions.rb +++ b/lib/dentaku/exceptions.rb @@ -7,4 +7,7 @@ def initialize(unbound_variables) super("no value provided for variables: #{ unbound_variables.join(', ') }") end end + + class ParseError < StandardError + end end diff --git a/lib/dentaku/parser.rb b/lib/dentaku/parser.rb index e8fef1ba..40ed11af 100644 --- a/lib/dentaku/parser.rb +++ b/lib/dentaku/parser.rb @@ -102,7 +102,7 @@ def parse end unless operations.count == 1 && operations.last == AST::Case - fail "Unprocessed token #{ token.value }" + fail ParseError, "Unprocessed token #{ token.value }" end consume(arities.pop.succ) when :when @@ -139,7 +139,7 @@ def parse operations.push(AST::CaseElse) else - fail "Unknown case token #{ token.value }" + fail ParseError, "Unknown case token #{ token.value }" end when :grouping @@ -158,7 +158,7 @@ def parse end lparen = operations.pop - fail "Unbalanced parenthesis" unless lparen == AST::Grouping + fail ParseError, "Unbalanced parenthesis" unless lparen == AST::Grouping if operations.last && operations.last < AST::Function consume(arities.pop.succ) @@ -171,11 +171,11 @@ def parse end else - fail "Unknown grouping token #{ token.value }" + fail ParseError, "Unknown grouping token #{ token.value }" end else - fail "Not implemented for tokens of category #{ token.category }" + fail ParseError, "Not implemented for tokens of category #{ token.category }" end end @@ -184,7 +184,7 @@ def parse end unless output.count == 1 - fail "Parse error" + fail ParseError, "Invalid statement" end output.first diff --git a/spec/ast/addition_spec.rb b/spec/ast/addition_spec.rb index b47df497..90e2b217 100644 --- a/spec/ast/addition_spec.rb +++ b/spec/ast/addition_spec.rb @@ -17,7 +17,7 @@ it 'requires numeric operands' do expect { described_class.new(five, t) - }.to raise_error(RuntimeError, /requires numeric operands/) + }.to raise_error(Dentaku::ParseError, /requires numeric operands/) expression = Dentaku::AST::Multiplication.new(five, five) group = Dentaku::AST::Grouping.new(expression) diff --git a/spec/ast/and_spec.rb b/spec/ast/and_spec.rb index 9aa2e409..75e2da04 100644 --- a/spec/ast/and_spec.rb +++ b/spec/ast/and_spec.rb @@ -17,7 +17,7 @@ it 'requires logical operands' do expect { described_class.new(t, five) - }.to raise_error(RuntimeError, /requires logical operands/) + }.to raise_error(Dentaku::ParseError, /requires logical operands/) expression = Dentaku::AST::LessThanOrEqual.new(five, five) expect { diff --git a/spec/ast/division_spec.rb b/spec/ast/division_spec.rb index c4118700..2f532b04 100644 --- a/spec/ast/division_spec.rb +++ b/spec/ast/division_spec.rb @@ -17,7 +17,7 @@ it 'requires numeric operands' do expect { described_class.new(five, t) - }.to raise_error(RuntimeError, /requires numeric operands/) + }.to raise_error(Dentaku::ParseError, /requires numeric operands/) expression = Dentaku::AST::Multiplication.new(five, five) group = Dentaku::AST::Grouping.new(expression) diff --git a/spec/ast/function_spec.rb b/spec/ast/function_spec.rb index 9410a0df..0f106654 100644 --- a/spec/ast/function_spec.rb +++ b/spec/ast/function_spec.rb @@ -7,7 +7,9 @@ end it 'raises an exception when trying to access an undefined function' do - expect { described_class.get("flarble") }.to raise_error(RuntimeError, /undefined function/i) + expect { + described_class.get("flarble") + }.to raise_error(Dentaku::ParseError, /undefined function/i) end it 'registers a custom function' do diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 8d083ec7..78cc4a6e 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -123,4 +123,14 @@ case_close]).parse expect(node.value(x: 3)).to eq(4) end + + it 'raises an error on parse failure' do + five = Dentaku::Token.new(:numeric, 5) + times = Dentaku::Token.new(:operator, :multiply) + minus = Dentaku::Token.new(:operator, :subtract) + + expect { + described_class.new([five, times, minus]).parse + }.to raise_error(Dentaku::ParseError) + end end