# File lib/puppet/parser/parser_support.rb, line 115
115:     def find_or_load(namespace, name, type)
116:         method = "find_#{type}"
117:         namespace = namespace.downcase
118:         name      = name.downcase
119:         fullname = (namespace + "::" + name).sub(/^::/, '')
120: 
121:         if name =~ /^::/
122:             names_to_try = [name.sub(/^::/, '')]
123:         else
124:             names_to_try = [fullname]
125: 
126:             # Try to load the module init file if we're a qualified name
127:             names_to_try << fullname.split("::")[0] if fullname.include?("::")
128: 
129:             # Otherwise try to load the bare name on its own.  This
130:             # is appropriate if the class we're looking for is in a
131:             # module that's different from our namespace.
132:             names_to_try << name
133:             names_to_try.compact!
134:         end
135: 
136:         until (result = @loaded_code.send(method, namespace, name)) or names_to_try.empty? do
137:             self.load(names_to_try.shift)
138:         end
139:         return result
140:     end
141: 
142:     # Import our files.
143:     def import(file)
144:         if Puppet[:ignoreimport]
145:             return AST::ASTArray.new(:children => [])
146:         end
147:         # use a path relative to the file doing the importing
148:         if @lexer.file
149:             dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '')
150:         else
151:             dir = "."
152:         end
153:         if dir == ""
154:             dir = "."
155:         end
156:         result = ast AST::ASTArray
157: 
158:         # We can't interpolate at this point since we don't have any
159:         # scopes set up. Warn the user if they use a variable reference
160:         raise "Got no file" unless file
161:         pat = file
162:         if pat.index("$")
163:             Puppet.warning(
164:                "The import of #{pat} contains a variable reference;" +
165:                " variables are not interpolated for imports " +
166:                "in file #{@lexer.file} at line #{@lexer.line}"
167:             )
168:         end
169:         files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => @environment)
170:         if files.size == 0
171:             raise Puppet::ImportError.new("No file(s) found for import " +
172:                                           "of '#{pat}'")
173:         end
174: 
175:         files.collect { |file|
176:             parser = Puppet::Parser::Parser.new(:loaded_code => @loaded_code, :environment => @environment)
177:             parser.files = self.files
178:             Puppet.debug("importing '%s'" % file)
179: 
180:             unless file =~ /^#{File::SEPARATOR}/
181:                 file = File.join(dir, file)
182:             end
183:             begin
184:                 parser.file = file
185:             rescue Puppet::AlreadyImportedError
186:                 # This file has already been imported to just move on
187:                 next
188:             end
189: 
190:             # This will normally add code to the 'main' class.
191:             parser.parse
192:         }
193:     end
194: 
195:     def initialize(options = {})
196:         @loaded_code = options[:loaded_code] || Puppet::Parser::LoadedCode.new
197:         @environment = options[:environment]
198:         initvars()
199:     end
200: 
201:     # Initialize or reset all of our variables.
202:     def initvars
203:         @lexer = Puppet::Parser::Lexer.new()
204:         @files = {}
205:         @loaded = []
206:         @loading = {}
207:         @loading.extend(MonitorMixin)
208:         class << @loading
209:             def done_with(item)
210:                 synchronize do 
211:                     delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current
212:                 end
213:             end
214:             def owner_of(item)
215:                 synchronize do
216:                     if !self.has_key? item
217:                         self[item] = { :loader => Thread.current, :busy => self.new_cond}
218:                         :nobody
219:                       elsif self[item][:loader] == Thread.current
220:                         :this_thread
221:                       else
222:                         flag = self[item][:busy]
223:                         flag.wait
224:                         flag.signal
225:                         :another_thread
226:                     end
227:                 end
228:             end
229:         end
230:     end
231: 
232:     # Utility method factored out of load
233:     def able_to_import?(classname,item,msg)
234:         unless @loaded.include?(item)
235:             begin
236:               case @loading.owner_of(item)
237:               when :this_thread
238:                   return
239:               when :another_thread
240:                   return able_to_import?(classname,item,msg)
241:               when :nobody
242:                   import(item)
243:                   Puppet.info "Autoloaded #{msg}"
244:                   @loaded << item
245:               end
246:             rescue Puppet::ImportError => detail
247:                 # We couldn't load the item
248:             ensure
249:                 @loading.done_with(item)
250:             end
251:         end
252:         # We don't know whether we're looking for a class or definition, so we have
253:         # to test for both.
254:         return @loaded_code.hostclass(classname) || @loaded_code.definition(classname)
255:     end
256: 
257:     # Try to load a class, since we could not find it.
258:     def load(classname)
259:         return false if classname == ""
260:         filename = classname.gsub("::", File::SEPARATOR)
261:         mod = filename.scan(/^[\w-]+/).shift
262: 
263:         # First try to load the top-level module then the individual file
264:         [[mod,     "module %s"              %            mod ],
265:          [filename,"file %s from module %s" % [filename, mod]]
266:         ].any? { |item,description| able_to_import?(classname,item,description) }
267:     end
268: 
269:     # Split an fq name into a namespace and name
270:     def namesplit(fullname)
271:         ary = fullname.split("::")
272:         n = ary.pop || ""
273:         ns = ary.join("::")
274:         return ns, n
275:     end
276: 
277:     # Create a new class, or merge with an existing class.
278:     def newclass(name, options = {})
279:         name = name.downcase
280: 
281:         if @loaded_code.definition(name)
282:             raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
283:         end
284:         code = options[:code]
285:         parent = options[:parent]
286:         doc = options[:doc]
287: 
288:         # If the class is already defined, then add code to it.
289:         if other = @loaded_code.hostclass(name) || @loaded_code.definition(name)
290:             # Make sure the parents match
291:             if parent and other.parentclass and (parent != other.parentclass)
292:                 error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
293:             end
294: 
295:             # This might be dangerous...
296:             if parent and ! other.parentclass
297:                 other.parentclass = parent
298:             end
299: 
300:             # This might just be an empty, stub class.
301:             if code
302:                 tmp = name
303:                 if tmp == ""
304:                     tmp = "main"
305:                 end
306: 
307:                 Puppet.debug addcontext("Adding code to %s" % tmp)
308:                 # Else, add our code to it.
309:                 if other.code and code
310:                     # promote if neededcodes to ASTArray so that we can append code
311:                     # ASTArray knows how to evaluate its members.
312:                     other.code = ast AST::ASTArray, :children => [other.code] unless other.code.is_a?(AST::ASTArray)
313:                     code = ast AST::ASTArray, :children => [code] unless code.is_a?(AST::ASTArray)
314:                     other.code.children += code.children
315:                 else
316:                     other.code ||= code
317:                 end
318:             end
319: 
320:             if other.doc and doc
321:                 other.doc += doc
322:             else
323:                 other.doc ||= doc
324:             end
325:         else
326:             # Define it anew.
327:             # Note we're doing something somewhat weird here -- we're setting
328:             # the class's namespace to its fully qualified name.  This means
329:             # anything inside that class starts looking in that namespace first.
330:             args = {:namespace => name, :classname => name, :parser => self}
331:             args[:code] = code if code
332:             args[:parentclass] = parent if parent
333:             args[:doc] = doc
334:             args[:line] = options[:line]
335: 
336:             @loaded_code.add_hostclass(name, ast(AST::HostClass, args))
337:         end
338: 
339:         return @loaded_code.hostclass(name)
340:     end
341: 
342:     # Create a new definition.
343:     def newdefine(name, options = {})
344:         name = name.downcase
345:         if @loaded_code.hostclass(name)
346:             raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
347:                 name
348:         end
349:         # Make sure our definition doesn't already exist
350:         if other = @loaded_code.definition(name)
351:             error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
352:         end
353: 
354:         ns, whatever = namesplit(name)
355:         args = {
356:             :namespace => ns,
357:             :arguments => options[:arguments],
358:             :code => options[:code],
359:             :parser => self,
360:             :classname => name,
361:             :doc => options[:doc],
362:             :line => options[:line]
363:         }
364: 
365:         [:code, :arguments].each do |param|
366:             args[param] = options[param] if options[param]
367:         end
368: 
369:         @loaded_code.add_definition(name, ast(AST::Definition, args))
370:     end
371: 
372:     # Create a new node.  Nodes are special, because they're stored in a global
373:     # table, not according to namespaces.
374:     def newnode(names, options = {})
375:         names = [names] unless names.instance_of?(Array)
376:         doc = lexer.getcomment
377:         names.collect do |name|
378:             name = AST::HostName.new :value => name unless name.is_a?(AST::HostName)
379:             if other = @loaded_code.node_exists?(name)
380:                 error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
381:             end
382:             name = name.to_s if name.is_a?(Symbol)
383:             args = {
384:                 :name => name,
385:                 :parser => self,
386:                 :doc => doc,
387:                 :line => options[:line]
388:             }
389:             if options[:code]
390:                 args[:code] = options[:code]
391:             end
392:             if options[:parent]
393:                 args[:parentclass] = options[:parent]
394:             end
395:             node = ast(AST::Node, args)
396:             node.classname = name.to_classname
397:             @loaded_code.add_node(name, node)
398:             node
399:         end
400:     end
401: 
402:     def on_error(token,value,stack)
403:         if token == 0 # denotes end of file
404:             value = 'end of file'
405:         else
406:             value = "'%s'" % value[:value]
407:         end
408:         error = "Syntax error at %s" % [value]
409: 
410:         if brace = @lexer.expected
411:             error += "; expected '%s'" % brace
412:         end
413: 
414:         except = Puppet::ParseError.new(error)
415:         except.line = @lexer.line
416:         if @lexer.file
417:             except.file = @lexer.file
418:         end
419: 
420:         raise except
421:     end
422: 
423:     # how should I do error handling here?
424:     def parse(string = nil)
425:         if string
426:             self.string = string
427:         end
428:         begin
429:             @yydebug = false
430:             main = yyparse(@lexer,:scan)
431:         rescue Racc::ParseError => except
432:             error = Puppet::ParseError.new(except)
433:             error.line = @lexer.line
434:             error.file = @lexer.file
435:             error.set_backtrace except.backtrace
436:             raise error
437:         rescue Puppet::ParseError => except
438:             except.line ||= @lexer.line
439:             except.file ||= @lexer.file
440:             raise except
441:         rescue Puppet::Error => except
442:             # and this is a framework error
443:             except.line ||= @lexer.line
444:             except.file ||= @lexer.file
445:             raise except
446:         rescue Puppet::DevError => except
447:             except.line ||= @lexer.line
448:             except.file ||= @lexer.file
449:             raise except
450:         rescue => except
451:             error = Puppet::DevError.new(except.message)
452:             error.line = @lexer.line
453:             error.file = @lexer.file
454:             error.set_backtrace except.backtrace
455:             raise error
456:         end
457:         if main
458:             # Store the results as the top-level class.
459:             newclass("", :code => main)
460:         end
461:         return @loaded_code
462:     ensure
463:         @lexer.clear
464:     end
465: 
466:     # See if any of the files have changed.
467:     def reparse?
468:         if file = @files.detect { |name, file| file.changed?  }
469:             return file[1].stamp
470:         else
471:             return false
472:         end
473:     end
474: 
475:     def string=(string)
476:         @lexer.string = string
477:     end
478: 
479:     def version
480:         return @version if defined?(@version)
481: 
482:         if Puppet[:config_version] == ""
483:             @version = Time.now.to_i
484:             return @version
485:         end
486: 
487:         @version = Puppet::Util.execute([Puppet[:config_version]]).strip
488: 
489:     rescue Puppet::ExecutionFailure => e
490:         raise Puppet::ParseError, "Unable to set config_version: #{e.message}"
491:     end
492: 
493:     # Add a new file to be checked when we're checking to see if we should be
494:     # reparsed.  This is basically only used by the TemplateWrapper to let the
495:     # parser know about templates that should be parsed.
496:     def watch_file(filename)
497:             check_and_add_to_watched_files(filename)
498:     end
499: 
500:     private
501: 
502:     def check_and_add_to_watched_files(filename)
503:         unless @files.include?(filename)
504:             @files[filename] = Puppet::Util::LoadedFile.new(filename)
505:             return true
506:         else
507:             return false
508:         end
509:     end
510: end