This class specializes the TextScanner class to detect the tokens of the TJP syntax.
# File lib/ProjectFileScanner.rb, line 21 21: def initialize(masterFile, messageHandler) 22: super 23: 24: tokenPatterns = [ 25: # Any white spaces 26: [ nil, /\s+/, :tjp, method('newPos') ], 27: 28: # Single line comments starting with # 29: [ nil, /#.*\n?/, :tjp, method('newPos') ], 30: 31: # C++ style single line comments starting with // 32: [ nil, /\/\/.*\n?/, :tjp, method('newPos') ], 33: 34: # C style single line comment /* .. */. 35: [ nil, /\/\*.*\*\//, :tjp, method('newPos') ], 36: 37: # C style multi line comment: We need three patterns here. The first 38: # one is for the start of the string. It switches the scanner mode to 39: # the :cppComment mode. 40: [ nil, /\/\*([^*]*[^\/]|.*)\n/, :tjp, method('startComment') ], 41: # This is the string end pattern. It switches back to tjp mode. 42: [ nil, /.*\*\//, :cppComment, method('endComment') ], 43: # This pattern matches string lines that contain neither the start, 44: # nor the end of the string. 45: [ nil, /^.*\n/, :cppComment ], 46: 47: # Macro Call: This case is more complicated because we want to replace 48: # macro calls inside of numbers, strings and identifiers. For this to 49: # work, macro calls may have a prefix that looks like a number, a part 50: # of a string or an identifier. This prefix is preserved and 51: # re-injected into the scanner together with the expanded text. Macro 52: # calls may span multiple lines. The ${ and the macro name must be in 53: # the first line. Arguments that span multiple lines are not 54: # supported. As above, we need rules for the start, the end and lines 55: # with neither start nor end. Macro calls inside of strings need a 56: # special start pattern that is active in the string modes. Both 57: # patterns switch the scanner to macroCall mode. 58: [ nil, /([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*|'(\\'|[^'])*|-8<-.*)?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/, 59: :tjp, method('startMacroCall') ], 60: # This pattern is similar to the previous one, but is active inside of 61: # multi-line strings. The corresponding rule for sizzors strings 62: # can be found below. 63: [ nil, /(\\"|[^"])*\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/, 64: :dqString, method('startMacroCall') ], 65: [ nil, /(\\'|[^'])*\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/, 66: :sqString, method('startMacroCall') ], 67: # This pattern matches the end of a macro call. It injects the prefix 68: # and the expanded macro into the scanner again. The mode is restored 69: # to the previous mode. 70: [ nil, /(\s*"(\\"|[^"])*")*\s*\}/, :macroCall, method('endMacroCall') ], 71: # This pattern collects macro call arguments in lines that contain 72: # neither the start nor the end of the macro. 73: [ nil, /.*\n/, :macroCall, method('midMacroCall') ], 74: 75: # An ID with a colon suffix: foo: 76: [ 'ID_WITH_COLON', /[a-zA-Z_]\w*:/, :tjp, method('chop') ], 77: 78: # An absolute ID: a.b.c 79: [ 'ABSOLUTE_ID', /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)+/ ], 80: 81: # A normal ID: bar 82: [ 'ID', /[a-zA-Z_]\w*/ ], 83: 84: # A date 85: [ 'DATE', /\d{4}-\d{1,2}-\d{1,2}(-\d{1,2}:\d{1,2}(:\d{1,2})?(-[-+]?\d{4})?)?/, :tjp, method('to_date') ], 86: 87: # A time of day 88: [ 'TIME', /\d{1,2}:\d{2}/, :tjp, method('to_time') ], 89: 90: # A floating point number (e. g. 3.143) 91: [ 'FLOAT', /\d*\.\d+/, :tjp, method('to_f') ], 92: 93: # An integer number 94: [ 'INTEGER', /\d+/, :tjp, method('to_i') ], 95: 96: # Multi line string enclosed with double quotes. The string may 97: # contain double quotes prefixed by a backslash. The first rule 98: # switches the scanner to dqString mode. 99: [ 'nil', /"(\\"|[^"])*/, :tjp, method('startStringDQ') ], 100: # Any line not containing the start or end. 101: [ 'nil', /^(\\"|[^"])*\n/, :dqString, method('midStringDQ') ], 102: # The end of the string. 103: [ 'STRING', /(\\"|[^"])*"/, :dqString, method('endStringDQ') ], 104: 105: # Multi line string enclosed with single quotes. 106: [ 'nil', /'(\\'|[^'])*/, :tjp, method('startStringSQ') ], 107: # Any line not containing the start or end. 108: [ 'nil', /^(\\'|[^'])*\n/, :sqString, method('midStringSQ') ], 109: # The end of the string. 110: [ 'STRING', /(\\'|[^'])*'/, :sqString, method('endStringSQ') ], 111: 112: # Scizzors marked string -8<- ... ->8-: The opening mark must be the 113: # last thing in the line. The indentation of the first line after the 114: # opening mark determines the indentation for all following lines. So, 115: # we first switch the scanner to szrString1 mode. 116: [ 'nil', /-8<-.*\n/, :tjp, method('startStringSZR') ], 117: # Since the first line can be the last line (empty string case), we 118: # need to detect the end in szrString1 and szrString mode. The 119: # patterns switch the scanner back to tjp mode. 120: [ 'STRING', /\s*->8-/, :szrString1, method('endStringSZR') ], 121: [ 'STRING', /\s*->8-/, :szrString, method('endStringSZR') ], 122: # This rule handles macros inside of sizzors strings. 123: [ nil, /.*\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/, 124: [ :szrString, :szrString1 ], method('startMacroCall') ], 125: # Any line not containing the start or end. 126: [ 'nil', /.*\n/, :szrString1, method('firstStringSZR') ], 127: [ 'nil', /.*\n/, :szrString, method('midStringSZR') ], 128: 129: # Single line macro definition 130: [ 'MACRO', /\[.*\]\n/, :tjp, method('chop2nl') ], 131: 132: # Multi line macro definition: The pattern switches the scanner into 133: # macroDef mode. 134: [ nil, /\[.*\n/, :tjp, method('startMacroDef') ], 135: # The end of the macro is marked by a ']' that is immediately followed 136: # by a line break. It switches the scanner back to tjp mode. 137: [ 'MACRO', /.*\]\n/, :macroDef, method('endMacroDef') ], 138: # Any line not containing the start or end. 139: [ nil, /.*\n/, :macroDef, method('midMacroDef') ], 140: 141: # Some multi-char literals. 142: [ 'LITERAL', /<=?/ ], 143: [ 'LITERAL', />=?/ ], 144: [ 'LITERAL', /!=?/ ], 145: 146: # Everything else is returned as a single-char literal. 147: [ 'LITERAL', /./ ] 148: ] 149: 150: tokenPatterns.each do |pat| 151: type = pat[0] 152: regExp = pat[1] 153: mode = pat[2] || :tjp 154: postProc = pat[3] 155: addPattern(type, regExp, mode, postProc) 156: end 157: self.mode = :tjp 158: end
# File lib/ProjectFileScanner.rb, line 187 187: def chop(type, match) 188: [ type, match[0..2] ] 189: end
# File lib/ProjectFileScanner.rb, line 191 191: def chop2(type, match) 192: # Remove first and last character. 193: [ type, match[1..2] ] 194: end
# File lib/ProjectFileScanner.rb, line 196 196: def chop2nl(type, match) 197: # remove first and last 2 characters. 198: [ type, match[1..3] ] 199: end
# File lib/ProjectFileScanner.rb, line 206 206: def endComment(type, match) 207: self.mode = :tjp 208: [ nil, '' ] 209: end
# File lib/ProjectFileScanner.rb, line 320 320: def endMacroCall(type, match) 321: self.mode = @macroCallPreviousMode 322: @macroCall += match 323: 324: # Store any characters that precede the ${ in prefix and remove it from 325: # @macroCall. 326: if (macroStart = @macroCall.index('${')) > 0 327: prefix = @macroCall[0..(macroStart - 1)] 328: @macroCall = @macroCall[macroStart..1] 329: else 330: prefix = '' 331: end 332: 333: # Remove '${' and '}' 334: argsStr = @macroCall[2..2] 335: # Extract the macro name. 336: if (nameEnd = argsStr.index(' ')).nil? 337: expandMacro(prefix, [ argsStr ]) 338: else 339: macroName = argsStr[0, argsStr.index(' ')] 340: # Remove the name part from argsStr 341: argsStr = argsStr[macroName.length..1] 342: # Array to hold the arguments 343: args = [] 344: # We use another StringScanner to clean the double quotes. 345: scanner = StringScanner.new(argsStr) 346: while (scanner.scan(/\s*"/)) 347: args << scanner.scan(/(\\"|[^"])*/).gsub(/\\"/, '"') 348: scanner.scan(/"\s*/) 349: end 350: 351: unless scanner.eos? 352: raise "Junk found at end of macro: #{argsStr[scanner.pos..-1]}" 353: end 354: 355: # Expand the macro and inject it into the scanner. 356: expandMacro(prefix, [ macroName ] + args) 357: end 358: 359: [ nil, '' ] 360: end
# File lib/ProjectFileScanner.rb, line 301 301: def endMacroDef(type, match) 302: self.mode = :tjp 303: # Remove "]\n" 304: @macroDef += match[0..3] 305: [ 'MACRO', @macroDef ] 306: end
# File lib/ProjectFileScanner.rb, line 224 224: def endStringDQ(type, match) 225: self.mode = :tjp 226: # Remove the trailing " and remove the backslashes from escaped ". 227: @string += match[0..2].gsub(/\\"/, '"') 228: [ 'STRING', @string ] 229: end
# File lib/ProjectFileScanner.rb, line 244 244: def endStringSQ(type, match) 245: self.mode = :tjp 246: # Remove the trailing ' and remove the backslashes from escaped '. 247: @string += match[0..2].gsub(/\\'/, "'") 248: [ 'STRING', @string ] 249: end
# File lib/ProjectFileScanner.rb, line 284 284: def endStringSZR(type, match) 285: self.mode = :tjp 286: [ 'STRING', @string ] 287: end
# File lib/ProjectFileScanner.rb, line 266 266: def firstStringSZR(type, match) 267: self.mode = :szrString 268: # Split the leading indentation and the rest of the string. 269: foo, @indent, @string = */(\s*)(.*\n)/.match(match) 270: [ nil, '' ] 271: end
# File lib/ProjectFileScanner.rb, line 315 315: def midMacroCall(type, match) 316: @macroCall += match 317: [ nil, '' ] 318: end
# File lib/ProjectFileScanner.rb, line 296 296: def midMacroDef(type, match) 297: @macroDef += match 298: [ nil, '' ] 299: end
# File lib/ProjectFileScanner.rb, line 218 218: def midStringDQ(type, match) 219: # Remove the backslashes from escaped ". 220: @string += match.gsub(/\\"/, '"') 221: [ nil, '' ] 222: end
# File lib/ProjectFileScanner.rb, line 238 238: def midStringSQ(type, match) 239: # Remove the backslashes from escaped '. 240: @string += match.gsub(/\\'/, "'") 241: [ nil, '' ] 242: end
# File lib/ProjectFileScanner.rb, line 273 273: def midStringSZR(type, match) 274: # Ignore all the characters from the begining of match that are the same 275: # in @indent. 276: i = 0 277: while i < @indent.length && @indent[i] == match[i] 278: i += 1 279: end 280: @string += match[i..1] 281: [ nil, '' ] 282: end
# File lib/ProjectFileScanner.rb, line 182 182: def newPos(type, match) 183: @startOfToken = sourceFileInfo 184: [ nil, '' ] 185: end
# File lib/ProjectFileScanner.rb, line 201 201: def startComment(type, match) 202: self.mode = :cppComment 203: [ nil, '' ] 204: end
# File lib/ProjectFileScanner.rb, line 308 308: def startMacroCall(type, match) 309: @macroCallPreviousMode = @scannerMode 310: self.mode = :macroCall 311: @macroCall = match 312: [ nil, '' ] 313: end
# File lib/ProjectFileScanner.rb, line 289 289: def startMacroDef(type, match) 290: self.mode = :macroDef 291: # Remove the opening '[' 292: @macroDef = match[1..1] 293: [ nil, '' ] 294: end
# File lib/ProjectFileScanner.rb, line 211 211: def startStringDQ(type, match) 212: self.mode = :dqString 213: # Remove the opening " and remove the backslashes from escaped ". 214: @string = match[1..1].gsub(/\\"/, '"') 215: [ nil, '' ] 216: end
# File lib/ProjectFileScanner.rb, line 231 231: def startStringSQ(type, match) 232: self.mode = :sqString 233: # Remove the opening ' and remove the backslashes from escaped '. 234: @string = match[1..1].gsub(/\\'/, "'") 235: [ nil, '' ] 236: end
# File lib/ProjectFileScanner.rb, line 251 251: def startStringSZR(type, match) 252: # There should be a line break after the cut mark, but we allow some 253: # spaces between the mark and the line break as well. 254: if match.length != 5 && /-8<-\s*\n$/.match(match).nil? 255: @lineDelta = 1 256: error('junk_after_cut', 257: 'The cut mark -8<- must be immediately followed by a ' + 258: 'line break.') 259: end 260: self.mode = :szrString1 261: @startOfToken = sourceFileInfo 262: @string = '' 263: [ nil, '' ] 264: end
# File lib/ProjectFileScanner.rb, line 178 178: def to_date(type, match) 179: [ type, TjTime.new(match) ] 180: end
# File lib/ProjectFileScanner.rb, line 166 166: def to_f(type, match) 167: [ type, match.to_f ] 168: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.