class Puppet::Pops::Parser::EppSupport::EppScanner

A scanner specialized in processing text with embedded EPP (Embedded Puppet) tags. The scanner is initialized with a StringScanner which it mutates as scanning takes place. The intent is to use one instance of EppScanner per wanted scan, and this instance represents the state after the scan.

@example Sample usage

a = "some text <% pp code %> some more text"
scan = StringScanner.new(a)
eppscan = EppScanner.new(scan)
str = eppscan.scan
eppscan.mode # => :epp
eppscan.lines # => 0
eppscan

The scanner supports

Note that the intent is to use this specialized scanner to scan the text parts, when continuation mode is `:epp` or `:expr` the pp lexer should advance scanning (using the string scanner) until it reaches and consumes a `-%>` or '%>ยด token. If it finds a `-%> token it should pass this on as a `skip_leading` parameter when it performs the next {#scan}.

Attributes

issue[R]

An error issue if `mode == :error`, `nil` otherwise.

mode[R]

The resulting mode after the scan. The mode is one of `:text` (the initial mode), `:epp` embedded code (no output), `:expr` (embedded expression), or `:error`

scanner[R]

The original scanner used by the lexer/container using EppScanner

skip_leading[R]

If the first scan should skip leading whitespace (typically detected by the pp lexer when the pp mode end-token is found (i.e. `-%>`) and then passed on to the scanner.

Public Class Methods

new(scanner) click to toggle source

Creates an EppScanner based on a StringScanner that represents the state where EppScanner should start scanning. The given scanner will be mutated (i.e. position moved) to reflect the EppScanner's end state after a scan.

    # File lib/puppet/pops/parser/epp_support.rb
164 def initialize(scanner)
165   @scanner = scanner
166 end

Public Instance Methods

message() click to toggle source

Here for backwards compatibility. @deprecated Use issue instead @return [String] the issue message

    # File lib/puppet/pops/parser/epp_support.rb
171 def message
172   @issue.nil? ? nil : @issue.format
173 end
scan(skip_leading=false) click to toggle source

Scans from the current position in the configured scanner, advances this scanner's position until the end of the input, or to the first position after a mode switching token (`<%`, `<%-` or `<%=`). Number of processed lines and continuation mode can be obtained via {#lines}, and {#mode}.

@return [String, nil] the scanned and processed text, or nil if at the end of the input.

    # File lib/puppet/pops/parser/epp_support.rb
181 def scan(skip_leading=false)
182   @mode = :text
183   @skip_leading = skip_leading
184 
185   return nil if scanner.eos?
186   s = String.new
187   until scanner.eos?
188     part = @scanner.scan_until(/(<%)|\z/)
189     if @skip_leading
190       part.sub!(/^[ \t]*\r?(?:\n|\z)?/,'')
191       @skip_leading = false
192     end
193     # The spec for %%> is to transform it into a literal %>. This is done here, as %%> otherwise would go
194     # undetected in text mode. (i.e. it is not really necessary to escape %> with %%> in text mode unless
195     # adding checks stating that a literal %> is illegal in text (unbalanced).
196     #
197     part.gsub!(/%%>/, '%>')
198     s += part
199     case @scanner.peek(1)
200     when ""
201       # at the end
202       # if s ends with <% then this is an error (unbalanced <% %>)
203       if s.end_with? "<%"
204         @mode = :error
205         @issue = Issues::EPP_UNBALANCED_EXPRESSION
206       end
207       return s
208 
209     when "-"
210       # trim trailing whitespace on same line from accumulated s
211       # return text and signal switch to pp mode
212       @scanner.getch # drop the -
213       s.sub!(/[ \t]*<%\z/, '')
214       @mode = :epp
215       return s
216 
217     when "%"
218       # verbatim text
219       # keep the scanned <%, and continue scanning after skipping one %
220       # (i.e. do nothing here)
221       @scanner.getch # drop the % to get a literal <% in the output
222 
223     when "="
224       # expression
225       # return text and signal switch to expression mode
226       # drop the scanned <%, and skip past -%>, or %>, but also skip %%>
227       @scanner.getch # drop the =
228       s.slice!(-2..-1)
229       @mode = :expr
230       return s
231 
232     when "#"
233       # template comment
234 
235       # drop the scanned <%, and skip past -%>, or %>, but also skip %%>
236       s.slice!(-2..-1)
237 
238       # unless there is an immediate termination i.e. <%#%> scan for the next %> that is not
239       # preceded by a % (i.e. skip %%>)
240       part = scanner.scan_until(/[^%]%>/)
241       unless part
242         @issue = Issues::EPP_UNBALANCED_COMMENT
243         @mode = :error
244         return s
245       end
246       # Trim leading whitespace on the same line when start was <%#-
247       if part[1] == '-'
248         s.sub!(/[ \t]*\z/, '')
249       end
250 
251       @skip_leading = true if part.end_with?("-%>")
252       # Continue scanning for more text
253 
254     else
255       # Switch to pp after having removed the <%
256       s.slice!(-2..-1)
257       @mode = :epp
258       return s
259     end
260   end
261 end