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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +322 -46
- data/lib/predicator/ast.rb +74 -187
- data/lib/predicator/boundary.rb +237 -0
- data/lib/predicator/civil.rb +112 -0
- data/lib/predicator/duration_text.rb +92 -0
- data/lib/predicator/emitter/chains.rb +62 -0
- data/lib/predicator/emitter/statements.rb +86 -0
- data/lib/predicator/emitter.rb +187 -0
- data/lib/predicator/errors.rb +140 -0
- data/lib/predicator/evaluator/access.rb +108 -0
- data/lib/predicator/evaluator/arithmetic.rb +132 -0
- data/lib/predicator/evaluator/calls.rb +129 -0
- data/lib/predicator/evaluator/casts.rb +158 -0
- data/lib/predicator/evaluator/chronology.rb +88 -0
- data/lib/predicator/evaluator/compare.rb +158 -0
- data/lib/predicator/evaluator/json_functions.rb +167 -0
- data/lib/predicator/evaluator/json_scan.rb +303 -0
- data/lib/predicator/evaluator/library.rb +127 -0
- data/lib/predicator/evaluator/math_functions.rb +72 -0
- data/lib/predicator/evaluator/order.rb +120 -0
- data/lib/predicator/evaluator/rich_types.rb +146 -0
- data/lib/predicator/evaluator/statements.rb +151 -0
- data/lib/predicator/evaluator.rb +391 -117
- data/lib/predicator/float_text.rb +94 -0
- data/lib/predicator/iso_text.rb +99 -0
- data/lib/predicator/lexer/literals.rb +115 -0
- data/lib/predicator/lexer/numbers.rb +91 -0
- data/lib/predicator/lexer.rb +194 -0
- data/lib/predicator/parser/durations.rb +108 -0
- data/lib/predicator/parser/operators.rb +86 -0
- data/lib/predicator/parser/postfix.rb +57 -0
- data/lib/predicator/parser/primaries.rb +122 -0
- data/lib/predicator/parser/statements.rb +114 -0
- data/lib/predicator/parser/token_text.rb +52 -0
- data/lib/predicator/parser.rb +217 -438
- data/lib/predicator/result.rb +46 -0
- data/lib/predicator/tagged/reader.rb +139 -0
- data/lib/predicator/tagged/writer.rb +106 -0
- data/lib/predicator/tagged.rb +134 -0
- data/lib/predicator/values.rb +147 -0
- data/lib/predicator/version.rb +4 -1
- data/lib/predicator.rb +384 -14
- metadata +53 -147
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -46
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.md +0 -49
- data/Gemfile +0 -4
- data/HISTORY.md +0 -51
- data/Rakefile +0 -24
- data/lib/predicator/context.rb +0 -18
- data/lib/predicator/lexer.rex +0 -69
- data/lib/predicator/lexer.rex.rb +0 -187
- data/lib/predicator/parser.y +0 -120
- data/lib/predicator/visitors/dot.rb +0 -113
- data/lib/predicator/visitors/each.rb +0 -16
- data/lib/predicator/visitors/instructions.rb +0 -183
- data/lib/predicator/visitors/string.rb +0 -76
- data/lib/predicator/visitors/visitor.rb +0 -100
- data/lib/predicator/visitors.rb +0 -5
- 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/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
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
|
data/lib/predicator/context.rb
DELETED
|
@@ -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
|
data/lib/predicator/lexer.rex
DELETED
|
@@ -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
|
data/lib/predicator/lexer.rex.rb
DELETED
|
@@ -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
|
data/lib/predicator/parser.y
DELETED
|
@@ -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
|