truth_serum 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fe740d3a98649998bfc874b79cccc33b954ed0ad
4
+ data.tar.gz: 22759b2db9b456e1072b2abae771568308cef393
5
+ SHA512:
6
+ metadata.gz: 1caac98110f89c78249c1c7726eeeca0ba2c4cfc5f64bdc56602e00e8baa1300b971e33380d88ceea8729ec22bd14d997373ba6d420ef68643dfb26310b6b674
7
+ data.tar.gz: 043fe0f877d243500b715e6cde139fc4615cf76d8e0d31d2684b8e92c2545e4fe8faaaa2c4d793b97caa2ded3e007a39168185471f57788cce893a6abbe12464
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.1
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,7 @@
1
+ clearing :on
2
+
3
+ guard :minitest do
4
+ watch(%r{^test/(.*)\/?(.*)_test\.rb$})
5
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}#{m[2]}_test.rb" }
6
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
7
+ end
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # TRUTH SERUM
2
+
3
+ Features
4
+
5
+ * Parses `keywords` as well as `filters:value` syntax.
6
+ * Handles `"quotes spaces ::: escapes \r\n newlines"`.
7
+ * Has proper lexing and parsing system instead of a regular expression.
8
+
9
+ # INSTALL
10
+
11
+ ```
12
+ gem install truth_serum
13
+ ```
14
+
15
+ # USAGE
16
+
17
+ ```rb
18
+ require 'truth_serum'
19
+
20
+ result = TruthSerum.parse("hello world key:value -negate:yes")
21
+ puts result.terms
22
+ puts result.filters
23
+ puts result.negative_filters
24
+ ```
25
+
26
+ # LICENSE
27
+
28
+ MIT
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "truth_serum"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,125 @@
1
+ module TruthSerum
2
+ class Lexer
3
+ def initialize(input)
4
+ @input = input
5
+ @index = 0
6
+ @tokens = []
7
+ @buffer = ''
8
+ end
9
+
10
+ def lex
11
+ @tokens = []
12
+ lex_line
13
+ @tokens
14
+ end
15
+
16
+ def lex_line
17
+ until eof?
18
+ case peek
19
+ when '"' then lex_quoted_word
20
+ when ':', '+', '-' then lex_symbol
21
+ when ' ' then lex_spaces
22
+ else
23
+ lex_word
24
+ end
25
+ end
26
+ end
27
+
28
+ def lex_symbol
29
+ case consume
30
+ when ':' then emit(:colon)
31
+ when '+' then emit(:plus)
32
+ when '-' then emit(:minus)
33
+ else
34
+ raise 'not a symbol'
35
+ end
36
+ end
37
+
38
+ def lex_spaces
39
+ consume while peek == ' '
40
+ emit(:space)
41
+ end
42
+
43
+ def lex_word
44
+ until eof?
45
+ case peek
46
+ when ' ', ':', '+', '-' then return emit(:term)
47
+ when '"' then discard
48
+ else
49
+ consume
50
+ end
51
+ end
52
+
53
+ emit(:term)
54
+ end
55
+
56
+ def lex_quoted_word
57
+ raise 'not in quoted word' unless discard == '"'
58
+
59
+ until eof?
60
+ case peek
61
+ when '\\' then lex_escaped_char
62
+ when '"'
63
+ discard
64
+ break
65
+ else
66
+ consume
67
+ end
68
+ end
69
+
70
+ emit(:term)
71
+ end
72
+
73
+ def lex_escaped_char
74
+ raise 'not in escaped char' unless discard == '\\'
75
+ return @buffer += '\\' if eof?
76
+
77
+ case peek
78
+ when 'r'
79
+ discard
80
+ @buffer += "\r"
81
+ when 'n'
82
+ discard
83
+ @buffer += "\n"
84
+ else # '\\' '"' and everything else is treated literally
85
+ consume
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def eof?
92
+ @index >= @input.length
93
+ end
94
+
95
+ def chars_left
96
+ @input.length - @index
97
+ end
98
+
99
+ def peek
100
+ return nil if eof?
101
+ @input[@index]
102
+ end
103
+
104
+ def consume
105
+ raise 'consume beyond EOF' if eof?
106
+
107
+ @buffer += peek
108
+ discard
109
+ end
110
+
111
+ def discard
112
+ @index += 1
113
+ @input[@index - 1]
114
+ end
115
+
116
+ def emit(type)
117
+ return nil if @buffer == ''
118
+
119
+ token = Token.new(type, @buffer)
120
+ @tokens << token
121
+ @buffer = ''
122
+ token
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,85 @@
1
+ module TruthSerum
2
+ class Parser
3
+ def initialize(tokens)
4
+ @tokens = tokens
5
+ end
6
+
7
+ def reset
8
+ @result = {
9
+ terms: [],
10
+ negative_terms: [],
11
+ filters: {},
12
+ negative_filters: {},
13
+ }
14
+ end
15
+
16
+ def parse
17
+ reset
18
+ parse_line
19
+ @result
20
+ end
21
+
22
+ def parse_line
23
+ until eof?
24
+ token = peek
25
+
26
+ case
27
+ when token.plus? || token.minus?
28
+ parse_sign
29
+ when token.term?
30
+ parse_term_or_filter
31
+ else
32
+ consume # stray colons and spaces
33
+ end
34
+ end
35
+ end
36
+
37
+ def parse_sign
38
+ negate = consume.minus? while peek.plus? || peek.minus?
39
+ if peek.term?
40
+ parse_term_or_filter(negate: negate)
41
+ else
42
+ nil
43
+ end
44
+ end
45
+
46
+ def parse_term_or_filter(negate: false)
47
+ term = ''
48
+ value = ''
49
+
50
+ # first portion of text
51
+ term = consume.text
52
+ return emit_term(term, negate: negate) unless peek.colon?
53
+
54
+ # if we have a filter (detect `:`)
55
+ consume while peek.colon?
56
+
57
+ value = consume.text
58
+ emit_filter(term, value, negate: negate)
59
+ end
60
+
61
+ private
62
+
63
+ def emit_term(term, negate: false)
64
+ @result[:terms] << term unless negate
65
+ @result[:negative_terms] << term if negate
66
+ end
67
+
68
+ def emit_filter(key, value, negate: false)
69
+ @result[:filters][key] = value unless negate
70
+ @result[:negative_filters][key] = value if negate
71
+ end
72
+
73
+ def eof?
74
+ peek.is_a?(NilToken)
75
+ end
76
+
77
+ def peek
78
+ @tokens[0] || NilToken.new
79
+ end
80
+
81
+ def consume
82
+ @tokens.shift || NilToken.new
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,12 @@
1
+ module TruthSerum
2
+ class Result
3
+ attr_accessor :terms, :negative_terms, :filters, :negative_filters
4
+
5
+ def initialize(terms:, negative_terms:, filters:, negative_filters:)
6
+ @terms = terms
7
+ @negative_terms = negative_terms
8
+ @filters = filters
9
+ @negative_filters = negative_filters
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ module TruthSerum
2
+ class Token
3
+ VALID_TYPES = [
4
+ :term,
5
+ :colon,
6
+ :space,
7
+ :plus,
8
+ :minus
9
+ ].freeze
10
+
11
+ attr_accessor :type, :text
12
+
13
+ def initialize(type, text)
14
+ @type = type
15
+ @text = text
16
+ end
17
+
18
+ def valid?
19
+ VALID_TYPES.include?(@type) &&
20
+ !@text.nil? &&
21
+ !@text.empty?
22
+ end
23
+
24
+ VALID_TYPES.each do |type|
25
+ define_method "#{type}?" do
26
+ @type == type
27
+ end
28
+ end
29
+ end
30
+
31
+ class NilToken < Token
32
+ def initialize
33
+ super(:nil, '')
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,3 @@
1
+ module TruthSerum
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require "truth_serum/version"
2
+ require "truth_serum/result"
3
+ require "truth_serum/token"
4
+ require "truth_serum/lexer"
5
+ require "truth_serum/parser"
6
+
7
+ module TruthSerum
8
+ def self.parse(line)
9
+ Result.new(**Parser.new(Lexer.new(line).lex).parse)
10
+ end
11
+ end
@@ -0,0 +1,27 @@
1
+ # vim: filetype=ruby
2
+ # coding: utf-8
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'truth_serum/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "truth_serum"
9
+ spec.version = TruthSerum::VERSION
10
+ spec.authors = ["Chakrit Wichian"]
11
+ spec.email = ["chakrit@omise.co"]
12
+
13
+ spec.summary = "Parser for github-style search."
14
+ spec.homepage = "https://github.com/chakrit/truth_serum"
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.licenses = ["MIT"]
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", '~> 1.12'
22
+ spec.add_development_dependency "rake", '~> 11.1'
23
+ spec.add_development_dependency "minitest", '~> 5.8'
24
+ spec.add_development_dependency "guard", '~> 2.13'
25
+ spec.add_development_dependency "guard-minitest", '~> 2.4'
26
+ spec.add_development_dependency "pry", '~> 0.10'
27
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: truth_serum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Chakrit Wichian
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '11.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '11.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.13'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.13'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.10'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.10'
97
+ description:
98
+ email:
99
+ - chakrit@omise.co
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".gitignore"
105
+ - ".travis.yml"
106
+ - Gemfile
107
+ - Guardfile
108
+ - README.md
109
+ - Rakefile
110
+ - bin/console
111
+ - bin/setup
112
+ - lib/truth_serum.rb
113
+ - lib/truth_serum/lexer.rb
114
+ - lib/truth_serum/parser.rb
115
+ - lib/truth_serum/result.rb
116
+ - lib/truth_serum/token.rb
117
+ - lib/truth_serum/version.rb
118
+ - truth_serum.gemspec
119
+ homepage: https://github.com/chakrit/truth_serum
120
+ licenses:
121
+ - MIT
122
+ metadata: {}
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 2.5.1
140
+ signing_key:
141
+ specification_version: 4
142
+ summary: Parser for github-style search.
143
+ test_files: []