summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser
diff options
context:
space:
mode:
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/ast/component.rb15
-rw-r--r--lib/puppet/parser/ast/hostclass.rb47
-rw-r--r--lib/puppet/parser/functions.rb8
-rw-r--r--lib/puppet/parser/scope.rb287
4 files changed, 217 insertions, 140 deletions
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
index 00677e1ea..50e8df5a8 100644
--- a/lib/puppet/parser/ast/component.rb
+++ b/lib/puppet/parser/ast/component.rb
@@ -14,12 +14,12 @@ class Puppet::Parser::AST
#def evaluate(scope,hash,objtype,objname)
def evaluate(hash)
- scope = hash[:scope]
+ origscope = hash[:scope]
objtype = hash[:type]
objname = hash[:name]
arguments = hash[:arguments] || {}
- scope = scope.newscope(
+ scope = origscope.newscope(
:type => @type,
:name => objname,
:keyword => self.keyword
@@ -95,9 +95,14 @@ class Puppet::Parser::AST
# Now just evaluate the code with our new bindings.
self.code.safeevaluate(:scope => scope)
- # We return the scope, so that our children can make their scopes
- # under ours. This allows them to find our definitions.
- return scope
+ # If we're being evaluated as a parent class, we want to return the
+ # scope, so it can be overridden and such, but if not, we want to
+ # return a TransBucket of our objects.
+ if hash.include?(:asparent)
+ return scope
+ else
+ return scope.to_trans
+ end
end
# Check whether a given argument is valid. Searches up through
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index 8290f4ef0..a69d185e0 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -11,7 +11,7 @@ class Puppet::Parser::AST
scope = hash[:scope]
objtype = hash[:type]
objname = hash[:name]
- hash = hash[:arguments]
+ args = hash[:arguments]
# Verify that we haven't already been evaluated
# FIXME The second subclass won't evaluate the parent class
# code at all, and any overrides will throw an error.
@@ -26,30 +26,51 @@ class Puppet::Parser::AST
# Default to creating a new context
newcontext = true
+ transscope = nil
if parentscope = self.evalparent(
- :scope => scope, :arguments => hash, :name => objname
+ :scope => scope, :arguments => args, :name => objname
)
# Override our scope binding with the parent scope
# binding. This is quite hackish, but I can't think
# of another way to make sure our scopes end up under
# our parent scopes.
+ if parentscope.is_a? Puppet::TransBucket
+ raise Puppet::DevError, "Got a bucket instead of a scope"
+ end
+
scope = parentscope
+ transscope = parentscope
# But don't create a new context if our parent created one
newcontext = false
end
- # just use the Component evaluate method, but change the type
- # to our own type
- #retval = super(scope,hash,@name,objname)
- retval = super(
+ # Just use the Component evaluate method, but change the type
+ # to our own type.
+ result = super(
:scope => scope,
- :arguments => hash,
+ :arguments => args,
:type => @type,
- :name => objname,
- :newcontext => newcontext
+ :name => objname, # might be nil
+ :newcontext => newcontext,
+ :asparent => hash[:asparent] # might be nil
)
- return retval
+
+ # This is important but painfully difficult. If we're the top-level
+ # class, that is, we have no parent classes, then the transscope
+ # is our own scope, but if there are parent classes, then the topmost
+ # parent's scope is the transscope, since it contains its code and
+ # all of the subclass's code.
+ transscope ||= result
+
+ if hash[:asparent]
+ # If we're a parent class, then return the scope object itself.
+ return result
+ else
+ # But if we're the final subclass, translate the whole scope tree
+ # into TransObjects and TransBuckets.
+ return transscope.to_trans
+ end
end
# Evaluate our parent class. Parent classes are evaluated in the
@@ -97,11 +118,13 @@ class Puppet::Parser::AST
raise error
end
# We don't need to pass the type, because the parent will just
- # use its own type
+ # use its own type. Specify that it's being evaluated as a parent,
+ # so that it returns the scope, not a transbucket.
return parentobj.safeevaluate(
:scope => scope,
:arguments => args,
- :name => name
+ :name => name,
+ :asparent => true
)
else
return false
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 63201924b..b60d94b0a 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -27,7 +27,6 @@ module Functions
fname = "function_" + name.to_s
Puppet::Parser::Scope.send(:define_method, fname, &block)
- #FCollection.send(:module_function,name)
# Someday we'll support specifying an arity, but for now, nope
#@functions[name] = {:arity => arity, :type => ftype}
@@ -63,10 +62,11 @@ module Functions
newfunction(:include) do |vals|
vals.each do |val|
if objecttype = lookuptype(val)
- # It's a defined type
- objecttype.safeevaluate(
+ # It's a defined type, so set it into the scope so it can
+ # be evaluated.
+ setobject(
:type => val,
- :scope => self
+ :arguments => {}
)
else
raise Puppet::ParseError, "Unknown class %s" % val
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 394c46033..0fb3510b0 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -179,7 +179,7 @@ module Puppet::Parser
# Yield each child scope in turn
def each
- @children.reject { |child|
+ @children.each { |child|
yield child
}
end
@@ -189,11 +189,9 @@ module Puppet::Parser
return unless classes
classes.each do |klass|
if code = lookuptype(klass)
- code.safeevaluate(
- :scope => self,
- :facts => {},
- :type => klass
- )
+ # Just reuse the 'include' function, since that's the equivalent
+ # of what we're doing here.
+ function_include(klass)
end
end
end
@@ -201,7 +199,6 @@ module Puppet::Parser
# Evaluate a specific node's code. This method will normally be called
# on the top-level scope, but it actually evaluates the node at the
# appropriate scope.
- #def evalnode(names, facts, classes = nil, parent = nil)
def evalnode(hash)
objects = hash[:ast]
names = hash[:names] or
@@ -240,17 +237,19 @@ module Puppet::Parser
scope.evalclasses(classes)
end
- # Evaluate normally, with no node definitions. This is a bit of a
- # silly method, in that it just calls evaluate on the passed-in
- # objects, and then calls to_trans on itself. It just conceals
- # a paltry amount of info from whomever's using the scope object.
+ # The top-level evaluate, used to evaluate a whole AST tree. This is
+ # a strange method, in that it turns around and calls evaluate() on its
+ # :ast argument.
def evaluate(hash)
objects = hash[:ast]
facts = hash[:facts] || {}
+ @@done = []
+
unless objects
raise Puppet::DevError, "Evaluation requires an AST tree"
end
+
# Set all of our facts in the top-level scope.
facts.each { |var, value|
self.setvar(var, value)
@@ -258,9 +257,10 @@ module Puppet::Parser
# Evaluate all of our configuration. This does not evaluate any
# node definitions.
- objects.safeevaluate(:scope => self)
+ result = objects.safeevaluate(:scope => self)
- # If they've provided a name or a parent, we assume they're looking for nodes.
+ # If they've provided a name or a parent, we assume they're looking
+ # for nodes.
if hash.include? :parentnode
# Specifying a parent node takes precedence, because it is assumed
# that this node was found in a remote repository like ldap.
@@ -277,15 +277,14 @@ module Puppet::Parser
# a cfengine module
end
- objects = self.to_trans
- objects.top = true
+ bucket = self.to_trans
# Add our class list
unless self.classlist.empty?
- objects.classes = self.classlist
+ bucket.classes = self.classlist
end
- return objects
+ return bucket
end
# Pull in all of the appropriate classes and evaluate them. It'd
@@ -306,8 +305,8 @@ module Puppet::Parser
#Puppet.notice "hash is %s" %
# hash.inspect
- Puppet.notice "Classes are %s, parent is %s" %
- [classes.inspect, parent.inspect]
+ #Puppet.notice "Classes are %s, parent is %s" %
+ # [classes.inspect, parent.inspect]
if parent
arghash[:parentclass] = parent
@@ -325,52 +324,6 @@ module Puppet::Parser
scope.evalclasses(classes)
end
- # Take all of our objects and evaluate them.
-# def finish
-# self.info "finishing"
-# @objectlist.each { |object|
-# if object.is_a? ScopeObj
-# self.info "finishing %s" % object.name
-# if obj = finishobject(object)
-# @children << obj
-# end
-# end
-# }
-#
-# @finished = true
-#
-# self.info "finished"
-# end
-#
-# # If the object is defined in an upper scope, then add our
-# # params to that upper scope; else, create a transobject
-# # or evaluate the definition.
-# def finishobject(object)
-# type = object.type
-# name = object.name
-#
-# # It should be a defined type.
-# definedtype = lookuptype(type)
-#
-# unless definedtype
-# error = Puppet::ParseError.new("No such type %s" % type)
-# error.line = object.line
-# error.file = object.file
-# raise error
-# end
-#
-# return definedtype.safeevaluate(
-# :scope => self,
-# :arguments => object,
-# :type => type,
-# :name => name
-# )
-# end
-#
-# def finished?
-# @finished
-# end
-
# Initialize our new scope. Defaults to having no parent and to
# being declarative.
def initialize(hash = {})
@@ -395,7 +348,10 @@ module Puppet::Parser
end
self.istop(hash[:declarative])
else
+ # This is here, rather than in newchild(), so that all
+ # of the later variable initialization works.
@parent.child = self
+
@level = @parent.level + 1
@interp = @parent.interp
@topscope = @parent.topscope
@@ -425,9 +381,6 @@ module Puppet::Parser
typehash[typekey] = Hash.new(nil)
}
- # The list of simpler hash objects.
- @objectlist = []
-
# This is just for collecting statements locally, so we can
# verify that there is no overlap within this specific scope
@localobjectable = Hash.new { |typehash,typekey|
@@ -591,25 +544,14 @@ module Puppet::Parser
obj = nil
- # If it's a builtin type, then use a transobject, else use
- # a ScopeObj, which will get replaced later.
- if self.builtintype?(hash[:type])
- obj = Puppet::TransObject.new(hash[:name], hash[:type])
+ obj = Puppet::TransObject.new(hash[:name], hash[:type])
- @children << obj
- else
- obj = ScopeObj.new(nil)
- obj.name = hash[:name]
- obj.type = hash[:type]
- end
+ @children << obj
@objectable[hash[:type]][hash[:name]] = obj
@definedtable[hash[:type]][hash[:name]] = obj
- # Keep them in order, just for kicks
- @objectlist << obj
-
return obj
end
@@ -737,48 +679,43 @@ module Puppet::Parser
}
end
- if objecttype = lookuptype(type)
- # It's a defined type
- objecttype.safeevaluate(
- :name => name,
- :type => type,
- :arguments => params,
- :scope => self
- )
- else
- # First look for it in a parent scope
- obj = lookupobject(:name => name, :type => type)
-
- unless obj and obj != :undefined
- unless obj = @objectable[type][name]
- obj = self.newobject(
- :type => type,
- :name => name,
- :line => line,
- :file => file
- )
+ # First look for it in a parent scope
+ obj = lookupobject(:name => name, :type => type)
- # only set these if we've created the object,
- # which is the most common case
- # FIXME we eventually need to store the file
- # and line with each param, not the object
- # itself.
- obj.file = file
- obj.line = line
- end
- end
+ unless obj and obj != :undefined
+ unless obj = @objectable[type][name]
+ obj = self.newobject(
+ :type => type,
+ :name => name,
+ :line => line,
+ :file => file
+ )
- # Now add our parameters. This has the function of overriding
- # existing values, which might have been defined in a higher
- # scope.
- params.each { |var,value|
- # Add it to our found object
- obj[var] = value
- }
+ # only set these if we've created the object,
+ # which is the most common case
+ # FIXME we eventually need to store the file
+ # and line with each param, not the object
+ # itself.
+ obj.file = file
+ obj.line = line
+ end
end
- @localobjectable[type][name] ||= {}
+ # Now add our parameters. This has the function of overriding
+ # existing values, which might have been defined in a higher
+ # scope.
+ params.each { |var,value|
+ # Add it to our found object
+ obj[var] = value
+ }
+ # This is only used for override verification -- the local object
+ # table does not have transobjects or whatever in it, it just has
+ # simple hashes. This is necessary because setobject can modify
+ # our object table or a parent class's object table, and we
+ # still need to make sure param settings cannot be duplicated
+ # within our scope.
+ @localobjectable[type][name] ||= {}
params.each { |var,value|
# And add it to the local table; mmm, hack
@localobjectable[type][name][var] = value
@@ -867,9 +804,121 @@ module Puppet::Parser
end
end
- # Convert our scope to a list of Transportable objects.
+ # Convert our scope to a TransBucket. Everything in our @localobjecttable
+ # gets converted to either an evaluated definition, or a TransObject
def to_trans
+ results = []
+
+ @children.dup.each do |child|
+
+ if @@done.include?(child)
+ raise Puppet::DevError, "Already translated %s" % child.object_id
+ else
+ info "Translating %s" % child.object_id
+ @@done << child
+ end
+ #warning "Working on %s of type %s with id %s" %
+ # [child.type, child.class, child.object_id]
+
+ # If it's a scope, then it can only be a subclass's scope, so
+ # convert it to a transbucket and store it in our results list
+ result = nil
+ case child
+ when Scope
+ #raise Puppet::DevError, "got a child scope"
+ result = child.to_trans
+ when Puppet::TransObject
+ # These objects can map to defined types or builtin types.
+ # Builtin types should be passed out as they are, but defined
+ # types need to be evaluated. We have to wait until this
+ # point so that subclass overrides can happen.
+ # Wait until the last minute to set tags, although this
+ # probably should not matter
+ child.tags = self.tags
+
+ # Add any defaults.
+ self.adddefaults(child)
+
+ # Then make sure this child's tags are stored in the
+ # central table. This should maybe be in the evaluate
+ # methods, but, eh.
+ @topscope.addtags(child)
+
+ # Now that all that is done, check to see what kind of object
+ # it is.
+ if objecttype = lookuptype(child.type)
+ # It's a defined type, so evaluate it
+ result = objecttype.safeevaluate(
+ :name => child.name,
+ :type => child.type,
+ :arguments => child.to_hash,
+ :scope => self
+ )
+ else
+ # It's a builtin type, so just chunk it on in
+ result = child
+ end
+ else
+ raise Puppet::DevError,
+ "Puppet::Parse::Scope cannot handle objects of type %s" %
+ child.class
+ end
+
+ # Skip empty builtin types or defined types
+ if result and ! result.empty?
+ results << result
+ end
+ end
+ # Get rid of any nil objects.
+ results.reject! { |child|
+ child.nil?
+ }
+
+ # If we have a name and type, then make a TransBucket, which
+ # becomes a component.
+ # Else, just stack all of the objects into the current bucket.
+ if @type
+ bucket = Puppet::TransBucket.new
+
+ if defined? @name and @name
+ bucket.name = @name
+ end
+ # it'd be nice not to have to do this...
+ results.each { |result|
+ #Puppet.warning "Result type is %s" % result.class
+ bucket.push(result)
+ }
+ bucket.type = @type
+
+ if defined? @keyword
+ bucket.keyword = @keyword
+ end
+ #Puppet.debug(
+ # "TransBucket with name %s and type %s in scope %s" %
+ # [@name,@type,self.object_id]
+ #)
+
+ # now find metaparams
+ @symtable.each { |var,value|
+ if Puppet::Type.metaparam?(var.intern)
+ #Puppet.debug("Adding metaparam %s" % var)
+ bucket.param(var,value)
+ else
+ #Puppet.debug("%s is not a metaparam" % var)
+ end
+ }
+ #Puppet.debug "Returning bucket %s from scope %s" %
+ # [bucket.name,self.object_id]
+ return bucket
+ else
+ Puppet.debug "typeless scope; just returning a list"
+ return results
+ end
+ end
+
+ # Convert our scope to a list of Transportable objects.
+ def oldto_trans
results = []
# Iterate across our child scopes and call to_trans on them