filter_lexer 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4eb20adfd043c015d21dbc1639c4669945131cef
4
- data.tar.gz: a0f2855582ae1094e6ef329b82d04f4b8b03fcec
3
+ metadata.gz: 47fbb2acdc5535e2e74b0355dbb042e289de54b1
4
+ data.tar.gz: 4be18e0e750a3b298e4572e175e2b2e43fbeea3e
5
5
  SHA512:
6
- metadata.gz: ccc25baabaa2ca2af95d5b7653dd996ab674c6822708bef2ff92db4fd26d6c7b25dad79c1c2fd0e71cd472763c05abbf78c2af6b5f7c8693b0837e325c7c9d65
7
- data.tar.gz: 71f924d03e1af187be0e270c0ceab349f092cb02cb8ff0fcdcfe3ba09864f82c693595ddd4b01c82fb821e5a7a1e2ec6755558a31a0699eeff14f065b12330a8
6
+ metadata.gz: 9e83318063753adab91aeddb75fbe34f3405a35f47b82dd01eed69a9276b5b97e9110a830da5f981a1a8d633159da158ce38a636562ce82d0da591a48c8e4503
7
+ data.tar.gz: a7ff4f435b9ab633daf95f8b31c1a98b5c001b8b86cd82dfcfaf69749b4e2b61bcbbfffb4f8ff020af0f2cee9d081297d12060c9b2a6008b2628a3599829c513
data/.codeclimate.yml ADDED
@@ -0,0 +1,11 @@
1
+ ---
2
+ engines:
3
+ fixme:
4
+ enabled: true
5
+ rubocop:
6
+ enabled: true
7
+ ratings:
8
+ paths:
9
+ - "**.rb"
10
+ exclude_paths:
11
+ - spec/**/*
data/.inch.yml ADDED
@@ -0,0 +1,6 @@
1
+ files:
2
+ included:
3
+ - lib/**/.rb
4
+ excluded:
5
+ - lib/filter_lexer/formatters/**/*.rb
6
+
data/.rubocop.yml ADDED
@@ -0,0 +1,34 @@
1
+ AllCops:
2
+ Exclude:
3
+ - 'bin/**/*'
4
+
5
+ Metrics/AbcSize:
6
+ Max: 20
7
+
8
+ Metrics/LineLength:
9
+ Max: 120
10
+
11
+ Style/CaseIndentation:
12
+ IndentWhenRelativeTo: 'case'
13
+ IndentOneStep: true
14
+
15
+ Style/IndentHash:
16
+ Enabled: false
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Style/FirstParameterIndentation:
22
+ Enabled: false
23
+
24
+ Style/IndentationWidth:
25
+ Enabled: false
26
+
27
+ Style/MultilineOperationIndentation:
28
+ EnforcedStyle: 'indented'
29
+
30
+ Style/RedundantReturn:
31
+ Enabled: false
32
+
33
+ Style/TrailingComma:
34
+ EnforcedStyleForMultiline: comma
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # FilterLexer
1
+ # FilterLexer [![Dependency Status](https://gemnasium.com/MaienM/FilterLexer.svg)](https://gemnasium.com/MaienM/FilterLexer) [![Code Climate](https://codeclimate.com/github/MaienM/FilterLexer/badges/gpa.svg)](https://codeclimate.com/github/MaienM/FilterLexer) [![Inline docs](http://inch-ci.org/github/MaienM/FilterLexer.svg?branch=master)](http://inch-ci.org/github/MaienM/FilterLexer)
2
2
 
3
3
  This is a simple treetop implementation for a basic SQL-like filtering syntax.
4
4
 
