predicator 0.4.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +3 -2
- data/HISTORY.md +9 -0
- data/README.md +7 -8
- data/Rakefile +14 -5
- data/lib/predicator.rb +18 -10
- data/lib/predicator/ast.rb +138 -0
- data/lib/predicator/context.rb +7 -63
- data/lib/predicator/evaluator.rb +108 -0
- data/lib/predicator/lexer.rex +51 -0
- data/lib/predicator/lexer.rex.rb +160 -0
- data/lib/predicator/parser.rb +291 -7
- data/lib/predicator/parser.y +66 -40
- data/lib/predicator/version.rb +1 -1
- data/lib/predicator/visitors.rb +5 -0
- data/lib/predicator/visitors/dot.rb +100 -0
- data/lib/predicator/visitors/each.rb +16 -0
- data/lib/predicator/visitors/instructions.rb +117 -0
- data/lib/predicator/visitors/string.rb +60 -0
- data/lib/predicator/visitors/visitor.rb +48 -0
- data/predicator.gemspec +3 -2
- metadata +29 -32
- data/lib/predicator/errors.rb +0 -5
- data/lib/predicator/generated_parser.rb +0 -335
- data/lib/predicator/lexer.rb +0 -125
- data/lib/predicator/nodes.rb +0 -6
- data/lib/predicator/nodes/base_node.rb +0 -53
- data/lib/predicator/nodes/date_node.rb +0 -13
- data/lib/predicator/nodes/fixnum_node.rb +0 -9
- data/lib/predicator/nodes/float_node.rb +0 -9
- data/lib/predicator/nodes/nil_class_node.rb +0 -25
- data/lib/predicator/nodes/string_node.rb +0 -13
- data/lib/predicator/predicates.rb +0 -14
- data/lib/predicator/predicates/and.rb +0 -20
- data/lib/predicator/predicates/between.rb +0 -31
- data/lib/predicator/predicates/equal.rb +0 -9
- data/lib/predicator/predicates/false.rb +0 -13
- data/lib/predicator/predicates/greater_than.rb +0 -9
- data/lib/predicator/predicates/greater_than_or_equal.rb +0 -9
- data/lib/predicator/predicates/less_than.rb +0 -9
- data/lib/predicator/predicates/less_than_or_equal.rb +0 -9
- data/lib/predicator/predicates/method.rb +0 -17
- data/lib/predicator/predicates/not.rb +0 -20
- data/lib/predicator/predicates/not_equal.rb +0 -9
- data/lib/predicator/predicates/or.rb +0 -20
- data/lib/predicator/predicates/relation.rb +0 -31
- data/lib/predicator/predicates/true.rb +0 -13
- data/lib/predicator/variable.rb +0 -26
data/lib/predicator/parser.y
CHANGED
@@ -1,57 +1,83 @@
|
|
1
|
-
class Predicator::
|
1
|
+
class Predicator::Parser
|
2
|
+
|
2
3
|
options no_result_var
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
|
5
|
+
token TRUE FALSE LPAREN RPAREN LBRACKET RBRACKET
|
6
|
+
BANG NOT DOT COMMA AT AND OR
|
7
|
+
EQ GT LT BETWEEN IN
|
8
|
+
INTEGER STRING IDENTIFIER
|
9
|
+
|
9
10
|
rule
|
10
11
|
predicate
|
11
12
|
: boolean_predicate
|
12
13
|
| logical_predicate
|
13
|
-
|
|
14
|
-
|
|
15
|
-
| tLPAREN predicate tRPAREN { val[1] }
|
16
|
-
| value tBETWEEN value tAND value { Predicator::Predicates::Between.new val[0], val[2], val[4] }
|
14
|
+
| group_predicate
|
15
|
+
| comparison_predicate
|
17
16
|
;
|
18
17
|
boolean_predicate
|
19
|
-
:
|
20
|
-
|
|
18
|
+
: TRUE { AST::True.new true }
|
19
|
+
| FALSE { AST::False.new false }
|
20
|
+
| variable { AST::BooleanVariable.new val.first }
|
21
21
|
;
|
22
22
|
logical_predicate
|
23
|
-
:
|
24
|
-
| predicate
|
25
|
-
|
|
26
|
-
;
|
27
|
-
relation_predicate
|
28
|
-
: value tEQ value { Predicator::Predicates::Equal.new val[0], val[2] }
|
29
|
-
| value tGT value { Predicator::Predicates::GreaterThan.new val[0], val[2] }
|
30
|
-
| value tLT value { Predicator::Predicates::LessThan.new val[0], val[2] }
|
31
|
-
| value tGEQ value { Predicator::Predicates::GreaterThanOrEqual.new val[0], val[2] }
|
32
|
-
| value tLEQ value { Predicator::Predicates::LessThanOrEqual.new val[0], val[2] }
|
33
|
-
| value tNEQ value { Predicator::Predicates::NotEqual.new val[0], val[2] }
|
34
|
-
;
|
35
|
-
method_predicate
|
36
|
-
: value tDOT tBLANK { Predicator::Predicates::Method.new val[0], val[2] }
|
37
|
-
| value tDOT tPRESENT { Predicator::Predicates::Method.new val[0], val[2] }
|
23
|
+
: BANG predicate { AST::Not.new val.last }
|
24
|
+
| predicate AND predicate { AST::And.new val.first, val.last }
|
25
|
+
| predicate OR predicate { AST::Or.new val.first, val.last }
|
38
26
|
;
|
39
|
-
|
40
|
-
:
|
41
|
-
|
27
|
+
group_predicate
|
28
|
+
: LPAREN predicate RPAREN { AST::Group.new val[1] }
|
29
|
+
;
|
30
|
+
comparison_predicate
|
31
|
+
: value EQ value { AST::Equal.new val.first, val.last }
|
32
|
+
| value GT value { AST::GreaterThan.new val.first, val.last }
|
33
|
+
| value LT value { AST::LessThan.new val.first, val.last }
|
34
|
+
| value BETWEEN value AND value { AST::Between.new val.first, val[2], val.last }
|
35
|
+
| value IN array { AST::In.new val.first, val.last }
|
36
|
+
| value NOT IN array { AST::NotIn.new val.first, val.last }
|
42
37
|
;
|
43
|
-
|
44
|
-
:
|
45
|
-
| literal
|
38
|
+
array
|
39
|
+
: LBRACKET array_contents RBRACKET { AST::Array.new val[1] }
|
46
40
|
;
|
47
|
-
|
48
|
-
:
|
41
|
+
array_contents
|
42
|
+
: literal
|
43
|
+
| array_contents COMMA literal { [val.first, val.last].flatten }
|
44
|
+
;
|
45
|
+
value
|
46
|
+
: literal
|
47
|
+
| variable
|
49
48
|
;
|
50
49
|
literal
|
51
|
-
:
|
52
|
-
|
|
53
|
-
| tDATE { Date.new *val[0] }
|
50
|
+
: STRING { AST::String.new val.first }
|
51
|
+
| INTEGER { AST::Integer.new val.first.to_i }
|
54
52
|
;
|
55
53
|
variable
|
56
|
-
:
|
54
|
+
: IDENTIFIER { AST::Variable.new val.first }
|
55
|
+
| variable DOT IDENTIFIER { AST::Variable.new [val.first, val.last].flatten.join(".") }
|
57
56
|
;
|
57
|
+
end
|
58
|
+
|
59
|
+
---- inner
|
60
|
+
def initialize
|
61
|
+
@lexer = Lexer.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def parse string
|
65
|
+
@lexer.parse string
|
66
|
+
do_parse
|
67
|
+
end
|
68
|
+
|
69
|
+
def next_token
|
70
|
+
@lexer.next_token
|
71
|
+
end
|
72
|
+
|
73
|
+
def on_error type, val, values
|
74
|
+
super
|
75
|
+
rescue Racc::ParseError
|
76
|
+
trace = values.each_with_index.map{|l, i| "#{' ' * i}#{l}"}
|
77
|
+
raise ParseError, "\nparse error on value #{val.inspect}\n#{trace.join("\n")}"
|
78
|
+
end
|
79
|
+
|
80
|
+
---- header
|
81
|
+
require "predicator/lexer.rex"
|
82
|
+
require "predicator/visitors"
|
83
|
+
require "predicator/ast"
|
data/lib/predicator/version.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Predicator
|
2
|
+
module Visitors
|
3
|
+
class Dot < Visitor
|
4
|
+
attr_reader :nodes, :edges
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@nodes = []
|
8
|
+
@edges = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept node
|
12
|
+
super
|
13
|
+
<<-eodot
|
14
|
+
digraph parse_tree {
|
15
|
+
size="8,5"
|
16
|
+
node [shape = none];
|
17
|
+
edge [dir = none];
|
18
|
+
#{@nodes.join "\n "}
|
19
|
+
#{@edges.join "\n "}
|
20
|
+
}
|
21
|
+
eodot
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def binary node
|
27
|
+
node.children.each do |c|
|
28
|
+
@edges << "#{node.object_id} -> #{c.object_id};"
|
29
|
+
end
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
alias_method :ternary, :binary
|
34
|
+
|
35
|
+
def unary node
|
36
|
+
@edges << "#{node.object_id} -> #{node.left.object_id};"
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_EQ node
|
41
|
+
@nodes << "#{node.object_id} [label=\"=\"];"
|
42
|
+
super
|
43
|
+
end
|
44
|
+
|
45
|
+
def visit_GT node
|
46
|
+
@nodes << "#{node.object_id} [label=\">\"];"
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def visit_LT node
|
51
|
+
@nodes << "#{node.object_id} [label=\"<\"];"
|
52
|
+
super
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_BETWEEN node
|
56
|
+
@nodes << "#{node.object_id} [label=\"between\"];"
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def visit_AND node
|
61
|
+
@nodes << "#{node.object_id} [label=\"and\"];"
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
def visit_OR node
|
66
|
+
@nodes << "#{node.object_id} [label=\"or\"];"
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
def visit_NOT node
|
71
|
+
@nodes << "#{node.object_id} [label=\"!\"];"
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def visit_GROUP node
|
76
|
+
@nodes << "#{node.object_id} [label=\"( )\"];"
|
77
|
+
super
|
78
|
+
end
|
79
|
+
|
80
|
+
def visit_STRING node
|
81
|
+
value = node.left
|
82
|
+
@nodes << "#{node.object_id} [label=\"'#{value}'\"];"
|
83
|
+
end
|
84
|
+
|
85
|
+
def terminal node
|
86
|
+
value = node.left.to_s
|
87
|
+
@nodes << "#{node.object_id} [label=\"#{value}\"];"
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_source source, parent
|
91
|
+
ast = Predicator.parse source
|
92
|
+
vis = Dot.new
|
93
|
+
vis.accept ast
|
94
|
+
@nodes += vis.nodes
|
95
|
+
@edges += vis.edges
|
96
|
+
@edges << "#{parent.object_id} -> #{ast.object_id};"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Predicator
|
2
|
+
module Visitors
|
3
|
+
class Instructions < Visitor
|
4
|
+
attr_reader :instructions
|
5
|
+
|
6
|
+
def initialize instructions=[]
|
7
|
+
@instructions = instructions
|
8
|
+
@label_locations = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def accept ast
|
12
|
+
super
|
13
|
+
update_jumps
|
14
|
+
remove_labels
|
15
|
+
@instructions
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def visit_AND node
|
21
|
+
visit node.left
|
22
|
+
@instructions.push jump_instruction("false", node)
|
23
|
+
visit node.right
|
24
|
+
@instructions.push label_instruction(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
def visit_OR node
|
28
|
+
visit node.left
|
29
|
+
@instructions.push jump_instruction("true", node)
|
30
|
+
visit node.right
|
31
|
+
@instructions.push label_instruction(node)
|
32
|
+
end
|
33
|
+
|
34
|
+
def visit_NOT node
|
35
|
+
super
|
36
|
+
@instructions.push ["not"]
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_EQ node
|
40
|
+
super
|
41
|
+
@instructions.push ["compare", "EQ"]
|
42
|
+
end
|
43
|
+
|
44
|
+
def visit_GT node
|
45
|
+
super
|
46
|
+
@instructions.push ["compare", "GT"]
|
47
|
+
end
|
48
|
+
|
49
|
+
def visit_LT node
|
50
|
+
super
|
51
|
+
@instructions.push ["compare", "LT"]
|
52
|
+
end
|
53
|
+
|
54
|
+
def visit_BETWEEN node
|
55
|
+
super
|
56
|
+
@instructions.push ["compare", "BETWEEN"]
|
57
|
+
end
|
58
|
+
|
59
|
+
def visit_IN node
|
60
|
+
super
|
61
|
+
@instructions.push ["compare", "IN"]
|
62
|
+
end
|
63
|
+
|
64
|
+
def visit_NOTIN node
|
65
|
+
super
|
66
|
+
@instructions.push ["compare", "NOTIN"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def visit_ARRAY node
|
70
|
+
contents = node.left.map{ |item| item.left }
|
71
|
+
@instructions.push ["array", contents]
|
72
|
+
end
|
73
|
+
|
74
|
+
def visit_VARIABLE node
|
75
|
+
@instructions.push ["load", node.symbol]
|
76
|
+
end
|
77
|
+
|
78
|
+
def visit_BOOL node
|
79
|
+
super
|
80
|
+
@instructions.push ["to_bool"]
|
81
|
+
end
|
82
|
+
|
83
|
+
def terminal node
|
84
|
+
@instructions.push ["lit", node.symbol]
|
85
|
+
end
|
86
|
+
|
87
|
+
def jump_instruction condition, node
|
88
|
+
["j#{condition}", node.object_id.to_s]
|
89
|
+
end
|
90
|
+
|
91
|
+
def label_instruction node
|
92
|
+
label = node.object_id.to_s
|
93
|
+
@label_locations[label] = @instructions.size
|
94
|
+
["label", label]
|
95
|
+
end
|
96
|
+
|
97
|
+
def update_jumps
|
98
|
+
@instructions.each_with_index do |inst, idx|
|
99
|
+
next unless inst.first =~ /^j/
|
100
|
+
label = inst.pop
|
101
|
+
offset = calculate_offset idx, @label_locations[label]
|
102
|
+
inst.push offset
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def calculate_offset from_idx, to_idx
|
107
|
+
offset = to_idx - from_idx
|
108
|
+
num_labels = @instructions[from_idx...to_idx].count{ |i| i.first == "label" }
|
109
|
+
offset - num_labels
|
110
|
+
end
|
111
|
+
|
112
|
+
def remove_labels
|
113
|
+
@instructions.delete_if{ |inst| inst.first == "label" }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Predicator
|
2
|
+
module Visitors
|
3
|
+
class String < Visitor
|
4
|
+
private
|
5
|
+
|
6
|
+
def terminal node
|
7
|
+
node.left.to_s
|
8
|
+
end
|
9
|
+
|
10
|
+
def visit_STRING node
|
11
|
+
"'#{node.left}'"
|
12
|
+
end
|
13
|
+
|
14
|
+
def visit_ARRAY node
|
15
|
+
contents = node.left.map{ |item| visit item }.join(", ")
|
16
|
+
"[#{contents}]"
|
17
|
+
end
|
18
|
+
|
19
|
+
def visit_NOT node
|
20
|
+
"!#{visit node.left}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit_GROUP node
|
24
|
+
"(#{visit node.left})"
|
25
|
+
end
|
26
|
+
|
27
|
+
def visit_EQ node
|
28
|
+
[visit(node.left), " = ", visit(node.right)].join
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_GT node
|
32
|
+
[visit(node.left), " > ", visit(node.right)].join
|
33
|
+
end
|
34
|
+
|
35
|
+
def visit_LT node
|
36
|
+
[visit(node.left), " < ", visit(node.right)].join
|
37
|
+
end
|
38
|
+
|
39
|
+
def visit_AND node
|
40
|
+
[visit(node.left), " and ", visit(node.right)].join
|
41
|
+
end
|
42
|
+
|
43
|
+
def visit_OR node
|
44
|
+
[visit(node.left), " or ", visit(node.right)].join
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_BETWEEN node
|
48
|
+
[visit(node.left), " between ", visit(node.middle), " and ", visit(node.right)].join
|
49
|
+
end
|
50
|
+
|
51
|
+
def visit_IN node
|
52
|
+
[visit(node.left), " in ", visit(node.right)].join
|
53
|
+
end
|
54
|
+
|
55
|
+
def visit_NOTIN node
|
56
|
+
[visit(node.left), " not in ", visit(node.right)].join
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Predicator
|
2
|
+
module Visitors
|
3
|
+
class Visitor
|
4
|
+
DISPATCH_CACHE = Hash.new { |h,k|
|
5
|
+
h[k] = "visit_#{k}"
|
6
|
+
}
|
7
|
+
|
8
|
+
def accept node
|
9
|
+
visit node
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def visit node
|
15
|
+
send DISPATCH_CACHE[node.type], node
|
16
|
+
end
|
17
|
+
|
18
|
+
def visit_children node
|
19
|
+
node.children.each {|child| visit child}
|
20
|
+
end
|
21
|
+
|
22
|
+
def visit_NOT node; visit_children node; end
|
23
|
+
def visit_GROUP node; visit_children node; end
|
24
|
+
def visit_BOOL node; visit_children node; end
|
25
|
+
|
26
|
+
def visit_EQ node; visit_children node; end
|
27
|
+
def visit_GT node; visit_children node; end
|
28
|
+
def visit_LT node; visit_children node; end
|
29
|
+
def visit_AND node; visit_children node; end
|
30
|
+
def visit_OR node; visit_children node; end
|
31
|
+
def visit_IN node; visit_children node; end
|
32
|
+
def visit_NOTIN node; visit_children node; end
|
33
|
+
|
34
|
+
def visit_BETWEEN node; visit_children node; end
|
35
|
+
|
36
|
+
def terminal node; end
|
37
|
+
def visit_TRUE node; terminal node; end
|
38
|
+
def visit_FALSE node; terminal node; end
|
39
|
+
def visit_INTEGER node; terminal node; end
|
40
|
+
def visit_STRING node; terminal node; end
|
41
|
+
def visit_VARIABLE node; terminal node; end
|
42
|
+
|
43
|
+
def visit_ARRAY node
|
44
|
+
node.left.each{ |item| visit item }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|