predicator 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY.md +9 -0
- data/README.md +20 -0
- data/Rakefile +5 -0
- data/bin/console +1 -0
- data/lib/predicator.rb +6 -17
- data/lib/predicator/context.rb +24 -0
- data/lib/predicator/generated_parser.rb +885 -0
- data/lib/predicator/lexer.rb +93 -0
- data/lib/predicator/parser.rb +24 -37
- data/lib/predicator/parser.y +46 -0
- data/lib/predicator/predicates/and.rb +15 -9
- data/lib/predicator/predicates/equals.rb +21 -0
- data/lib/predicator/predicates/false.rb +13 -0
- data/lib/predicator/predicates/not.rb +15 -8
- data/lib/predicator/predicates/or.rb +15 -9
- data/lib/predicator/predicates/true.rb +13 -0
- data/lib/predicator/variable.rb +26 -0
- data/lib/predicator/version.rb +1 -1
- data/predicator.gemspec +4 -4
- metadata +35 -12
- data/lib/predicator/transform.rb +0 -10
@@ -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
|
data/lib/predicator/parser.rb
CHANGED
@@ -1,47 +1,34 @@
|
|
1
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
3
|
+
require "predicator/generated_parser"
|
4
|
+
require "predicator/lexer"
|
5
|
+
require "predicator/variable"
|
18
6
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
rparen).as(:or)
|
17
|
+
class Parser < GeneratedParser
|
18
|
+
def next_token
|
19
|
+
@lexer.next_token
|
33
20
|
end
|
34
21
|
|
35
|
-
|
22
|
+
def parse string
|
23
|
+
@lexer = Lexer.new string
|
24
|
+
do_parse
|
25
|
+
end
|
36
26
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
2
|
-
|
3
|
-
|
1
|
+
module Predicator
|
2
|
+
module Predicates
|
3
|
+
class And
|
4
|
+
attr_reader :predicates
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
@@ -1,13 +1,20 @@
|
|
1
|
-
module Predicator
|
2
|
-
|
3
|
-
|
1
|
+
module Predicator
|
2
|
+
module Predicates
|
3
|
+
class Not
|
4
|
+
attr_reader :predicate
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
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
|
2
|
-
|
3
|
-
|
1
|
+
module Predicator
|
2
|
+
module Predicates
|
3
|
+
class Or
|
4
|
+
attr_reader :predicates
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
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,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
|
data/lib/predicator/version.rb
CHANGED
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
|
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 "
|
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.
|
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-
|
11
|
+
date: 2016-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
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: :
|
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:
|
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:
|
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:
|
70
|
+
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- -
|
73
|
+
- - ">="
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
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:
|
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/
|
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:
|