| Class | Puppet::Parser::Parser |
| In: |
lib/puppet/parser/parser_support.rb
lib/puppet/parser/parser.rb |
| Parent: | Object |
| ASTSet | = | Struct.new(:classes, :definitions, :nodes) |
| AST | = | Puppet::Parser::AST |
| Racc_arg | = | [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] |
| Racc_token_to_s_table | = | [ '$end', 'error', 'LBRACK', 'DQTEXT', 'SQTEXT', 'RBRACK', 'LBRACE', 'RBRACE', 'SYMBOL', 'FARROW', 'COMMA', 'TRUE', 'FALSE', 'EQUALS', 'APPENDS', 'LESSEQUAL', 'NOTEQUAL', 'DOT', 'COLON', 'LLCOLLECT', 'RRCOLLECT', 'QMARK', 'LPAREN', 'RPAREN', 'ISEQUAL', 'GREATEREQUAL', 'GREATERTHAN', 'LESSTHAN', 'IF', 'ELSE', 'IMPORT', 'DEFINE', 'ELSIF', 'VARIABLE', 'CLASS', 'INHERITS', 'NODE', 'BOOLEAN', 'NAME', 'SEMIC', 'CASE', 'DEFAULT', 'AT', 'LCOLLECT', 'RCOLLECT', 'CLASSNAME', 'CLASSREF', 'NOT', 'OR', 'AND', 'UNDEF', 'PARROW', '$start', 'program', 'statements', 'nil', 'statement', 'resource', 'virtualresource', 'collection', 'assignment', 'casestatement', 'ifstatement', 'import', 'fstatement', 'definition', 'hostclass', 'nodedef', 'resourceoverride', 'append', 'funcvalues', 'namestrings', 'resourcerefs', 'namestring', 'name', 'variable', 'type', 'boolean', 'funcrvalue', 'selector', 'quotedtext', 'resourceref', 'classname', 'resourceinstances', 'endsemi', 'params', 'endcomma', 'classref', 'anyparams', 'at', 'collectrhand', 'collstatements', 'collstatement', 'colljoin', 'collexpr', 'colllval', 'simplervalue', 'resourceinst', 'resourcename', 'undef', 'array', 'rvalue', 'param', 'addparam', 'anyparam', 'rvalues', 'comma', 'iftest', 'else', 'caseopts', 'caseopt', 'casevalues', 'selectlhand', 'svalues', 'selectval', 'sintvalues', 'qtexts', 'argumentlist', 'classparent', 'hostnames', 'nodeparent', 'hostname', 'nothing', 'arguments', 'argument', 'classnameordefault'] |
| Racc_debug_parser | = | false |
| environment | [R] | |
| files | [RW] | |
| version | [R] |
# File lib/puppet/parser/parser_support.rb, line 209
209: def initialize(options = {})
210: @astset = options[:astset] || ASTSet.new({}, {}, {})
211: @environment = options[:environment]
212: initvars()
213: end
# File lib/puppet/parser/parser.rb, line 1818
1818: def _reduce_none( val, _values, result )
1819: result
1820: end
Add context to a message; useful for error messages and such.
# File lib/puppet/parser/parser_support.rb, line 23
23: def addcontext(message, obj = nil)
24: obj ||= @lexer
25:
26: message += " on line %s" % obj.line
27: if file = obj.file
28: message += " in file %s" % file
29: end
30:
31: return message
32: end
Create an AST array out of all of the args
# File lib/puppet/parser/parser_support.rb, line 35
35: def aryfy(*args)
36: if args[0].instance_of?(AST::ASTArray)
37: result = args.shift
38: args.each { |arg|
39: result.push arg
40: }
41: else
42: result = ast AST::ASTArray, :children => args
43: end
44:
45: return result
46: end
Create an AST object, and automatically add the file and line information if available.
# File lib/puppet/parser/parser_support.rb, line 50
50: def ast(klass, hash = {})
51: hash[:line] = @lexer.line unless hash.include?(:line)
52:
53: unless hash.include?(:file)
54: if file = @lexer.file
55: hash[:file] = file
56: end
57: end
58:
59: return klass.new(hash)
60: end
The fully qualifed name, with the full namespace.
# File lib/puppet/parser/parser_support.rb, line 63
63: def classname(name)
64: [@lexer.namespace, name].join("::").sub(/^::/, '')
65: end
Raise a Parse error.
# File lib/puppet/parser/parser_support.rb, line 72
72: def error(message)
73: if brace = @lexer.expected
74: message += "; expected '%s'"
75: end
76: except = Puppet::ParseError.new(message)
77: except.line = @lexer.line
78: if @lexer.file
79: except.file = @lexer.file
80: end
81:
82: raise except
83: end
# File lib/puppet/parser/parser_support.rb, line 89
89: def file=(file)
90: unless FileTest.exists?(file)
91: unless file =~ /\.pp$/
92: file = file + ".pp"
93: end
94: unless FileTest.exists?(file)
95: raise Puppet::Error, "Could not find file %s" % file
96: end
97: end
98: if check_and_add_to_watched_files(file)
99: @lexer.file = file
100: else
101: raise Puppet::AlreadyImportedError.new("Import loop detected")
102: end
103: end
Find a class definition, relative to the current namespace.
# File lib/puppet/parser/parser_support.rb, line 106
106: def findclass(namespace, name)
107: fqfind namespace, name, classes
108: end
Find a component definition, relative to the current namespace.
# File lib/puppet/parser/parser_support.rb, line 111
111: def finddefine(namespace, name)
112: fqfind namespace, name, definitions
113: end
This is only used when nodes are looking up the code for their parent nodes.
# File lib/puppet/parser/parser_support.rb, line 117
117: def findnode(name)
118: fqfind "", name, nodes
119: end
The recursive method used to actually look these objects up.
# File lib/puppet/parser/parser_support.rb, line 122
122: def fqfind(namespace, name, table)
123: namespace = namespace.downcase
124: name = name.to_s.downcase
125:
126: # If our classname is fully qualified or we have no namespace,
127: # just try directly for the class, and return either way.
128: if name =~ /^::/ or namespace == ""
129: classname = name.sub(/^::/, '')
130: self.load(classname) unless table[classname]
131: return table[classname]
132: end
133:
134: # Else, build our namespace up piece by piece, checking
135: # for the class in each namespace.
136: ary = namespace.split("::")
137:
138: while ary.length > 0
139: newname = (ary + [name]).join("::").sub(/^::/, '')
140: if obj = table[newname] or (self.load(newname) and obj = table[newname])
141: return obj
142: end
143:
144: # Delete the second to last object, which reduces our namespace by one.
145: ary.pop
146: end
147:
148: # If we've gotten to this point without finding it, see if the name
149: # exists at the top namespace
150: if obj = table[name] or (self.load(name) and obj = table[name])
151: return obj
152: end
153:
154: return nil
155: end
Import our files.
# File lib/puppet/parser/parser_support.rb, line 158
158: def import(file)
159: if Puppet[:ignoreimport]
160: return AST::ASTArray.new(:children => [])
161: end
162: # use a path relative to the file doing the importing
163: if @lexer.file
164: dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '')
165: else
166: dir = "."
167: end
168: if dir == ""
169: dir = "."
170: end
171: result = ast AST::ASTArray
172:
173: # We can't interpolate at this point since we don't have any
174: # scopes set up. Warn the user if they use a variable reference
175: pat = file
176: if pat.index("$")
177: Puppet.warning(
178: "The import of #{pat} contains a variable reference;" +
179: " variables are not interpolated for imports " +
180: "in file #{@lexer.file} at line #{@lexer.line}"
181: )
182: end
183: files = Puppet::Module::find_manifests(pat, :cwd => dir, :environment => @environment)
184: if files.size == 0
185: raise Puppet::ImportError.new("No file(s) found for import " +
186: "of '#{pat}'")
187: end
188:
189: files.collect { |file|
190: parser = Puppet::Parser::Parser.new(:astset => @astset, :environment => @environment)
191: parser.files = self.files
192: Puppet.debug("importing '%s'" % file)
193:
194: unless file =~ /^#{File::SEPARATOR}/
195: file = File.join(dir, file)
196: end
197: begin
198: parser.file = file
199: rescue Puppet::AlreadyImportedError
200: # This file has already been imported to just move on
201: next
202: end
203:
204: # This will normally add code to the 'main' class.
205: parser.parse
206: }
207: end
Initialize or reset all of our variables.
# File lib/puppet/parser/parser_support.rb, line 216
216: def initvars
217: @lexer = Puppet::Parser::Lexer.new()
218: @files = {}
219: @loaded = []
220: end
Try to load a class, since we could not find it.
# File lib/puppet/parser/parser_support.rb, line 223
223: def load(classname)
224: return false if classname == ""
225: filename = classname.gsub("::", File::SEPARATOR)
226:
227: # First try to load the top-level module
228: mod = filename.scan(/^[\w-]+/).shift
229: unless @loaded.include?(mod)
230: @loaded << mod
231: begin
232: import(mod)
233: Puppet.info "Autoloaded module %s" % mod
234: rescue Puppet::ImportError => detail
235: # We couldn't load the module
236: end
237: end
238:
239: # We don't know whether we're looking for a class or definition, so we have
240: # to test for both.
241: return true if classes.include?(classname) || definitions.include?(classname)
242:
243: unless @loaded.include?(filename)
244: @loaded << filename
245: # Then the individual file
246: begin
247: import(filename)
248: Puppet.info "Autoloaded file %s from module %s" % [filename, mod]
249: rescue Puppet::ImportError => detail
250: # We couldn't load the file
251: end
252: end
253: # We don't know whether we're looking for a class or definition, so we have
254: # to test for both.
255: return classes.include?(classname) || definitions.include?(classname)
256: end
Split an fq name into a namespace and name
# File lib/puppet/parser/parser_support.rb, line 259
259: def namesplit(fullname)
260: ary = fullname.split("::")
261: n = ary.pop || ""
262: ns = ary.join("::")
263: return ns, n
264: end
Create a new class, or merge with an existing class.
# File lib/puppet/parser/parser_support.rb, line 267
267: def newclass(name, options = {})
268: name = name.downcase
269:
270: if definitions.include?(name)
271: raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
272: end
273: code = options[:code]
274: parent = options[:parent]
275:
276: # If the class is already defined, then add code to it.
277: if other = @astset.classes[name]
278: # Make sure the parents match
279: if parent and other.parentclass and (parent != other.parentclass)
280: error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
281: end
282:
283: # This might be dangerous...
284: if parent and ! other.parentclass
285: other.parentclass = parent
286: end
287:
288: # This might just be an empty, stub class.
289: if code
290: tmp = name
291: if tmp == ""
292: tmp = "main"
293: end
294:
295: Puppet.debug addcontext("Adding code to %s" % tmp)
296: # Else, add our code to it.
297: if other.code and code
298: other.code.children += code.children
299: else
300: other.code ||= code
301: end
302: end
303: else
304: # Define it anew.
305: # Note we're doing something somewhat weird here -- we're setting
306: # the class's namespace to its fully qualified name. This means
307: # anything inside that class starts looking in that namespace first.
308: args = {:namespace => name, :classname => name, :parser => self}
309: args[:code] = code if code
310: args[:parentclass] = parent if parent
311: @astset.classes[name] = ast AST::HostClass, args
312: end
313:
314: return @astset.classes[name]
315: end
Create a new definition.
# File lib/puppet/parser/parser_support.rb, line 318
318: def newdefine(name, options = {})
319: name = name.downcase
320: if @astset.classes.include?(name)
321: raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
322: name
323: end
324: # Make sure our definition doesn't already exist
325: if other = @astset.definitions[name]
326: error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
327: end
328:
329: ns, whatever = namesplit(name)
330: args = {
331: :namespace => ns,
332: :arguments => options[:arguments],
333: :code => options[:code],
334: :parser => self,
335: :classname => name
336: }
337:
338: [:code, :arguments].each do |param|
339: args[param] = options[param] if options[param]
340: end
341:
342: @astset.definitions[name] = ast AST::Definition, args
343: end
Create a new node. Nodes are special, because they‘re stored in a global table, not according to namespaces.
# File lib/puppet/parser/parser_support.rb, line 347
347: def newnode(names, options = {})
348: names = [names] unless names.instance_of?(Array)
349: names.collect do |name|
350: name = name.to_s.downcase
351: if other = @astset.nodes[name]
352: error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
353: end
354: name = name.to_s if name.is_a?(Symbol)
355: args = {
356: :name => name,
357: :parser => self
358: }
359: if options[:code]
360: args[:code] = options[:code]
361: end
362: if options[:parent]
363: args[:parentclass] = options[:parent]
364: end
365: @astset.nodes[name] = ast(AST::Node, args)
366: @astset.nodes[name].classname = name
367: @astset.nodes[name]
368: end
369: end
# File lib/puppet/parser/parser_support.rb, line 371
371: def on_error(token,value,stack)
372: if token == 0 # denotes end of file
373: value = 'end of file'
374: else
375: value = "'%s'" % value
376: end
377: error = "Syntax error at %s" % [value]
378:
379: if brace = @lexer.expected
380: error += "; expected '%s'" % brace
381: end
382:
383: except = Puppet::ParseError.new(error)
384: except.line = @lexer.line
385: if @lexer.file
386: except.file = @lexer.file
387: end
388:
389: raise except
390: end
how should I do error handling here?
# File lib/puppet/parser/parser_support.rb, line 393
393: def parse(string = nil)
394: if string
395: self.string = string
396: end
397: begin
398: main = yyparse(@lexer,:scan)
399: rescue Racc::ParseError => except
400: error = Puppet::ParseError.new(except)
401: error.line = @lexer.line
402: error.file = @lexer.file
403: error.set_backtrace except.backtrace
404: raise error
405: rescue Puppet::ParseError => except
406: except.line ||= @lexer.line
407: except.file ||= @lexer.file
408: raise except
409: rescue Puppet::Error => except
410: # and this is a framework error
411: except.line ||= @lexer.line
412: except.file ||= @lexer.file
413: raise except
414: rescue Puppet::DevError => except
415: except.line ||= @lexer.line
416: except.file ||= @lexer.file
417: raise except
418: rescue => except
419: error = Puppet::DevError.new(except.message)
420: error.line = @lexer.line
421: error.file = @lexer.file
422: error.set_backtrace except.backtrace
423: raise error
424: end
425: if main
426: # Store the results as the top-level class.
427: newclass("", :code => main)
428: end
429: @version = Time.now.to_i
430: return @astset
431: ensure
432: @lexer.clear
433: end
See if any of the files have changed.
# File lib/puppet/parser/parser_support.rb, line 436
436: def reparse?
437: if file = @files.detect { |name, file| file.changed? }
438: return file[1].stamp
439: else
440: return false
441: end
442: end
# File lib/puppet/parser/parser_support.rb, line 444
444: def string=(string)
445: @lexer.string = string
446: end
Add a new file to be checked when we‘re checking to see if we should be reparsed. This is basically only used by the TemplateWrapper to let the parser know about templates that should be parsed.
# File lib/puppet/parser/parser_support.rb, line 451
451: def watch_file(filename)
452: check_and_add_to_watched_files(filename)
453: end