Class Index [+]

Quicksearch

TaskJuggler::ProjectFileScanner

This class specializes the TextScanner class to detect the tokens of the TJP syntax.

Public Class Methods

new(masterFile, messageHandler) click to toggle source
     # 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

Private Instance Methods

chop(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 187
187:     def chop(type, match)
188:       [ type, match[0..2] ]
189:     end
chop2(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 191
191:     def chop2(type, match)
192:       # Remove first and last character.
193:       [ type, match[1..2] ]
194:     end
chop2nl(type, match) click to toggle source
     # 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
endComment(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 206
206:     def endComment(type, match)
207:       self.mode = :tjp
208:       [ nil, '' ]
209:     end
endMacroCall(type, match) click to toggle source
     # 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
endMacroDef(type, match) click to toggle source
     # 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
endStringDQ(type, match) click to toggle source
     # 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
endStringSQ(type, match) click to toggle source
     # 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
endStringSZR(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 284
284:     def endStringSZR(type, match)
285:       self.mode = :tjp
286:       [ 'STRING', @string ]
287:     end
firstStringSZR(type, match) click to toggle source
     # 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
midMacroCall(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 315
315:     def midMacroCall(type, match)
316:       @macroCall += match
317:       [ nil, '' ]
318:     end
midMacroDef(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 296
296:     def midMacroDef(type, match)
297:       @macroDef += match
298:       [ nil, '' ]
299:     end
midStringDQ(type, match) click to toggle source
     # 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
midStringSQ(type, match) click to toggle source
     # 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
midStringSZR(type, match) click to toggle source
     # 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
newPos(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 182
182:     def newPos(type, match)
183:       @startOfToken = sourceFileInfo
184:       [ nil, '' ]
185:     end
startComment(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 201
201:     def startComment(type, match)
202:       self.mode = :cppComment
203:       [ nil, '' ]
204:     end
startMacroCall(type, match) click to toggle source
     # 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
startMacroDef(type, match) click to toggle source
     # 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
startStringDQ(type, match) click to toggle source
     # 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
startStringSQ(type, match) click to toggle source
     # 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
startStringSZR(type, match) click to toggle source
     # 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
to_date(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 178
178:     def to_date(type, match)
179:       [ type, TjTime.new(match) ]
180:     end
to_f(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 166
166:     def to_f(type, match)
167:       [ type, match.to_f ]
168:     end
to_i(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 162
162:     def to_i(type, match)
163:       [ type, match.to_i ]
164:     end
to_time(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 170
170:     def to_time(type, match)
171:       h, m, s = match.split(':')
172:       h = h.to_i
173:       m = m.to_i
174:       s = 0 if s.nil?
175:       [ type, h * 3600 + m * 60 + s ]
176:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.