predicator 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,93 @@
1
+ require "stringio"
2
+ require "strscan"
3
+
4
+ module Predicator
5
+ class Lexer
6
+ SPACE = /[ \t\r\n]/
7
+ DOT = /\./
8
+ BANG = /!/
9
+ EQUAL = /=/
10
+ LPAREN = /\(/
11
+ RPAREN = /\)/
12
+ DATE = /(\d{4})-(\d{2})-(\d{2})/i
13
+ FLOAT = /[+-]?(?:[0-9_]+\.[0-9_]*|\.[0-9_]+|\d+(?=[eE]))(?:[eE][+-]?[0-9_]+)?\b/
14
+ INTEGER = /[+-]?\d(_?\d)*\b/
15
+ TRUE = /true\b/
16
+ FALSE = /false\b/
17
+ AND = /and/i
18
+ OR = /or/i
19
+ STRING = /(["])(?:\\?.)*?\1/
20
+ IDENTIFIER = /[a-z][A-Za-z0-9_]*/
21
+
22
+ def initialize string_or_io
23
+ io = string_or_io.is_a?(String) ?
24
+ StringIO.new(string_or_io) :
25
+ string_or_io
26
+
27
+ @ss = StringScanner.new io.read
28
+ @tokens = []
29
+ tokenize
30
+ end
31
+
32
+ def next_token
33
+ @tokens.shift
34
+ end
35
+
36
+ def tokenize
37
+ until @ss.eos?
38
+ case
39
+ when @ss.scan(SPACE)
40
+ # ignore space
41
+
42
+ when text = @ss.scan(DOT)
43
+ @tokens.push [:tDOT, text]
44
+
45
+ when text = @ss.scan(BANG)
46
+ @tokens.push [:tBANG, text]
47
+
48
+ when text = @ss.scan(EQUAL)
49
+ @tokens.push [:tEQUAL, text]
50
+
51
+ when text = @ss.scan(LPAREN)
52
+ @tokens.push [:tLPAREN, text]
53
+
54
+ when text = @ss.scan(RPAREN)
55
+ @tokens.push [:tRPAREN, text]
56
+
57
+ when text = @ss.scan(DATE)
58
+ args = [ @ss[1], @ss[2], @ss[3] ].map(&:to_i)
59
+ @tokens.push [:tDATE, args]
60
+
61
+ when text = @ss.scan(FLOAT)
62
+ @tokens.push [:tFLOAT, text]
63
+
64
+ when text = @ss.scan(INTEGER)
65
+ @tokens.push [:tINTEGER, text]
66
+
67
+ when text = @ss.scan(TRUE)
68
+ @tokens.push [:tTRUE, text]
69
+
70
+ when text = @ss.scan(FALSE)
71
+ @tokens.push [:tFALSE, text]
72
+
73
+ when text = @ss.scan(AND)
74
+ @tokens.push [:tAND, text]
75
+
76
+ when text = @ss.scan(OR)
77
+ @tokens.push [:tOR, text]
78
+
79
+ when text = @ss.scan(STRING)
80
+ @tokens.push [:tSTRING, text[1..-2]]
81
+
82
+ when text = @ss.scan(IDENTIFIER)
83
+ @tokens.push [:tIDENTIFIER, text]
84
+
85
+ else
86
+ raise "Unexpected characters: #{@ss.peek(5).inspect}"
87
+ end
88
+ end
89
+
90
+ @tokens.push [false, false]
91
+ end
92
+ end
93
+ end
@@ -1,47 +1,34 @@
1
- module Predicator
2
- class Parser < Parslet::Parser
3
- rule(:space) { match[" "].repeat(1) }
4
- rule(:space?) { space.maybe }
5
- rule(:comma) { s(",") }
6
- rule(:lparen) { s("(") }
7
- rule(:rparen) { s(")") }
8
- rule(:and_op) { s("and") }
9
- rule(:or_op) { s("or") }
10
- rule(:not_op) { s("!") | s("not") }
11
- rule :boolean do
12
- (s("true") | s("false")).as :boolean
13
- end
1
+ require "date"
14
2
 
15
- rule(:predicate) do
16
- boolean | not_predicate | or_predicate | and_predicate
17
- end
3
+ require "predicator/generated_parser"
4
+ require "predicator/lexer"
5
+ require "predicator/variable"
18
6
 
19
- rule :not_predicate do
20
- (not_op >> lparen >> predicate >> rparen).as(:not)
21
- end
7
+ require "predicator/predicates/equals"
8
+ require "predicator/predicates/true"
9
+ require "predicator/predicates/false"
10
+ require "predicator/predicates/and"
11
+ require "predicator/predicates/or"
12
+ require "predicator/predicates/not"
22
13
 
23
- rule :and_predicate do
24
- (and_op >> lparen >>
25
- (predicate >> (comma >> predicate).repeat.maybe).as(:array) >>
26
- rparen).as(:and)
27
- end
14
+ module Predicator
15
+ class ParseError < StandardError; end
28
16
 
29
- rule :or_predicate do
30
- (or_op >> lparen >>
31
- (predicate >> (comma >> predicate).repeat.maybe).as(:array) >>
32
- rparen).as(:or)
17
+ class Parser < GeneratedParser
18
+ def next_token
19
+ @lexer.next_token
33
20
  end
34
21
 
35
- root :predicate
22
+ def parse string
23
+ @lexer = Lexer.new string
24
+ do_parse
25
+ end
36
26
 
37
- private
38
- # Defines a string followed by any number of spaces.
39
- def s str, name=nil
40
- if name
41
- str(str).as(name) >> space?
42
- else
43
- str(str) >> space?
44
- end
27
+ def on_error type, val, values
28
+ super
29
+ rescue Racc::ParseError => e
30
+ trace = values.each_with_index.map{|l, i| "#{' ' * i}#{l}"}
31
+ raise ParseError, "\nparse error on value #{val.inspect}\n#{trace.join("\n")}"
45
32
  end
46
33
  end
47
34
  end
@@ -0,0 +1,46 @@
1
+ class Predicator::GeneratedParser
2
+ options no_result_var
3
+ prechigh
4
+ right tBANG
5
+ left tAND tOR
6
+ preclow
7
+ token tTRUE tFALSE tSTRING tFLOAT tINTEGER tDATE tIDENTIFIER tDOT tEQUAL
8
+ tLPAREN tRPAREN tAND tOR tBANG
9
+ rule
10
+ predicate
11
+ : equals_predicate
12
+ | boolean_predicate
13
+ | logical_predicate
14
+ | tLPAREN predicate tRPAREN
15
+ ;
16
+ equals_predicate
17
+ : value tEQUAL value { Predicator::Predicates::Equals.new val[0], val[2] }
18
+ ;
19
+ boolean_predicate
20
+ : tTRUE { Predicator::Predicates::True.new }
21
+ | tFALSE { Predicator::Predicates::False.new }
22
+ ;
23
+ logical_predicate
24
+ : predicate tAND predicate { Predicator::Predicates::And.new [val[0], val[2]] }
25
+ | predicate tOR predicate { Predicator::Predicates::Or.new [val[0], val[2]] }
26
+ | tBANG predicate { Predicator::Predicates::Not.new val[0] }
27
+ ;
28
+ value
29
+ : scalar
30
+ | variable
31
+ ;
32
+ scalar
33
+ : string
34
+ | literal
35
+ ;
36
+ string
37
+ : tSTRING { val[0] }
38
+ ;
39
+ literal
40
+ : tFLOAT { val[0].to_f }
41
+ | tINTEGER { val[0].to_i }
42
+ | tDATE { Date.new *val[0] }
43
+ ;
44
+ variable
45
+ : tIDENTIFIER tDOT tIDENTIFIER { Predicator::Variable.new val[0], val[2] }
46
+ ;
@@ -1,14 +1,20 @@
1
- module Predicator::Predicates
2
- class And
3
- attr_reader :predicates
1
+ module Predicator
2
+ module Predicates
3
+ class And
4
+ attr_reader :predicates
4
5
 
5
- def initialize predicates
6
- @predicates = predicates
7
- end
6
+ def initialize predicates
7
+ @predicates = predicates
8
+ end
9
+
10
+ def satisfied? context=Predicator::Context.new
11
+ predicates.all?{ |pred| pred.satisfied? context }
12
+ end
8
13
 
9
- def == other
10
- other.kind_of?(self.class) &&
11
- other.predicates == predicates
14
+ def == other
15
+ other.kind_of?(self.class) &&
16
+ other.predicates == predicates
17
+ end
12
18
  end
13
19
  end
14
20
  end
@@ -0,0 +1,21 @@
1
+ module Predicator
2
+ module Predicates
3
+ class Equals
4
+ attr_reader :left, :right
5
+
6
+ def initialize left, right
7
+ @left, @right = left, right
8
+ end
9
+
10
+ def satisfied? context=Predicator::Context.new
11
+ context.value_for(left) == context.value_for(right)
12
+ end
13
+
14
+ def == other
15
+ other.kind_of?(self.class) &&
16
+ other.left == left &&
17
+ other.right == right
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ module Predicator
2
+ module Predicates
3
+ class False
4
+ def satisfied? context=Predicator::Context.new
5
+ false
6
+ end
7
+
8
+ def == other
9
+ other.kind_of?(self.class)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,13 +1,20 @@
1
- module Predicator::Predicates
2
- class Not
3
- attr_reader :predicate
1
+ module Predicator
2
+ module Predicates
3
+ class Not
4
+ attr_reader :predicate
4
5
 
5
- def initialize predicate
6
- @predicate = predicate
7
- end
6
+ def initialize predicate
7
+ @predicate = predicate
8
+ end
9
+
10
+ def satisfied? context=Predicator::Context.new
11
+ !predicate.satisfied? context
12
+ end
8
13
 
9
- def == other
10
- other.kind_of?(self.class) && other.predicate == predicate
14
+ def == other
15
+ other.kind_of?(self.class) &&
16
+ other.predicate == predicate
17
+ end
11
18
  end
12
19
  end
13
20
  end
@@ -1,14 +1,20 @@
1
- module Predicator::Predicates
2
- class Or
3
- attr_reader :predicates
1
+ module Predicator
2
+ module Predicates
3
+ class Or
4
+ attr_reader :predicates
4
5
 
5
- def initialize predicates
6
- @predicates = predicates
7
- end
6
+ def initialize predicates
7
+ @predicates = predicates
8
+ end
9
+
10
+ def satisfied? context=Predicator::Context.new
11
+ predicates.any?{ |pred| pred.satisfied? context }
12
+ end
8
13
 
9
- def == other
10
- other.kind_of?(self.class) &&
11
- other.predicates == predicates
14
+ def == other
15
+ other.kind_of?(self.class) &&
16
+ other.predicates == predicates
17
+ end
12
18
  end
13
19
  end
14
20
  end
@@ -0,0 +1,13 @@
1
+ module Predicator
2
+ module Predicates
3
+ class True
4
+ def satisfied? context=Predicator::Context.new
5
+ true
6
+ end
7
+
8
+ def == other
9
+ other.kind_of?(self.class)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ module Predicator
2
+ class Variable
3
+ attr_reader :model, :attribute
4
+
5
+ def initialize model, attribute
6
+ @model = model
7
+ @attribute = attribute
8
+ end
9
+
10
+ def value_in context
11
+ entity_name = model.to_s
12
+ entity = context.entities[model]
13
+ if entity.nil?
14
+ raise ArgumentError, "Unknown entity #{entity_name}"
15
+ else
16
+ entity.send attribute
17
+ end
18
+ end
19
+
20
+ def == other
21
+ other.kind_of?(self.class) &&
22
+ other.model == model &&
23
+ other.attribute == attribute
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module Predicator
2
- VERSION = "0.0.0"
2
+ VERSION = "0.1.0"
3
3
  end
data/predicator.gemspec CHANGED
@@ -15,12 +15,12 @@ Gem::Specification.new do |spec|
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
17
  spec.bindir = "exe"
18
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename f }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency "parslet"
22
-
23
21
  spec.add_development_dependency "bundler"
24
- spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "coveralls"
25
23
  spec.add_development_dependency "minitest", "= 5.4.2"
24
+ spec.add_development_dependency "racc"
25
+ spec.add_development_dependency "rake"
26
26
  end
metadata CHANGED
@@ -1,23 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: predicator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - JohnnyT
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-02-08 00:00:00.000000000 Z
11
+ date: 2016-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: parslet
14
+ name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
- type: :runtime
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: bundler
28
+ name: coveralls
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,7 +39,21 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 5.4.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 5.4.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: racc
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -53,19 +67,19 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: minitest
70
+ name: rake
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
- version: 5.4.2
75
+ version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
- version: 5.4.2
82
+ version: '0'
69
83
  description:
70
84
  email:
71
85
  - ubergeek3141@gmail.com
@@ -77,17 +91,25 @@ files:
77
91
  - ".travis.yml"
78
92
  - CODE_OF_CONDUCT.md
79
93
  - Gemfile
94
+ - HISTORY.md
80
95
  - LICENSE.txt
81
96
  - README.md
82
97
  - Rakefile
83
98
  - bin/console
84
99
  - bin/setup
85
100
  - lib/predicator.rb
101
+ - lib/predicator/context.rb
102
+ - lib/predicator/generated_parser.rb
103
+ - lib/predicator/lexer.rb
86
104
  - lib/predicator/parser.rb
105
+ - lib/predicator/parser.y
87
106
  - lib/predicator/predicates/and.rb
107
+ - lib/predicator/predicates/equals.rb
108
+ - lib/predicator/predicates/false.rb
88
109
  - lib/predicator/predicates/not.rb
89
110
  - lib/predicator/predicates/or.rb
90
- - lib/predicator/transform.rb
111
+ - lib/predicator/predicates/true.rb
112
+ - lib/predicator/variable.rb
91
113
  - lib/predicator/version.rb
92
114
  - predicator.gemspec
93
115
  homepage: https://github.com/johnnyt/predicator
@@ -115,3 +137,4 @@ signing_key:
115
137
  specification_version: 4
116
138
  summary: Predicate Engine
117
139
  test_files: []
140
+ has_rdoc: