predicator 1.2.1 → 2.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +322 -46
  5. data/lib/predicator/ast.rb +74 -187
  6. data/lib/predicator/boundary.rb +237 -0
  7. data/lib/predicator/civil.rb +112 -0
  8. data/lib/predicator/duration_text.rb +92 -0
  9. data/lib/predicator/emitter/chains.rb +62 -0
  10. data/lib/predicator/emitter/statements.rb +86 -0
  11. data/lib/predicator/emitter.rb +187 -0
  12. data/lib/predicator/errors.rb +140 -0
  13. data/lib/predicator/evaluator/access.rb +108 -0
  14. data/lib/predicator/evaluator/arithmetic.rb +132 -0
  15. data/lib/predicator/evaluator/calls.rb +129 -0
  16. data/lib/predicator/evaluator/casts.rb +158 -0
  17. data/lib/predicator/evaluator/chronology.rb +88 -0
  18. data/lib/predicator/evaluator/compare.rb +158 -0
  19. data/lib/predicator/evaluator/json_functions.rb +167 -0
  20. data/lib/predicator/evaluator/json_scan.rb +303 -0
  21. data/lib/predicator/evaluator/library.rb +127 -0
  22. data/lib/predicator/evaluator/math_functions.rb +72 -0
  23. data/lib/predicator/evaluator/order.rb +120 -0
  24. data/lib/predicator/evaluator/rich_types.rb +146 -0
  25. data/lib/predicator/evaluator/statements.rb +151 -0
  26. data/lib/predicator/evaluator.rb +391 -117
  27. data/lib/predicator/float_text.rb +94 -0
  28. data/lib/predicator/iso_text.rb +99 -0
  29. data/lib/predicator/lexer/literals.rb +115 -0
  30. data/lib/predicator/lexer/numbers.rb +91 -0
  31. data/lib/predicator/lexer.rb +194 -0
  32. data/lib/predicator/parser/durations.rb +108 -0
  33. data/lib/predicator/parser/operators.rb +86 -0
  34. data/lib/predicator/parser/postfix.rb +57 -0
  35. data/lib/predicator/parser/primaries.rb +122 -0
  36. data/lib/predicator/parser/statements.rb +114 -0
  37. data/lib/predicator/parser/token_text.rb +52 -0
  38. data/lib/predicator/parser.rb +217 -438
  39. data/lib/predicator/result.rb +46 -0
  40. data/lib/predicator/tagged/reader.rb +139 -0
  41. data/lib/predicator/tagged/writer.rb +106 -0
  42. data/lib/predicator/tagged.rb +134 -0
  43. data/lib/predicator/values.rb +147 -0
  44. data/lib/predicator/version.rb +4 -1
  45. data/lib/predicator.rb +384 -14
  46. metadata +53 -147
  47. data/.gitignore +0 -11
  48. data/.rubocop.yml +0 -46
  49. data/.travis.yml +0 -5
  50. data/CODE_OF_CONDUCT.md +0 -49
  51. data/Gemfile +0 -4
  52. data/HISTORY.md +0 -51
  53. data/Rakefile +0 -24
  54. data/lib/predicator/context.rb +0 -18
  55. data/lib/predicator/lexer.rex +0 -69
  56. data/lib/predicator/lexer.rex.rb +0 -187
  57. data/lib/predicator/parser.y +0 -120
  58. data/lib/predicator/visitors/dot.rb +0 -113
  59. data/lib/predicator/visitors/each.rb +0 -16
  60. data/lib/predicator/visitors/instructions.rb +0 -183
  61. data/lib/predicator/visitors/string.rb +0 -76
  62. data/lib/predicator/visitors/visitor.rb +0 -100
  63. data/lib/predicator/visitors.rb +0 -5
  64. data/predicator.gemspec +0 -34
