class TaskJuggler::ProjectFileScanner

This class specializes the TextParser::Scanner class to detect the tokens of the TJP syntax.

Public Class Methods

new(masterFile) click to toggle source
# File lib/taskjuggler/ProjectFileScanner.rb, line 20
    def initialize(masterFile)
      tokenPatterns = [
        # Any white spaces
        [ nil, '\s+', %r\s+/, :tjp, method('newPos') ],

        # Single line comments starting with #
        [ nil, '#.*\n?', %r#.*\n?/, :tjp, method('newPos') ],

        # C++ style single line comments starting with //
        [ nil, '//.*\n?', %r\/\/.*\n?/, :tjp, method('newPos') ],

        # C style single line comment /* .. */.
        [ nil, '/\*.*\*/', %r\/\*.*\*\//, :tjp, method('newPos') ],

        # C style multi line comment: We need three patterns here. The first
        # one is for the start of the string. It switches the scanner mode to
        # the :cppComment mode.
        [ nil, '/\*([^*]*[^/]|.*)\n', %r\/\*([^*]*[^\/]|.*)\n/, :tjp, method('startComment') ],
        # This is the string end pattern. It switches back to tjp mode.
        [ nil, '.*\*/', %r.*\*\//, :cppComment, method('endComment') ],
        # This pattern matches string lines that contain neither the start,
        # nor the end of the string.
        [ nil, '^.*\n', %r^.*\n/, :cppComment ],

        # Macro Call: This case is more complicated because we want to replace
        # macro calls inside of numbers, strings and identifiers. For this to
        # work, macro calls may have a prefix that looks like a number, a part
        # of a string or an identifier. This prefix is preserved and
        # re-injected into the scanner together with the expanded text. Macro
        # calls may span multiple lines. The ${ and the macro name must be in
        # the first line. Arguments that span multiple lines are not
        # supported. As above, we need rules for the start, the end and lines
        # with neither start nor end. Macro calls inside of strings need a
        # special start pattern that is active in the string modes. Both
        # patterns switch the scanner to macroCall mode.
        [ nil, '([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*?|\(\\\|[^\])*?)?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*', %r([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*?|'(\\'|[^'])*?)?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
          :tjp, method('startMacroCall') ],
        # This pattern is similar to the previous one, but is active inside of
        # multi-line strings. The corresponding rule for sizzors strings
        # can be found below.
        [ nil, '(\\"|[^"])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*',
%r(\\"|[^"])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
          :dqString, method('startMacroCall') ],
        [ nil, '(\\\|[^\])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*',
%r(\\'|[^'])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
          :sqString, method('startMacroCall') ],
        # This pattern matches the end of a macro call. It injects the prefix
        # and the expanded macro into the scanner again. The mode is restored
        # to the previous mode.
        [ nil, '(\s*"(\\"|[^"])*")*\s*\}', %r(\s*"(\\"|[^"])*")*\s*\}/, :macroCall, method('endMacroCall') ],
        # This pattern collects macro call arguments in lines that contain
        # neither the start nor the end of the macro.
        [ nil, '.*\n', %r.*\n/, :macroCall, method('midMacroCall') ],

        # Environment variable reference. This is similar to the macro call,
        # but the it can only extend within the starting line.
        [ nil, '([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*?|\(\\\|[^\])*?)?\$\([A-Z_][A-Z_0-9]*\)', %r([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*?|'(\\'|[^'])*?)?\$\([A-Z_][A-Z_0-9]*\)/,
          :tjp, method('environmentVariable') ],
        # An ID with a colon suffix: foo:
        [ :ID_WITH_COLON, '[a-zA-Z_]\w*:', %r[a-zA-Z_]\w*:/, :tjp, method('chop') ],

        # An absolute ID: a.b.c
        [ :ABSOLUTE_ID, '[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)+', %r[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)+/ ],

        # A normal ID: bar
        [ :ID, '[a-zA-Z_]\w*', %r[a-zA-Z_]\w*/ ],

        # A date
        [ :DATE, '\d{4}-\d{1,2}-\d{1,2}(-\d{1,2}:\d{1,2}(:\d{1,2})?(-[-+]?\d{4})?)?', %r\d{4}-\d{1,2}-\d{1,2}(-\d{1,2}:\d{1,2}(:\d{1,2})?(-[-+]?\d{4})?)?/, :tjp, method('to_date') ],

        # A time of day
        [ :TIME, '\d{1,2}:\d{2}', %r\d{1,2}:\d{2}/, :tjp, method('to_time') ],

        # A floating point number (e. g. 3.143)
        [ :FLOAT, '\d*\.\d+', %r\d*\.\d+/, :tjp, method('to_f') ],

        # An integer number
        [ :INTEGER, '\d+', %r\d+/, :tjp, method('to_i') ],

        # Multi line string enclosed with double quotes. The string may
        # contain double quotes prefixed by a backslash. The first rule
        # switches the scanner to dqString mode.
        [ 'nil', '"(\\"|[^"])*', %r"(\\"|[^"])*/, :tjp, method('startStringDQ') ],
        # Any line not containing the start or end.
        [ 'nil', '^(\\"|[^"])*\n', %r^(\\"|[^"])*\n/, :dqString, method('midStringDQ') ],
        # The end of the string.
        [ :STRING, '(\\"|[^"])*"', %r(\\"|[^"])*"/, :dqString, method('endStringDQ') ],

        # Multi line string enclosed with single quotes.
        [ 'nil', '\(\\\|[^\])*', %r'(\\'|[^'])*/, :tjp, method('startStringSQ') ],
        # Any line not containing the start or end.
        [ 'nil', '^(\\\|[^\])*\n', %r^(\\'|[^'])*\n/, :sqString, method('midStringSQ') ],
        # The end of the string.
        [ :STRING, '(\\\|[^\])*\', %r(\\'|[^'])*'/, :sqString, method('endStringSQ') ],

        # Scizzors marked string -8<- ... ->8-: The opening mark must be the
        # last thing in the line. The indentation of the first line after the
        # opening mark determines the indentation for all following lines. So,
        # we first switch the scanner to szrString1 mode.
        [ 'nil', '-8<-.*\n', %r-8<-.*\n/, :tjp, method('startStringSZR') ],
        # Since the first line can be the last line (empty string case), we
        # need to detect the end in szrString1 and szrString mode. The
        # patterns switch the scanner back to tjp mode.
        [ :STRING, '\s*->8-', %r\s*->8-/, :szrString1, method('endStringSZR') ],
        [ :STRING, '\s*->8-', %r\s*->8-/, :szrString, method('endStringSZR') ],
        # This rule handles macros inside of sizzors strings.
        [ nil, '.*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*', %r.*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
          [ :szrString, :szrString1 ], method('startMacroCall') ],
        # Any line not containing the start or end.
        [ 'nil', '.*\n', %r.*\n/, :szrString1, method('firstStringSZR') ],
        [ 'nil', '.*\n', %r.*\n/, :szrString, method('midStringSZR') ],

        # Single line macro definition
        [ :MACRO, '\[.*\](\n|$)', %r\[.*\]\n/, :tjp, method('chop2nl') ],

        # Multi line macro definition: The pattern switches the scanner into
        # macroDef mode.
        [ nil, '\[.*\n', %r\[.*\n/, :tjp, method('startMacroDef') ],
        # The end of the macro is marked by a ']' that is immediately followed
        # by a line break. It switches the scanner back to tjp mode.
        [ :MACRO, '.*\](\n|$)', %r.*\]\n/, :macroDef, method('endMacroDef') ],
        # Any line not containing the start or end.
        [ nil, '.*\n', %r.*\n/, :macroDef, method('midMacroDef') ],

        # Some multi-char literals.
        [ :LITERAL, '<=?', %r<=?/ ],
        [ :LITERAL, '>=?', %r>=?/ ],
        [ :LITERAL, '!=?', %r!=?/ ],

        # Everything else is returned as a single-char literal.
        [ :LITERAL, '.', %r./ ]
      ]

      super(masterFile, Log, tokenPatterns, :tjp)
    end