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:       tokenPatterns = [
 23:         # Any white spaces
 24:         [ nil, /\s+/, :tjp, method('newPos') ],
 25: 
 26:         # Single line comments starting with #
 27:         [ nil, /#.*\n?/, :tjp, method('newPos') ],
 28: 
 29:         # C++ style single line comments starting with //
 30:         [ nil, /\/\/.*\n?/, :tjp, method('newPos') ],
 31: 
 32:         # C style single line comment /* .. */.
 33:         [ nil, /\/\*.*\*\//, :tjp, method('newPos') ],
 34: 
 35:         # C style multi line comment: We need three patterns here. The first
 36:         # one is for the start of the string. It switches the scanner mode to
 37:         # the :cppComment mode.
 38:         [ nil, /\/\*([^*]*[^\/]|.*)\n/, :tjp, method('startComment') ],
 39:         # This is the string end pattern. It switches back to tjp mode.
 40:         [ nil, /.*\*\//, :cppComment, method('endComment') ],
 41:         # This pattern matches string lines that contain neither the start,
 42:         # nor the end of the string.
 43:         [ nil, /^.*\n/, :cppComment ],
 44: 
 45:         # Macro Call: This case is more complicated because we want to replace
 46:         # macro calls inside of numbers, strings and identifiers. For this to
 47:         # work, macro calls may have a prefix that looks like a number, a part
 48:         # of a string or an identifier. This prefix is preserved and
 49:         # re-injected into the scanner together with the expanded text. Macro
 50:         # calls may span multiple lines. The ${ and the macro name must be in
 51:         # the first line. Arguments that span multiple lines are not
 52:         # supported. As above, we need rules for the start, the end and lines
 53:         # with neither start nor end. Macro calls inside of strings need a
 54:         # special start pattern that is active in the string modes. Both
 55:         # patterns switch the scanner to macroCall mode.
 56:         [ nil, /([-a-zA-Z_0-9>:.+]*|"(\\"|[^"])*?|'(\\'|[^'])*?)?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
 57:           :tjp, method('startMacroCall') ],
 58:         # This pattern is similar to the previous one, but is active inside of
 59:         # multi-line strings. The corresponding rule for sizzors strings
 60:         # can be found below.
 61:         [ nil, /(\\"|[^"])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
 62:           :dqString, method('startMacroCall') ],
 63:         [ nil, /(\\'|[^'])*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
 64:           :sqString, method('startMacroCall') ],
 65:         # This pattern matches the end of a macro call. It injects the prefix
 66:         # and the expanded macro into the scanner again. The mode is restored
 67:         # to the previous mode.
 68:         [ nil, /(\s*"(\\"|[^"])*")*\s*\}/, :macroCall, method('endMacroCall') ],
 69:         # This pattern collects macro call arguments in lines that contain
 70:         # neither the start nor the end of the macro.
 71:         [ nil, /.*\n/, :macroCall, method('midMacroCall') ],
 72: 
 73:         # An ID with a colon suffix: foo:
 74:         [ 'ID_WITH_COLON', /[a-zA-Z_]\w*:/, :tjp, method('chop') ],
 75: 
 76:         # An absolute ID: a.b.c
 77:         [ 'ABSOLUTE_ID', /[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)+/ ],
 78: 
 79:         # A normal ID: bar
 80:         [ 'ID', /[a-zA-Z_]\w*/ ],
 81: 
 82:         # A date
 83:         [ 'DATE', /\d{4}-\d{1,2}-\d{1,2}(-\d{1,2}:\d{1,2}(:\d{1,2})?(-[-+]?\d{4})?)?/, :tjp, method('to_date') ],
 84: 
 85:         # A time of day
 86:         [ 'TIME', /\d{1,2}:\d{2}/, :tjp, method('to_time') ],
 87: 
 88:         # A floating point number (e. g. 3.143)
 89:         [ 'FLOAT', /\d*\.\d+/, :tjp, method('to_f') ],
 90: 
 91:         # An integer number
 92:         [ 'INTEGER', /\d+/, :tjp, method('to_i') ],
 93: 
 94:         # Multi line string enclosed with double quotes. The string may
 95:         # contain double quotes prefixed by a backslash. The first rule
 96:         # switches the scanner to dqString mode.
 97:         [ 'nil', /"(\\"|[^"])*/, :tjp, method('startStringDQ') ],
 98:         # Any line not containing the start or end.
 99:         [ 'nil', /^(\\"|[^"])*\n/, :dqString, method('midStringDQ') ],
100:         # The end of the string.
101:         [ 'STRING', /(\\"|[^"])*"/, :dqString, method('endStringDQ') ],
102: 
103:         # Multi line string enclosed with single quotes.
104:         [ 'nil', /'(\\'|[^'])*/, :tjp, method('startStringSQ') ],
105:         # Any line not containing the start or end.
106:         [ 'nil', /^(\\'|[^'])*\n/, :sqString, method('midStringSQ') ],
107:         # The end of the string.
108:         [ 'STRING', /(\\'|[^'])*'/, :sqString, method('endStringSQ') ],
109: 
110:         # Scizzors marked string -8<- ... ->8-: The opening mark must be the
111:         # last thing in the line. The indentation of the first line after the
112:         # opening mark determines the indentation for all following lines. So,
113:         # we first switch the scanner to szrString1 mode.
114:         [ 'nil', /-8<-.*\n/, :tjp, method('startStringSZR') ],
115:         # Since the first line can be the last line (empty string case), we
116:         # need to detect the end in szrString1 and szrString mode. The
117:         # patterns switch the scanner back to tjp mode.
118:         [ 'STRING', /\s*->8-/, :szrString1, method('endStringSZR') ],
119:         [ 'STRING', /\s*->8-/, :szrString, method('endStringSZR') ],
120:         # This rule handles macros inside of sizzors strings.
121:         [ nil, /.*?\$\{\s*([a-zA-Z_]\w*)(\s*"(\\"|[^"])*")*/,
122:           [ :szrString, :szrString1 ], method('startMacroCall') ],
123:         # Any line not containing the start or end.
124:         [ 'nil', /.*\n/, :szrString1, method('firstStringSZR') ],
125:         [ 'nil', /.*\n/, :szrString, method('midStringSZR') ],
126: 
127:         # Single line macro definition
128:         [ 'MACRO', /\[.*\]\n/, :tjp, method('chop2nl') ],
129: 
130:         # Multi line macro definition: The pattern switches the scanner into
131:         # macroDef mode.
132:         [ nil, /\[.*\n/, :tjp, method('startMacroDef') ],
133:         # The end of the macro is marked by a ']' that is immediately followed
134:         # by a line break. It switches the scanner back to tjp mode.
135:         [ 'MACRO', /.*\]\n/, :macroDef, method('endMacroDef') ],
136:         # Any line not containing the start or end.
137:         [ nil, /.*\n/, :macroDef, method('midMacroDef') ],
138: 
139:         # Some multi-char literals.
140:         [ 'LITERAL', /<=?/ ],
141:         [ 'LITERAL', />=?/ ],
142:         [ 'LITERAL', /!=?/ ],
143: 
144:         # Everything else is returned as a single-char literal.
145:         [ 'LITERAL', /./ ]
146:       ]
147: 
148:       super(masterFile, messageHandler, tokenPatterns, :tjp)
149:     end

Private Instance Methods

chop(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 178
178:     def chop(type, match)
179:       [ type, match[0..2] ]
180:     end
chop2(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 182
182:     def chop2(type, match)
183:       # Remove first and last character.
184:       [ type, match[1..2] ]
185:     end
chop2nl(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 187
187:     def chop2nl(type, match)
188:       # remove first and last 2 characters.
189:       [ type, match[1..3] ]
190:     end
endComment(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 197
197:     def endComment(type, match)
198:       self.mode = :tjp
199:       [ nil, '' ]
200:     end
endMacroCall(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 311
311:     def endMacroCall(type, match)
312:       self.mode = @macroCallPreviousMode
313:       @macroCall += match
314: 
315:       # Store any characters that precede the ${ in prefix and remove it from
316:       # @macroCall.
317:       if (macroStart = @macroCall.index('${')) > 0
318:         prefix = @macroCall[0..(macroStart - 1)]
319:         @macroCall = @macroCall[macroStart..1]
320:       else
321:         prefix = ''
322:       end
323: 
324:       # Remove '${' and '}'
325:       argsStr = @macroCall[2..2]
326:       # Extract the macro name.
327:       if argsStr.index(' ').nil?
328:         expandMacro(prefix, [ argsStr ])
329:       else
330:         macroName = argsStr[0, argsStr.index(' ')]
331:         # Remove the name part from argsStr
332:         argsStr = argsStr[macroName.length..1]
333:         # Array to hold the arguments
334:         args = []
335:         # We use another StringScanner to clean the double quotes.
336:         scanner = StringScanner.new(argsStr)
337:         while (scanner.scan(/\s*"/))
338:           args << scanner.scan(/(\\"|[^"])*/).gsub(/\\"/, '"')
339:           scanner.scan(/"\s*/)
340:         end
341: 
342:         unless scanner.eos?
343:           raise "Junk found at end of macro: #{argsStr[scanner.pos..-1]}"
344:         end
345: 
346:         # Expand the macro and inject it into the scanner.
347:         expandMacro(prefix, [ macroName ] + args)
348:       end
349: 
350:       [ nil, '' ]
351:     end
endMacroDef(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 292
292:     def endMacroDef(type, match)
293:       self.mode = :tjp
294:       # Remove "]\n"
295:       @macroDef += match[0..3]
296:       [ 'MACRO', @macroDef ]
297:     end
endStringDQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 215
215:     def endStringDQ(type, match)
216:       self.mode = :tjp
217:       # Remove the trailing " and remove the backslashes from escaped ".
218:       @string += match[0..2].gsub(/\\"/, '"')
219:       [ 'STRING', @string ]
220:     end
endStringSQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 235
235:     def endStringSQ(type, match)
236:       self.mode = :tjp
237:       # Remove the trailing ' and remove the backslashes from escaped '.
238:       @string += match[0..2].gsub(/\\'/, "'")
239:       [ 'STRING', @string ]
240:     end
endStringSZR(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 275
275:     def endStringSZR(type, match)
276:       self.mode = :tjp
277:       [ 'STRING', @string ]
278:     end
firstStringSZR(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 257
257:     def firstStringSZR(type, match)
258:       self.mode = :szrString
259:       # Split the leading indentation and the rest of the string.
260:       @indent, @string = */(\s*)(.*\n)/.match(match)[1, 2]
261:       [ nil, '' ]
262:     end
midMacroCall(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 306
306:     def midMacroCall(type, match)
307:       @macroCall += match
308:       [ nil, '' ]
309:     end
midMacroDef(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 287
287:     def midMacroDef(type, match)
288:       @macroDef += match
289:       [ nil, '' ]
290:     end
midStringDQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 209
209:     def midStringDQ(type, match)
210:       # Remove the backslashes from escaped ".
211:       @string += match.gsub(/\\"/, '"')
212:       [ nil, '' ]
213:     end
midStringSQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 229
229:     def midStringSQ(type, match)
230:       # Remove the backslashes from escaped '.
231:       @string += match.gsub(/\\'/, "'")
232:       [ nil, '' ]
233:     end
midStringSZR(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 264
264:     def midStringSZR(type, match)
265:       # Ignore all the characters from the begining of match that are the same
266:       # in @indent.
267:       i = 0
268:       while i < @indent.length && @indent[i] == match[i]
269:         i += 1
270:       end
271:       @string += match[i..1]
272:       [ nil, '' ]
273:     end
newPos(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 173
173:     def newPos(type, match)
174:       @startOfToken = sourceFileInfo
175:       [ nil, '' ]
176:     end
startComment(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 192
192:     def startComment(type, match)
193:       self.mode = :cppComment
194:       [ nil, '' ]
195:     end
startMacroCall(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 299
299:     def startMacroCall(type, match)
300:       @macroCallPreviousMode = @scannerMode
301:       self.mode = :macroCall
302:       @macroCall = match
303:       [ nil, '' ]
304:     end
startMacroDef(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 280
280:     def startMacroDef(type, match)
281:       self.mode = :macroDef
282:       # Remove the opening '['
283:       @macroDef = match[1..1]
284:       [ nil, '' ]
285:     end
startStringDQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 202
202:     def startStringDQ(type, match)
203:       self.mode = :dqString
204:       # Remove the opening " and remove the backslashes from escaped ".
205:       @string = match[1..1].gsub(/\\"/, '"')
206:       [ nil, '' ]
207:     end
startStringSQ(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 222
222:     def startStringSQ(type, match)
223:       self.mode = :sqString
224:       # Remove the opening ' and remove the backslashes from escaped '.
225:       @string = match[1..1].gsub(/\\'/, "'")
226:       [ nil, '' ]
227:     end
startStringSZR(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 242
242:     def startStringSZR(type, match)
243:       # There should be a line break after the cut mark, but we allow some
244:       # spaces between the mark and the line break as well.
245:       if match.length != 5 && /-8<-\s*\n$/.match(match).nil?
246:         @lineDelta = 1
247:         error('junk_after_cut',
248:               'The cut mark -8<- must be immediately followed by a ' +
249:               'line break.')
250:       end
251:       self.mode = :szrString1
252:       @startOfToken = sourceFileInfo
253:       @string = ''
254:       [ nil, '' ]
255:     end
to_date(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 169
169:     def to_date(type, match)
170:       [ type, TjTime.new(match) ]
171:     end
to_f(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 157
157:     def to_f(type, match)
158:       [ type, match.to_f ]
159:     end
to_i(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 153
153:     def to_i(type, match)
154:       [ type, match.to_i ]
155:     end
to_time(type, match) click to toggle source
     # File lib/ProjectFileScanner.rb, line 161
161:     def to_time(type, match)
162:       h, m, s = match.split(':')
163:       h = h.to_i
164:       m = m.to_i
165:       s = 0 if s.nil?
166:       [ type, h * 3600 + m * 60 + s ]
167:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.