jrr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a8d6ef127fac8457e34a4a262b330795ca9bd4ff
4
+ data.tar.gz: 4384f180fc3da1d524c39dd2c67cddbd916dc744
5
+ SHA512:
6
+ metadata.gz: 3374d28083b994db49d6a771cc62eeaf776e33e645094c5aa1478202f1eab2d3cdc081fb04272efea06f159761f8582b10003e9374ca0df18ae1be9083c8dd63
7
+ data.tar.gz: d951c93717278842d14e5bf1b0ee1a1018335112f59fe214f0b75214d89d8a6d84f86dfc240fedf2f84a2892996ac2ad6fca3ed4dd7124cfd7ac638ba1ce17d5
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.3
5
+ before_install: gem install bundler -v 1.15.4
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in jrr.gemspec
6
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Solomon White
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,25 @@
1
+ # JRR Tokenizer
2
+
3
+ Parses text into fantastic tokens!
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'jrr'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install jrr
20
+
21
+ ## Usage
22
+
23
+ ## License
24
+
25
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "jrr"
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(__FILE__)
@@ -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,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "jrr/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "jrr"
8
+ spec.version = Jrr::VERSION
9
+ spec.authors = ["Solomon White"]
10
+ spec.email = ["rubysolo@gmail.com"]
11
+
12
+ spec.summary = %q{parse strings into fantastic tokens}
13
+ spec.homepage = "https://github.com/rubysolo/jrr_tokenizer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.15"
24
+ spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.0"
27
+ end
@@ -0,0 +1,7 @@
1
+ require "jrr/version"
2
+
3
+ module Jrr
4
+ def self.tokenize(input)
5
+ Tokenizer.new.tokenize(input)
6
+ end
7
+ end
@@ -0,0 +1,117 @@
1
+ require 'bigdecimal'
2
+ require 'time'
3
+ require 'jrr/token'
4
+
5
+ module Jrr
6
+ module DefaultScanners
7
+ def null
8
+ new(:null, 'null\b', ->(*) { [nil] })
9
+ end
10
+
11
+ def whitespace
12
+ new(:whitespace, '\s+', ->(*) { ' ' })
13
+ end
14
+
15
+ def datetime
16
+ new(:datetime, /\d{2}\d{2}?-\d{1,2}-\d{1,2}( \d{1,2}:\d{1,2}:\d{1,2})? ?(Z|((\+|\-)\d{2}\:?\d{2}))?/, ->(raw) {
17
+ Time.parse(raw).to_datetime
18
+ })
19
+ end
20
+
21
+ def numeric
22
+ new(:numeric, '((?:\d+(\.\d+)?|\.\d+)(?:(e|E)(\+|-)?\d+)?)\b', ->(raw) {
23
+ raw =~ /\./ ? BigDecimal.new(raw) : raw.to_i
24
+ })
25
+ end
26
+
27
+ def hexadecimal
28
+ new(:numeric, '(0x[0-9a-f]+)\b', ->(raw) { raw[2..-1].to_i(16) })
29
+ end
30
+
31
+ def double_quoted_string
32
+ new(:string, '"[^"]*"', ->(raw) { raw.gsub(/^"|"$/, '') })
33
+ end
34
+
35
+ def single_quoted_string
36
+ new(:string, "'[^']*'", ->(raw) { raw.gsub(/^'|'$/, '') })
37
+ end
38
+
39
+ def negate
40
+ new(:arithmetic_operator, '-', ->(raw) { :negate }, ->(previous_token) {
41
+ previous_token.nil? ||
42
+ previous_token.is?(:arithmetic_operator) ||
43
+ previous_token.is?(:comparison_operator) ||
44
+ previous_token.is?(:boolean_operator) ||
45
+ previous_token.value == :open ||
46
+ previous_token.value == :comma
47
+ })
48
+ end
49
+
50
+ def boolean_operator
51
+ names = { and: '&&', or: '||' }.invert
52
+
53
+ new(:boolean_operator, '(and|or|&&|\|\|)\s+', ->(raw) {
54
+ norm = raw.strip.downcase
55
+ names.fetch(norm) { norm.to_sym }
56
+ })
57
+ end
58
+
59
+ def arithmetic_operator
60
+ names = {
61
+ pow: '^', add: '+', subtract: '-', multiply: '*', divide: '/', mod: '%',
62
+ bitor: '|', bitand: '&'
63
+ }.invert
64
+
65
+ new(:arithmetic_operator, '\^|\+|-|\*|\/|%|\||&', ->(raw) { names.fetch(raw) })
66
+ end
67
+
68
+ def grouping
69
+ names = { open: '(', close: ')', comma: ',' }.invert
70
+ new(:grouping, '\(|\)|,', ->(raw) { names.fetch(raw) })
71
+ end
72
+
73
+ def access
74
+ names = { lbracket: '[', rbracket: ']' }.invert
75
+ new(:access, '\[|\]', ->(raw) { names.fetch(raw) })
76
+ end
77
+
78
+ def case_statement
79
+ names = { open: 'case', close: 'end', then: 'then', when: 'when', else: 'else' }.invert
80
+ new(:case, '(case|end|then|when|else)\b', ->(raw) { names.fetch(raw.downcase) })
81
+ end
82
+
83
+ def comparison_operator
84
+ names = { le: '<=', ge: '>=', ne: '!=', lt: '<', gt: '>', eq: '=' }.invert
85
+ alternate = { ne: '<>', eq: '==' }.invert
86
+ new(:comparison_operator, '<=|>=|!=|<>|<|>|==|=', ->(raw) {
87
+ names.fetch(raw) { alternate.fetch(raw) }
88
+ })
89
+ end
90
+
91
+ def boolean
92
+ new(:boolean, '(true|false)\b', ->(raw) { raw.strip.downcase == 'true' })
93
+ end
94
+
95
+ def function
96
+ new(:function, '\w+!?\s*\(', ->(raw) do
97
+ function_name = raw.gsub('(', '')
98
+ [
99
+ Token.new(:function, function_name.strip.downcase.to_sym, function_name),
100
+ Token.new(:grouping, :open, '(')
101
+ ]
102
+ end)
103
+ end
104
+
105
+ def identifier
106
+ new(:identifier, '[\w\.]+\b', ->(raw) { standardize_case(raw.strip) })
107
+ end
108
+
109
+ def standardize_case(value)
110
+ if @case_sensitive
111
+ value
112
+ else
113
+ value.downcase
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,25 @@
1
+ module Jrr
2
+ class TokenizerError < StandardError
3
+ attr_reader :reason, :meta
4
+
5
+ def initialize(reason, **meta)
6
+ @reason = reason
7
+ @meta = meta
8
+ end
9
+
10
+ private_class_method :new
11
+
12
+ VALID_REASONS = %i[
13
+ parse_error too_many_opening_parentheses too_many_closing_parentheses
14
+ unexpected_zero_width_match
15
+ ].freeze
16
+
17
+ def self.for(reason, **meta)
18
+ unless VALID_REASONS.include?(reason)
19
+ raise ::ArgumentError, "Unhandled #{reason}"
20
+ end
21
+
22
+ new reason, meta
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,138 @@
1
+ require 'jrr/token'
2
+
3
+ module Jrr
4
+ class Matcher
5
+ attr_reader :children, :categories, :values
6
+
7
+ def initialize(categories = nil, values = nil, children = [])
8
+ # store categories and values as hash to optimize key lookup, h/t @jan-mangs
9
+ @categories = [categories].compact.flatten.each_with_object({}) { |c, h| h[c] = 1 }
10
+ @values = [values].compact.flatten.each_with_object({}) { |v, h| h[v] = 1 }
11
+ @children = children.compact
12
+ @invert = false
13
+
14
+ @min = 1
15
+ @max = 1
16
+ @range = (@min..@max)
17
+ end
18
+
19
+ def | (other_matcher)
20
+ self.class.new(:nomatch, :nomatch, leaf_matchers + other_matcher.leaf_matchers)
21
+ end
22
+
23
+ def invert
24
+ @invert = ! @invert
25
+ self
26
+ end
27
+
28
+ def ==(token)
29
+ leaf_matcher? ? matches_token?(token) : any_child_matches_token?(token)
30
+ end
31
+
32
+ def match(token_stream, offset = 0)
33
+ matched_tokens = []
34
+ matched = false
35
+
36
+ while self == token_stream[matched_tokens.length + offset] && matched_tokens.length < @max
37
+ matched_tokens << token_stream[matched_tokens.length + offset]
38
+ end
39
+
40
+ if @range.cover?(matched_tokens.length)
41
+ matched = true
42
+ end
43
+
44
+ [matched, matched_tokens]
45
+ end
46
+
47
+ def caret
48
+ @caret = true
49
+ self
50
+ end
51
+
52
+ def caret?
53
+ @caret
54
+ end
55
+
56
+ def star
57
+ @min = 0
58
+ @max = Float::INFINITY
59
+ @range = (@min..@max)
60
+ self
61
+ end
62
+
63
+ def plus
64
+ @max = Float::INFINITY
65
+ @range = (@min..@max)
66
+ self
67
+ end
68
+
69
+ def leaf_matcher?
70
+ children.empty?
71
+ end
72
+
73
+ def leaf_matchers
74
+ leaf_matcher? ? [self] : children
75
+ end
76
+
77
+ private
78
+
79
+ def any_child_matches_token?(token)
80
+ children.any? { |child| child == token }
81
+ end
82
+
83
+ def matches_token?(token)
84
+ return false if token.nil?
85
+ (category_match(token.category) && value_match(token.value)) ^ @invert
86
+ end
87
+
88
+ def category_match(category)
89
+ @categories.empty? || @categories.key?(category)
90
+ end
91
+
92
+ def value_match(value)
93
+ @values.empty? || @values.key?(value)
94
+ end
95
+
96
+ def self.datetime; new(:datetime); end
97
+ def self.numeric; new(:numeric); end
98
+ def self.string; new(:string); end
99
+ def self.logical; new(:logical); end
100
+ def self.value
101
+ new(:datetime) | new(:numeric) | new(:string) | new(:logical)
102
+ end
103
+
104
+ def self.addsub; new(:operator, [:add, :subtract]); end
105
+ def self.subtract; new(:operator, :subtract); end
106
+ def self.anchored_minus; new(:operator, :subtract).caret; end
107
+ def self.muldiv; new(:operator, [:multiply, :divide]); end
108
+ def self.pow; new(:operator, :pow); end
109
+ def self.mod; new(:operator, :mod); end
110
+ def self.combinator; new(:combinator); end
111
+
112
+ def self.comparator; new(:comparator); end
113
+ def self.comp_gt; new(:comparator, [:gt, :ge]); end
114
+ def self.comp_lt; new(:comparator, [:lt, :le]); end
115
+
116
+ def self.open; new(:grouping, :open); end
117
+ def self.close; new(:grouping, :close); end
118
+ def self.comma; new(:grouping, :comma); end
119
+ def self.non_group; new(:grouping).invert; end
120
+ def self.non_group_star; new(:grouping).invert.star; end
121
+ def self.non_close_plus; new(:grouping, :close).invert.plus; end
122
+ def self.arguments; (value | comma).plus; end
123
+
124
+ def self.if; new(:function, :if); end
125
+ def self.round; new(:function, :round); end
126
+ def self.roundup; new(:function, :roundup); end
127
+ def self.rounddown; new(:function, :rounddown); end
128
+ def self.not; new(:function, :not); end
129
+
130
+ def self.method_missing(name, *args, &block)
131
+ new(:function, name)
132
+ end
133
+
134
+ def self.respond_to_missing?(name, include_priv)
135
+ true
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,83 @@
1
+ require 'jrr/default_scanners'
2
+ require 'jrr/token'
3
+
4
+ module Jrr
5
+ class Scanner
6
+ attr_reader :category, :regex, :converter, :condition
7
+ extend DefaultScanners
8
+
9
+ def initialize(category, regex, converter=nil, condition=nil)
10
+ @category = category
11
+ @regex = %r[\A(#{ regex })]i
12
+ @converter = converter
13
+ @condition = condition || ->(*) { true }
14
+ end
15
+
16
+ def scan(input, previous_token=nil)
17
+ if (m = regex.match(input)) && continue?(previous_token)
18
+ raw = m.to_s
19
+
20
+ return Array(convert(raw)).map do |value|
21
+ case value
22
+ when Token then value
23
+ else Token.new(category, value, raw)
24
+ end
25
+ end
26
+ end
27
+
28
+ false
29
+ end
30
+
31
+ def continue?(previous_token)
32
+ condition.call(previous_token)
33
+ end
34
+
35
+ def convert(raw_value)
36
+ if converter
37
+ converter.call(raw_value)
38
+ else
39
+ raw_value
40
+ end
41
+ end
42
+
43
+ def self.default_scanners
44
+ [
45
+ :null,
46
+ :whitespace,
47
+ :datetime,
48
+ :numeric,
49
+ :hexadecimal,
50
+ :double_quoted_string,
51
+ :single_quoted_string,
52
+ :negate,
53
+ :boolean_operator,
54
+ :arithmetic_operator,
55
+ :grouping,
56
+ :access,
57
+ :case_statement,
58
+ :comparison_operator,
59
+ :boolean,
60
+ :function,
61
+ :identifier
62
+ ]
63
+ end
64
+
65
+ def self.register_default_scanners
66
+ @scanners = default_scanners.map { |key| [key, self.send(key)] }
67
+ end
68
+
69
+ def self.scanners=(keys)
70
+ @scanners.select! { |(key,_)| keys.include?(key) }
71
+ end
72
+
73
+ def self.register_scanner(key, scanner)
74
+ @scanners.push([key, scanner])
75
+ end
76
+
77
+ def self.scanners(options={})
78
+ @scanners.map { |(_, scanner)| scanner }
79
+ end
80
+
81
+ register_default_scanners
82
+ end
83
+ end
@@ -0,0 +1,31 @@
1
+ module Jrr
2
+ class Token
3
+ attr_reader :category, :value, :raw_value
4
+
5
+ def initialize(category, value, raw_value=nil)
6
+ @category = category
7
+ @value = value
8
+ @raw_value = raw_value
9
+ end
10
+
11
+ def to_s
12
+ (raw_value || value).to_s
13
+ end
14
+
15
+ def length
16
+ raw_value.to_s.length
17
+ end
18
+
19
+ def empty?
20
+ length.zero?
21
+ end
22
+
23
+ def is?(some_category)
24
+ category == some_category
25
+ end
26
+
27
+ def ==(other_token)
28
+ is?(other_token.category) && value == other_token.value
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,85 @@
1
+ require 'jrr/exceptions'
2
+ require 'jrr/matcher'
3
+ require 'jrr/scanner'
4
+ require 'jrr/token'
5
+
6
+ module Jrr
7
+ class Tokenizer
8
+ attr_reader :case_sensitive
9
+
10
+ LPAREN = Matcher.new(:grouping, :open)
11
+ RPAREN = Matcher.new(:grouping, :close)
12
+
13
+ def tokenize(string, options = {})
14
+ @nesting = 0
15
+ @tokens = []
16
+ input = strip_comments(string.to_s.dup)
17
+ @case_sensitive = options.fetch(:case_sensitive, false)
18
+
19
+ until input.empty?
20
+ scanned = Scanner.scanners(case_sensitive: case_sensitive).any? do |scanner|
21
+ scanned, input = scan(input, scanner)
22
+ scanned
23
+ end
24
+
25
+ unless scanned
26
+ fail! :parse_error, at: input
27
+ end
28
+ end
29
+
30
+ fail! :too_many_opening_parentheses if @nesting > 0
31
+
32
+ @tokens
33
+ end
34
+
35
+ def last_token
36
+ @tokens.last
37
+ end
38
+
39
+ def scan(string, scanner)
40
+ if tokens = scanner.scan(string, last_token)
41
+ tokens.each do |token|
42
+ if token.empty?
43
+ fail! :unexpected_zero_width_match,
44
+ token_category: token.category, at: string
45
+ end
46
+
47
+ @nesting += 1 if LPAREN == token
48
+ @nesting -= 1 if RPAREN == token
49
+ fail! :too_many_closing_parentheses if @nesting < 0
50
+
51
+ @tokens << token unless token.is?(:whitespace)
52
+ end
53
+
54
+ match_length = tokens.map(&:length).reduce(:+)
55
+ [true, string[match_length..-1]]
56
+ else
57
+ [false, string]
58
+ end
59
+ end
60
+
61
+ def strip_comments(input)
62
+ input.gsub(/\/\*[^*]*\*+(?:[^*\/][^*]*\*+)*\//, '')
63
+ end
64
+
65
+ private
66
+
67
+ def fail!(reason, **meta)
68
+ message =
69
+ case reason
70
+ when :parse_error
71
+ "parse error at: '#{meta.fetch(:at)}'"
72
+ when :too_many_opening_parentheses
73
+ "too many opening parentheses"
74
+ when :too_many_closing_parentheses
75
+ "too many closing parentheses"
76
+ when :unexpected_zero_width_match
77
+ "unexpected zero-width match (:#{meta.fetch(:category)}) at '#{meta.fetch(:at)}'"
78
+ else
79
+ raise ::ArgumentError, "Unhandled #{reason}"
80
+ end
81
+
82
+ raise TokenizerError.for(reason, meta), message
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ module Jrr
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jrr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Solomon White
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-27 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.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description:
70
+ email:
71
+ - rubysolo@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - jrr.gemspec
86
+ - lib/jrr.rb
87
+ - lib/jrr/default_scanners.rb
88
+ - lib/jrr/exceptions.rb
89
+ - lib/jrr/matcher.rb
90
+ - lib/jrr/scanner.rb
91
+ - lib/jrr/token.rb
92
+ - lib/jrr/tokenizer.rb
93
+ - lib/jrr/version.rb
94
+ homepage: https://github.com/rubysolo/jrr_tokenizer
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.6.13
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: parse strings into fantastic tokens
118
+ test_files: []