data/.rubocop.yml DELETED
@@ -1,46 +0,0 @@
1
- AllCops:
2
- TargetRubyVersion: 2.3
3
- DisabledByDefault: true
4
- Exclude:
5
- - "lib/predicator/parser.rb" #generated file
6
- - "lib/predicator/lexer.rex.rb" #generated file
7
-
8
- Bundler/DuplicatedGem:
9
- Enabled: true
10
- Lint:
11
- Enabled: true
12
- Performance:
13
- Enabled: true
14
- Security:
15
- Enabled: true
16
-
17
- # Single quotes being faster is hardly measurable and only affects parse time.
18
- # Enforcing double quotes reduces the times where you need to change them
19
- # when introducing an interpolation. Use single quotes only if their semantics
20
- # are needed.
21
- Style/StringLiterals:
22
- EnforcedStyle: double_quotes
23
-
24
- # Seattle style
25
- Style/MethodDefParentheses:
26
- EnforcedStyle: require_no_parentheses
27
-
28
- # Allow regex argument in Seattle style
29
- Lint/AmbiguousRegexpLiteral:
30
- Enabled: false
31
-
32
- # Allow block argument in Seattle style
33
- Lint/AmbiguousOperator:
34
- Enabled: false
35
-
36
- # No long classes
37
- Style/ClassLength:
38
- Enabled: true
39
- Exclude:
40
- - "test/**/*.rb"
41
- - "lib/predicator/visitors/instructions.rb"
42
- - "lib/predicator/evaluator.rb"
43
-
44
- Lint/HandleExceptions:
45
- Exclude:
46
- - lib/tasks/**/*.rake
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.0
4
- - 2.1
5
- - 2.2
data/CODE_OF_CONDUCT.md DELETED
@@ -1,49 +0,0 @@
1
- # Contributor Code of Conduct
2
-
3
- As contributors and maintainers of this project, and in the interest of
4
- fostering an open and welcoming community, we pledge to respect all people who
5
- contribute through reporting issues, posting feature requests, updating
6
- documentation, submitting pull requests or patches, and other activities.
7
-
8
- We are committed to making participation in this project a harassment-free
9
- experience for everyone, regardless of level of experience, gender, gender
10
- identity and expression, sexual orientation, disability, personal appearance,
11
- body size, race, ethnicity, age, religion, or nationality.
12
-
13
- Examples of unacceptable behavior by participants include:
14
-
15
- * The use of sexualized language or imagery
16
- * Personal attacks
17
- * Trolling or insulting/derogatory comments
18
- * Public or private harassment
19
- * Publishing other's private information, such as physical or electronic
20
- addresses, without explicit permission
21
- * Other unethical or unprofessional conduct
22
-
23
- Project maintainers have the right and responsibility to remove, edit, or
24
- reject comments, commits, code, wiki edits, issues, and other contributions
25
- that are not aligned to this Code of Conduct, or to ban temporarily or
26
- permanently any contributor for other behaviors that they deem inappropriate,
27
- threatening, offensive, or harmful.
28
-
29
- By adopting this Code of Conduct, project maintainers commit themselves to
30
- fairly and consistently applying these principles to every aspect of managing
31
- this project. Project maintainers who do not follow or enforce the Code of
32
- Conduct may be permanently removed from the project team.
33
-
34
- This code of conduct applies both within project spaces and in public spaces
35
- when an individual is representing the project or its community.
36
-
37
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
- reported by contacting a project maintainer at ubergeek3141@gmail.com. All
39
- complaints will be reviewed and investigated and will result in a response that
40
- is deemed necessary and appropriate to the circumstances. Maintainers are
41
- obligated to maintain confidentiality with regard to the reporter of an
42
- incident.
43
-
44
- This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
- version 1.3.0, available at
46
- [http://contributor-covenant.org/version/1/3/0/][version]
47
-
48
- [homepage]: http://contributor-covenant.org
49
- [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in predicator.gemspec
4
- gemspec
data/HISTORY.md DELETED
@@ -1,51 +0,0 @@
1
- ### 1.2.1 / 2020-08-04
2
-
3
- * Moves project to riddler/predicator
4
-
5
- ### 1.2.0 / 2018-09-13
6
-
7
- * Adds type casting to comparisons
8
- * Adds date type and predicates
9
- * Adds "from now" and "ago" relative dates
10
- * Adds "present" and "blank" predicates
11
- * Adds "starts with" and "ends with" string comparisons
12
-
13
- ### 1.1.0 / 2017-12-06
14
-
15
- * Rescues errors when comparing invalid types
16
-
17
- ### 1.0.0 / 2017-09-22
18
-
19
- * Adds new lexer
20
- * Introduces the stack machine and instructions
21
- * Adds LessThan predicate
22
- * Adds Between predicate
23
- * Adds In and Not In predicates (with arrays)
24
- * Adds BooleanVariable predicate
25
-
26
- ### 0.4.0 / 2016-06-09
27
-
28
- * Adds double dispatch for relation predicates
29
- * Adds method predicate (.blank? and .present?)
30
- * Adds Context#eval
31
-
32
- ### 0.3.0 / 2016-03-11
33
-
34
- * Adds more relation predicates (GT, LT, GTE, LTE, NEQ)
35
- * Adds between predicate
36
-
37
- ### 0.2.1 / 2016-02-22
38
-
39
- * Makes RACC a development dependency (trying to get working on MagLev)
40
-
41
- ### 0.2.0 / 2016-02-21
42
-
43
- * Makes RACC a runtime dependency
44
-
45
- ### 0.1.0 / 2016-02-21
46
-
47
- * Changed from Parslet to RACC
48
-
49
- ### 0.0.0 / 2016-02-08
50
-
51
- * Birthday!
data/Rakefile DELETED
@@ -1,24 +0,0 @@
1
- require "rubygems"
2
- require "bundler/gem_tasks"
3
- require "rake/testtask"
4
- require "oedipus_lex"
5
-
6
- Rake.application.rake_require "oedipus_lex"
7
-
8
- Rake::TestTask.new :test do |t|
9
- t.libs << "test"
10
- t.test_files = FileList["test/**/test_*.rb"]
11
- t.warning = false
12
- end
13
-
14
- desc "Generate the lexer"
15
- task lexer: "lib/predicator/lexer.rex.rb"
16
-
17
- desc "Compile and generate the parser"
18
- task parser: :lexer do
19
- sh "racc -l -o lib/predicator/parser.rb lib/predicator/parser.y"
20
- end
21
-
22
- task test: :parser
23
-
24
- task default: :test
@@ -1,18 +0,0 @@
1
- module Predicator
2
- class Context
3
- def initialize params={}
4
- @bindings = {}
5
- params.each{ |key,value| bind key, value }
6
- end
7
-
8
- def bind name, value
9
- @bindings[name.to_s] = value
10
- end
11
- alias :[]= :bind
12
-
13
- def binding_for name
14
- @bindings[name.to_s]
15
- end
16
- alias :[] :binding_for
17
- end
18
- end
@@ -1,69 +0,0 @@
1
- class Predicator::Lexer
2
- options
3
- lineno
4
- column
5
- macro
6
- SPACE /[ \t\r\n]/
7
- LPAREN /\(/
8
- RPAREN /\)/
9
- LBRACKET /\[/
10
- RBRACKET /\]/
11
- TRUE /true\b/
12
- FALSE /false\b/
13
- BETWEEN /between\b/
14
- IN /in\b/
15
- BANG /!/
16
- NOT /not\b/
17
- DOT /\./
18
- COMMA /,/
19
- AND /and\b/
20
- OR /or\b/
21
- EQ /=/
22
- GT />/
23
- LT /</
24
- ENDSWITH /ends with/
25
- STARTSWITH /starts with/
26
- BEGINSWITH /begins with/
27
- BLANK /is blank/
28
- PRESENT /is present/
29
- AGO /ago/
30
- FROMNOW /from now/
31
- DATE /\d{4}[-|\/]\d{2}[-|\/]\d{2}/i
32
- DURATION /\d+d/
33
- INTEGER /[+-]?\d(_?\d)*\b/
34
- STRING /(["'])(?:\\?.)*?\1/
35
- IDENTIFIER /[a-z][A-Za-z0-9_]*\b/
36
- rule
37
- /#{SPACE}/ # ignore space
38
- /#{LPAREN}/ { [:LPAREN, text] }
39
- /#{RPAREN}/ { [:RPAREN, text] }
40
- /#{LBRACKET}/ { [:LBRACKET, text] }
41
- /#{RBRACKET}/ { [:RBRACKET, text] }
42
- /#{TRUE}/ { [:TRUE, text] }
43
- /#{FALSE}/ { [:FALSE, text] }
44
- /#{BETWEEN}/ { [:BETWEEN, text] }
45
- /#{IN}/ { [:IN, text] }
46
- /#{BANG}/ { [:BANG, text] }
47
- /#{NOT}/ { [:NOT, text] }
48
- /#{DOT}/ { [:DOT, text] }
49
- /#{COMMA}/ { [:COMMA, text] }
50
- /#{AND}/ { [:AND, text] }
51
- /#{OR}/ { [:OR, text] }
52
- /#{EQ}/ { [:EQ, text] }
53
- /#{GT}/ { [:GT, text] }
54
- /#{LT}/ { [:LT, text] }
55
- /#{AGO}/ { [:AGO, text] }
56
- /#{FROMNOW}/ { [:FROMNOW, text] }
57
- /#{ENDSWITH}/ { [:ENDSWITH, text] }
58
- /#{STARTSWITH}/ { [:STARTSWITH, text] }
59
- /#{BEGINSWITH}/ { [:STARTSWITH, text] }
60
- /#{BLANK}/ { [:BLANK, text] }
61
- /#{PRESENT}/ { [:PRESENT, text] }
62
- /#{DATE}/ { [:DATE, text] }
63
- /#{DURATION}/ { [:DURATION, text] }
64
- /#{INTEGER}/ { [:INTEGER, text] }
65
- /#{STRING}/ { [:STRING, text[1...-1]] }
66
- /#{IDENTIFIER}/ { [:IDENTIFIER, text] }
67
- inner
68
- def do_parse; end
69
- end
@@ -1,187 +0,0 @@
1
- # encoding: UTF-8
2
- #--
3
- # This file is automatically generated. Do not modify it.
4
- # Generated by: oedipus_lex version 2.5.0.
5
- # Source: lib/predicator/lexer.rex
6
- #++
7
-
8
- class Predicator::Lexer
9
- require 'strscan'
10
-
11
- SPACE = /[ \t\r\n]/
12
- LPAREN = /\(/
13
- RPAREN = /\)/
14
- LBRACKET = /\[/
15
- RBRACKET = /\]/
16
- TRUE = /true\b/
17
- FALSE = /false\b/
18
- BETWEEN = /between\b/
19
- IN = /in\b/
20
- BANG = /!/
21
- NOT = /not\b/
22
- DOT = /\./
23
- COMMA = /,/
24
- AND = /and\b/
25
- OR = /or\b/
26
- EQ = /=/
27
- GT = />/
28
- LT = /</
29
- ENDSWITH = /ends with/
30
- STARTSWITH = /starts with/
31
- BEGINSWITH = /begins with/
32
- BLANK = /is blank/
33
- PRESENT = /is present/
34
- AGO = /ago/
35
- FROMNOW = /from now/
36
- DATE = /\d{4}[-|\/]\d{2}[-|\/]\d{2}/i
37
- DURATION = /\d+d/
38
- INTEGER = /[+-]?\d(_?\d)*\b/
39
- STRING = /(["'])(?:\\?.)*?\1/
40
- IDENTIFIER = /[a-z][A-Za-z0-9_]*\b/
41
-
42
- class LexerError < StandardError ; end
43
- class ScanError < LexerError ; end
44
-
45
- attr_accessor :lineno
46
- attr_accessor :filename
47
- attr_accessor :ss
48
- attr_accessor :state
49
-
50
- alias :match :ss
51
-
52
- def matches
53
- m = (1..9).map { |i| ss[i] }
54
- m.pop until m[-1] or m.empty?
55
- m
56
- end
57
-
58
- def action
59
- yield
60
- end
61
-
62
- attr_accessor :old_pos
63
-
64
- def column
65
- idx = ss.string.rindex("\n", old_pos) || -1
66
- old_pos - idx - 1
67
- end
68
-
69
- def scanner_class
70
- StringScanner
71
- end unless instance_methods(false).map(&:to_s).include?("scanner_class")
72
-
73
- def parse str
74
- self.ss = scanner_class.new str
75
- self.lineno = 1
76
- self.state ||= nil
77
-
78
- do_parse
79
- end
80
-
81
- def parse_file path
82
- self.filename = path
83
- open path do |f|
84
- parse f.read
85
- end
86
- end
87
-
88
- def location
89
- [
90
- (filename || "<input>"),
91
- lineno,
92
- column,
93
- ].compact.join(":")
94
- end
95
-
96
- def next_token
97
-
98
- token = nil
99
-
100
- until ss.eos? or token do
101
- self.lineno += 1 if ss.peek(1) == "\n"
102
- self.old_pos = ss.pos
103
- token =
104
- case state
105
- when nil then
106
- case
107
- when ss.skip(/#{SPACE}/) then
108
- # do nothing
109
- when text = ss.scan(/#{LPAREN}/) then
110
- action { [:LPAREN, text] }
111
- when text = ss.scan(/#{RPAREN}/) then
112
- action { [:RPAREN, text] }
113
- when text = ss.scan(/#{LBRACKET}/) then
114
- action { [:LBRACKET, text] }
115
- when text = ss.scan(/#{RBRACKET}/) then
116
- action { [:RBRACKET, text] }
117
- when text = ss.scan(/#{TRUE}/) then
118
- action { [:TRUE, text] }
119
- when text = ss.scan(/#{FALSE}/) then
120
- action { [:FALSE, text] }
121
- when text = ss.scan(/#{BETWEEN}/) then
122
- action { [:BETWEEN, text] }
123
- when text = ss.scan(/#{IN}/) then
124
- action { [:IN, text] }
125
- when text = ss.scan(/#{BANG}/) then
126
- action { [:BANG, text] }
127
- when text = ss.scan(/#{NOT}/) then
128
- action { [:NOT, text] }
129
- when text = ss.scan(/#{DOT}/) then
130
- action { [:DOT, text] }
131
- when text = ss.scan(/#{COMMA}/) then
132
- action { [:COMMA, text] }
133
- when text = ss.scan(/#{AND}/) then
134
- action { [:AND, text] }
135
- when text = ss.scan(/#{OR}/) then
136
- action { [:OR, text] }
137
- when text = ss.scan(/#{EQ}/) then
138
- action { [:EQ, text] }
139
- when text = ss.scan(/#{GT}/) then
140
- action { [:GT, text] }
141
- when text = ss.scan(/#{LT}/) then
142
- action { [:LT, text] }
143
- when text = ss.scan(/#{AGO}/) then
144
- action { [:AGO, text] }
145
- when text = ss.scan(/#{FROMNOW}/) then
146
- action { [:FROMNOW, text] }
147
- when text = ss.scan(/#{ENDSWITH}/) then
148
- action { [:ENDSWITH, text] }
149
- when text = ss.scan(/#{STARTSWITH}/) then
150
- action { [:STARTSWITH, text] }
151
- when text = ss.scan(/#{BEGINSWITH}/) then
152
- action { [:STARTSWITH, text] }
153
- when text = ss.scan(/#{BLANK}/) then
154
- action { [:BLANK, text] }
155
- when text = ss.scan(/#{PRESENT}/) then
156
- action { [:PRESENT, text] }
157
- when text = ss.scan(/#{DATE}/) then
158
- action { [:DATE, text] }
159
- when text = ss.scan(/#{DURATION}/) then
160
- action { [:DURATION, text] }
161
- when text = ss.scan(/#{INTEGER}/) then
162
- action { [:INTEGER, text] }
163
- when text = ss.scan(/#{STRING}/) then
164
- action { [:STRING, text[1...-1]] }
165
- when text = ss.scan(/#{IDENTIFIER}/) then
166
- action { [:IDENTIFIER, text] }
167
- else
168
- text = ss.string[ss.pos .. -1]
169
- raise ScanError, "can not match (#{state.inspect}) at #{location}: '#{text}'"
170
- end
171
- else
172
- raise ScanError, "undefined state at #{location}: '#{state}'"
173
- end # token = case state
174
-
175
- next unless token # allow functions to trigger redo w/ nil
176
- end # while
177
-
178
- raise LexerError, "bad lexical result at #{location}: #{token.inspect}" unless
179
- token.nil? || (Array === token && token.size >= 2)
180
-
181
- # auto-switch state
182
- self.state = token.last if token && token.first == :state
183
-
184
- token
185
- end # def next_token
186
- def do_parse; end
187
- end # class
@@ -1,120 +0,0 @@
1
- class Predicator::Parser
2
-
3
- options no_result_var
4
-
5
- token TRUE FALSE LPAREN RPAREN LBRACKET RBRACKET
6
- BANG NOT DOT COMMA AT AND OR
7
- EQ GT LT BETWEEN IN PRESENT BLANK
8
- INTEGER STRING IDENTIFIER
9
- DATE DURATION AGO FROMNOW
10
- STARTSWITH ENDSWITH
11
-
12
- rule
13
- predicate
14
- : boolean_predicate
15
- | logical_predicate
16
- | group_predicate
17
- | comparison_predicate
18
- ;
19
- boolean_predicate
20
- : TRUE { AST::True.new true }
21
- | FALSE { AST::False.new false }
22
- | variable { AST::BooleanVariable.new val.first }
23
- ;
24
- logical_predicate
25
- : BANG predicate { AST::Not.new val.last }
26
- | predicate AND predicate { AST::And.new val.first, val.last }
27
- | predicate OR predicate { AST::Or.new val.first, val.last }
28
- | variable PRESENT { AST::Present.new val.first }
29
- | variable BLANK { AST::Blank.new val.first }
30
- ;
31
- group_predicate
32
- : LPAREN predicate RPAREN { AST::Group.new val[1] }
33
- ;
34
- comparison_predicate
35
- : integer_comparison_predicate
36
- | string_comparison_predicate
37
- | date_comparison_predicate
38
- ;
39
- integer_comparison_predicate
40
- : variable EQ integer { AST::IntegerEqual.new val.first, val.last }
41
- | variable GT integer { AST::IntegerGreaterThan.new val.first, val.last }
42
- | variable LT integer { AST::IntegerLessThan.new val.first, val.last }
43
- | variable BETWEEN integer AND integer { AST::IntegerBetween.new val.first, val[2], val.last }
44
- | variable IN integer_array { AST::IntegerIn.new val.first, val.last }
45
- | variable NOT IN integer_array { AST::IntegerNotIn.new val.first, val.last }
46
- ;
47
- string_comparison_predicate
48
- : variable EQ string { AST::StringEqual.new val.first, val.last }
49
- | variable GT string { AST::StringGreaterThan.new val.first, val.last }
50
- | variable LT string { AST::StringLessThan.new val.first, val.last }
51
- | variable IN string_array { AST::StringIn.new val.first, val.last }
52
- | variable NOT IN string_array { AST::StringNotIn.new val.first, val.last }
53
- | variable STARTSWITH string { AST::StringStartsWith.new val.first, val.last }
54
- | variable ENDSWITH string { AST::StringEndsWith.new val.first, val.last }
55
- ;
56
- date_comparison_predicate
57
- : variable EQ date { AST::DateEqual.new val.first, val.last }
58
- | variable GT date { AST::DateGreaterThan.new val.first, val.last }
59
- | variable LT date { AST::DateLessThan.new val.first, val.last }
60
- | variable BETWEEN date AND date { AST::DateBetween.new val.first, val[2], val.last }
61
- ;
62
- integer_array
63
- : LBRACKET integer_array_contents RBRACKET { AST::IntegerArray.new val[1] }
64
- ;
65
- integer_array_contents
66
- : integer
67
- | integer_array_contents COMMA integer { [val.first, val.last].flatten }
68
- ;
69
- string_array
70
- : LBRACKET string_array_contents RBRACKET { AST::StringArray.new val[1] }
71
- ;
72
- string_array_contents
73
- : string
74
- | string_array_contents COMMA string { [val.first, val.last].flatten }
75
- ;
76
- integer
77
- : INTEGER { AST::Integer.new val.first.to_i }
78
- ;
79
- string
80
- : STRING { AST::String.new val.first }
81
- ;
82
- date
83
- : DATE { AST::Date.new val.first }
84
- | duration AGO { AST::DateAgo.new val.first }
85
- | duration FROMNOW { AST::DateFromNow.new val.first }
86
- ;
87
- duration
88
- : DURATION { AST::Duration.new val.first }
89
- ;
90
- variable
91
- : IDENTIFIER { AST::Variable.new val.first }
92
- | variable DOT IDENTIFIER { AST::Variable.new [val.first, val.last].flatten.join(".") }
93
- ;
94
- end
95
-
96
- ---- inner
97
- def initialize
98
- @lexer = Lexer.new
99
- end
100
-
101
- def parse string
102
- @lexer.parse string
103
- do_parse
104
- end
105
-
106
- def next_token
107
- @lexer.next_token
108
- end
109
-
110
- def on_error type, val, values
111
- super
112
- rescue Racc::ParseError
113
- trace = values.each_with_index.map{|l, i| "#{' ' * i}#{l}"}
114
- raise ParseError, "\nparse error on value #{val.inspect}\n#{trace.join("\n")}"
115
- end
116
-
117
- ---- header
118
- require "predicator/lexer.rex"
119
- require "predicator/visitors"
120
- require "predicator/ast"
@@ -1,113 +0,0 @@
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 visit_children node
27
- node.children.each do |c|
28
- @edges << "#{node.object_id} -> #{c.object_id};"
29
- end
30
- super
31
- end
32
-
33
- def visit_EQ node
34
- @nodes << "#{node.object_id} [label=\"=\"];"
35
- super
36
- end
37
-
38
- def visit_GT node
39
- @nodes << "#{node.object_id} [label=\">\"];"
40
- super
41
- end
42
-
43
- def visit_LT node
44
- @nodes << "#{node.object_id} [label=\"<\"];"
45
- super
46
- end
47
-
48
- def visit_BETWEEN node
49
- @nodes << "#{node.object_id} [label=\"between\"];"
50
- super
51
- end
52
-
53
- def visit_AND node
54
- @nodes << "#{node.object_id} [label=\"and\"];"
55
- super
56
- end
57
-
58
- def visit_OR node
59
- @nodes << "#{node.object_id} [label=\"or\"];"
60
- super
61
- end
62
-
63
- def visit_NOT node
64
- @nodes << "#{node.object_id} [label=\"!\"];"
65
- super
66
- end
67
-
68
- def visit_GROUP node
69
- @nodes << "#{node.object_id} [label=\"( )\"];"
70
- super
71
- end
72
-
73
- def visit_DATEAGO node
74
- @nodes << "#{node.object_id} [label=\"ago\"];"
75
- super
76
- end
77
-
78
- def visit_DATEFROMNOW node
79
- @nodes << "#{node.object_id} [label=\"from now\"];"
80
- super
81
- end
82
-
83
- def visit_STRSTARTSWITH node
84
- @nodes << "#{node.object_id} [label=\"starts with\"];"
85
- super
86
- end
87
-
88
- def visit_STRENDSWITH node
89
- @nodes << "#{node.object_id} [label=\"ends with\"];"
90
- super
91
- end
92
-
93
- def visit_STRING node
94
- value = node.left
95
- @nodes << "#{node.object_id} [label=\"'#{value}'\"];"
96
- end
97
-
98
- def terminal node
99
- value = node.left.to_s
100
- @nodes << "#{node.object_id} [label=\"#{value}\"];"
101
- end
102
-
103
- def add_source source, parent
104
- ast = Predicator.parse source
105
- vis = Dot.new
106
- vis.accept ast
107
- @nodes += vis.nodes
108
- @edges += vis.edges
109
- @edges << "#{parent.object_id} -> #{ast.object_id};"
110
- end
111
- end
112
- end
113
- end