Class TaskJuggler::TextParser::Scanner::StreamHandle
In: lib/taskjuggler/TextParser/Scanner.rb
Parent: Object

This class is used to handle the low-level input operations. It knows whether it deals with a text buffer or a file and abstracts this to the Scanner. For each nested file the scanner puts an StreamHandle on the stack while the file is scanned. With this stack the scanner can resume the processing of the enclosing file once the included files has been completely processed.

Methods

close   dirname   eof?   error   injectMacro   injectText   line   lineNo   mrxscan   new   peek   scan  

Attributes

fileName  [R] 
macroStack  [R] 

Public Class methods

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 55
      def initialize(log, textScanner)
        @log = log
        @textScanner = textScanner
        @fileName = nil
        @stream = nil
        @line = nil
        @endPos = 1
        @scanner = nil
        @mrxScanner = nil
        @wrapped = false
        @macroStack = []
        @nextMacroEnd = nil
      end

Public Instance methods

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 73
      def close
        @stream = nil
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 178
      def dirname
        @fileName ? File.dirname(@fileName) : ''
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 174
      def eof?
        @stream.eof? && @scanner.eos?
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 69
      def error(id, message)
        @textScanner.error(id, message)
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 98
      def injectMacro(macro, args, text, callLength)
        # We add 3 chars more for the ${}.
        injectText(text, callLength)

        # Simple detection for recursive macro calls.
        return false if @macroStack.length > 20

        @macroStack << MacroStackEntry.new(macro, args, text, @nextMacroEnd)
        true
      end

Inject the String text into the input stream at the current cursor position.

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 79
      def injectText(text, callLength)
        if @scanner.is_a?(MRXScanner)
          @scanner.scan(@line)
          @scanner.seek(preCall.bytesize)
        else
          # Remove the macro call from the end of the already parsed input.
          preCall = @scanner.pre_match[0..-(callLength + 1)]
          # Store the end position of the inserted macro in bytes.
          @nextMacroEnd = preCall.bytesize + text.bytesize
          # Compose the new @line from the cleaned input, the injected text and
          # the remainer of the old @line.
          @line = preCall + text + @scanner.post_match
          # Start the StringScanner again at the first character of the injected
          # text.
          @scanner = StringScanner.new(@line)
          @scanner.pos = preCall.bytesize
        end
      end

Return the already processed part of the current line.

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 193
      def line
        return '' unless @line

        (@scanner.pre_match || '') + (@scanner.matched || '')
      end

Return the number of the currently processed line.

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 183
      def lineNo
        # The IO object counts the lines for us by counting the gets() calls.
        currentLine = @stream && @scanner ? @stream.lineno : 1
        # If we've just read the LF, we have to add 1. The LF can only be the
        # last character of the line.
        currentLine += 1 if @wrapped && @line && @scanner && @scanner.eos?
        currentLine
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 109
      def mrxscan(mode)
        # We read the file line by line with gets(). If we don't have a line
        # yet or we've reached the end of a line, we get the next one.
        if @scanner.nil? || @scanner.eos?
          if (@line = @stream.gets)
            # Update activity meter about every 1024 lines.
            @log.activity if (@stream.lineno & 0x3FF) == 0
          else
            # We've reached the end of the current file.
            @scanner = nil
            # Return special EOF symbol.
            return :scannerEOF
          end
          @scanner = @textScanner.mrxs
          @scanner.read(@line)
          @wrapped = @line[-1] == ?\n
        end
        return nil if (token = @scanner.scan(nil, nil, mode)).nil?
        #puts "#{re.to_s[0..20]}: [#{token}]"

        if @nextMacroEnd
          pos = @scanner.pos
          while @nextMacroEnd && @nextMacroEnd < pos
            @macroStack.pop
            @nextMacroEnd = @macroStack.empty? ? nil : @macroStack.last.endPos
          end
        end

        token
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 170
      def peek(n)
        @scanner ? @scanner.peek(n) : nil
      end

[Source]

# File lib/taskjuggler/TextParser/Scanner.rb, line 140
      def scan(re)
        # We read the file line by line with gets(). If we don't have a line
        # yet or we've reached the end of a line, we get the next one.
        if @scanner.nil? || @scanner.eos?
          if (@line = @stream.gets)
            # Update activity meter about every 1024 lines.
            @log.activity if (@stream.lineno & 0x3FF) == 0
          else
            # We've reached the end of the current file.
            @scanner = nil
            # Return special EOF symbol.
            return :scannerEOF
          end
          @scanner = StringScanner.new(@line)
          @wrapped = @line[-1] == ?\n
        end
        return nil if (token = @scanner.scan(re)).nil?
        #puts "#{re.to_s[0..20]}: [#{token}]"

        if @nextMacroEnd
          pos = @scanner.pos
          while @nextMacroEnd && @nextMacroEnd < pos
            @macroStack.pop
            @nextMacroEnd = @macroStack.empty? ? nil : @macroStack.last.endPos
          end
        end

        token
      end

[Validate]