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 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, line 167
def message
  @issue.nil? ? nil : @issue.format
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, line 177
def scan(skip_leading=false)
  @mode = :text
  @skip_leading = skip_leading

  return nil if scanner.eos?
  s = ""
  until scanner.eos?
    part = @scanner.scan_until(/(<%)|\z/)
    if @skip_leading
      part.sub!(/^[ \t]*\r?(?:\n|\z)?/,'')
      @skip_leading = false
    end
    # The spec for %%> is to transform it into a literal %>. This is done here, as %%> otherwise would go
    # undetected in text mode. (i.e. it is not really necessary to escape %> with %%> in text mode unless
    # adding checks stating that a literal %> is illegal in text (unbalanced).
    #
    part.gsub!(/%%>/, '%>')
    s += part
    case @scanner.peek(1)
    when ""
      # at the end
      # if s ends with <% then this is an error (unbalanced <% %>)
      if s.end_with? "<%"
        @mode = :error
        @issue = Puppet::Pops::Issues::EPP_UNBALANCED_EXPRESSION
      else
        mode = :epp
      end
      return s

    when "-"
      # trim trailing whitespace on same line from accumulated s
      # return text and signal switch to pp mode
      @scanner.getch # drop the -
      s.sub!(/[ \t]*<%\z/, '')
      @mode = :epp
      return s

    when "%"
      # verbatim text
      # keep the scanned <%, and continue scanning after skipping one %
      # (i.e. do nothing here)
      @scanner.getch # drop the % to get a literal <% in the output

    when "="
      # expression
      # return text and signal switch to expression mode
      # drop the scanned <%, and skip past -%>, or %>, but also skip %%>
      @scanner.getch # drop the =
      s.slice!(-2..-1)
      @mode = :expr
      return s

    when "#"
      # template comment
      # drop the scanned <%, and skip past -%>, or %>, but also skip %%>
      s.slice!(-2..-1)

      # unless there is an immediate termination i.e. <%#%> scan for the next %> that is not
      # preceded by a % (i.e. skip %%>)
      part = scanner.scan_until(/[^%]%>/)
      unless part
        @issue = Puppet::Pops::Issues::EPP_UNBALANCED_COMMENT
        @mode = :error
        return s
      end
      # Always trim leading whitespace on the same line when there is a comment
      s.sub!(/[ \t]*\z/, '')
      @skip_leading = true if part.end_with?("-%>")
      # Continue scanning for more text

    else
      # Switch to pp after having removed the <%
      s.slice!(-2..-1)
      @mode = :epp
      return s
    end
  end
end

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, line 160
def initialize(scanner)
  @scanner = scanner
end