predicator 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: