lamep 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fc0ce5e8f408a519a1c060670e476287be1d9f67
4
- data.tar.gz: c2930b9fbdcdc8c53df84b860f2d331adf53d911
3
+ metadata.gz: a557b4e3fe5b67811e338d680c486345ec1bb7ed
4
+ data.tar.gz: f9ad186e84d8465682dfc46ce32da2362663d1db
5
5
  SHA512:
6
- metadata.gz: fbf0d01e871e567f1e9fb466512a581c17b10631845611b09e02e1374d338a5c6edfd53a7acd480186b8a7df5e14c23dfc025cd26929441eed5863a4087dd466
7
- data.tar.gz: 1e0f8b49ed2b77cadc2cb0aa7b10fbebe9a7d21865647a34e25af1e6a47bcb8769a87cf6c5420e44bf86683cbee3f1f89157e68f582a2fa80e6f5d60e36b60f9
6
+ metadata.gz: 64b5b8d9bed78b78e68b481e86bdc641b4b34dc3b16c8365e2c9d45348c751129a53d9b5cfdfe8271bdabd23f08a5b742f94ef5cabb1e79f3099e710be5ab99f
7
+ data.tar.gz: 67133170d913738facf242cd13a124802306a11ef9d244c47e54049a6820016320691ff258403bfa9e563e909d3173f624d709f8b7ff6fef5213cd547bedf0c5
data/lamep.gemspec CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'lamep'
7
- spec.version = 0.1
7
+ spec.version = 0.2
8
8
  spec.authors = ['Martin Svoboda', 'Miroslav Csonka']
9
9
  spec.email = ['miroslav.csonka@gmail.com']
10
10
  spec.summary = %q{Logical and mathematical expression parser}
@@ -5,4 +5,8 @@ class And < Arity2Operators
5
5
  "(#{@left.to_sql} AND #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(attributes={})
9
+ !!@left.evaluate(attributes) && !!@right.evaluate(attributes)
10
+ end
11
+
8
12
  end
@@ -5,4 +5,8 @@ class Equal < Arity2Operators
5
5
  "(#{@left.to_sql} = #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(values = {})
9
+ @left.evaluate(values) == @right.evaluate(values)
10
+ end
11
+
8
12
  end
@@ -5,4 +5,9 @@ class GreaterThan < Arity2Operators
5
5
  "(#{@left.to_sql} > #{@right.to_sql})"
6
6
  end
7
7
 
8
+
9
+ def evaluate(attributes={})
10
+ @left.evaluate(attributes).to_i > @right.evaluate(attributes).to_i
11
+ end
12
+
8
13
  end
@@ -5,4 +5,8 @@ class GreaterThanEqual < Arity2Operators
5
5
  "(#{@left.to_sql} >= #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(attributes={})
9
+ @left.evaluate(attributes).to_i >= @right.evaluate(attributes).to_i
10
+ end
11
+
8
12
  end
@@ -5,4 +5,8 @@ class LessThan < Arity2Operators
5
5
  "(#{@left.to_sql} < #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(attributes={})
9
+ @left.evaluate(attributes).to_i < @right.evaluate(attributes).to_i
10
+ end
11
+
8
12
  end
@@ -5,4 +5,8 @@ class LessThanEqual < Arity2Operators
5
5
  "(#{@left.to_sql} <= #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(attributes={})
9
+ @left.evaluate(attributes).to_i <= @right.evaluate(attributes).to_i
10
+ end
11
+
8
12
  end
@@ -5,4 +5,8 @@ class NotEqual < Arity2Operators
5
5
  "(#{@left.to_sql} != #{@right.to_sql})"
6
6
  end
7
7
 
8
+ def evaluate(attributes={})
9
+ @left.evaluate(attributes) != @right.evaluate(attributes)
10
+ end
11
+
8
12
  end
@@ -27,4 +27,8 @@ class Operator
27
27
  raise NotImplementedError
28
28
  end
29
29
 
30
+ def evaluate(attributes = {})
31
+ raise NotImplementedError
32
+ end
33
+
30
34
  end
@@ -4,4 +4,8 @@ class Or < Arity2Operators
4
4
  def to_sql
5
5
  "(#{@left.to_sql} OR #{@right.to_sql})"
6
6
  end
7
+
8
+ def evaluate(attributes={})
9
+ !!@left.evaluate(attributes) || !!@right.evaluate(attributes)
10
+ end
7
11
  end
@@ -7,4 +7,8 @@ class UnaryMinus < Arity1Operators
7
7
  -operand.to_i
8
8
  end
9
9
 
10
+ def evaluate(attributes={})
11
+ -@operand.evaluate(attributes).to_i
12
+ end
13
+
10
14
  end
@@ -8,4 +8,12 @@ class ValueExpression < Arity1Operators
8
8
  operand
9
9
  end
10
10
 
11
+ def evaluate(attributes = {})
12
+ if attributes.key? @operand
13
+ attributes[@operand]
14
+ else
15
+ @operand
16
+ end
17
+ end
18
+
11
19
  end
@@ -7,10 +7,10 @@ class AbstractSyntaxTreeBuilder
7
7
  def build_tree
8
8
  until @postfix.length == 1
9
9
  operator_index = first_operator_index
10
- fail 'Not enough operators' if operator_index.nil?
10
+ fail NotEnoughOperatorsException if operator_index.nil?
11
11
  exp = Operator.factory!(@postfix.slice!(operator_index))
12
12
  most_left_child = operator_index - exp::ARITY
13
- fail "Not enough operands for operator #{exp}" if most_left_child < 0
13
+ fail NotEnoughOperandsException, expression: exp if most_left_child < 0
14
14
  children = @postfix.slice!(most_left_child, exp::ARITY)
15
15
  @postfix.insert(most_left_child, exp.new(*children))
16
16
  end
@@ -0,0 +1,2 @@
1
+ class MissingLeftParenthesesError < Exception
2
+ end
@@ -0,0 +1,2 @@
1
+ class MissingRightParenthesesError < Exception
2
+ end
@@ -0,0 +1,10 @@
1
+ class NotEnoughOperandsException < Exception
2
+
3
+ attr_reader :expression
4
+
5
+ def initialize(*argv)
6
+ @expression = argv[0].fetch(:expression)
7
+ super
8
+ end
9
+
10
+ end
@@ -0,0 +1,2 @@
1
+ class NotEnoughOperatorsException < Exception
2
+ end
@@ -16,7 +16,7 @@ class ShuntingYard
16
16
  @stack << token
17
17
  when ')'
18
18
  bracket_sum -= 1
19
- fail('Right parentheses mismatch') if bracket_sum < 0
19
+ fail MissingLeftParenthesesError if bracket_sum < 0
20
20
  burn_stack_to_parentheses
21
21
  else
22
22
  if Operator.exists?(token)
@@ -27,12 +27,12 @@ class ShuntingYard
27
27
  end
28
28
  end
29
29
  end
30
- fail('Left parentheses mismatch') if bracket_sum > 0
30
+ fail MissingRightParenthesesError if bracket_sum > 0
31
31
  @output += @stack.reverse
32
32
  @output
33
33
  end
34
34
 
35
- def burn_stack_to_higher_precedence(token)
35
+ private def burn_stack_to_higher_precedence(token)
36
36
  until @stack.empty? || @stack.last == '(' || Operator.precedence!(token) < Operator.precedence!(@stack.last)
37
37
  @output << @stack.pop
38
38
  end
data/lib/lamep.rb ADDED
@@ -0,0 +1,28 @@
1
+ require './lib/lamep/exceptions/not_enough_operands_exception'
2
+ require './lib/lamep/exceptions/not_enough_operators_exception'
3
+ require './lib/lamep/exceptions/missing_left_parentheses_error'
4
+ require './lib/lamep/exceptions/missing_right_parentheses_error'
5
+ require './lib/lamep/Expressions/operator'
6
+ require './lib/lamep/Expressions/arity1_operators'
7
+ require './lib/lamep/Expressions/arity2_operators'
8
+ require './lib/lamep/Expressions/value_expression'
9
+ require './lib/lamep/token_parser'
10
+ require './lib/lamep/shunting_yard'
11
+ require './lib/lamep/Expressions/equal'
12
+ require './lib/lamep/Expressions/greater_than'
13
+ require './lib/lamep/Expressions/less_than'
14
+ require './lib/lamep/Expressions/greater_than_equal'
15
+ require './lib/lamep/Expressions/less_than_equal'
16
+ require './lib/lamep/Expressions/and'
17
+ require './lib/lamep/Expressions/or'
18
+ require './lib/lamep/Expressions/unary_minus'
19
+ require './lib/lamep/Expressions/not_equal'
20
+ require './lib/lamep/abstract_syntax_tree_builder'
21
+
22
+ class Lamep
23
+ def evaluate(expression, attributes={})
24
+ tokens = TokenParser.new.parse(expression)
25
+ postfix = ShuntingYard.new(tokens).postfix
26
+ AbstractSyntaxTreeBuilder.new(postfix).build_tree.evaluate(attributes)
27
+ end
28
+ end
@@ -57,11 +57,11 @@ describe AbstractSyntaxTreeBuilder do
57
57
  end
