| Class | Puppet::Parser::Lexer |
| In: |
lib/puppet/parser/lexer.rb
|
| Parent: | Object |
| TOKENS | = | TokenList.new |
| KEYWORDS | = | TokenList.new |
| file | [R] | |
| indefine | [RW] | |
| last | [R] | |
| line | [RW] |
# File lib/puppet/parser/lexer.rb, line 299
299: def initialize
300: @find = 0
301: @regex = 0
302: initvars()
303: end
# File lib/puppet/parser/lexer.rb, line 205
205: def expected
206: return nil if @expected.empty?
207: name = @expected[-1]
208: raise "Could not find expected token %s" % name unless token = TOKENS.lookup(name)
209:
210: return token
211: end
this is probably pretty damned inefficient… it‘d be nice not to have to load the whole file first…
# File lib/puppet/parser/lexer.rb, line 228
228: def file=(file)
229: @file = file
230: @line = 1
231: File.open(file) { |of|
232: str = ""
233: of.each { |line| str += line }
234: @scanner = StringScanner.new(str)
235: }
236: end
Find the next token that matches a regex. We look for these first.
# File lib/puppet/parser/lexer.rb, line 257
257: def find_regex_token
258: @regex += 1
259: matched_token = nil
260: value = ""
261: length = 0
262:
263: # I tried optimizing based on the first char, but it had
264: # a slightly negative affect and was a good bit more complicated.
265: TOKENS.regex_tokens.each do |token|
266: next unless match_length = @scanner.match?(token.regex)
267:
268: # We've found a longer match
269: if match_length > length
270: value = @scanner.scan(token.regex)
271: length = value.length
272: matched_token = token
273: end
274: end
275:
276: return matched_token, value
277: end
# File lib/puppet/parser/lexer.rb, line 238
238: def find_string_token
239: matched_token = value = nil
240:
241: # We know our longest string token is three chars, so try each size in turn
242: # until we either match or run out of chars. This way our worst-case is three
243: # tries, where it is otherwise the number of string chars we have. Also,
244: # the lookups are optimized hash lookups, instead of regex scans.
245: [3, 2, 1].each do |i|
246: str = @scanner.peek(i)
247: if matched_token = TOKENS.lookup(str)
248: value = @scanner.scan(matched_token.regex)
249: break
250: end
251: end
252:
253: return matched_token, value
254: end
Find the next token, returning the string and the token.
# File lib/puppet/parser/lexer.rb, line 280
280: def find_token
281: @find += 1
282: matched_token, value = find_regex_token
283:
284: unless matched_token
285: matched_token, value = find_string_token
286: end
287:
288: return matched_token, value
289: end
scan the whole file basically just used for testing
# File lib/puppet/parser/lexer.rb, line 215
215: def fullscan
216: array = []
217:
218: self.scan { |token, str|
219: # Ignore any definition nesting problems
220: @indefine = false
221: array.push([token,str])
222: }
223: return array
224: end
# File lib/puppet/parser/lexer.rb, line 291
291: def indefine?
292: if defined? @indefine
293: @indefine
294: else
295: false
296: end
297: end
# File lib/puppet/parser/lexer.rb, line 305
305: def initvars
306: @line = 1
307: @previous_token = nil
308: @scanner = nil
309: @file = nil
310: # AAARRGGGG! okay, regexes in ruby are bloody annoying
311: # no one else has "\n" =~ /\s/
312: @skip = %r{[ \t]+}
313:
314: @namestack = []
315: @indefine = false
316: @expected = []
317: end
Make any necessary changes to the token and/or value.
# File lib/puppet/parser/lexer.rb, line 320
320: def munge_token(token, value)
321: @line += 1 if token.incr_line
322:
323: skip() if token.skip_text
324:
325: return if token.skip
326:
327: token, value = token.convert(self, value) if token.respond_to?(:convert)
328:
329: return unless token
330:
331: return token, value
332: end
this is the heart of the lexer
# File lib/puppet/parser/lexer.rb, line 356
356: def scan
357: #Puppet.debug("entering scan")
358: raise Puppet::LexError.new("Invalid or empty string") unless @scanner
359:
360: # Skip any initial whitespace.
361: skip()
362:
363: until @scanner.eos? do
364: yielded = false
365: matched_token, value = find_token
366:
367: # error out if we didn't match anything at all
368: if matched_token.nil?
369: nword = nil
370: # Try to pull a 'word' out of the remaining string.
371: if @scanner.rest =~ /^(\S+)/
372: nword = $1
373: elsif @scanner.rest =~ /^(\s+)/
374: nword = $1
375: else
376: nword = @scanner.rest
377: end
378: raise "Could not match '%s'" % nword
379: end
380:
381: final_token, value = munge_token(matched_token, value)
382:
383: next unless final_token
384:
385: if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE
386: @expected << match
387: elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE
388: @expected.pop
389: end
390:
391: yield [final_token.name, value]
392:
393: if @previous_token
394: namestack(value) if @previous_token.name == :CLASS
395:
396: if @previous_token.name == :DEFINE
397: if indefine?
398: msg = "Cannot nest definition %s inside %s" % [value, @indefine]
399: self.indefine = false
400: raise Puppet::ParseError, msg
401: end
402:
403: @indefine = value
404: end
405: end
406:
407: @previous_token = final_token
408: skip()
409: end
410: @scanner = nil
411:
412: # This indicates that we're done parsing.
413: yield [false,false]
414: end
Skip any skipchars in our remaining string.
# File lib/puppet/parser/lexer.rb, line 417
417: def skip
418: @scanner.skip(@skip)
419: end
we‘ve encountered an opening quote… slurp in the rest of the string and return it
# File lib/puppet/parser/lexer.rb, line 423
423: def slurpstring(quote)
424: # we search for the next quote that isn't preceded by a
425: # backslash; the caret is there to match empty strings
426: str = @scanner.scan_until(/([^\\]|^)#{quote}/)
427: if str.nil?
428: raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" %
429: [self.last,self.rest])
430: else
431: str.sub!(/#{quote}\Z/,"")
432: str.gsub!(/\\#{quote}/,quote)
433: end
434:
435: return str
436: end