expressionist 0.0.1 → 0.0.2
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/.gitignore +2 -1
- data/Gemfile.lock +1 -1
- data/README.md +51 -8
- data/expressionist.gemspec +3 -2
- data/lib/expressionist.rb +11 -1
- data/lib/expressionist/context.rb +83 -0
- data/lib/expressionist/expression.rb +43 -0
- data/lib/expressionist/function.rb +35 -0
- data/lib/expressionist/functions.rb +30 -0
- data/lib/expressionist/functions/boolean.rb +15 -0
- data/lib/expressionist/functions/core.rb +11 -0
- data/lib/expressionist/functions/math.rb +15 -0
- data/lib/expressionist/functions/operators.rb +37 -0
- data/lib/expressionist/grammar.rb +66 -0
- data/lib/expressionist/parser.rb +48 -0
- data/lib/expressionist/transformer.rb +51 -0
- data/lib/expressionist/version.rb +1 -1
- metadata +16 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1da1a2ceee72ee773a38c0473c4b204274369bcaeb9ee5b80de93b6d8fcc84e2
|
4
|
+
data.tar.gz: c657865ed1042f95856f2acfb1beec2adb0667469e3c3eb23cc93b672296bfd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adcea62b1ca67c192f7ad89e1b3a1516b5074d8e42215934dfa33885767465fe2a36d5ea52b704babaf63a2685a75afbd15e8ab8db76a5392f315994976e7c7d
|
7
|
+
data.tar.gz: 97788da9e6736afa85d186ecb835083f4be7de33168a1fea4d364ac52c5320edda58cf0f60dc7b495fe7b9fa2ef8f954ba584ab22906d854e368898ee5724e9b
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Expressionist
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Simple query language for tree structured data
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -22,18 +20,63 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
|
23
|
+
```ruby
|
24
|
+
require 'expressionist'
|
25
|
+
|
26
|
+
context = Expressionist::Context.new
|
27
|
+
|
28
|
+
context['some.key'] = 'value'
|
29
|
+
|
30
|
+
expression = Expressionist.compile('some.key == "value" AND count(some.*) == 1')
|
31
|
+
|
32
|
+
expression.call(context) # => true
|
33
|
+
|
34
|
+
context['some.key2'] = 'value'
|
35
|
+
|
36
|
+
expression.call(context) # => false
|
37
|
+
|
38
|
+
context.export # => {"some"=>{"key"=>{"."=>"value"}, "key2"=>{"."=>"value"}}}
|
39
|
+
|
40
|
+
tmp = expression.executable # => Array
|
41
|
+
|
42
|
+
another = Expressionist.compile(tmp)
|
43
|
+
|
44
|
+
another.call(context) # => false
|
45
|
+
```
|
46
|
+
|
47
|
+
## Contexts
|
48
|
+
|
49
|
+
Context defines tree data structure that can be queried. Data can be importad
|
50
|
+
from an existing `Hash` and can be exported using `context.export`.
|
51
|
+
|
52
|
+
The `.` is used to separate the path. There can never be two `.` next to each
|
53
|
+
other and path can not start with a `.`. The `.` character can not be escaped.
|
54
|
+
|
55
|
+
Paths can only contain these `/[A-Za-z0-9.\-_]/` characters.
|
56
|
+
|
57
|
+
## Query language (expressions)
|
58
|
+
|
59
|
+
ToDo: update the section, ATM check the `lib/expressionist/grammar.rb` and
|
60
|
+
`lib/expressionist/functions/*.rb` files.
|
26
61
|
|
27
62
|
## Development
|
28
63
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
64
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then,
|
65
|
+
run `rake test` to run the tests. You can also run `bin/console` for an
|
66
|
+
interactive prompt that will allow you to experiment.
|
30
67
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
68
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
69
|
+
To release a new version, update the version number in `version.rb`,
|
70
|
+
and then run `bundle exec rake release`, which will create a git tag for the
|
71
|
+
version, push git commits and tags, and push the `.gem` file to
|
72
|
+
[rubygems.org](https://rubygems.org).
|
32
73
|
|
33
74
|
## Contributing
|
34
75
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at
|
76
|
+
Bug reports and pull requests are welcome on GitHub at
|
77
|
+
[marekjelen/expressionist](https://github.com/marekjelen/expressionist).
|
36
78
|
|
37
79
|
## License
|
38
80
|
|
39
|
-
The gem is available as open source under the terms of the
|
81
|
+
The gem is available as open source under the terms of the
|
82
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
data/expressionist.gemspec
CHANGED
@@ -11,8 +11,9 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.authors = ['Marek Jelen']
|
12
12
|
spec.email = ['marek@jelen.biz']
|
13
13
|
|
14
|
-
spec.summary = 'Simple query language for
|
15
|
-
spec.description = '
|
14
|
+
spec.summary = 'Simple query language for tree structured data'
|
15
|
+
spec.description = 'Safely make queries against tree data structure with ' \
|
16
|
+
'ability to serialize queries for later usage'
|
16
17
|
spec.homepage = 'https://www.github.com/marekjelen/expressionist'
|
17
18
|
spec.license = 'MIT'
|
18
19
|
|
data/lib/expressionist.rb
CHANGED
@@ -1,6 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'parslet'
|
4
|
+
|
3
5
|
require 'expressionist/version'
|
6
|
+
require 'expressionist/context'
|
7
|
+
require 'expressionist/expression'
|
8
|
+
require 'expressionist/parser'
|
4
9
|
|
5
10
|
module Expressionist
|
6
|
-
|
11
|
+
|
12
|
+
def self.compile(expression)
|
13
|
+
Expression.new(Parser.new(expression))
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
class Context
|
6
|
+
|
7
|
+
def initialize(data = {})
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
def []=(key, value)
|
12
|
+
segments = key.split('.')
|
13
|
+
len = segments.length
|
14
|
+
data = @data
|
15
|
+
(0...len).each do |i|
|
16
|
+
key = segments[i]
|
17
|
+
data = (data[key] ||= {})
|
18
|
+
end
|
19
|
+
data['.'] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete(key)
|
23
|
+
segments = key.split('.')
|
24
|
+
len = segments.length
|
25
|
+
data = @data
|
26
|
+
(0...len).each do |i|
|
27
|
+
break unless data
|
28
|
+
data = data[segments[i]]
|
29
|
+
end
|
30
|
+
data.delete('.') if data
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
find(key.split('.'), @data)
|
35
|
+
end
|
36
|
+
|
37
|
+
def find(segments = [], data = nil)
|
38
|
+
data ||= @data
|
39
|
+
segment = segments[0]
|
40
|
+
subsegments = segments[1..-1]
|
41
|
+
|
42
|
+
case
|
43
|
+
when data == nil
|
44
|
+
[nil]
|
45
|
+
when segments.length == 0
|
46
|
+
[cast(data['.'])]
|
47
|
+
when segment == '?'
|
48
|
+
(data.keys - ['.']).map do |k|
|
49
|
+
find(subsegments, data[k])
|
50
|
+
end
|
51
|
+
when segment == '*' && subsegments.length > 0 && data[subsegments[0]]
|
52
|
+
find(subsegments[1..-1], data[subsegments[0]])
|
53
|
+
when segment == '*'
|
54
|
+
(subsegments.length == 0 ? [data['.']] : []) + (data.keys - ['.']).map do |k|
|
55
|
+
find(segments, data[k])
|
56
|
+
end
|
57
|
+
else
|
58
|
+
find(subsegments, data[segment])
|
59
|
+
end.flatten.compact
|
60
|
+
end
|
61
|
+
|
62
|
+
def export
|
63
|
+
@data
|
64
|
+
end
|
65
|
+
|
66
|
+
def cast(value)
|
67
|
+
case value
|
68
|
+
when 'true'
|
69
|
+
true
|
70
|
+
when 'false'
|
71
|
+
false
|
72
|
+
when /[0-9]+[.,][0-9]+/
|
73
|
+
Float(value)
|
74
|
+
when /[0-9]+/
|
75
|
+
Integer(value)
|
76
|
+
else
|
77
|
+
value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'expressionist/function'
|
4
|
+
require 'expressionist/functions'
|
5
|
+
|
6
|
+
module Expressionist
|
7
|
+
|
8
|
+
class Expression
|
9
|
+
|
10
|
+
def initialize(value)
|
11
|
+
if value.kind_of?(Parser)
|
12
|
+
@parser = value
|
13
|
+
else
|
14
|
+
@executable = value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def executable
|
19
|
+
@executable ||= @parser.executable
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(context = {}, executable = nil)
|
23
|
+
executable ||= self.executable
|
24
|
+
|
25
|
+
args = executable[1..-1].map do |exp|
|
26
|
+
if exp.kind_of?(Array)
|
27
|
+
call(context, exp)
|
28
|
+
else
|
29
|
+
exp
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
function = Functions.get(executable[0])
|
34
|
+
unless function
|
35
|
+
raise ArgumentError, "Function #{executable[0].inspect} is missing"
|
36
|
+
end
|
37
|
+
|
38
|
+
function.call(context, *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
class Function
|
6
|
+
|
7
|
+
attr_reader :name, :args
|
8
|
+
|
9
|
+
def initialize(name, *args)
|
10
|
+
if name.kind_of?(Array) && args.length == 0
|
11
|
+
@name = name[0]
|
12
|
+
@args = name[1..-1].map do |arg|
|
13
|
+
arg.kind_of?(Array) ? Function.new(arg) : arg
|
14
|
+
end
|
15
|
+
else
|
16
|
+
@name = name
|
17
|
+
@args = args
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_a
|
22
|
+
[name] + args.map { |arg| arg.kind_of?(Function) ? arg.to_a : arg }
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
"#{name}(#{args.map(&to_s).join(', ')})"
|
27
|
+
end
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
self.class == other.class && other.name == self.name && other.args == self.args
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
module Functions
|
6
|
+
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def functions
|
10
|
+
@functions ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(name, &block)
|
14
|
+
functions[name] = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def get(name)
|
18
|
+
functions[name]
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'expressionist/functions/core'
|
28
|
+
require 'expressionist/functions/operators'
|
29
|
+
require 'expressionist/functions/boolean'
|
30
|
+
require 'expressionist/functions/math'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
module Functions
|
5
|
+
|
6
|
+
add('and') do |context, *items|
|
7
|
+
items.inject(true) {|c, i| c && i}
|
8
|
+
end
|
9
|
+
|
10
|
+
add('or') do |context, *items|
|
11
|
+
items.inject(false) {|c, i| c || i}
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
module Functions
|
6
|
+
|
7
|
+
add('>') do |context, left, right|
|
8
|
+
left > right
|
9
|
+
end
|
10
|
+
|
11
|
+
add('>=') do |context, left, right|
|
12
|
+
left >= right
|
13
|
+
end
|
14
|
+
|
15
|
+
add('=') do |context, left, right|
|
16
|
+
left == right
|
17
|
+
end
|
18
|
+
|
19
|
+
add('==') do |context, left, right|
|
20
|
+
left == right
|
21
|
+
end
|
22
|
+
|
23
|
+
add('!=') do |context, left, right|
|
24
|
+
left != right
|
25
|
+
end
|
26
|
+
|
27
|
+
add('<=') do |context, left, right|
|
28
|
+
left <= right
|
29
|
+
end
|
30
|
+
|
31
|
+
add('<') do |context, left, right|
|
32
|
+
left < right
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
class Grammar < Parslet::Parser
|
6
|
+
|
7
|
+
rule(:space) {match('\s').repeat(1)}
|
8
|
+
rule(:space?) {space.maybe}
|
9
|
+
|
10
|
+
rule(:lparen) {space? >> str('(') >> space?}
|
11
|
+
rule(:rparen) {space? >> str(')') >> space?}
|
12
|
+
|
13
|
+
rule(:comma) {space? >> str(',') >> space?}
|
14
|
+
rule(:dot) {space? >> str('.') >> space?}
|
15
|
+
|
16
|
+
rule(:lquote) {space? >> str('"')}
|
17
|
+
rule(:rquote) {str('"') >> space?}
|
18
|
+
|
19
|
+
rule(:ande) {space? >> str('AND').as(:boolean_operator) >> space?}
|
20
|
+
rule(:ore) {space? >> str('OR').as(:boolean_operator) >> space?}
|
21
|
+
|
22
|
+
rule(:anum) {match('[0-9a-zA-Z]')}
|
23
|
+
rule(:anums) {match('[.-_]') >> anum}
|
24
|
+
rule(:sanums) {match('[-_]') >> anum}
|
25
|
+
|
26
|
+
rule(:quoted) {(str('\"').as(:char) | (str('"').absent? >> any).as(:char)).repeat}
|
27
|
+
rule(:string) {lquote >> quoted.as(:string) >> rquote}
|
28
|
+
rule(:integer) {match('[0-9]').repeat(1).as(:integer)}
|
29
|
+
rule(:float) {(match('[0-9a-zA-Z]').repeat(1) >> str('.') >> match('[0-9a-zA-Z]').repeat(1)).as(:float)}
|
30
|
+
rule(:bool) {(str('true') | str('false')).as(:bool)}
|
31
|
+
|
32
|
+
rule(:keyword) {anum >> (anum | sanums).repeat}
|
33
|
+
|
34
|
+
rule(:segment) { str('*').as(:segment) | str('?').as(:segment) | ((anum >> (anum | sanums).repeat).as(:segment)) }
|
35
|
+
rule(:path) {(segment >> (dot >> segment).repeat).as(:path)}
|
36
|
+
|
37
|
+
rule(:func) {keyword.as(:function) >> lparen >> path >> rparen}
|
38
|
+
|
39
|
+
rule(:operator) {str('>=') | str('<=') | str('!=') | str('==') | str('=') | str('<') | str('>')}
|
40
|
+
|
41
|
+
rule(:value) {string | bool | float | integer}
|
42
|
+
|
43
|
+
rule(:expression) do
|
44
|
+
space? >>
|
45
|
+
(func.as(:func) | path) >>
|
46
|
+
space? >>
|
47
|
+
operator.as(:operator) >>
|
48
|
+
space? >>
|
49
|
+
value.as(:value) >>
|
50
|
+
space?
|
51
|
+
end
|
52
|
+
|
53
|
+
rule(:primary) { lparen >> or_expression >> rparen | expression }
|
54
|
+
|
55
|
+
rule(:and_expression) { (primary.as(:left) >> ande >> and_expression.as(:right)).as(:and) | primary }
|
56
|
+
|
57
|
+
rule(:or_expression) { (and_expression.as(:left) >> ore >> or_expression.as(:right)).as(:or) | and_expression }
|
58
|
+
|
59
|
+
rule(:base) { or_expression }
|
60
|
+
# https://github.com/kschiess/parslet/blob/master/example/boolean_algebra.rb
|
61
|
+
|
62
|
+
root(:base)
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'expressionist/grammar'
|
4
|
+
require 'expressionist/transformer'
|
5
|
+
|
6
|
+
module Expressionist
|
7
|
+
|
8
|
+
class Parser
|
9
|
+
|
10
|
+
def initialize(expression)
|
11
|
+
@parsed = nil
|
12
|
+
@compiled = nil
|
13
|
+
@executable = nil
|
14
|
+
|
15
|
+
case expression
|
16
|
+
when String
|
17
|
+
@expression = expression
|
18
|
+
when Function
|
19
|
+
@compiled = expression
|
20
|
+
when Array
|
21
|
+
@executable = expression
|
22
|
+
else
|
23
|
+
raise ArgumentError, "Unknown expression type: #{expression}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def parsed
|
28
|
+
raise RuntimeError, 'Expression was not passed' unless @expression
|
29
|
+
@parsed ||= Grammar.new.parse(@expression)
|
30
|
+
rescue Parslet::ParseFailed => e
|
31
|
+
# puts expression
|
32
|
+
# puts e.parse_failure_cause.ascii_tree
|
33
|
+
raise ArgumentError, "Invalid expression: #{@expression}"
|
34
|
+
end
|
35
|
+
|
36
|
+
def compiled
|
37
|
+
raise RuntimeError, 'Raw nor compiled form was passed' unless @compiled || parsed
|
38
|
+
@compiled ||= Transformer.new.apply(parsed)
|
39
|
+
end
|
40
|
+
|
41
|
+
def executable
|
42
|
+
raise RuntimeError, 'Compiled nor executable form was passed' unless @executable || compiled
|
43
|
+
@executable ||= compiled.to_a
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Expressionist
|
4
|
+
|
5
|
+
class Transformer < Parslet::Transform
|
6
|
+
|
7
|
+
rule(integer: simple(:value)) { Integer(value) }
|
8
|
+
rule(float: simple(:value)) { Float(value) }
|
9
|
+
rule(integer: simple(:value)) { Integer(value) }
|
10
|
+
rule(bool: simple(:value)) { ['false'].include?(value) ? false : true }
|
11
|
+
|
12
|
+
rule(char: simple(:value)) { value == '\\"' ? '"' : value }
|
13
|
+
rule(string: sequence(:value)) { value.join }
|
14
|
+
|
15
|
+
rule(segment: simple(:value)) { value.str }
|
16
|
+
|
17
|
+
rule(path: sequence(:value)) { value }
|
18
|
+
rule(path: simple(:value)) { value }
|
19
|
+
|
20
|
+
rule(function: simple(:name), path: simple(:path)) do
|
21
|
+
Function.new(name.str, path)
|
22
|
+
end
|
23
|
+
|
24
|
+
rule(function: simple(:name), path: sequence(:path)) do
|
25
|
+
Function.new(name.str, *path)
|
26
|
+
end
|
27
|
+
|
28
|
+
rule(operator: simple(:op), value: simple(:value), func: simple(:func)) do
|
29
|
+
Function.new(op.str, func, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
rule(operator: simple(:op), value: simple(:value), path: simple(:path)) do
|
33
|
+
Function.new(op.str, Function.new('get', path), value)
|
34
|
+
end
|
35
|
+
|
36
|
+
rule(operator: simple(:op), value: simple(:value), path: sequence(:path)) do
|
37
|
+
Function.new(op.str, Function.new('get', *path), value)
|
38
|
+
end
|
39
|
+
|
40
|
+
rule(expression: simple(:value)) { value }
|
41
|
+
|
42
|
+
rule(boolean_operator: simple(:value), left: simple(:left), right: simple(:right)) do
|
43
|
+
Function.new(value.str.strip.downcase, left, right)
|
44
|
+
end
|
45
|
+
|
46
|
+
rule(and: simple(:value)) { value }
|
47
|
+
rule(or: simple(:value)) { value }
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: expressionist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Marek Jelen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parslet
|
@@ -80,7 +80,8 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '10.0'
|
83
|
-
description:
|
83
|
+
description: Safely make queries against tree data structure with ability to serialize
|
84
|
+
queries for later usage
|
84
85
|
email:
|
85
86
|
- marek@jelen.biz
|
86
87
|
executables: []
|
@@ -98,6 +99,17 @@ files:
|
|
98
99
|
- bin/setup
|
99
100
|
- expressionist.gemspec
|
100
101
|
- lib/expressionist.rb
|
102
|
+
- lib/expressionist/context.rb
|
103
|
+
- lib/expressionist/expression.rb
|
104
|
+
- lib/expressionist/function.rb
|
105
|
+
- lib/expressionist/functions.rb
|
106
|
+
- lib/expressionist/functions/boolean.rb
|
107
|
+
- lib/expressionist/functions/core.rb
|
108
|
+
- lib/expressionist/functions/math.rb
|
109
|
+
- lib/expressionist/functions/operators.rb
|
110
|
+
- lib/expressionist/grammar.rb
|
111
|
+
- lib/expressionist/parser.rb
|
112
|
+
- lib/expressionist/transformer.rb
|
101
113
|
- lib/expressionist/version.rb
|
102
114
|
homepage: https://www.github.com/marekjelen/expressionist
|
103
115
|
licenses:
|
@@ -122,5 +134,5 @@ rubyforge_project:
|
|
122
134
|
rubygems_version: 2.7.6
|
123
135
|
signing_key:
|
124
136
|
specification_version: 4
|
125
|
-
summary: Simple query language for
|
137
|
+
summary: Simple query language for tree structured data
|
126
138
|
test_files: []
|