58
58
 
59
59
  it 'only operands' do
60
- expect {AbstractSyntaxTreeBuilder.new(%w(materiál cena)).build_tree }.to raise_error(RuntimeError)
60
+ expect {AbstractSyntaxTreeBuilder.new(%w(materiál cena)).build_tree }.to raise_error(NotEnoughOperatorsException)
61
61
  end
62
62
 
63
63
  it 'only operators' do
64
- expect {AbstractSyntaxTreeBuilder.new(%w(&& -)).build_tree }.to raise_error(RuntimeError)
64
+ expect {AbstractSyntaxTreeBuilder.new(%w(&& -)).build_tree }.to raise_error(NotEnoughOperandsException)
65
65
  end
66
66
 
67
67
  end
@@ -0,0 +1,66 @@
1
+ require './spec/spec_helper'
2
+
3
+ describe 'Evaluating of expressions' do
4
+
5
+ def matches?(filter, values={})
6
+ tokens = TokenParser.new.parse(filter)
7
+ postfix = ShuntingYard.new(tokens).postfix
8
+ AbstractSyntaxTreeBuilder.new(postfix).build_tree.evaluate(values)
9
+ end
10
+
11
+ describe Equal do
12
+
13
+ it 'is false' do
14
+ expect(matches?('true = false')).to eq false
15
+ expect(matches?('true = true')).to eq true
16
+ end
17
+
18
+ it 'replacing value holders with actual values' do
19
+ expect(matches?('name = mirek', { 'name' => 'mirek' })).to eq true
20
+ expect(matches?('name = mirek', { 'name' => 'Tomáš' })).to eq false
21
+ end
22
+
23
+ end
24
+
25
+
26
+ it GreaterThan do
27
+ expect(matches?('price > 200', { 'price' => '300' })).to eq true
28
+ expect(matches?('price > 200', { 'price' => 150 })).to eq false
29
+ end
30
+
31
+ it LessThan do
32
+ expect(matches?('(price < 200)', { 'price' => 150 })).to eq true
33
+ expect(matches?('(price < 200)', { 'price' => 500 })).to eq false
34
+ end
35
+
36
+ it GreaterThanEqual do
37
+ expect(matches?('price >= 200', { 'price' => '200' })).to eq true
38
+ expect(matches?('price >= 200', { 'price' => 201 })).to eq true
39
+ expect(matches?('price >= 200', { 'price' => 199 })).to eq false
40
+ end
41
+
42
+ it LessThanEqual do
43
+ expect(matches?('(price <= 200)', { 'price' => 201 })).to eq false
44
+ expect(matches?('(price <= 200)', { 'price' => 200 })).to eq true
45
+ expect(matches?('(price <= 200)', { 'price' => 199 })).to eq true
46
+ end
47
+
48
+ it And do
49
+ expect(matches?('(price > 200) && price < 500', { 'price' => 400 })).to eq true
50
+ end
51
+
52
+ it NotEqual do
53
+ expect(matches?('name != mirek')).to eq true
54
+ expect(matches?('name != name')).to eq false
55
+ end
56
+
57
+ it Or do
58
+ expect(matches?('(price > 200) || (category = 1)', { 'price' => 0, 'category' => '1' })).to eq true
59
+ expect(matches?('price > 200 || category = 1', { 'price' => '0', 'category' => '0' })).to eq false
60
+ end
61
+
62
+ it UnaryMinus do
63
+ expect(matches?('-1 = -(1)', {})).to eq true
64
+ end
65
+
66
+ end
@@ -35,8 +35,8 @@ describe ShuntingYard do
35
35
  it 'brackets' do
