summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser/ast
diff options
context:
space:
mode:
authorluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-10-04 18:24:24 +0000
committerluke <luke@980ebf18-57e1-0310-9a29-db15c13687c0>2006-10-04 18:24:24 +0000
commit28cee40689440388994a4768bd301ae32c8ecc05 (patch)
treec865ab44f4c9247052cf83de16ffc8ebe8b15e54 /lib/puppet/parser/ast
parente0e291332bd4676962a28c7b220ae5c5e9651c0a (diff)
downloadpuppet-28cee40689440388994a4768bd301ae32c8ecc05.tar.gz
puppet-28cee40689440388994a4768bd301ae32c8ecc05.tar.xz
puppet-28cee40689440388994a4768bd301ae32c8ecc05.zip
Merging the changes from the override-refactor branch. This is a significant rewrite of the parser, but it has little affect on the rest of the code tree.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1726 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/parser/ast')
-rw-r--r--lib/puppet/parser/ast/astarray.rb44
-rw-r--r--lib/puppet/parser/ast/classdef.rb77
-rw-r--r--lib/puppet/parser/ast/collection.rb97
-rw-r--r--lib/puppet/parser/ast/collexpr.rb81
-rw-r--r--lib/puppet/parser/ast/compdef.rb98
-rw-r--r--lib/puppet/parser/ast/component.rb198
-rw-r--r--lib/puppet/parser/ast/function.rb5
-rw-r--r--lib/puppet/parser/ast/hostclass.rb166
-rw-r--r--lib/puppet/parser/ast/leaf.rb23
-rw-r--r--lib/puppet/parser/ast/node.rb113
-rw-r--r--lib/puppet/parser/ast/nodedef.rb73
-rw-r--r--lib/puppet/parser/ast/objectdef.rb353
-rw-r--r--lib/puppet/parser/ast/objectref.rb77
-rw-r--r--lib/puppet/parser/ast/resourcedef.rb215
-rw-r--r--lib/puppet/parser/ast/resourcedefaults.rb (renamed from lib/puppet/parser/ast/typedefaults.rb)24
-rw-r--r--lib/puppet/parser/ast/resourceoverride.rb62
-rw-r--r--lib/puppet/parser/ast/resourceparam.rb (renamed from lib/puppet/parser/ast/objectparam.rb)21
-rw-r--r--lib/puppet/parser/ast/resourceref.rb44
-rw-r--r--lib/puppet/parser/ast/selector.rb6
-rw-r--r--lib/puppet/parser/ast/vardef.rb14
20 files changed, 654 insertions, 1137 deletions
diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb
index c42f658fd..fb0a3f671 100644
--- a/lib/puppet/parser/ast/astarray.rb
+++ b/lib/puppet/parser/ast/astarray.rb
@@ -25,14 +25,10 @@ class Puppet::Parser::AST
# This is such a stupid hack. I've no real idea how to make a
# "real" declarative language, so I hack it so it looks like
# one, yay.
- definelist = [
- AST::CompDef, AST::NodeDef, AST::ClassDef
- ]
setlist = [
- AST::VarDef, AST::TypeDefaults, AST::Function
+ AST::VarDef, AST::ResourceDefaults, AST::Function
]
- definers = []
settors = []
others = []
@@ -53,15 +49,13 @@ class Puppet::Parser::AST
# Now sort them all according to the type of action
items.each { |child|
- if definelist.include?(child.class)
- definers << child
- elsif setlist.include?(child.class)
+ if setlist.include?(child.class)
settors << child
else
others << child
end
}
- rets = [definers, settors, others].flatten.collect { |child|
+ rets = [settors, others].flatten.collect { |child|
child.safeevaluate(:scope => scope)
}
else
@@ -107,37 +101,7 @@ class Puppet::Parser::AST
# Used for abstracting the grammar declarations. Basically unnecessary
# except that I kept finding bugs because I had too many arrays that
# meant completely different things.
- class ObjectInst < ASTArray; end
-
- # Another simple container class to make sure we can correctly arrayfy
- # things.
- class CompArgument < ASTArray
- @@warnings = {}
- def initialize(hash)
- super
- name = @children[0].value
-
- # If it's not a metaparamer, we're fine.
- return unless Puppet::Type.metaparamclass(name)
-
- if @children[1]
- if @children[1].value == false
- raise Puppet::ParseError,
- "%s is a metaparameter; please choose another name" %
- name
- else
- unless @@warnings[name]
- @@warnings[name] = true
- Puppet.warning "%s is a metaparam; this value will inherit to all contained elements" % name
- end
- end
- else
- raise Puppet::ParseError,
- "%s is a metaparameter; please choose another name" %
- name
- end
- end
- end
+ class ResourceInst < ASTArray; end
end
# $Id$
diff --git a/lib/puppet/parser/ast/classdef.rb b/lib/puppet/parser/ast/classdef.rb
deleted file mode 100644
index e09db985f..000000000
--- a/lib/puppet/parser/ast/classdef.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'puppet/parser/ast/compdef'
-
-class Puppet::Parser::AST
- # Define a new class. Syntactically similar to component definitions,
- # but classes are always singletons -- only one can exist on a given
- # host.
- class ClassDef < AST::CompDef
- @keyword = "class"
-
- def self.genclass
- AST::HostClass
- end
-
- def each
- if @parentclass
- #[@type,@args,@parentclass,@code].each { |child| yield child }
- [@type,@parentclass,@code].each { |child| yield child }
- else
- #[@type,@args,@code].each { |child| yield child }
- [@type,@code].each { |child| yield child }
- end
- end
-
- # Store our parse tree according to type.
- def disabled_evaluate(hash)
- scope = hash[:scope]
- type = @type.safeevaluate(:scope => scope)
- #args = @args.safeevaluate(:scope => scope)
-
- #:args => args,
- arghash = {
- :type => type,
- :code => @code
- }
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
- #Puppet.debug("defining hostclass '%s' with arguments [%s]" %
- # [type,args])
-
- begin
- hclass = HostClass.new(arghash)
- hclass.keyword = self.keyword
- scope.settype(type, hclass)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- end
-
- def tree(indent = 0)
- #@args.tree(indent + 1),
- return [
- @type.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("class")),
- @parentclass ? @parentclass.tree(indent + 1) : "",
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "class %s(%s) inherits %s {\n%s }" %
- [@type, @parentclass, @code]
- #[@type, @args, @parentclass, @code]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb
index 8e9acce57..fa480b179 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -1,91 +1,30 @@
-require 'puppet/rails'
+require 'puppet'
+require 'puppet/parser/ast/branch'
+require 'puppet/parser/collector'
+# An object that collects stored objects from the central cache and returns
+# them to the current host, yo.
class Puppet::Parser::AST
- # An object that collects stored objects from the central cache and returns
- # them to the current host, yo.
- class Collection < AST::Branch
- attr_accessor :type
+class Collection < AST::Branch
+ attr_accessor :type, :query, :form
- # We cannot evaluate directly here; instead we need to store a
- # CollectType object, which will do the collection. This is
- # the only way to find certain exported types in the current
- # configuration.
- def evaluate(hash)
- scope = hash[:scope]
+ # We return an object that does a late-binding evaluation.
+ def evaluate(hash)
+ scope = hash[:scope]
- @convertedtype = @type.safeevaluate(:scope => scope)
-
- scope.newcollection(self)
+ if self.query
+ q = self.query.safeevaluate :scope => scope
+ else
+ q = nil
end
- # Now perform the actual collection, yo.
- def perform(scope)
- # First get everything from the export table.
-
- # FIXME This will only find objects that are before us in the tree,
- # which is a problem.
- objects = scope.exported(@convertedtype)
-
- # We want to return all of these objects, and then whichever objects
- # we find in the db.
- array = objects.values
-
- # Mark all of these objects as collected, so that they also get
- # returned to the client. We don't store them in our scope
- # or anything, which is a little odd, but eh.
- array.each do |obj|
- obj.collected = true
- end
-
- count = array.length
-
- # Now we also have to see if there are any exported objects
- # in our own scope.
- scope.lookupexported(@convertedtype).each do |obj|
- objects[obj.name] = obj
- obj.collected = true
- end
-
- bucket = Puppet::TransBucket.new
-
- Puppet::Rails::RailsObject.find_all_by_collectable(true).each do |obj|
- # FIXME This should check that the source of the object is the
- # host we're running on, else it's a bad conflict.
- if objects.include?(obj.name)
- scope.debug("%s[%s] is already exported" % [@convertedtype, obj.name])
- next
- end
- count += 1
- trans = obj.to_trans
- bucket.push(trans)
+ newcoll = Puppet::Parser::Collector.new(scope, @type, q, self.form)
- args = {
- :name => trans.name,
- :type => trans.type,
- }
+ scope.newcollection(newcoll)
- [:file, :line].each do |param|
- if val = trans.send(param)
- args[param] = val
- end
- end
-
- args[:arguments] = {}
- trans.each do |p,v| args[:arguments][p] = v end
-
-
- # XXX Because the scopes don't expect objects to return values,
- # we have to manually add our objects to the scope. This is
- # uber-lame.
- scope.setobject(args)
- end
-
- scope.debug("Collected %s objects of type %s" %
- [count, @convertedtype])
-
- return bucket
- end
+ newcoll
end
end
+end
# $Id$
diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb
new file mode 100644
index 000000000..f69b2e92e
--- /dev/null
+++ b/lib/puppet/parser/ast/collexpr.rb
@@ -0,0 +1,81 @@
+require 'puppet'
+require 'puppet/parser/ast/branch'
+require 'puppet/parser/collector'
+
+# An object that collects stored objects from the central cache and returns
+# them to the current host, yo.
+class Puppet::Parser::AST
+class CollExpr < AST::Branch
+ attr_accessor :test1, :test2, :oper, :form, :type, :parens
+
+ # We return an object that does a late-binding evaluation.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # Make sure our contained expressions have all the info they need.
+ [@test1, @test2].each do |t|
+ if t.is_a?(self.class)
+ t.form ||= self.form
+ t.type ||= self.type
+ end
+ end
+
+ # The code is only used for virtual lookups
+ str1, code1 = @test1.safeevaluate :scope => scope
+ str2, code2 = @test2.safeevaluate :scope => scope
+
+ # First build up the virtual code.
+ # If we're a conjunction operator, then we're calling code. I did
+ # some speed comparisons, and it's at least twice as fast doing these
+ # case statements as doing an eval here.
+ code = proc do |resource|
+ case @oper
+ when "and": code1.call(resource) and code2.call(resource)
+ when "or": code1.call(resource) or code2.call(resource)
+ when "==": resource[str1] == str2
+ when "!=": resource[str1] != str2
+ end
+ end
+
+ # Now build up the rails conditions code
+ if self.parens and self.form == :exported
+ Puppet.warning "Parentheses are ignored in Rails searches"
+ end
+
+ case @oper
+ when "and", "or": oper = @oper.upcase
+ when "==": oper = "="
+ else
+ oper = @oper
+ end
+
+ if oper == "=" or oper == "!="
+ # Add the rails association info where necessary
+ if str1 == "title"
+ str = "title #{oper} '#{str2}'"
+ else
+ unless self.form == :virtual or str1 == "title"
+ parsefail "Collection from the database only supports " +
+ "title matching currently"
+ end
+ str = "rails_parameters.name = '#{str1}' and " +
+ "rails_parameters.value #{oper} '#{str2}'"
+ end
+ else
+ str = [str1, oper, str2].join(" ")
+ end
+
+ return str, code
+ end
+
+ def initialize(hash = {})
+ super
+
+ unless %w{== != and or}.include?(@oper)
+ raise ArgumentError, "Invalid operator %s" % @oper
+ end
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/compdef.rb b/lib/puppet/parser/ast/compdef.rb
deleted file mode 100644
index 17ee60181..000000000
--- a/lib/puppet/parser/ast/compdef.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-class Puppet::Parser::AST
- # Define a new component. This basically just stores the
- # associated parse tree by name in our current scope. Note that
- # there is currently a mismatch in how we look up components -- it
- # usually uses scopes, but sometimes uses '@@settypes'.
- # FIXME This class should verify that each of its direct children
- # has an abstractable name -- i.e., if a file does not include a
- # variable in its name, then the user is essentially guaranteed to
- # encounter an error if the component is instantiated more than
- # once.
- class CompDef < AST::Branch
- attr_accessor :type, :args, :code, :scope, :parentclass
- attr_writer :keyword
-
- @keyword = "define"
-
- class << self
- attr_reader :keyword
- end
-
-
- def self.genclass
- AST::Component
- end
-
- def each
- [@type,@args,@code].each { |child| yield child }
- end
-
- # Store the parse tree.
- def evaluate(hash)
- scope = hash[:scope]
- arghash = {:code => @code}
- arghash[:type] = @type.safeevaluate(:scope => scope)
-
- if @args
- arghash[:args] = @args.safeevaluate(:scope => scope)
- end
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
-
- begin
- comp = self.class.genclass.new(arghash)
- comp.keyword = self.keyword
- scope.settype(arghash[:type], comp)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- end
-
- def initialize(hash)
- @parentclass = nil
- @args = nil
- super
-
- #if @parentclass
- # Puppet.notice "Parent class of %s is %s" %
- # [@type.value, @parentclass.value]
- #end
-
- #Puppet.debug "Defining type %s" % @type.value
- end
-
- def keyword
- if defined? @keyword
- @keyword
- else
- self.class.keyword
- end
- end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("define")),
- @args.tree(indent + 1),
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "define %s(%s) {\n%s }" % [@type, @args, @code]
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
index ebbf21959..312b11d99 100644
--- a/lib/puppet/parser/ast/component.rb
+++ b/lib/puppet/parser/ast/component.rb
@@ -1,80 +1,68 @@
+require 'puppet/parser/ast/branch'
+
class Puppet::Parser::AST
# Evaluate the stored parse tree for a given component. This will
# receive the arguments passed to the component and also the type and
# name of the component.
class Component < AST::Branch
+ include Puppet::Util
+ include Puppet::Util::Warnings
+ include Puppet::Util::MethodHelper
class << self
attr_accessor :name
end
# The class name
- @name = :component
+ @name = :definition
- attr_accessor :type, :args, :code, :scope, :keyword
- attr_accessor :collectable, :parentclass
+ attr_accessor :type, :arguments, :code, :scope, :keyword
+ attr_accessor :exported, :namespace, :fqname, :interp
# These are retrieved when looking up the superclass
- attr_accessor :name, :arguments
+ attr_accessor :name
def evaluate(hash)
origscope = hash[:scope]
objtype = hash[:type]
- @name = hash[:name]
- @arguments = hash[:arguments] || {}
+ name = hash[:name]
+ args = symbolize_options(hash[:arguments] || {})
- @collectable = hash[:collectable]
+ exported = hash[:exported]
pscope = origscope
- #pscope = if ! Puppet[:lexical] or hash[:asparent] == false
- # origscope
- #else
- # @scope
- #end
- scope = pscope.newscope(
- :type => @type,
- :name => @name,
- :keyword => self.keyword
- )
- newcontext = hash[:newcontext]
+ scope = subscope(pscope, name)
- if @collectable or origscope.collectable
- scope.collectable = true
+ if exported or origscope.exported?
+ scope.exported = true
end
-
- unless self.is_a? AST::HostClass and ! newcontext
- #scope.warning "Setting context to %s" % self.object_id
- scope.context = self.object_id
- end
- @scope = scope
-
+ scope = scope
# Additionally, add a tag for whatever kind of class
# we are
- scope.tag(@type)
+ if @type != ""
+ scope.tag(@type)
+ end
- unless @name.nil?
- scope.tag(@name)
+ unless name.nil? or name =~ /[^\w]/
+ scope.tag(name)
end
# define all of the arguments in our local scope
- if self.args
+ if self.arguments
# Verify that all required arguments are either present or
# have been provided with defaults.
# FIXME This should probably also require each parent
# class's arguments...
- self.args.each { |arg, default|
- unless @arguments.include?(arg)
+ self.arguments.each { |arg, default|
+ arg = symbolize(arg)
+ unless args.include?(arg)
if defined? default and ! default.nil?
- @arguments[arg] = default
+ default = default.safeevaluate :scope => scope
+ args[arg] = default
#Puppet.debug "Got default %s for %s in %s" %
# [default.inspect, arg.inspect, @name.inspect]
else
- error = Puppet::ParseError.new(
- "Must pass %s to %s of type %s" %
- [arg.inspect,@name,@type]
- )
- error.line = self.line
- error.file = self.file
- raise error
+ parsefail "Must pass %s to %s of type %s" %
+ [arg,name,@type]
end
end
}
@@ -82,54 +70,116 @@ class Puppet::Parser::AST
# Set each of the provided arguments as variables in the
# component's scope.
- @arguments.each { |arg,value|
- begin
- scope.setvar(arg,@arguments[arg])
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => except
- error = Puppet::ParseError.new(except.message)
- error.line = self.line
- error.file = self.file
- error.set_backtrace except.backtrace
- raise error
+ args.each { |arg,value|
+ unless validattr?(arg)
+ parsefail "%s does not accept attribute %s" % [@type, arg]
+ end
+
+ exceptwrap do
+ scope.setvar(arg.to_s,args[arg])
end
}
- unless @arguments.include? "name"
- scope.setvar("name",@name)
+ unless args.include? "name"
+ scope.setvar("name",name)
end
- # Now just evaluate the code with our new bindings.
- #scope.inside(self) do # part of definition inheritance
- self.code.safeevaluate(:scope => scope)
- #end
+ if self.code
+ return self.code.safeevaluate(:scope => scope)
+ else
+ return nil
+ end
+ end
+
+ def initialize(hash = {})
+ @arguments = nil
+ @parentclass = nil
+ super
+
+ # Deal with metaparams in the argument list.
+ if @arguments
+ @arguments.each do |arg, defvalue|
+ next unless Puppet::Type.metaparamclass(arg)
+ if defvalue
+ warnonce "%s is a metaparam; this value will inherit to all contained elements" % arg
+ else
+ raise Puppet::ParseError,
+ "%s is a metaparameter; please choose another name" %
+ name
+ end
+ end
+ end
+ end
+
+ def parentclass
+ parentobj do |name|
+ @interp.findclass(namespace, name)
+ end
+ end
- # 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
+ # Set our parent class, with a little check to avoid some potential
+ # weirdness.
+ def parentclass=(name)
+ if name == @type
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentclass = name
+ end
+
+ # Hunt down our class object.
+ def parentobj
+ if @parentclass
+ # Cache our result, since it should never change.
+ unless @parentclass.is_a?(AST::HostClass)
+ unless tmp = yield(@parentclass)
+ parsefail "Could not find %s %s" % [self.class.name, @parentclass]
+ end
+
+ if tmp == self
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentclass = tmp
+ end
+ @parentclass
else
- return scope.to_trans
+ nil
end
end
+ # Create a new subscope in which to evaluate our code.
+ def subscope(scope, name = nil)
+ args = {
+ :type => @type,
+ :keyword => self.keyword,
+ :namespace => self.namespace
+ }
+
+ args[:name] = name if name
+ args[:type] = self.type if self.type
+ scope = scope.newscope(args)
+ scope.source = self
+
+ return scope
+ end
+
+ def to_s
+ fqname
+ end
+
# Check whether a given argument is valid. Searches up through
# any parent classes that might exist.
- def validarg?(param)
+ def validattr?(param)
+ return true if Puppet::Type.metaparam?(param)
+
+ param = param.to_s
found = false
- unless @args.is_a? Array
- @args = [@args]
+ unless @arguments.is_a? Array
+ @arguments = [@arguments]
end
- found = @args.detect { |arg|
+ found = @arguments.detect { |arg|
if arg.is_a? Array
arg[0] == param
else
diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb
index 2c63c8b76..f67531ae3 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -11,7 +11,10 @@ class Puppet::Parser::AST
args = @arguments.safeevaluate(:scope => scope)
- return scope.send("function_" + @name, args)
+ #exceptwrap :message => "Failed to execute %s" % @name,
+ # :type => Puppet::ParseError do
+ return scope.send("function_" + @name, args)
+ #end
end
def initialize(hash)
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index 44077983d..4a4664f0f 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -1,3 +1,5 @@
+require 'puppet/parser/ast/component'
+
class Puppet::Parser::AST
# The code associated with a class. This is different from components
# in that each class is a singleton -- only one will exist for a given
@@ -5,152 +7,50 @@ class Puppet::Parser::AST
class HostClass < AST::Component
@name = :class
+ # Are we a child of the passed class? Do a recursive search up our
+ # parentage tree to figure it out.
+ def child_of?(klass)
+ return false unless self.parentclass
+
+ if klass == self.parentclass
+ return true
+ else
+ return self.parentclass.child_of?(klass)
+ end
+ end
+
+ # Evaluate the code associated with this class.
def evaluate(hash)
scope = hash[:scope]
- objname = hash[:name]
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.
- if myscope = scope.lookupclass(self.object_id)
+ if scope.setclass?(self)
Puppet.debug "%s class already evaluated" % @type
-
- # Not used, but will eventually be used to fix #140.
- if myscope.is_a? Puppet::Parser::Scope
- unless scope.object_id == myscope.object_id
- #scope.parent = myscope
- end
- end
return nil
end
- # Set the class before we do anything else, so that it's set
- # during the evaluation and can be inspected.
- scope.setclass(self.object_id, @type)
-
- origscope = scope
-
- # Default to creating a new context
- newcontext = true
-
- # If we've got a parent, then we pass it the original scope we
- # received. It will get passed all the way up to the top class,
- # which will create a subscope and pass that subscope to its
- # subclass.
- if @parentscope = self.evalparent(
- :scope => scope, :arguments => args, :name => objname
- )
- if @parentscope.is_a? Puppet::TransBucket
- raise Puppet::DevError, "Got a bucket instead of a scope"
- end
-
- # Override our scope binding with the parent scope
- # binding.
- scope = @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.
- result = super(
- :scope => scope,
- :arguments => args,
- :type => @type,
- :name => objname, # might be nil
- :newcontext => newcontext,
- :asparent => hash[:asparent] || false # might be nil
- )
-
- # Now set the class again, this time using the scope. This way
- # we can look up the parent scope of this class later, so we
- # can hook the children together.
- scope.setscope(self.object_id, result)
-
- # 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
- transscope = nil
- if @parentscope
- transscope = @parentscope
- until transscope.parent.object_id == origscope.object_id
- transscope = transscope.parent
- end
+ if @parentclass
+ if pklass = self.parentclass
+ pklass.safeevaluate :scope => scope
else
- transscope = result
+ parsefail "Could not find class %s" % @parentclass
end
-
- # 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
- # exact same scope as the children. This is maybe not a good idea
- # but, eh.
- #def evalparent(scope, args, name)
- def evalparent(hash)
- scope = hash[:scope]
- args = hash[:arguments]
- name = hash[:name]
- if @parentclass
- #scope.warning "parent class of %s is %s" %
- # [@type, @parentclass.inspect]
- parentobj = nil
+ # Set the class before we do anything else, so that it's set
+ # during the evaluation and can be inspected.
+ scope.setclass(self)
- begin
- parentobj = scope.lookuptype(@parentclass)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
- unless parentobj
- error = Puppet::ParseError.new(
- "Could not find parent '%s' of '%s'" %
- [@parentclass,@name])
- error.line = self.line
- error.file = self.file
- raise error
- end
+ unless hash[:nosubscope]
+ scope = subscope(scope)
+ end
- # Verify that the parent and child are of the same type
- unless parentobj.class == self.class
- error = Puppet::ParseError.new(
- "Class %s has incompatible parent type, %s vs %s" %
- [@type, parentobj.class, self.class]
- )
- error.file = self.file
- error.line = self.line
- raise error
- end
- # We don't need to pass the type, because the parent will just
- # 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,
- :asparent => true,
- :collectable => self.collectable
- )
+ # Now evaluate our code, yo.
+ if self.code
+ return self.code.evaluate(:scope => scope)
else
- return false
+ return nil
end
end
@@ -158,7 +58,7 @@ class Puppet::Parser::AST
@parentclass = nil
super
end
-
end
-
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index 7b9a283d7..298c0168f 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -28,15 +28,11 @@ class Puppet::Parser::AST
def initialize(hash)
super
- unless @value == 'true' or @value == 'false'
+ unless @value == true or @value == false
raise Puppet::DevError,
"'%s' is not a boolean" % @value
end
- if @value == 'true'
- @value = true
- else
- @value = false
- end
+ @value
end
end
@@ -69,6 +65,9 @@ class Puppet::Parser::AST
# Lower-case words.
class Name < AST::Leaf; end
+ # double-colon separated class names
+ class ClassName < AST::Leaf; end
+
# Host names, either fully qualified or just the short name
class HostName < AST::Leaf
def initialize(hash)
@@ -87,18 +86,8 @@ class Puppet::Parser::AST
# Looks up the value of the object in the scope tree (does
# not include syntactical constructs, like '$' and '{}').
def evaluate(hash)
- begin
+ parsewrap do
return hash[:scope].lookupvar(@value)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::DevError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
end
end
end
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index e4e69bed9..3f508b2bf 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -1,113 +1,70 @@
+require 'puppet/parser/ast/hostclass'
+
class Puppet::Parser::AST
# The specific code associated with a host. Nodes are annoyingly unlike
# other objects. That's just the way it is, at least for now.
class Node < AST::HostClass
@name = :node
- attr_accessor :type, :args, :code, :parentclass
+ attr_accessor :name
#def evaluate(scope, facts = {})
def evaluate(hash)
- origscope = hash[:scope]
- facts = hash[:facts] || {}
-
- # nodes are never instantiated like a normal object,
- # but we need the type to be the name users would use for
- # instantiation, otherwise tags don't work out
+ scope = hash[:scope]
- pscope = origscope
#pscope = if ! Puppet[:lexical] or hash[:asparent]
# @scope
#else
# origscope
#end
- scope = pscope.newscope(
- :type => self.type,
- :keyword => @keyword
- )
- scope.context = self.object_id
+ Puppet.warning "%s => %s" % [scope.type, self.name]
- # Now set all of the facts inside this scope
- facts.each { |var, value|
- scope.setvar(var, value)
- }
-
- if tmp = self.evalparent(scope)
- # Again, override our scope with the parent scope, if
- # there is one.
- scope = tmp
+ # We don't have to worry about the declarativeness of node parentage,
+ # because the entry point is always a single node definition.
+ if parent = self.parentclass
+ scope = parent.safeevaluate :scope => scope
end
+ Puppet.notice "%s => %s" % [scope.type, self.name]
- #scope.tag(@name)
-
- # We never pass the facts to the parent class, because they've
- # already been defined at this top-level scope.
- #super(scope, facts, @name, @name)
+ scope = scope.newscope(
+ :type => self.name,
+ :keyword => @keyword,
+ :source => self,
+ :namespace => "" # nodes are always in ""
+ )
+ Puppet.info "%s => %s" % [scope.type, self.name]
# Mark our node name as a class, too, but strip it of the domain
# name. Make the mark before we evaluate the code, so that it is
# marked within the code itself.
- scope.setclass(self.object_id, @type.sub(/\..+/, ''))
+ scope.setclass(self)
- # And then evaluate our code.
- @code.safeevaluate(:scope => scope)
+ # And then evaluate our code if we have any
+ if self.code
+ @code.safeevaluate(:scope => scope)
+ end
return scope
end
- # Evaluate our parent class.
- def evalparent(scope)
- if @parentclass
- # This is pretty messed up. I don't know if this will
- # work in the long term, but we need to evaluate the node
- # in our own scope, even though our parent node has
- # a scope associated with it, because otherwise we 1) won't
- # get our facts defined, and 2) we won't actually get the
- # objects returned, based on how nodes work.
-
- # We also can't just evaluate the node itself, because
- # it would create a node scope within this scope,
- # and that would cause mass havoc.
- node = nil
-
- # The 'node' method just returns a hash of the node
- # code and name. It's used here, and in 'evalnode'.
- unless hash = scope.node(@parentclass)
- raise Puppet::ParseError,
- "Could not find parent node %s" %
- @parentclass
- end
-
- node = hash[:node]
- type = nil
- if type = node.type
- scope.tag(node.type)
- end
-
- begin
- code = node.code
- code.safeevaluate(:scope => scope, :asparent => true)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
+ def initialize(hash)
+ @parentclass = nil
+ super
- if node.parentclass
- node.evalparent(scope)
- end
+ # Do some validation on the node name
+ if @name =~ /[^-\w.]/
+ raise Puppet::ParseError, "Invalid node name %s" % @name
end
end
- def initialize(hash)
- @parentclass = nil
- super
+ def parentclass
+ parentobj do |name|
+ @interp.nodesearch(name)
+ end
+ @parentclass
end
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/nodedef.rb b/lib/puppet/parser/ast/nodedef.rb
deleted file mode 100644
index 06b104828..000000000
--- a/lib/puppet/parser/ast/nodedef.rb
+++ /dev/null
@@ -1,73 +0,0 @@
-class Puppet::Parser::AST
- # Define a node. The node definition stores a parse tree for each
- # specified node, and this parse tree is only ever looked up when
- # a client connects.
- class NodeDef < AST::Branch
- attr_accessor :names, :code, :parentclass, :keyword, :scope
-
- def each
- [@names,@code].each { |child| yield child }
- end
-
- # Do implicit iteration over each of the names passed.
- def evaluate(hash)
- scope = hash[:scope]
- names = @names.safeevaluate(:scope => scope)
-
- unless names.is_a?(Array)
- names = [names]
- end
-
- names.each { |name|
- #Puppet.debug("defining host '%s' in scope %s" %
- # [name, scope.object_id])
- # We use 'type' here instead of name, because every component
- # type supports both 'type' and 'name', and 'type' is a more
- # appropriate description of the syntactic role that this term
- # plays.
- arghash = {
- :type => name,
- :code => @code
- }
-
- if @parentclass
- arghash[:parentclass] = @parentclass.safeevaluate(:scope => scope)
- end
-
- begin
- node = Node.new(arghash)
- node.keyword = true
- scope.setnode(name, node)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- raise error
- end
- }
- end
-
- def initialize(hash)
- @parentclass = nil
- @keyword = "node"
- super
- end
-
- def tree(indent = 0)
- return [
- @names.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap("node")),
- @code.tree(indent + 1),
- ].join("\n")
- end
-
- def to_s
- return "node %s {\n%s }" % [@name, @code]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/objectdef.rb b/lib/puppet/parser/ast/objectdef.rb
deleted file mode 100644
index ac5fe3070..000000000
--- a/lib/puppet/parser/ast/objectdef.rb
+++ /dev/null
@@ -1,353 +0,0 @@
-class Puppet::Parser::AST
- # Any normal puppet object declaration. Can result in a class or a
- # component, in addition to builtin types.
- class ObjectDef < AST::Branch
- attr_accessor :name, :type, :collectable
- attr_reader :params
-
- # probably not used at all
- def []=(index,obj)
- @params[index] = obj
- end
-
- # probably not used at all
- def [](index)
- return @params[index]
- end
-
- # Iterate across all of our children.
- def each
- [@type,@name,@params].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Does not actually return an object; instead sets an object
- # in the current scope.
- def evaluate(hash)
- scope = hash[:scope]
- @scope = scope
- hash = {}
-
- # Get our type and name.
- objtype = @type.safeevaluate(:scope => scope)
-
- # Disable definition inheritance, for now. 8/27/06, luke
- #if objtype == "super"
- # objtype = supertype()
- # @subtype = true
- #else
- @subtype = false
- #end
-
- # If the type was a variable, we wouldn't have typechecked yet.
- # Do it now, if so.
- unless @checked
- self.typecheck(objtype)
- end
-
- # See if our object type was defined. If not, we know it's
- # builtin because we already typechecked.
- begin
- object = scope.lookuptype(objtype)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
-
- hash = {}
- # Evaluate all of the specified params.
- @params.each { |param|
- ary = param.safeevaluate(:scope => scope)
- hash[ary[0]] = ary[1]
- }
-
- # Now collect info from our parent.
- parentname = nil
- if @subtype
- parentname = supersetup(hash)
- end
-
- objnames = [nil]
- # Determine our name if we have one.
- if self.name
- objnames = @name.safeevaluate(:scope => scope)
- # it's easier to always use an array, even for only one name
- unless objnames.is_a?(Array)
- objnames = [objnames]
- end
- else
- if parentname
- objnames = [parentname]
- else
- # See if they specified the name as a parameter instead of
- # as a normal name (i.e., before the colon).
- unless object # we're a builtin
- if objclass = Puppet::Type.type(objtype)
- namevar = objclass.namevar
-
- tmp = hash["name"] || hash[namevar.to_s]
-
- if tmp
- objnames = [tmp]
- end
- else
- # this should never happen, because we've already
- # typechecked, but it's no real problem if it does
- # happen. We just end up with an object with no
- # name.
- end
- end
- end
- end
-
- # this is where our implicit iteration takes place;
- # if someone passed an array as the name, then we act
- # just like the called us many times
- objnames.collect { |objname|
- # If the object is a class, that means it's a builtin type, so
- # we just store it in the scope
- begin
- #Puppet.debug(
- # ("Setting object '%s' " +
- # "in scope %s " +
- # "with arguments %s") %
- # [objname, scope.object_id, hash.inspect]
- #)
- obj = scope.setobject(
- :type => objtype,
- :name => objname,
- :arguments => hash,
- :file => @file,
- :line => @line,
- :collectable => self.collectable
- )
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
- }.reject { |obj| obj.nil? }
- end
-
- # Create our ObjectDef. Handles type checking for us.
- def initialize(hash)
- @checked = false
- super
-
- #self.typecheck(@type.value)
- end
-
- # Verify that all passed parameters are valid
- def paramcheck(builtin, objtype)
- # This defaults to true
- unless Puppet[:paramcheck]
- return
- end
-
- @params.each { |param|
- if builtin
- self.parambuiltincheck(builtin, param)
- else
- self.paramdefinedcheck(objtype, param)
- end
- }
-
- # Mark that we've made it all the way through.
- @checked = true
- end
-
- def parambuiltincheck(type, param)
- unless param.is_a?(AST::ObjectParam)
- raise Puppet::DevError,
- "Got something other than param"
- end
- begin
- pname = param.param.value
- rescue => detail
- raise Puppet::DevError, detail.to_s
- end
-
- return if pname == "name" # always allow these
- unless type.validattr?(pname)
- error = Puppet::ParseError.new(
- "Invalid parameter '%s' for type '%s'" %
- [pname, type.name]
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
-
- def paramdefinedcheck(objtype, param)
- # FIXME We might need to do more here eventually. Metaparams
- # behave strangely on containers.
- if Puppet::Type.metaparam?(param.param.value.intern)
- return
- end
-
- begin
- pname = param.param.value
- rescue => detail
- raise Puppet::DevError, detail.to_s
- end
-
- unless objtype.validarg?(pname)
- error = Puppet::ParseError.new(
- "Invalid parameter '%s' for type '%s'" %
- [pname,objtype.type]
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
-
- # Set the parameters for our object.
- def params=(params)
- if params.is_a?(AST::ASTArray)
- @params = params
- else
- @params = AST::ASTArray.new(
- :line => params.line,
- :file => params.file,
- :children => [params]
- )
- end
- end
-
- def supercomp
- unless defined? @supercomp
- if @scope and comp = @scope.inside
- @supercomp = comp
- else
- error = Puppet::ParseError.new(
- "'super' is only valid within definitions"
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supercomp
- end
-
- # Take all of the arguments of our parent and add them into our own,
- # without overriding anything.
- def supersetup(hash)
- comp = supercomp()
-
- # Now check each of the arguments from the parent.
- comp.arguments.each do |name, value|
- unless hash.has_key? name
- hash[name] = value
- end
- end
-
- # Return the parent name, so it can be used if appropriate.
- return comp.name
- end
-
- # Retrieve our supertype.
- def supertype
- unless defined? @supertype
- if parent = supercomp.parentclass
- @supertype = parent
- else
- error = Puppet::ParseError.new(
- "%s does not have a parent class" % comp.type
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supertype
- end
-
- # Print this object out.
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @name.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @params.collect { |param|
- begin
- param.tree(indent + 1)
- rescue NoMethodError => detail
- Puppet.err @params.inspect
- error = Puppet::DevError.new(
- "failed to tree a %s" % self.class
- )
- error.set_backtrace detail.backtrace
- raise error
- end
- }.join("\n")
- ].join("\n")
- end
-
- # Verify that the type is valid. This throws an error if there's
- # a problem, so the return value doesn't matter
- def typecheck(objtype)
- # This will basically always be on, but I wanted to make it at
- # least simple to turn off if it came to that
- unless Puppet[:typecheck]
- return
- end
-
- builtin = false
- begin
- builtin = Puppet::Type.type(objtype)
- rescue TypeError
- # nothing; we've already set builtin to false
- end
-
- typeobj = nil
- if builtin
- self.paramcheck(builtin, objtype)
- else
- # If there's no set scope, then we're in initialize, not
- # evaluate, so we can't test defined types.
- return true unless defined? @scope and @scope
-
- # Unless we can look up the type, throw an error
- unless typeobj = @scope.lookuptype(objtype)
- error = Puppet::ParseError.new(
- "Unknown type '%s'" % objtype
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
-
- # Now that we have the type, verify all of the parameters.
- # Note that we're now passing an AST Class object or whatever
- # as the type, not a simple string.
- self.paramcheck(builtin, typeobj)
- end
- end
-
- def to_s
- return "%s => { %s }" % [@name,
- @params.collect { |param|
- param.to_s
- }.join("\n")
- ]
- end
- end
-end
diff --git a/lib/puppet/parser/ast/objectref.rb b/lib/puppet/parser/ast/objectref.rb
deleted file mode 100644
index f9a63e222..000000000
--- a/lib/puppet/parser/ast/objectref.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-class Puppet::Parser::AST
- # A reference to an object. Only valid as an rvalue.
- class ObjectRef < AST::Branch
- attr_accessor :name, :type
-
- def each
- [@type,@name].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Evaluate our object, but just return a simple array of the type
- # and name.
- def evaluate(hash)
- scope = hash[:scope]
- objtype = @type.safeevaluate(:scope => scope)
- objnames = @name.safeevaluate(:scope => scope)
-
- # it's easier to always use an array, even for only one name
- unless objnames.is_a?(Array)
- objnames = [objnames]
- end
-
- # See if we can look the object up in our scope tree.
- begin
- object = scope.lookuptype(objtype)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
- end
-
- # If the type isn't defined, verify that it's builtin
- unless object or Puppet::Type.type(objtype)
- error = Puppet::ParseError.new("Could not find type %s" %
- objtype.inspect)
- error.line = self.line
- error.file = self.file
- raise error
- end
-
- # should we implicitly iterate here?
- # yes, i believe that we essentially have to...
- ret = objnames.collect { |objname|
- if object.is_a?(AST::Component)
- objname = "%s[%s]" % [objtype,objname]
- objtype = "component"
- end
- [objtype,objname]
- }.reject { |obj| obj.nil? }
-
- # Return a flattened array, since we know that we've created an
- # array
- return *ret
- end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @name.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin))
- ].join("\n")
- end
-
- def to_s
- return "%s[%s]" % [@name,@type]
- end
- end
-
-end
diff --git a/lib/puppet/parser/ast/resourcedef.rb b/lib/puppet/parser/ast/resourcedef.rb
new file mode 100644
index 000000000..1c9333030
--- /dev/null
+++ b/lib/puppet/parser/ast/resourcedef.rb
@@ -0,0 +1,215 @@
+require 'puppet/parser/ast/branch'
+
+# Any normal puppet object declaration. Can result in a class or a
+# component, in addition to builtin types.
+class Puppet::Parser::AST
+class ResourceDef < AST::Branch
+ attr_accessor :title, :type, :exported, :virtual
+ attr_reader :params
+
+ # probably not used at all
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ # probably not used at all
+ def [](index)
+ return @params[index]
+ end
+
+ # Iterate across all of our children.
+ def each
+ [@type,@title,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Does not actually return an object; instead sets an object
+ # in the current scope.
+ def evaluate(hash)
+ scope = hash[:scope]
+ @scope = scope
+ hash = {}
+
+ # Get our type and name.
+ objtype = @type
+
+ # Disable definition inheritance, for now. 8/27/06, luke
+ #if objtype == "super"
+ # objtype = supertype()
+ # @subtype = true
+ #else
+ @subtype = false
+ #end
+
+ # Evaluate all of the specified params.
+ paramobjects = @params.collect { |param|
+ param.safeevaluate(:scope => scope)
+ }
+
+ # Now collect info from our parent.
+ parentname = nil
+ if @subtype
+ parentname = supersetup(hash)
+ end
+
+ objtitles = nil
+ # Determine our name if we have one.
+ if self.title
+ objtitles = @title.safeevaluate(:scope => scope)
+ # it's easier to always use an array, even for only one name
+ unless objtitles.is_a?(Array)
+ objtitles = [objtitles]
+ end
+ else
+ if parentname
+ objtitles = [parentname]
+ else
+ # See if they specified the name as a parameter instead of
+ # as a normal name (i.e., before the colon).
+ unless object # we're a builtin
+ if objclass = Puppet::Type.type(objtype)
+ namevar = objclass.namevar
+
+ tmp = hash["name"] || hash[namevar.to_s]
+
+ if tmp
+ objtitles = [tmp]
+ end
+ else
+ # This isn't grammatically legal.
+ raise Puppet::ParseError, "Got a resource with no title"
+ end
+ end
+ end
+ end
+
+ # This is where our implicit iteration takes place; if someone
+ # passed an array as the name, then we act just like the called us
+ # many times.
+ objtitles.collect { |objtitle|
+ exceptwrap :type => Puppet::ParseError do
+ obj = Puppet::Parser::Resource.new(
+ :type => objtype,
+ :title => objtitle,
+ :params => paramobjects,
+ :file => @file,
+ :line => @line,
+ :exported => self.exported || scope.exported,
+ :virtual => self.virtual,
+ :source => scope.source,
+ :scope => scope
+ )
+
+ # And then store the resource in the scope.
+ # XXX At some point, we need to switch all of this to return
+ # objects instead of storing them like this.
+ scope.setresource(obj)
+ obj
+ end
+ }.reject { |obj| obj.nil? }
+ end
+
+ # Create our ResourceDef. Handles type checking for us.
+ def initialize(hash)
+ @checked = false
+ super
+
+ #self.typecheck(@type.value)
+ end
+
+ # Set the parameters for our object.
+ def params=(params)
+ if params.is_a?(AST::ASTArray)
+ @params = params
+ else
+ @params = AST::ASTArray.new(
+ :line => params.line,
+ :file => params.file,
+ :children => [params]
+ )
+ end
+ end
+
+ def supercomp
+ unless defined? @supercomp
+ if @scope and comp = @scope.inside
+ @supercomp = comp
+ else
+ error = Puppet::ParseError.new(
+ "'super' is only valid within definitions"
+ )
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ end
+ @supercomp
+ end
+
+ # Take all of the arguments of our parent and add them into our own,
+ # without overriding anything.
+ def supersetup(hash)
+ comp = supercomp()
+
+ # Now check each of the arguments from the parent.
+ comp.arguments.each do |name, value|
+ unless hash.has_key? name
+ hash[name] = value
+ end
+ end
+
+ # Return the parent name, so it can be used if appropriate.
+ return comp.name
+ end
+
+ # Retrieve our supertype.
+ def supertype
+ unless defined? @supertype
+ if parent = supercomp.parentclass
+ @supertype = parent
+ else
+ error = Puppet::ParseError.new(
+ "%s does not have a parent class" % comp.type
+ )
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ end
+ @supertype
+ end
+
+ # Print this object out.
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @title.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @params.collect { |param|
+ begin
+ param.tree(indent + 1)
+ rescue NoMethodError => detail
+ Puppet.err @params.inspect
+ error = Puppet::DevError.new(
+ "failed to tree a %s" % self.class
+ )
+ error.set_backtrace detail.backtrace
+ raise error
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@title,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/typedefaults.rb b/lib/puppet/parser/ast/resourcedefaults.rb
index 2e11a1b94..44db4d465 100644
--- a/lib/puppet/parser/ast/typedefaults.rb
+++ b/lib/puppet/parser/ast/resourcedefaults.rb
@@ -1,32 +1,22 @@
class Puppet::Parser::AST
- # A statement syntactically similar to an ObjectDef, but uses a
+ # A statement syntactically similar to an ResourceDef, but uses a
# capitalized object type and cannot have a name.
- class TypeDefaults < AST::Branch
+ class ResourceDefaults < AST::Branch
attr_accessor :type, :params
def each
[@type,@params].each { |child| yield child }
end
- # As opposed to ObjectDef, this stores each default for the given
+ # As opposed to ResourceDef, this stores each default for the given
# object type.
def evaluate(hash)
scope = hash[:scope]
- type = @type.safeevaluate(:scope => scope)
+ type = @type.downcase
params = @params.safeevaluate(:scope => scope)
- begin
- scope.setdefaults(type.downcase,params)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
+ parsewrap do
+ scope.setdefaults(type, params)
end
end
@@ -44,3 +34,5 @@ class Puppet::Parser::AST
end
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/resourceoverride.rb b/lib/puppet/parser/ast/resourceoverride.rb
new file mode 100644
index 000000000..26d69ae97
--- /dev/null
+++ b/lib/puppet/parser/ast/resourceoverride.rb
@@ -0,0 +1,62 @@
+require 'puppet/parser/ast/resourcedef'
+
+class Puppet::Parser::AST
+ # Set a parameter on a resource specification created somewhere else in the
+ # configuration. The object is responsible for verifying that this is allowed.
+ class ResourceOverride < ResourceDef
+ attr_accessor :object
+ attr_reader :params
+
+ # Iterate across all of our children.
+ def each
+ [@object,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Does not actually return an object; instead sets an object
+ # in the current scope.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # Get our object reference.
+ object = @object.safeevaluate(:scope => scope)
+
+ hash = {}
+
+ # Evaluate all of the specified params.
+ params = @params.collect { |param|
+ param.safeevaluate(:scope => scope)
+ }
+
+ # Now we just create a normal resource, but we call a very different
+ # method on the scope.
+ obj = Puppet::Parser::Resource.new(
+ :type => object.type,
+ :title => object.title,
+ :params => params,
+ :file => @file,
+ :line => @line,
+ :source => scope.source,
+ :scope => scope
+ )
+
+ # Now we tell the scope that it's an override, and it behaves as
+ # necessary.
+ scope.setoverride(obj)
+
+ obj
+ end
+
+ # Create our ResourceDef. Handles type checking for us.
+ def initialize(hash)
+ @checked = false
+ super
+
+ #self.typecheck(@type.value)
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/objectparam.rb b/lib/puppet/parser/ast/resourceparam.rb
index 87c3e5e8e..248e91b32 100644
--- a/lib/puppet/parser/ast/objectparam.rb
+++ b/lib/puppet/parser/ast/resourceparam.rb
@@ -1,6 +1,8 @@
+require 'puppet/parser/ast/branch'
+
class Puppet::Parser::AST
- # The AST object for the parameters inside ObjectDefs and Selectors.
- class ObjectParam < AST::Branch
+ # The AST object for the parameters inside ResourceDefs and Selectors.
+ class ResourceParam < AST::Branch
attr_accessor :value, :param
def each
@@ -10,9 +12,17 @@ class Puppet::Parser::AST
# Return the parameter and the value.
def evaluate(hash)
scope = hash[:scope]
- param = @param.safeevaluate(:scope => scope)
+ param = @param
value = @value.safeevaluate(:scope => scope)
- return [param, value]
+
+ args = {:name => param, :value => value, :source => scope.source}
+ [:line, :file].each do |p|
+ if v = self.send(p)
+ args[p] = v
+ end
+ end
+
+ return Puppet::Parser::Resource::Param.new(args)
end
def tree(indent = 0)
@@ -27,5 +37,6 @@ class Puppet::Parser::AST
return "%s => %s" % [@param,@value]
end
end
-
end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/resourceref.rb b/lib/puppet/parser/ast/resourceref.rb
new file mode 100644
index 000000000..b0fe5f6d7
--- /dev/null
+++ b/lib/puppet/parser/ast/resourceref.rb
@@ -0,0 +1,44 @@
+require 'puppet/parser/ast/branch'
+
+class Puppet::Parser::AST
+ # A reference to an object. Only valid as an rvalue.
+ class ResourceRef < AST::Branch
+ attr_accessor :title, :type
+
+ def each
+ [@type,@title].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Evaluate our object, but just return a simple array of the type
+ # and name.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ # We want a lower-case type.
+ objtype = @type.downcase
+
+ title = @title.safeevaluate(:scope => scope)
+
+ return Puppet::Parser::Resource::Reference.new(
+ :type => objtype, :title => title
+ )
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ @title.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin))
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s[%s]" % [@type,@title]
+ end
+ end
+end
+
+# $Id$
diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb
index 5d1c41494..fe3fe9eaf 100644
--- a/lib/puppet/parser/ast/selector.rb
+++ b/lib/puppet/parser/ast/selector.rb
@@ -42,12 +42,8 @@ class Puppet::Parser::AST
if default
retvalue = default.value.safeevaluate(:scope => scope)
else
- error = Puppet::ParseError.new(
+ self.fail Puppet::ParseError,
"No value for selector param '%s'" % paramvalue
- )
- error.line = self.line
- error.file = self.file
- raise error
end
end
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index 79129f31a..30eef204a 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -10,18 +10,8 @@ class Puppet::Parser::AST
name = @name.safeevaluate(:scope => scope)
value = @value.safeevaluate(:scope => scope)
- begin
+ parsewrap do
scope.setvar(name,value)
- rescue Puppet::ParseError => except
- except.line = self.line
- except.file = self.file
- raise except
- rescue => detail
- error = Puppet::ParseError.new(detail)
- error.line = self.line
- error.file = self.file
- error.set_backtrace detail.backtrace
- raise error
end
end
@@ -43,3 +33,5 @@ class Puppet::Parser::AST
end
end
+
+# $Id$