Class Puppet::Parser::Lexer
In: lib/puppet/parser/lexer.rb
Parent: Object

Methods

Classes and Modules

Class Puppet::Parser::Lexer::Token
Class Puppet::Parser::Lexer::TokenList

Constants

TOKENS = TokenList.new
KEYWORDS = TokenList.new

Attributes

file  [R] 
indefine  [RW] 
last  [R] 
line  [RW] 

Public Class methods

[Source]

     # File lib/puppet/parser/lexer.rb, line 299
299:     def initialize
300:         @find = 0
301:         @regex = 0
302:         initvars()
303:     end

Public Instance methods

[Source]

     # File lib/puppet/parser/lexer.rb, line 201
201:     def clear
202:         initvars
203:     end

[Source]

     # 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…

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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

[Source]

     # 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.

[Source]

     # 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

Go up one in the namespace.

[Source]

     # File lib/puppet/parser/lexer.rb, line 335
335:     def namepop
336:         @namestack.pop
337:     end

Collect the current namespace.

[Source]

     # File lib/puppet/parser/lexer.rb, line 340
340:     def namespace
341:         @namestack.join("::")
342:     end
This value might have :in it, but we don‘t care — it‘ll be

handled normally when joining, and when popping we want to pop this full value, however long the namespace is.

[Source]

     # File lib/puppet/parser/lexer.rb, line 347
347:     def namestack(value)
348:         @namestack << value
349:     end

[Source]

     # File lib/puppet/parser/lexer.rb, line 351
351:     def rest
352:         @scanner.rest
353:     end

this is the heart of the lexer

[Source]

     # 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.

[Source]

     # 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

[Source]

     # 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

just parse a string, not a whole file

[Source]

     # File lib/puppet/parser/lexer.rb, line 439
439:     def string=(string)
440:         @scanner = StringScanner.new(string)
441:     end

[Validate]