36
36
  returns %w(( cena > 180 )), %w(cena 180 >)
37
37
  returns %w(( ( cena ) > ( 180 ) )), %w(cena 180 >)
38
- expect { ShuntingYard.new(%w{( cena ) > 180 )}).postfix }.to raise_error(RuntimeError)
39
- expect { ShuntingYard.new(%w{((( cena ) > 180 )}).postfix }.to raise_error(RuntimeError)
38
+ expect { ShuntingYard.new(%w{( cena ) > 180 )}).postfix }.to raise_error(MissingLeftParenthesesError )
39
+ expect { ShuntingYard.new(%w{( ( ( cena ) > 180 )}).postfix }.to raise_error(MissingRightParenthesesError)
40
40
  end
41
41
 
42
42
  it 'difficult cases' do
data/spec/spec_helper.rb CHANGED
@@ -1,19 +1,4 @@
1
- require './lib/lamep/Expressions/operator'
2
- require './lib/lamep/Expressions/arity1_operators'
3
- require './lib/lamep/Expressions/arity2_operators'
4
- require './lib/lamep/Expressions/value_expression'
5
- require './lib/lamep/token_parser'
6
- require './lib/lamep/shunting_yard'
7
- require './lib/lamep/Expressions/equal'
8
- require './lib/lamep/Expressions/greater_than'
9
- require './lib/lamep/Expressions/less_than'
10
- require './lib/lamep/Expressions/greater_than_equal'
11
- require './lib/lamep/Expressions/less_than_equal'
12
- require './lib/lamep/Expressions/and'
13
- require './lib/lamep/Expressions/or'
14
- require './lib/lamep/Expressions/unary_minus'
15
- require './lib/lamep/Expressions/not_equal'
16
- require './lib/lamep/abstract_syntax_tree_builder'
1
+ require './lib/lamep'
17
2
 
18
3
  RSpec.configure do |config|
19
4
  # rspec-expectations config goes here. You can use an alternate
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lamep
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Svoboda
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-14 00:00:00.000000000 Z
12
+ date: 2015-01-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -67,6 +67,7 @@ files:
67
67
  - README.md
68
68
  - Rakefile
69
69
  - lamep.gemspec
70
+ - lib/lamep.rb
70
71
  - lib/lamep/Expressions/and.rb
71
72
  - lib/lamep/Expressions/arity1_operators.rb
72
73
  - lib/lamep/Expressions/arity2_operators.rb
@@ -81,10 +82,15 @@ files:
81
82
  - lib/lamep/Expressions/unary_minus.rb
82
83
  - lib/lamep/Expressions/value_expression.rb
83
84
  - lib/lamep/abstract_syntax_tree_builder.rb
85
+ - lib/lamep/exceptions/missing_left_parentheses_error.rb
86
+ - lib/lamep/exceptions/missing_right_parentheses_error.rb
87
+ - lib/lamep/exceptions/not_enough_operands_exception.rb
88
+ - lib/lamep/exceptions/not_enough_operators_exception.rb
84
89
  - lib/lamep/shunting_yard.rb
85
90
  - lib/lamep/token_parser.rb
86
91
  - spec/lib/Expressions/operators_spec.rb
87
92
  - spec/lib/abstract_syntax_tree_builder_spec.rb
93
+ - spec/lib/evaluation_spec.rb
88
94
  - spec/lib/shunting_yard_spec.rb
89
95
  - spec/lib/token_parser_spec.rb
90
96
  - spec/spec_helper.rb
@@ -115,6 +121,7 @@ summary: Logical and mathematical expression parser
115
121
  test_files:
116
122
  - spec/lib/Expressions/operators_spec.rb
117
123
  - spec/lib/abstract_syntax_tree_builder_spec.rb
124
+ - spec/lib/evaluation_spec.rb
118
125
  - spec/lib/shunting_yard_spec.rb
119
126
  - spec/lib/token_parser_spec.rb
120
127
  - spec/spec_helper.rb