mustermann 1.0.2.rc1 → 1.0.2.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +799 -230
  3. data/{mustermann/bench → bench}/capturing.rb +0 -0
  4. data/{mustermann/bench → bench}/regexp.rb +0 -0
  5. data/{mustermann/bench → bench}/simple_vs_sinatra.rb +0 -0
  6. data/{mustermann/bench → bench}/template_vs_addressable.rb +0 -0
  7. data/{mustermann/lib → lib}/mustermann.rb +0 -0
  8. data/{mustermann/lib → lib}/mustermann/ast/boundaries.rb +0 -0
  9. data/{mustermann/lib → lib}/mustermann/ast/compiler.rb +0 -0
  10. data/{mustermann/lib → lib}/mustermann/ast/expander.rb +0 -0
  11. data/{mustermann/lib → lib}/mustermann/ast/node.rb +0 -0
  12. data/{mustermann/lib → lib}/mustermann/ast/param_scanner.rb +0 -0
  13. data/{mustermann/lib → lib}/mustermann/ast/parser.rb +0 -0
  14. data/{mustermann/lib → lib}/mustermann/ast/pattern.rb +0 -0
  15. data/{mustermann/lib → lib}/mustermann/ast/template_generator.rb +0 -0
  16. data/{mustermann/lib → lib}/mustermann/ast/transformer.rb +0 -0
  17. data/{mustermann/lib → lib}/mustermann/ast/translator.rb +0 -0
  18. data/{mustermann/lib → lib}/mustermann/ast/validation.rb +0 -0
  19. data/{mustermann/lib → lib}/mustermann/caster.rb +0 -0
  20. data/{mustermann/lib → lib}/mustermann/composite.rb +0 -0
  21. data/{mustermann/lib → lib}/mustermann/concat.rb +0 -0
  22. data/{mustermann/lib → lib}/mustermann/equality_map.rb +0 -0
  23. data/{mustermann/lib → lib}/mustermann/error.rb +0 -0
  24. data/{mustermann/lib → lib}/mustermann/expander.rb +0 -0
  25. data/{mustermann/lib → lib}/mustermann/extension.rb +0 -0
  26. data/{mustermann/lib → lib}/mustermann/identity.rb +0 -0
  27. data/{mustermann/lib → lib}/mustermann/mapper.rb +0 -0
  28. data/{mustermann/lib → lib}/mustermann/pattern.rb +0 -0
  29. data/{mustermann/lib → lib}/mustermann/pattern_cache.rb +0 -0
  30. data/{mustermann/lib → lib}/mustermann/regexp.rb +0 -0
  31. data/{mustermann/lib → lib}/mustermann/regexp_based.rb +0 -0
  32. data/{mustermann/lib → lib}/mustermann/regular.rb +0 -0
  33. data/{mustermann/lib → lib}/mustermann/simple_match.rb +0 -0
  34. data/{mustermann/lib → lib}/mustermann/sinatra.rb +0 -0
  35. data/{mustermann/lib → lib}/mustermann/sinatra/parser.rb +0 -0
  36. data/{mustermann/lib → lib}/mustermann/sinatra/safe_renderer.rb +0 -0
  37. data/{mustermann/lib → lib}/mustermann/sinatra/try_convert.rb +0 -0
  38. data/{mustermann/lib → lib}/mustermann/to_pattern.rb +0 -0
  39. data/{mustermann/lib → lib}/mustermann/version.rb +1 -1
  40. data/{mustermann/mustermann.gemspec → mustermann.gemspec} +0 -0
  41. data/{mustermann/spec → spec}/ast_spec.rb +0 -0
  42. data/{mustermann/spec → spec}/composite_spec.rb +0 -0
  43. data/{mustermann/spec → spec}/concat_spec.rb +0 -0
  44. data/{mustermann/spec → spec}/equality_map_spec.rb +0 -0
  45. data/{mustermann/spec → spec}/expander_spec.rb +0 -0
  46. data/{mustermann/spec → spec}/extension_spec.rb +0 -0
  47. data/{mustermann/spec → spec}/identity_spec.rb +0 -0
  48. data/{mustermann/spec → spec}/mapper_spec.rb +0 -0
  49. data/{mustermann/spec → spec}/mustermann_spec.rb +0 -0
  50. data/{mustermann/spec → spec}/pattern_spec.rb +0 -0
  51. data/{mustermann/spec → spec}/regexp_based_spec.rb +0 -0
  52. data/{mustermann/spec → spec}/regular_spec.rb +0 -0
  53. data/{mustermann/spec → spec}/simple_match_spec.rb +0 -0
  54. data/{mustermann/spec → spec}/sinatra_spec.rb +0 -0
  55. data/{mustermann/spec → spec}/to_pattern_spec.rb +0 -0
  56. metadata +71 -126
  57. data/.gitignore +0 -18
  58. data/.rspec +0 -5
  59. data/.travis.yml +0 -25
  60. data/.yardopts +0 -3
  61. data/Gemfile +0 -7
  62. data/Rakefile +0 -27
  63. data/mustermann-contrib/LICENSE +0 -23
  64. data/mustermann-contrib/README.md +0 -1155
  65. data/mustermann-contrib/examples/highlighting.rb +0 -35
  66. data/mustermann-contrib/highlighting.png +0 -0
  67. data/mustermann-contrib/irb.png +0 -0
  68. data/mustermann-contrib/lib/mustermann/cake.rb +0 -19
  69. data/mustermann-contrib/lib/mustermann/express.rb +0 -38
  70. data/mustermann-contrib/lib/mustermann/file_utils.rb +0 -218
  71. data/mustermann-contrib/lib/mustermann/file_utils/glob_pattern.rb +0 -40
  72. data/mustermann-contrib/lib/mustermann/fileutils.rb +0 -1
  73. data/mustermann-contrib/lib/mustermann/flask.rb +0 -199
  74. data/mustermann-contrib/lib/mustermann/pyramid.rb +0 -29
  75. data/mustermann-contrib/lib/mustermann/rails.rb +0 -47
  76. data/mustermann-contrib/lib/mustermann/shell.rb +0 -57
  77. data/mustermann-contrib/lib/mustermann/simple.rb +0 -51
  78. data/mustermann-contrib/lib/mustermann/string_scanner.rb +0 -314
  79. data/mustermann-contrib/lib/mustermann/strscan.rb +0 -1
  80. data/mustermann-contrib/lib/mustermann/template.rb +0 -63
  81. data/mustermann-contrib/lib/mustermann/uri_template.rb +0 -1
  82. data/mustermann-contrib/lib/mustermann/versions.rb +0 -47
  83. data/mustermann-contrib/lib/mustermann/visualizer.rb +0 -39
  84. data/mustermann-contrib/lib/mustermann/visualizer/highlight.rb +0 -138
  85. data/mustermann-contrib/lib/mustermann/visualizer/highlighter.rb +0 -38
  86. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ad_hoc.rb +0 -95
  87. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ast.rb +0 -103
  88. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/composite.rb +0 -46
  89. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/dummy.rb +0 -19
  90. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/regular.rb +0 -105
  91. data/mustermann-contrib/lib/mustermann/visualizer/pattern_extension.rb +0 -69
  92. data/mustermann-contrib/lib/mustermann/visualizer/renderer/ansi.rb +0 -24
  93. data/mustermann-contrib/lib/mustermann/visualizer/renderer/generic.rb +0 -47
  94. data/mustermann-contrib/lib/mustermann/visualizer/renderer/hansi_template.rb +0 -35
  95. data/mustermann-contrib/lib/mustermann/visualizer/renderer/html.rb +0 -51
  96. data/mustermann-contrib/lib/mustermann/visualizer/renderer/sexp.rb +0 -38
  97. data/mustermann-contrib/lib/mustermann/visualizer/tree.rb +0 -64
  98. data/mustermann-contrib/lib/mustermann/visualizer/tree_renderer.rb +0 -79
  99. data/mustermann-contrib/mustermann-contrib.gemspec +0 -19
  100. data/mustermann-contrib/spec/cake_spec.rb +0 -91
  101. data/mustermann-contrib/spec/express_spec.rb +0 -210
  102. data/mustermann-contrib/spec/file_utils_spec.rb +0 -120
  103. data/mustermann-contrib/spec/flask_spec.rb +0 -362
  104. data/mustermann-contrib/spec/flask_subclass_spec.rb +0 -369
  105. data/mustermann-contrib/spec/pattern_extension_spec.rb +0 -50
  106. data/mustermann-contrib/spec/pyramid_spec.rb +0 -102
  107. data/mustermann-contrib/spec/rails_spec.rb +0 -648
  108. data/mustermann-contrib/spec/shell_spec.rb +0 -148
  109. data/mustermann-contrib/spec/simple_spec.rb +0 -269
  110. data/mustermann-contrib/spec/string_scanner_spec.rb +0 -272
  111. data/mustermann-contrib/spec/template_spec.rb +0 -842
  112. data/mustermann-contrib/spec/visualizer_spec.rb +0 -199
  113. data/mustermann-contrib/theme.png +0 -0
  114. data/mustermann-contrib/tree.png +0 -0
  115. data/mustermann/LICENSE +0 -23
  116. data/mustermann/README.md +0 -853
  117. data/support/lib/support.rb +0 -7
  118. data/support/lib/support/coverage.rb +0 -23
  119. data/support/lib/support/env.rb +0 -19
  120. data/support/lib/support/expand_matcher.rb +0 -28
  121. data/support/lib/support/generate_template_matcher.rb +0 -27
  122. data/support/lib/support/match_matcher.rb +0 -39
  123. data/support/lib/support/pattern.rb +0 -42
  124. data/support/lib/support/projects.rb +0 -20
  125. data/support/lib/support/scan_matcher.rb +0 -63
  126. data/support/support.gemspec +0 -27
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/ast/pattern'
4
-
5
- module Mustermann
6
- # Pyramid style pattern implementation.
7
- #
8
- # @example
9
- # Mustermann.new('/<foo>', type: :pryamid) === '/bar' # => true
10
- #
11
- # @see Mustermann::Pattern
12
- # @see file:README.md#pryamid Syntax description in the README
13
- class Pyramid < AST::Pattern
14
- register :pyramid
15
-
16
- on(nil, ?}) { |c| unexpected(c) }
17
-
18
- on(?{) do |char|
19
- name = expect(/\w+/, char: char)
20
- constraint = read_brackets(?{, ?}) if scan(?:)
21
- expect(?}) unless constraint
22
- node(:capture, name, constraint: constraint)
23
- end
24
-
25
- on(?*) do |char|
26
- node(:named_splat, expect(/\w+$/, char: char), convert: -> e { e.split(?/) })
27
- end
28
- end
29
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/ast/pattern'
4
- require 'mustermann/versions'
5
-
6
- module Mustermann
7
- # Rails style pattern implementation.
8
- #
9
- # @example
10
- # Mustermann.new('/:foo', type: :rails) === '/bar' # => true
11
- #
12
- # @see Mustermann::Pattern
13
- # @see file:README.md#rails Syntax description in the README
14
- class Rails < AST::Pattern
15
- extend Versions
16
- register :rails
17
-
18
- # first parser, no optional parts
19
- version('2.3') do
20
- on(nil) { |c| unexpected(c) }
21
- on(?*) { |c| node(:named_splat) { scan(/\w+/) } }
22
- on(?:) { |c| node(:capture) { scan(/\w+/) } }
23
- end
24
-
25
- # rack-mount
26
- version('3.0', '3.1') do
27
- on(?)) { |c| unexpected(c) }
28
- on(?() { |c| node(:optional, node(:group) { read unless scan(?)) }) }
29
- on(?\\) { |c| node(:char, expect(/./)) }
30
- end
31
-
32
- # stand-alone journey
33
- version('3.2') do
34
- on(?|) { |c| raise ParseError, "the implementation of | is broken in ActionDispatch, cannot compile compatible pattern" }
35
- on(?\\) { |c| node(:char, c) }
36
- end
37
-
38
- # embedded journey, broken (ignored) escapes
39
- version('4.0', '4.1') { on(?\\) { |c| read } }
40
-
41
- # escapes got fixed in 4.2
42
- version('4.2') { on(?\\) { |c| node(:char, expect(/./)) } }
43
-
44
- # Rails 5.0 fixes |
45
- version('5.0') { on(?|) { |c| node(:or) }}
46
- end
47
- end
@@ -1,57 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/pattern'
4
- require 'mustermann/simple_match'
5
-
6
- module Mustermann
7
- # Matches strings that are identical to the pattern.
8
- #
9
- # @example
10
- # Mustermann.new('/*.*', type: :shell) === '/bar' # => false
11
- #
12
- # @see Mustermann::Pattern
13
- # @see file:README.md#shell Syntax description in the README
14
- class Shell < Pattern
15
- include Concat::Native
16
- register :shell
17
-
18
- # @!visibility private
19
- # @return [#highlight, nil]
20
- # highlighing logic for mustermann-visualizer,
21
- # nil if mustermann-visualizer hasn't been loaded
22
- def highlighter
23
- return unless defined? Mustermann::Visualizer::Highlighter
24
- @@highlighter ||= Mustermann::Visualizer::Highlighter.create do
25
- on('\\') { |matched| escaped(matched, scanner.getch) }
26
- on(/[\*\[\]]/, :special)
27
- on("{") { nested(:union, ?{, ?}, ?,) }
28
- end
29
- end
30
-
31
- # @param (see Mustermann::Pattern#initialize)
32
- # @return (see Mustermann::Pattern#initialize)
33
- # @see (see Mustermann::Pattern#initialize)
34
- def initialize(string, **options)
35
- @flags = File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB
36
- super(string, **options)
37
- end
38
-
39
- # @param (see Mustermann::Pattern#===)
40
- # @return (see Mustermann::Pattern#===)
41
- # @see (see Mustermann::Pattern#===)
42
- def ===(string)
43
- File.fnmatch? @string, unescape(string), @flags
44
- end
45
-
46
- # @param (see Mustermann::Pattern#peek_size)
47
- # @return (see Mustermann::Pattern#peek_size)
48
- # @see (see Mustermann::Pattern#peek_size)
49
- def peek_size(string)
50
- @peek_string ||= @string + "{**,/**,/**/*}"
51
- super if File.fnmatch? @peek_string, unescape(string), @flags
52
- end
53
-
54
- # Used by {Mustermann::FileUtils} to not use a generic glob pattern.
55
- alias_method :to_glob, :to_s
56
- end
57
- end
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/regexp_based'
4
-
5
- module Mustermann
6
- # Sinatra 1.3 style pattern implementation.
7
- #
8
- # @example
9
- # Mustermann.new('/:foo', type: :simple) === '/bar' # => true
10
- #
11
- # @see Mustermann::Pattern
12
- # @see file:README.md#simple Syntax description in the README
13
- class Simple < RegexpBased
14
- register :simple
15
- supported_options :greedy, :space_matches_plus
16
- instance_delegate highlighter: 'self.class'
17
-
18
- # @!visibility private
19
- # @return [#highlight, nil]
20
- # highlighing logic for mustermann-visualizer,
21
- # nil if mustermann-visualizer hasn't been loaded
22
- def self.highlighter
23
- return unless defined? Mustermann::Visualizer::Highlighter
24
- @highlighter ||= Mustermann::Visualizer::Highlighter.create do
25
- on(/:(\w+)/) { |matched| element(:capture, ':') { element(:name, matched[1..-1]) } }
26
- on("*" => :splat, "?" => :optional)
27
- end
28
- end
29
-
30
- def compile(greedy: true, uri_decode: true, space_matches_plus: true, **options)
31
- pattern = @string.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c, uri_decode, space_matches_plus) }
32
- pattern.gsub!(/((:\w+)|\*)/) do |match|
33
- match == "*" ? "(?<splat>.*?)" : "(?<#{$2[1..-1]}>[^/?#]+#{?? unless greedy})"
34
- end
35
- Regexp.new(pattern)
36
- rescue SyntaxError, RegexpError => error
37
- type = error.message["invalid group name"] ? CompileError : ParseError
38
- raise type, error.message, error.backtrace
39
- end
40
-
41
- def encoded(char, uri_decode, space_matches_plus)
42
- return Regexp.escape(char) unless uri_decode
43
- parser = URI::Parser.new
44
- encoded = Regexp.union(parser.escape(char), parser.escape(char, /./).downcase, parser.escape(char, /./).upcase)
45
- encoded = Regexp.union(encoded, encoded('+', true, true)) if space_matches_plus and char == " "
46
- encoded
47
- end
48
-
49
- private :compile, :encoded
50
- end
51
- end
@@ -1,314 +0,0 @@
1
- # frozen_string_literal: true
2
- require 'mustermann'
3
- require 'mustermann/pattern_cache'
4
- require 'delegate'
5
-
6
- module Mustermann
7
- # Class inspired by Ruby's StringScanner to scan an input string using multiple patterns.
8
- #
9
- # @example
10
- # require 'mustermann/string_scanner'
11
- # scanner = Mustermann::StringScanner.new("here is our example string")
12
- #
13
- # scanner.scan("here") # => "here"
14
- # scanner.getch # => " "
15
- #
16
- # if scanner.scan(":verb our")
17
- # scanner.scan(:noun, capture: :word)
18
- # scanner[:verb] # => "is"
19
- # scanner[:nound] # => "example"
20
- # end
21
- #
22
- # scanner.rest # => "string"
23
- #
24
- # @note
25
- # This structure is not thread-safe, you should not scan on the same StringScanner instance concurrently.
26
- # Even if it was thread-safe, scanning concurrently would probably lead to unwanted behaviour.
27
- class StringScanner
28
- # Exception raised if scan/unscan operation cannot be performed.
29
- ScanError = Class.new(::ScanError)
30
- PATTERN_CACHE = PatternCache.new
31
- private_constant :PATTERN_CACHE
32
-
33
- # Patterns created by {#scan} will be globally cached, since we assume that there is a finite number
34
- # of different patterns used and that they are more likely to be reused than not.
35
- # This method allows clearing the cache.
36
- #
37
- # @see Mustermann::PatternCache
38
- def self.clear_cache
39
- PATTERN_CACHE.clear
40
- end
41
-
42
- # @return [Integer] number of cached patterns
43
- # @see clear_cache
44
- # @api private
45
- def self.cache_size
46
- PATTERN_CACHE.size
47
- end
48
-
49
- # Encapsulates return values for {StringScanner#scan}, {StringScanner#check}, and friends.
50
- # Behaves like a String (the substring which matched the pattern), but also exposes its position
51
- # in the main string and any params parsed from it.
52
- class ScanResult < DelegateClass(String)
53
- # The scanner this result came from.
54
- # @example
55
- # require 'mustermann/string_scanner'
56
- # scanner = Mustermann::StringScanner.new('foo/bar')
57
- # scanner.scan(:name).scanner == scanner # => true
58
- attr_reader :scanner
59
-
60
- # @example
61
- # require 'mustermann/string_scanner'
62
- # scanner = Mustermann::StringScanner.new('foo/bar')
63
- # scanner.scan(:name).position # => 0
64
- # scanner.getch.position # => 3
65
- # scanner.scan(:name).position # => 4
66
- #
67
- # @return [Integer] position the substring starts at
68
- attr_reader :position
69
- alias_method :pos, :position
70
-
71
- # @example
72
- # require 'mustermann/string_scanner'
73
- # scanner = Mustermann::StringScanner.new('foo/bar')
74
- # scanner.scan(:name).length # => 3
75
- # scanner.getch.length # => 1
76
- # scanner.scan(:name).length # => 3
77
- #
78
- # @return [Integer] length of the substring
79
- attr_reader :length
80
-
81
- # Params parsed from the substring.
82
- # Will not include params from previous scan results.
83
- #
84
- # @example
85
- # require 'mustermann/string_scanner'
86
- # scanner = Mustermann::StringScanner.new('foo/bar')
87
- # scanner.scan(:name).params # => { "name" => "foo" }
88
- # scanner.getch.params # => {}
89
- # scanner.scan(:name).params # => { "name" => "bar" }
90
- #
91
- # @see Mustermann::StringScanner#params
92
- # @see Mustermann::StringScanner#[]
93
- #
94
- # @return [Hash] params parsed from the substring
95
- attr_reader :params
96
-
97
- # @api private
98
- def initialize(scanner, position, length, params = {})
99
- @scanner, @position, @length, @params = scanner, position, length, params
100
- end
101
-
102
- # @api private
103
- # @!visibility private
104
- def __getobj__
105
- @__getobj__ ||= scanner.to_s[position, length]
106
- end
107
- end
108
-
109
- # @return [Hash] default pattern options used for {#scan} and similar methods
110
- # @see #initialize
111
- attr_reader :pattern_options
112
-
113
- # Params from all previous matches from {#scan} and {#scan_until},
114
- # but not from {#check} and {#check_until}. Changes can be reverted
115
- # with {#unscan} and it can be completely cleared via {#reset}.
116
- #
117
- # @return [Hash] current params
118
- attr_reader :params
119
-
120
- # @return [Integer] current scan position on the input string
121
- attr_accessor :position
122
- alias_method :pos, :position
123
- alias_method :pos=, :position=
124
-
125
- # @example with different default type
126
- # require 'mustermann/string_scanner'
127
- # scanner = Mustermann::StringScanner.new("foo/bar/baz", type: :shell)
128
- # scanner.scan('*') # => "foo"
129
- # scanner.scan('**/*') # => "/bar/baz"
130
- #
131
- # @param [String] string the string to scan
132
- # @param [Hash] pattern_options default options used for {#scan}
133
- def initialize(string = "", **pattern_options)
134
- @pattern_options = pattern_options
135
- @string = String(string).dup
136
- reset
137
- end
138
-
139
- # Resets the {#position} to the start and clears all {#params}.
140
- # @return [Mustermann::StringScanner] the scanner itself
141
- def reset
142
- @position = 0
143
- @params = {}
144
- @history = []
145
- self
146
- end
147
-
148
- # Moves the position to the end of the input string.
149
- # @return [Mustermann::StringScanner] the scanner itself
150
- def terminate
151
- track_result ScanResult.new(self, @position, size - @position)
152
- self
153
- end
154
-
155
- # Checks if the given pattern matches any substring starting at the current position.
156
- #
157
- # If it does, it will advance the current {#position} to the end of the substring and merges any params parsed
158
- # from the substring into {#params}.
159
- #
160
- # @param (see Mustermann.new)
161
- # @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
162
- def scan(pattern, **options)
163
- track_result check(pattern, **options)
164
- end
165
-
166
- # Checks if the given pattern matches any substring starting at any position after the current position.
167
- #
168
- # If it does, it will advance the current {#position} to the end of the substring and merges any params parsed
169
- # from the substring into {#params}.
170
- #
171
- # @param (see Mustermann.new)
172
- # @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
173
- def scan_until(pattern, **options)
174
- result, prefix = check_until_with_prefix(pattern, **options)
175
- track_result(prefix, result)
176
- end
177
-
178
- # Reverts the last operation that advanced the position.
179
- #
180
- # Operations advancing the position: {#terminate}, {#scan}, {#scan_until}, {#getch}.
181
- # @return [Mustermann::StringScanner] the scanner itself
182
- def unscan
183
- raise ScanError, 'unscan failed: previous match record not exist' if @history.empty?
184
- previous = @history[0..-2]
185
- reset
186
- previous.each { |r| track_result(*r) }
187
- self
188
- end
189
-
190
- # Checks if the given pattern matches any substring starting at the current position.
191
- #
192
- # Does not affect {#position} or {#params}.
193
- #
194
- # @param (see Mustermann.new)
195
- # @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
196
- def check(pattern, **options)
197
- params, length = create_pattern(pattern, **options).peek_params(rest)
198
- ScanResult.new(self, @position, length, params) if params
199
- end
200
-
201
- # Checks if the given pattern matches any substring starting at any position after the current position.
202
- #
203
- # Does not affect {#position} or {#params}.
204
- #
205
- # @param (see Mustermann.new)
206
- # @return [Mustermann::StringScanner::ScanResult, nil] the matched substring, nil if it didn't match
207
- def check_until(pattern, **options)
208
- check_until_with_prefix(pattern, **options).first
209
- end
210
-
211
- def check_until_with_prefix(pattern, **options)
212
- start = @position
213
- @position += 1 until eos? or result = check(pattern, **options)
214
- prefix = ScanResult.new(self, start, @position - start) if result
215
- [result, prefix]
216
- ensure
217
- @position = start
218
- end
219
-
220
- # Reads a single character and advances the {#position} by one.
221
- # @return [Mustermann::StringScanner::ScanResult, nil] the character, nil if at end of string
222
- def getch
223
- track_result ScanResult.new(self, @position, 1) unless eos?
224
- end
225
-
226
- # Appends the given string to the string being scanned
227
- #
228
- # @example
229
- # require 'mustermann/string_scanner'
230
- # scanner = Mustermann::StringScanner.new
231
- # scanner << "foo"
232
- # scanner.scan(/.+/) # => "foo"
233
- #
234
- # @param [String] string will be appended
235
- # @return [Mustermann::StringScanner] the scanner itself
236
- def <<(string)
237
- @string << string
238
- self
239
- end
240
-
241
- # @return [true, false] whether or not the end of the string has been reached
242
- def eos?
243
- @position >= @string.size
244
- end
245
-
246
- # @return [true, false] whether or not the current position is at the start of a line
247
- def beginning_of_line?
248
- @position == 0 or @string[@position - 1] == "\n"
249
- end
250
-
251
- # @return [String] outstanding string not yet matched, empty string at end of input string
252
- def rest
253
- @string[@position..-1] || ""
254
- end
255
-
256
- # @return [Integer] number of character remaining to be scanned
257
- def rest_size
258
- @position > size ? 0 : size - @position
259
- end
260
-
261
- # Allows to peek at a number of still unscanned characters without advacing the {#position}.
262
- #
263
- # @param [Integer] length how many characters to look at
264
- # @return [String] the substring
265
- def peek(length = 1)
266
- @string[@position, length]
267
- end
268
-
269
- # Shorthand for accessing {#params}. Accepts symbols as keys.
270
- def [](key)
271
- params[key.to_s]
272
- end
273
-
274
- # (see #params)
275
- def to_h
276
- params.dup
277
- end
278
-
279
- # @return [String] the input string
280
- # @see #initialize
281
- # @see #<<
282
- def to_s
283
- @string.dup
284
- end
285
-
286
- # @return [Integer] size of the input string
287
- def size
288
- @string.size
289
- end
290
-
291
- # @!visibility private
292
- def inspect
293
- "#<%p %d/%d @ %p>" % [ self.class, @position, @string.size, @string ]
294
- end
295
-
296
- # @!visibility private
297
- def create_pattern(pattern, **options)
298
- PATTERN_CACHE.create_pattern(pattern, **options, **pattern_options)
299
- end
300
-
301
- # @!visibility private
302
- def track_result(*results)
303
- results.compact!
304
- @history << results if results.any?
305
- results.each do |result|
306
- @params.merge! result.params
307
- @position += result.length
308
- end
309
- results.last
310
- end
311
-
312
- private :create_pattern, :track_result, :check_until_with_prefix
313
- end
314
- end