data/Rakefile CHANGED
@@ -1 +1,44 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+
4
+ RuboCop::RakeTask.new(:rubocop, [:options]) do |task|
5
+ task.options.push '--cache=false'
6
+ end
7
+ module RuboCop
8
+ # We use tabs for indentation, not spaces. As many of the RuboCop cops seem
9
+ # to depend on two spaces being used for indentation, we pretend this is
10
+ # what we do. To allow us to still check indentation for correctness, we
11
+ # also change two spaces into tabs, so RuboCop can detect it as invalid
12
+ # indentation.
13
+ class File < ::File
14
+ SPACES = ' '
15
+ TABS = "\t"
16
+ PLACEHOLDER = "\0"
17
+
18
+ def self.read(*args)
19
+ source = super(*args)
20
+ source = swap_indents(source)
21
+ return source
22
+ end
23
+
24
+ def write(source, *args)
25
+ source = self.class.swap_indents(source)
26
+ return super(source, *args)
27
+ end
28
+
29
+ class << self
30
+ def swap_indents(source)
31
+ source = change_to(source, SPACES, PLACEHOLDER)
32
+ source = change_to(source, TABS, SPACES)
33
+ source = change_to(source, PLACEHOLDER, TABS)
34
+ return source
35
+ end
36
+
37
+ private
38
+
39
+ def change_to(source, from, to)
40
+ return source.gsub(/^((#{from})+)/) { |m| to * (m.size / from.size).to_i }
41
+ end
42
+ end
43
+ end
44
+ end
data/filter_lexer.gemspec CHANGED
@@ -9,15 +9,15 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ['Michon van Dooren']
10
10
  spec.email = ['michon1992@gmail.com']
11
11
 
12
- spec.summary = %q{A basic lexer that supports basic sql-like filtering logic.}
13
- spec.description = %q{Simple treetop lexer that supports writing filters in an SQL-like manner.}
12
+ spec.summary = 'A basic lexer that supports basic sql-like filtering logic.'
13
+ spec.description = 'Simple treetop lexer that supports writing filters in an SQL-like manner.'
14
14
  spec.homepage = 'http://github.com/MaienM/FilterLexer'
15
15
  spec.license = 'MIT'
16
16
 
17
17
  if spec.respond_to?(:metadata)
18
18
  spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
19
  else
20
- raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
20
+ fail 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
21
21
  end
22
22
 
23
23
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -28,5 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.add_development_dependency 'bundler', '~> 1.10'
29
29
  spec.add_development_dependency 'rake', '~> 10.0'
30
30
  spec.add_development_dependency 'rspec'
31
+ spec.add_development_dependency 'rubocop'
32
+ spec.add_development_dependency 'pry'
31
33
  spec.add_dependency 'treetop', '~> 1.6'
32
34
  end
data/lib/filter_lexer.rb CHANGED
@@ -1,24 +1,30 @@
1
1
  require 'treetop'
2
2
 
3
- require "filter_lexer/version"
4
- require "filter_lexer/exceptions"
5
- require "filter_lexer/nodes"
3
+ require 'filter_lexer/version'
4
+ require 'filter_lexer/exceptions'
5
+ require 'filter_lexer/nodes'
6
6
  require 'filter_lexer/syntax.treetop'
7
7
 
8
8
  module FilterLexer
9
+ # The parser is your main entrypoint for the lexer
10
+ #
11
+ # You never instantiate it, you just use it's static methods
9
12
  class Parser
10
- @@parser = FilterLexerParser.new
13
+ @parser = FilterLexerParser.new
11
14
 
12
15
  class << self
16
+ # The bread and butter of the entire thing: parsing, and it's pretty simple
17
+ #
18
+ # Just pass in a string, and out comes a parsed tree, or an exception
19
+ #
20
+ # The parsed tree will always be an FilterLexer::Expression object
21
+ # The exception will always be an FilterLexer::ParseException object
13
22
  def parse(data)
14
23
  # Pass the data over to the parser instance
15
- tree = @@parser.parse(data)
24
+ tree = @parser.parse(data)
16
25
 
17
26
  # If the AST is nil then there was an error during parsing
18
- # we need to report a simple error message to help the user
19
- if tree.nil?
20
- raise ParseException, @@parser
21
- end
27
+ fail ParseException, @parser if tree.nil?
22
28
 
23
29
  # Cleanup the tree
24
30
  clean_tree(tree)
@@ -26,6 +32,10 @@ module FilterLexer
26
32
  return tree
27
33
  end
28
34
 
35
+ # When using the lexer, it may be useful to have a visual representation of the tree
36
+ #
37
+ # Just pass the tree (or any node in it, if you're only interested in that part) to this function, and a visual
38
+ # representation of the tree will magically appear in stdout
29
39
  def output_tree(element, indent = '')
30
40
  puts "#{indent}#{element.class}: #{element.text_value}"
31
41
  (element.elements || []).each do |e|
@@ -35,7 +45,7 @@ module FilterLexer
35
45
 
36
46
  private
37
47
 
38
- def is_syn(node)
48
+ def syn?(node)
39
49
  return node.class.name == 'Treetop::Runtime::SyntaxNode'
40
50
  end
41
51
 
@@ -48,9 +58,9 @@ module FilterLexer
48
58
  # Clean child elements
49
59
  root_node.elements.each { |node| clean_tree(node) }
50
60
  # Remove empty syntax elements
51
- root_node.elements.reject! { |node| node_elem(node).empty? && is_syn(node) }
61
+ root_node.elements.reject! { |node| node_elem(node).empty? && syn?(node) }
52
62
  # Replace syntax elements with just one child with that child
53
- root_node.elements.map! { |node| (node_elem(node).size == 1 && is_syn(node)) ? node.elements.first : node }
63
+ root_node.elements.map! { |node| (node_elem(node).size == 1 && syn?(node)) ? node.elements.first : node }
54
64
  end
55
65
  end
56
66
  end
@@ -10,19 +10,18 @@ module FilterLexer
10
10
  end
11
11
 
12
12
  def context
13
- i1 = @index - 40
14
- i1 = 0 if i1 < 0
15
- i2 = @index + 40
16
- i2 = @input.size if i2 > @input.size
13
+ size = @input.size
14
+ i1 = [0, @index - 40].max
15
+ i2 = [size, @index + 40].min
17
16
 
18
17
  context = @input.slice(i1..i2)
19
- if i1 > 0
20
- context = "...#{context}"
21
- i1 -= 3
22
- end
23
- context = "#{context}..." if i2 < @input.size
18
+ context = "...#{context}" if i1 > 0
19
+ context = "#{context}..." if i2 < size
24
20
 
25
- return context + "\r\n" + ' ' * (@index - i1) + '^'
21
+ relpos = @index - i1
22
+ relpos += 1 if i1 > 0
23
+
24
+ return context + "\r\n" + ' ' * relpos + '^'
26
25
  end
27
26
  end
28
27
  end
@@ -5,40 +5,46 @@ module FilterLexer
5
5
  end
6
6
 
7
7
  def query_string
8
- return self.elements.map { |e| e.query_string }.join(' ')
8
+ return elements.map(&:query_string).join(' ')
9
9
  end
10
10
 
11
11
  def query_variables
12
- return self.elements.map { |e| e.query_variables }.flatten
12
+ return elements.map(&:query_variables).flatten
13
13
  end
14
14
  end
15
15
 
16
16
  class Group
17
17
  def query_string
18
- return "(#{self.elements.first.query_string})"
18
+ return "(#{elements.first.query_string})"
19
19
  end
20
20
 
21
21
  def query_variables
22
- return self.elements.first.query_variables
22
+ return elements.first.query_variables
23
23
  end
24
24
  end
25
25
 
26
26
  class Filter
27
27
  def query_string
28
- return "#{elements[0].sql} #{elements[1].sql} ?"
28
+ return "#{identifier.sql} #{operator.sql} ?"
29
29
  end
30
30
 
31
- def query_variables
32
- return [elements[2].sql]
31
+ def query_variables
32
+ return [data.sql]
33
33
  end
34
34
  end
35
35
 
36
- class Operator
36
+ class Identifier
37
+ def sql
38
+ return "`#{text_value}`"
39
+ end
40
+ end
41
+
42
+ class LogicOperator
37
43
  def query_string
38
- return self.sql
44
+ return sql
39
45
  end
40
46
 
41
- def query_variables
47
+ def query_variables
42
48
  return []
43
49
  end
44
50
  end
@@ -55,16 +61,26 @@ module FilterLexer
55
61
  end
56
62
  end
57
63
 
64
+ class Operator
65
+ def query_string
66
+ return sql
67
+ end
68
+
69
+ def query_variables
70
+ return []
71
+ end
72
+ end
73
+
58
74
  class EQOperator
59
75
  def sql
60
- return 'IS' if parent.value_class < ValueSpecial
76
+ return 'IS' if parent.data.is_a?(ValueSpecial)
61
77
  return '='
62
78
  end
63
79
  end
64
80
 
65
81
  class NEQOperator
66
82
  def sql
67
- return 'IS NOT' if parent.value_class < ValueSpecial
83
+ return 'IS NOT' if parent.data.is_a?(ValueSpecial)
68
84
  return '<>'
69
85
  end
70
86
  end
@@ -125,19 +141,13 @@ module FilterLexer
125
141
 
126
142
  class StringLiteral
127
143
  def sql
128
- return self.text_value
144
+ return text_value
129
145
  end
130
146
  end
131
147
 
132
148
  class NumberLiteral
133
149
  def sql
134
- return self.text_value
135
- end
136
- end
137
-
138
- class Identifier
139
- def sql
140
- return self.text_value
150
+ return text_value
141
151
  end
142
152
  end
143
153
  end
@@ -1,37 +1,52 @@
1
1
  module FilterLexer
2
+ # The root element, expression is a collection of other expressions and and/or operators
2
3
  class Expression < Treetop::Runtime::SyntaxNode
3
4
  end
4
5
 
6
+ # A group is simply an expression in parenthesis
5
7
  class Group < Treetop::Runtime::SyntaxNode
6
8
  end
7
9
 
10
+ # A filter is the core object of the lexer: an indentifier, an operator and a data
8
11
  class Filter < Treetop::Runtime::SyntaxNode
12
+ # The identifier element
13
+ #
14
+ # Of type FilterLexer::Identifier
9
15
  def identifier
10
- return elements[0].sql
16
+ return elements[0]
11
17
  end
12
18
 
19
+ # The operator element
20
+ #
21
+ # Subclass of FilterLexer::Operator
13
22
  def operator
14
- return elements[1].sql
23
+ return elements[1]
15
24
  end
16
25
 
17
- def value
18
- return elements[2].sql
26
+ # The value element
27
+ #
28
+ # Subclass of FilterLexer::Value
29
+ def data
30
+ return elements[2]
19
31
  end
32
+ end
20
33
 
21
- def value_class
22
- return elements[2].class
23
- end
34
+ # An logic operator is the glue between filters
35
+ class LogicOperator < Treetop::Runtime::SyntaxNode
24
36
  end
25
37
 
26
- # Operators
38
+ class OrOperator < LogicOperator
39
+ end
27
40
 
28
- class Operator < Treetop::Runtime::SyntaxNode
41
+ class AndOperator < LogicOperator
29
42
  end
30
43
 
31
- class OrOperator < Operator
44
+ # An identifier is the target (variable) of the filter
45
+ class Identifier < Treetop::Runtime::SyntaxNode
32
46
  end
33
47
 
34
- class AndOperator < Operator
48
+ # An operator is the type (function) of the filter
49
+ class Operator < Treetop::Runtime::SyntaxNode
35
50
  end
36
51
 
37
52
  class EQOperator < Operator
@@ -58,8 +73,7 @@ module FilterLexer
58
73
  class LikeOperator < Operator
59
74
  end
60
75
 
61
- # Values
62
-
76
+ # A value is the data of the filter
63
77
  class Value < Treetop::Runtime::SyntaxNode
64
78
  end
65
79
 
@@ -83,10 +97,4 @@ module FilterLexer
83
97
 
84
98
  class NumberLiteral < ValueScalar
85
99
  end
86
-
87
- # Identifier
88
-
89
- class Identifier < Treetop::Runtime::SyntaxNode
90
- end
91
100
  end
92
-
@@ -1,3 +1,3 @@
1
1
  module FilterLexer
2
- VERSION = "0.1.0"
2
+ VERSION = '0.1.1'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filter_lexer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michon van Dooren
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: treetop
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -73,7 +101,10 @@ executables: []
73
101
  extensions: []
74
102
  extra_rdoc_files: []
75
103
  files:
104
+ - ".codeclimate.yml"
76
105
  - ".gitignore"
106
+ - ".inch.yml"
107
+ - ".rubocop.yml"
77
108
  - ".travis.yml"
78
109
  - Gemfile
79
110
  - LICENSE