predicator 0.4.0 → 1.0.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.
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