predicator 0.4.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +3 -2
  4. data/HISTORY.md +9 -0
  5. data/README.md +7 -8
  6. data/Rakefile +14 -5
  7. data/lib/predicator.rb +18 -10
  8. data/lib/predicator/ast.rb +138 -0
  9. data/lib/predicator/context.rb +7 -63
  10. data/lib/predicator/evaluator.rb +108 -0
  11. data/lib/predicator/lexer.rex +51 -0
  12. data/lib/predicator/lexer.rex.rb +160 -0
  13. data/lib/predicator/parser.rb +291 -7
  14. data/lib/predicator/parser.y +66 -40
  15. data/lib/predicator/version.rb +1 -1
  16. data/lib/predicator/visitors.rb +5 -0
  17. data/lib/predicator/visitors/dot.rb +100 -0
  18. data/lib/predicator/visitors/each.rb +16 -0
  19. data/lib/predicator/visitors/instructions.rb +117 -0
  20. data/lib/predicator/visitors/string.rb +60 -0
  21. data/lib/predicator/visitors/visitor.rb +48 -0
  22. data/predicator.gemspec +3 -2
  23. metadata +29 -32
  24. data/lib/predicator/errors.rb +0 -5
  25. data/lib/predicator/generated_parser.rb +0 -335
  26. data/lib/predicator/lexer.rb +0 -125
  27. data/lib/predicator/nodes.rb +0 -6
  28. data/lib/predicator/nodes/base_node.rb +0 -53
  29. data/lib/predicator/nodes/date_node.rb +0 -13
  30. data/lib/predicator/nodes/fixnum_node.rb +0 -9
  31. data/lib/predicator/nodes/float_node.rb +0 -9
  32. data/lib/predicator/nodes/nil_class_node.rb +0 -25
  33. data/lib/predicator/nodes/string_node.rb +0 -13
  34. data/lib/predicator/predicates.rb +0 -14
  35. data/lib/predicator/predicates/and.rb +0 -20
  36. data/lib/predicator/predicates/between.rb +0 -31
  37. data/lib/predicator/predicates/equal.rb +0 -9
  38. data/lib/predicator/predicates/false.rb +0 -13
  39. data/lib/predicator/predicates/greater_than.rb +0 -9
  40. data/lib/predicator/predicates/greater_than_or_equal.rb +0 -9
  41. data/lib/predicator/predicates/less_than.rb +0 -9
  42. data/lib/predicator/predicates/less_than_or_equal.rb +0 -9
  43. data/lib/predicator/predicates/method.rb +0 -17
  44. data/lib/predicator/predicates/not.rb +0 -20
  45. data/lib/predicator/predicates/not_equal.rb +0 -9
  46. data/lib/predicator/predicates/or.rb +0 -20
  47. data/lib/predicator/predicates/relation.rb +0 -31
  48. data/lib/predicator/predicates/true.rb +0 -13
  49. data/lib/predicator/variable.rb +0 -26
@@ -1,57 +1,83 @@
1
- class Predicator::GeneratedParser
1
+ class Predicator::Parser
2
+
2
3
  options no_result_var
3
- prechigh
4
- right tBANG
5
- left tAND tOR
6
- preclow
7
- token tTRUE tFALSE tSTRING tFLOAT tINTEGER tDATE tIDENTIFIER tAND tOR tBETWEEN
8
- tDOT tLPAREN tRPAREN tBANG tEQ tNEQ tLEQ tGEQ tLT tGT tBLANK tPRESENT
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
- | relation_predicate
14
- | method_predicate
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
- : tTRUE { Predicator::Predicates::True.new }
20
- | tFALSE { Predicator::Predicates::False.new }
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
- : predicate tAND predicate { Predicator::Predicates::And.new [val[0], val[2]] }
24
- | predicate tOR predicate { Predicator::Predicates::Or.new [val[0], val[2]] }
25
- | tBANG predicate { Predicator::Predicates::Not.new val[0] }
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
- value
40
- : scalar
41
- | variable
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
- scalar
44
- : string
45
- | literal
38
+ array
39
+ : LBRACKET array_contents RBRACKET { AST::Array.new val[1] }
46
40
  ;
47
- string
48
- : tSTRING { val[0] }
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
- : tFLOAT { val[0].to_f }
52
- | tINTEGER { val[0].to_i }
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
- : tIDENTIFIER tDOT tIDENTIFIER { Predicator::Variable.new val[0], val[2] }
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"
@@ -1,3 +1,3 @@
1
1
  module Predicator
2
- VERSION = "0.4.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,5 @@
1
+ require "predicator/visitors/visitor"
2
+ require "predicator/visitors/each"
3
+ require "predicator/visitors/dot"
4
+ require "predicator/visitors/string"
5
+ require "predicator/visitors/instructions"
@@ -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,16 @@
1
+ module Predicator
2
+ module Visitors
3
+ class Each < Visitor
4
+ attr_reader :block
5
+
6
+ def initialize block
7
+ @block = block
8
+ end
9
+
10
+ def visit node
11
+ super
12
+ block.call node
13
+ end
14
+ end
15
+ end
16
+ 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