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