summaryrefslogtreecommitdiffstats
path: root/lib/puppet/parser
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2005-08-23 16:09:14 +0000
committerLuke Kanies <luke@madstop.com>2005-08-23 16:09:14 +0000
commit6029ef7812765775306ff8394005c326e359d886 (patch)
tree32cbe5ea68e0e9fbdc0935d0b41e58fdfcba9e3d /lib/puppet/parser
parente87eb58ce8dc40ba8c66233bf17cea61094e7647 (diff)
downloadpuppet-6029ef7812765775306ff8394005c326e359d886.tar.gz
puppet-6029ef7812765775306ff8394005c326e359d886.tar.xz
puppet-6029ef7812765775306ff8394005c326e359d886.zip
Moving all files into a consolidated trunk. All tests pass except the known-failing certificate test, but there appear to be some errors that are incorrectly not resulting in failurs. I will track those down ASAP.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@576 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'lib/puppet/parser')
-rw-r--r--lib/puppet/parser/ast.rb1238
-rw-r--r--lib/puppet/parser/grammar.ra672
-rw-r--r--lib/puppet/parser/interpreter.rb135
-rw-r--r--lib/puppet/parser/lexer.rb225
-rw-r--r--lib/puppet/parser/makefile5
-rw-r--r--lib/puppet/parser/parser.rb1264
-rw-r--r--lib/puppet/parser/scope.rb427
7 files changed, 3966 insertions, 0 deletions
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
new file mode 100644
index 000000000..ff237f62e
--- /dev/null
+++ b/lib/puppet/parser/ast.rb
@@ -0,0 +1,1238 @@
+#/usr/bin/ruby
+
+# $Id$
+# vim: syntax=ruby
+
+# the AST tree
+
+# the parent class for all of our syntactical objects
+module Puppet
+ module Parser
+ class ASTError < RuntimeError; end
+ #---------------------------------------------------------------
+ class AST
+ attr_accessor :line, :file, :parent
+
+ @@pink = ""
+ @@green = ""
+ @@yellow = ""
+ @@slate = ""
+ @@reset = ""
+
+ @@indent = " " * 4
+ @@indline = @@pink + ("-" * 4) + @@reset
+ @@midline = @@slate + ("-" * 4) + @@reset
+
+ @@settypes = Hash.new { |hash,key|
+ hash[key] = Hash.new(0)
+ }
+
+ def AST.indention
+ return @@indent * @@indention
+ end
+
+ def AST.midline
+ return @@midline
+ end
+
+ def evaluate(scope)
+ #Puppet.debug("Evaluating ast %s" % @name)
+ value = self.collect { |obj|
+ obj.evaluate(scope)
+ }.reject { |obj|
+ obj.nil?
+ }
+ end
+
+ def typewrap(string)
+ #return self.class.to_s.sub(/.+::/,'') +
+ #"(" + @@green + string.to_s + @@reset + ")"
+ return @@green + string.to_s + @@reset +
+ "(" + self.class.to_s.sub(/.+::/,'') + ")"
+ end
+
+ def initialize(args)
+ # this has to wait until all of the objects are defined
+ unless defined? @@klassorder
+ @@klassorder = [
+ AST::VarDef, AST::TypeDefaults,
+ AST::ObjectDef, AST::StatementArray
+ ]
+ end
+
+ args.each { |param,value|
+ method = param.to_s + "="
+ unless self.respond_to?(method)
+ error = Puppet::ParseError.new(
+ "Invalid parameter %s to object class %s" %
+ [method,self.class.to_s]
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+
+ begin
+ #Puppet.debug("sending %s to %s" % [method, self.class])
+ self.send(method,value)
+ rescue => detail
+ error = Puppet::DevError.new(
+ "Could not set parameter %s on class %s: %s" %
+ [method,self.class.to_s,detail]
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ end
+
+ #---------------------------------------------------------------
+ # this differentiation is used by the interpreter
+ # these objects have children
+ class Branch < AST
+ include Enumerable
+ attr_accessor :pin, :children
+
+ def each
+ @children.each { |child|
+ yield child
+ }
+ end
+
+ def evaluate(scope)
+ #Puppet.debug("Evaluating branch")
+# rets = nil
+# if scope.declarative
+# # if we're operating declaratively, then we want to get
+# # all of our 'setting' operations done first
+# rets = @children.sort { |a,b|
+# [a,b].each { |i|
+# unless @@klassorder.include?(i.class)
+# raise "Order not defined for %s" % i.class
+# end
+# }
+# @@klassorder.index(a.class) <=> @@klassorder.index(b.class)
+# }.collect { |item|
+# Puppet.debug "Decl evaluating %s" % item.class
+# item.evaluate(scope)
+# }
+# else
+# rets = @children.collect { |item|
+# item.evaluate(scope)
+# }
+# end
+ self.collect { |item|
+ #Puppet.debug "Evaluating %s" % item.class
+ item.evaluate(scope)
+ }.reject { |obj|
+ obj.nil
+ }
+ end
+
+ def initialize(arghash)
+ super(arghash)
+
+ unless defined? @children
+ @children = []
+ end
+
+ #puts "children is '%s'" % [@children]
+
+ self.each { |child|
+ if child.class == Array
+ error = Puppet::DevError.new(
+ "child for %s(%s) is array" % [self.class,self.parent]
+ )
+ error.stack = caller
+ raise error
+ end
+ unless child.nil?
+ child.parent = self
+ end
+ }
+ end
+
+ def tree(indent = 0)
+ return ((@@indline * indent) +
+ self.typewrap(self.pin)) + "\n" + self.collect { |child|
+ child.tree(indent + 1)
+ }.join("\n")
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ASTArray < AST::Branch
+ include Enumerable
+
+ def [](index)
+ @children[index]
+ end
+
+ def evaluate(scope)
+ rets = nil
+ if scope.declarative
+ test = [
+ AST::VarDef, AST::TypeDefaults
+ ]
+
+ # if we're operating declaratively, then we want to get
+ # all of our 'setting' operations done first
+ settors = []
+ others = []
+ @children.each { |child|
+ if test.include?(child.class)
+ settors.push child
+ else
+ others.push child
+ end
+ }
+ rets = [settors,others].flatten.collect { |child|
+ child.evaluate(scope)
+ }
+ else
+ rets = @children.collect { |item|
+ item.evaluate(scope)
+ }
+ end
+ rets = rets.reject { |obj| obj.nil? }
+ end
+
+ def initialize(hash)
+ super(hash)
+
+ @children.each { |child|
+ unless child.is_a?(AST)
+ Puppet.err("child %s is not an ast" % child)
+ exit
+ end
+ }
+ return self
+ end
+
+ def push(*ary)
+ ary.each { |child|
+ #Puppet.debug "adding %s(%s) of type %s to %s" %
+ # [child, child.object_id, child.class.to_s.sub(/.+::/,''),
+ # self.object_id]
+ @children.push(child)
+ }
+
+ return self
+ end
+
+ def to_s
+ return "[" + @children.collect { |child|
+ child.to_s
+ }.join(", ") + "]"
+ end
+
+ def tree(indent = 0)
+ #puts((AST.indent * indent) + self.pin)
+ self.collect { |child|
+ if child.class == Array
+ Puppet.debug "child is array for %s" % self.class
+ end
+ child.tree(indent)
+ }.join("\n" + (AST.midline * (indent+1)) + "\n")
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class StatementArray < ASTArray
+ def evaluate(scope)
+ rets = nil
+ if scope.declarative
+ # if we're operating declaratively, then we want to get
+ # all of our 'setting' operations done first
+ rets = @children.sort { |a,b|
+ [a,b].each { |i|
+ unless @@klassorder.include?(i.class)
+ error = Puppet::DevError.new(
+ "Order not defined for %s" % i.class
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ @@klassorder.index(a.class) <=> @@klassorder.index(b.class)
+ }.collect { |item|
+ Puppet.debug "Decl evaluating %s" % item.class
+ item.evaluate(scope)
+ }.reject { |obj| obj.nil? }
+ else
+ rets = @children.collect { |item|
+ item.evaluate(scope)
+ }.reject { |obj| obj.nil? }
+ end
+
+ return rets
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # and these ones don't
+ class Leaf < AST
+ attr_accessor :value, :type
+
+ # this only works if @value has already been evaluated
+ # otherwise you get AST objects, which you don't likely want...
+ def evaluate(scope)
+ return @value
+ end
+
+ def tree(indent = 0)
+ return ((@@indent * indent) + self.typewrap(self.value))
+ end
+
+ def to_s
+ return @value
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Boolean < AST::Leaf
+ def initialize(hash)
+ super
+
+ unless @value == 'true' or @value == 'false'
+ error = Puppet::DevError.new(
+ "'%s' is not a boolean" % @value
+ )
+ error.stack = caller
+ raise error
+ end
+ if @value == 'true'
+ @value = true
+ else
+ @value = false
+ end
+ end
+
+ def evaluate(scope)
+ return @value
+ end
+
+ def to_s
+ return @value
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class String < AST::Leaf
+ def evaluate(scope)
+ return scope.strinterp(@value)
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Word < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Type < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Name < AST::Leaf; end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Variable < Word
+ def evaluate(scope)
+ # look up the variable value in the symbol table
+ begin
+ return 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.stack = caller
+ raise error
+ end
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectDef < AST::Branch
+ attr_accessor :name, :type
+ attr_reader :params
+
+ def []=(index,obj)
+ @params[index] = obj
+ end
+
+ def [](index)
+ return @params[index]
+ end
+
+ def each
+ #Puppet.debug("each called on %s" % self)
+ [@type,@name,@params].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def evaluate(scope)
+ hash = {}
+
+ objtype = @type.evaluate(scope)
+ objnames = @name.evaluate(scope)
+
+ # first, retrieve the defaults
+ defaults = scope.lookupdefaults(objtype)
+ defaults.each { |var,value|
+ Puppet.debug "Found default %s for %s" %
+ [var,objtype]
+
+ hash[var] = value
+ }
+
+ # then set all of the specified params
+ @params.each { |param|
+ ary = param.evaluate(scope)
+ hash[ary[0]] = ary[1]
+ }
+
+ # it's easier to always use an array, even for only one name
+ unless objnames.is_a?(Array)
+ objnames = [objnames]
+ end
+
+ 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.stack = caller
+ raise error
+ 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 type is not defined in our scope, we assume
+ # that it's a type that the client will understand, so we
+ # just store it in our objectable
+ if object.nil?
+ begin
+ Puppet.debug("Setting object '%s' with arguments %s" %
+ [objname, hash.inspect])
+ obj = scope.setobject(
+ objtype,
+ objname,
+ hash,
+ @file,
+ @line
+ )
+ 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.stack = caller
+ raise error
+ end
+ else
+ # but things like components create a new type; if we find
+ # one of those, evaluate that with our arguments
+ Puppet.debug("Calling object '%s' with arguments %s" %
+ [object.name, hash.inspect])
+ object.evaluate(scope,hash,objtype,objname)
+ end
+ }.reject { |obj| obj.nil? }
+ end
+
+ def initialize(hash)
+ super
+
+ Puppet.debug "%s id is %s" % [@name, object_id]
+
+ # we don't have to evaluate because we require bare words
+ # for types
+ objtype = @type.value
+
+ if Puppet[:typecheck]
+ builtin = false
+ begin
+ builtin = Puppet::Type.type(objtype)
+ rescue TypeError
+ # nothing; we've already set builtin to false
+ end
+ if builtin
+ # we're a builtin type
+ #Puppet.debug "%s is a builtin type" % objtype
+ if Puppet[:paramcheck]
+ @params.each { |param|
+ pname = param.param.value
+ next if pname == "name" # always allow these
+ unless builtin.validarg?(pname)
+ error = Puppet::ParseError.new(
+ "Invalid parameter '%s' for type '%s'" %
+ [pname,objtype]
+ )
+ error.stack = caller
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ }
+ end
+ # FIXME this should use scoping rules to find the set type,
+ # not a global list
+ elsif @@settypes.include?(objtype)
+ # we've defined it locally
+ Puppet.debug "%s is a defined type" % objtype
+ hash = @@settypes[objtype]
+ @params.each { |param|
+ # FIXME we might need to do more here eventually...
+ if Puppet::Type.metaparam?(param.param.value.intern)
+ next
+ end
+
+ pname = param.param.value
+ unless hash.include?(pname)
+ error = Puppet::ParseError.new(
+ "Invalid parameter '%s' for type '%s'" %
+ [pname,objtype]
+ )
+ error.stack = caller
+ error.line = self.line
+ error.file = self.file
+ raise error
+ end
+ }
+ else
+ # we don't know anything about it
+ error = Puppet::ParseError.new(
+ "Unknown type '%s'" % objtype
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+ end
+
+ def params=(params)
+ if params.is_a?(AST::ASTArray)
+ @params = params
+ else
+ @params = AST::ASTArray.new(
+ :children => [params]
+ )
+ end
+ end
+
+ 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.stack = caller
+ raise error
+ end
+ }.join("\n")
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => { %s }" % [@name,
+ @params.collect { |param|
+ param.to_s
+ }.join("\n")
+ ]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectRef < AST::Branch
+ attr_accessor :name, :type
+
+ def each
+ #Puppet.debug("each called on %s" % self)
+ [@type,@name].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ def evaluate(scope)
+ objtype = @type.evaluate(scope)
+ objnames = @name.evaluate(scope)
+
+ # it's easier to always use an array, even for only one name
+ unless objnames.is_a?(Array)
+ objnames = [objnames]
+ end
+
+ 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.stack = caller
+ raise error
+ end
+ Puppet.debug "ObjectRef returned type %s" % object
+
+ # should we implicitly iterate here?
+ # yes, i believe that we essentially have to...
+ objnames.collect { |objname|
+ if object.is_a?(Component)
+ objname = "%s[%s]" % [objtype,objname]
+ objtype = "component"
+ end
+ [objtype,objname]
+ }.reject { |obj| obj.nil? }
+ 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
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class ObjectParam < AST::Branch
+ attr_accessor :value, :param
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ return [@param.evaluate(scope),@value.evaluate(scope)]
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@param,@value]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Test < AST::Branch
+ attr_accessor :lhs, :rhs
+
+ # is our test true or false?
+ def evaluate(scope)
+ # retrieve our values and make them a touch easier to manage
+ lvalue = @lhs.evaluate(scope)
+ rvalue = @rhs.evaluate(scope)
+
+ # FIXME this probably won't work except on strings right now...
+ retvalue = lvalue.send(@pin, rvalue)
+
+ #Puppet.debug "test '%s' returned %s" % [self.to_s,retvalue]
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ return [
+ @lhs.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @rhs.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@lhs,@rhs].each { |child| yield child }
+ end
+
+ def to_s
+ return "%s %s %s" % [@lhs,@pin,@rhs]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class If < AST::Branch
+ attr_accessor :test, :statements, :else, :elsif
+
+ # 'if' is a bit special, since we don't want to continue
+ # evaluating if a test turns up true
+ def evaluate(scope)
+ scope = scope.newscope
+ retvalue = nil
+ if @test.evaluate(scope)
+ Puppet.debug "%s is true" % @test
+ retvalue = @statements.evaluate(scope)
+ elsif defined? @elsif
+ Puppet.debug "%s is false" % @test
+ elsereturn = nil
+ @elsif.each { |elsetest|
+ if elsereturn = @elsif.evaluate(scope)
+ retvalue = elsereturn
+ end
+ }
+ elsif defined? @else
+ retvalue = @else.evaluate(scope)
+ else
+ Puppet.debug "None of the ifs are true"
+ end
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ rettree = [
+ @test.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @statements.tree(indent + 1)
+ ]
+ if defined? @elsif
+ @elsif.each { |elsetest|
+ rettree.push(elsetest.tree(indent + 1))
+ }
+ end
+
+ if defined? @else
+ rettree.push(@else.tree(indent+1))
+ end
+
+ return rettree.flatten.join("\n")
+ end
+
+ def each
+ list = [@test,@statements]
+
+ if defined? @elsif
+ @elsif.each { |tmp|
+ list.push(tmp)
+ }
+ end
+
+ if defined? @else
+ list.push(@else)
+ end
+
+ list.each { |child| yield child }
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class Selector < AST::Branch
+ attr_accessor :param, :value
+
+ # okay, here's a decision point...
+ def evaluate(scope)
+ # retrieve our values and make them a touch easier to manage
+ hash = Hash[*(@value.evaluate(scope).flatten)]
+
+ retvalue = nil
+
+ paramvalue = @param.evaluate(scope)
+
+ retvalue = hash.detect { |test,value|
+ # FIXME this will return variables named 'default'...
+ if paramvalue == test
+ break value
+ end
+ }
+ if retvalue.nil?
+ if hash.include?("default")
+ return hash["default"]
+ else
+ error = Puppet::ParseError.new(
+ "No value for selector param '%s'" % paramvalue
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = self.stack
+ raise error
+ end
+ end
+
+ return retvalue
+ end
+
+ def tree(indent = 0)
+ return [
+ @param.tree(indent + 1),
+ ((@@indline * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def each
+ [@param,@value].each { |child| yield child }
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class VarDef < AST::Branch
+ attr_accessor :name, :value
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+ value = @value.evaluate(scope)
+
+ Puppet.debug "setting %s to %s" % [name,value]
+ begin
+ 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.stack = caller
+ raise error
+ end
+ end
+
+ def each
+ [@name,@value].each { |child| yield child }
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @value.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s => %s" % [@name,@value]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ class TypeDefaults < AST::Branch
+ attr_accessor :type, :params
+
+ def each
+ [@type,@params].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ type = @type.evaluate(scope)
+ params = @params.evaluate(scope)
+
+ #Puppet.info "Params are %s" % params.inspect
+ #Puppet.debug("evaluating '%s.%s' with values [%s]" %
+ # [type,name,values])
+ # okay, now i need the interpreter's client object thing...
+ 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.stack = caller
+ raise error
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @type.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap(self.pin)),
+ @params.tree(indent + 1)
+ ].join("\n")
+ end
+
+ def to_s
+ return "%s { %s }" % [@type,@params]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # these are analogous to defining new object types
+ class CompDef < AST::Branch
+ attr_accessor :name, :args, :code
+
+ def each
+ [@name,@args,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+
+ args = @args.evaluate(scope)
+
+ #Puppet.debug("defining '%s' with arguments [%s]" %
+ # [name,args])
+ #p @args
+ #p args
+ # okay, now i need to evaluate all of the statements
+ # within a component and a new lexical scope...
+
+ begin
+ scope.settype(name,
+ Component.new(
+ :name => name,
+ :args => args,
+ :code => @code
+ )
+ )
+ 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.stack = caller
+ raise error
+ end
+ end
+
+ def initialize(hash)
+ super
+
+ Puppet.debug "Defining type %s" % @name.value
+
+ # we need to both mark that a given argument is valid,
+ # and we need to also store any provided default arguments
+ hash = @@settypes[@name.value]
+ if @args.is_a?(AST::ASTArray)
+ @args.each { |ary|
+ if ary.is_a?(AST::ASTArray)
+ arg = ary[0]
+ hash[arg.value] += 1
+ else
+ hash[ary.value] += 1
+ end
+ }
+ else
+ Puppet.warning "got arg %s" % @args.inspect
+ hash[@args.value] += 1
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.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 }" % [@name, @args, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # these are analogous to defining new object types
+ class ClassDef < AST::CompDef
+ attr_accessor :parentclass
+
+ def each
+ [@name,@args,@parentclass,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ name = @name.evaluate(scope)
+ args = @args.evaluate(scope)
+
+ #Puppet.debug "evaluating parent %s of type %s" %
+ # [@parent.name, @parent.class]
+ parent = @parentclass.evaluate(scope)
+
+ Puppet.debug("defining hostclass '%s' with arguments [%s]" %
+ [name,args])
+
+ begin
+ scope.settype(name,
+ HostClass.new(
+ :name => name,
+ :args => args,
+ :parent => parent,
+ :code => @code
+ )
+ )
+ 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.stack = caller
+ raise error
+ end
+ end
+
+ def tree(indent = 0)
+ return [
+ @name.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap("class")),
+ @args.tree(indent + 1),
+ @parentclass.tree(indent + 1),
+ @code.tree(indent + 1),
+ ].join("\n")
+ end
+
+ def to_s
+ return "class %s(%s) inherits %s {\n%s }" %
+ [@name, @args, @parentclass, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # host definitions are special, because they get called when a host
+ # whose name matches connects
+ class NodeDef < AST::Branch
+ attr_accessor :names, :code
+
+ def each
+ [@names,@code].each { |child| yield child }
+ end
+
+ def evaluate(scope)
+ names = @names.evaluate(scope)
+
+ unless names.is_a?(Array)
+ names = [names]
+ end
+ Puppet.debug("defining hosts '%s'" % [names.join(", ")])
+
+ names.each { |name|
+ begin
+ scope.sethost(name,
+ Host.new(
+ :name => name,
+ :code => @code
+ )
+ )
+ 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.stack = caller
+ raise error
+ end
+ }
+ end
+
+ def tree(indent = 0)
+ return [
+ @names.tree(indent + 1),
+ ((@@indline * 4 * indent) + self.typewrap("host")),
+ @code.tree(indent + 1),
+ ].join("\n")
+ end
+
+ def to_s
+ return "host %s {\n%s }" % [@name, @code]
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class Component < AST::Branch
+ attr_accessor :name, :args, :code
+
+ def evaluate(scope,hash,objtype,objname)
+ scope = scope.newscope
+ scope.type = objtype
+ scope.name = objname
+
+ # define all of the arguments in our local scope
+ if self.args
+ Puppet.debug "args are %s" % self.args.inspect
+ self.args.each { |arg, default|
+ unless hash.include?(arg)
+ if default
+ hash[arg] = default
+ else
+ error = Puppet.ParseError.new(
+ "Must pass %s to %s of type %s" %
+ [arg.inspect,name,objtype]
+ )
+ error.line = self.line
+ error.file = self.file
+ error.stack = caller
+ raise error
+ end
+ end
+ }
+
+ hash.each { |arg,value|
+ begin
+ scope.setvar(arg,hash[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.stack = caller
+ raise error
+ end
+ }
+ end
+
+ # now just evaluate the code with our new bindings
+ self.code.evaluate(scope)
+ end
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class HostClass < AST::Component
+ attr_accessor :parentclass
+
+ def evaluate(scope,hash,objtype,objname)
+ if @parentclass
+ 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
+ parentobj.evaluate(scope,hash,objtype,objname)
+ end
+
+ # just use the Component evaluate method, but change the type
+ # to our own type
+ super(scope,hash,@name,objname)
+ end
+
+ def initialize(hash)
+ @parentclass = nil
+ super
+ if self.parent.is_a?(Array)
+ self.parent = nil
+ end
+ end
+
+ end
+ #---------------------------------------------------------------
+
+ #---------------------------------------------------------------
+ # this is not really an AST node; it's just a placeholder
+ # for a bunch of AST code to evaluate later
+ class Host < AST::Component
+ attr_accessor :name, :args, :code, :parentclass
+
+ def evaluate(scope,hash,objtype,objname)
+ if @parentclass
+ 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
+ parentobj.evaluate(scope,hash,objtype,objname)
+ end
+
+ # just use the Component evaluate method, but change the type
+ # to our own type
+ super(scope,hash,@name,objname)
+ end
+ end
+ #---------------------------------------------------------------
+ end
+ end
+end
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
new file mode 100644
index 000000000..7386a0e15
--- /dev/null
+++ b/lib/puppet/parser/grammar.ra
@@ -0,0 +1,672 @@
+#/usr/bin/ruby
+
+# $Id$
+# vim: syntax=ruby
+
+# the parser
+
+class Puppet::Parser::Parser
+
+token LBRACK QTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE FALSE EQUALS
+token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL NOTEQUAL
+token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN DOT COLON TYPE
+token NAME SEMIC
+
+rule
+program: statements {
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :children => [val[0]]
+ )
+ end
+
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Puppet[:parseonly]
+ begin
+ if Puppet[:debug]
+ puts result.tree(0)
+ end
+ rescue NoMethodError => detail
+ Puppet.err detail
+ #exit(78)
+ end
+ #require 'puppet/parser/interpreter'
+ #result = Puppet::Server.new(result)
+ end
+}
+
+statements: statement
+ | statements statement {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :children => [val[0],val[1]]
+ )
+ end
+}
+
+statement: object
+ | assignment
+ | selector
+ | iftest
+ | import
+ | definition
+ | hostclass
+
+#object: name LBRACE objectname COLON params endcomma RBRACE {
+object: name LBRACE objectinstances endsemi RBRACE {
+ ary = val[2]
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ if ary[0].is_a?(AST::Leaf)
+ ary = [ary]
+ end
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file
+ )
+ ary.each { |instance|
+ Puppet.debug "Adding %s with name %s" % [val[0].value, instance[0].value]
+ result.push AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => instance[0],
+ :params => instance[1]
+ )
+ }
+} | name LBRACE params endcomma RBRACE {
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ # an object but without a name
+ # this cannot be an instance of a library type
+
+ Puppet.debug "Adding %s" % val[0].value
+ # make a unique name for bookkeeping purposes
+ name = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => [val[0].value, "-", val[0].object_id].join('')
+ )
+
+ result = AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => name,
+ :params => val[2]
+ )
+} | type LBRACE params endcomma RBRACE {
+ # a template setting for a type
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid type"
+ raise Puppet::ParseError, "Invalid type"
+ end
+ result = AST::TypeDefaults.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :params => val[2]
+ )
+}
+
+objectinst: objectname COLON params {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+}
+
+objectinstances: objectinst
+ | objectinstances SEMIC objectinst {
+ if val[0][0].is_a?(AST::ASTArray)
+ val[0].push val[2]
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+endsemi: # nothing
+ | SEMIC
+
+name: NAME {
+ result = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+type: TYPE {
+ result = AST::Type.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+objectname: quotedtext
+ | name
+ | selector
+ | variable
+ | array
+
+assignment: VARIABLE EQUALS rvalue {
+ # this is distinct from referencing a variable
+ variable = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0].sub(/^\$/,'')
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :name => variable,
+ :value => val[2]
+ )
+}
+
+params: # nothing
+{
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+}
+ | param { result = val[0] }
+ | params COMMA param {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+param: NAME FARROW rvalue {
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+rvalues: rvalue
+ | rvalues comma rvalue {
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0].push(val[2])
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+rvalue: quotedtext
+ | name
+ | type
+ | boolean
+ | selector
+ | object
+ | variable
+ | array
+ | objectref
+
+quotedtext: QTEXT {
+ result = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+boolean: BOOLEAN {
+ result = AST::Boolean.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+}
+
+objectref: name LBRACK rvalue RBRACK {
+ result = AST::ObjectRef.new(
+ :pin => '[]',
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => val[2]
+ )
+}
+
+iftest: IF test LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :statements => val[3]
+ )
+}
+ | IF test LBRACE statements RBRACE elsifs ELSE LBRACE statements RBRACE {
+ # make sure our elsifs are an array, as it will save lots of
+ # effort later
+ unless val[5].is_a?(AST::ASTArray)
+ val[5] = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[5]]
+ )
+ end
+
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[8],
+ :elsif => val[5]
+ )
+
+}
+ | IF test LBRACE statements RBRACE ELSE LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[7]
+ )
+
+}
+
+elsifs: ELSIF test LBRACE statements RBRACE {
+ result = AST::If.new(
+ :pin => "elseif",
+ :test => val[1],
+ :file => @lexer.file,
+ :statements => val[3],
+ :line => @lexer.line
+ )
+}
+ | elsifs ELSIF test LBRACE statements RBRACE {
+ second = AST::If.new(
+ :pin => "elsif",
+ :test => val[2],
+ :statements => val[4],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(second)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],second]
+ )
+ end
+}
+
+test: rvalue
+ | rvalue testop rvalue {
+ result = AST::Test.new(
+ :pin => val[1],
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :lhs => val[0],
+ :rhs => val[2]
+ )
+}
+
+testop: ISEQUAL
+ | GREATEREQUAL
+ | GREATERTHAN
+ | LESSTHAN
+ | LESSEQUAL
+ | NOTEQUAL
+
+selector: variable QMARK svalues {
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => val[0],
+ :value => val[2]
+ )
+}
+
+svalues: selectval
+ | LBRACE sintvalues RBRACE { result = val[1] }
+
+sintvalues: selectval
+ | sintvalues comma selectval {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+selectval: selectlhand FARROW rvalue {
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+}
+
+selectlhand: NAME
+ | TYPE
+ | QTEXT
+
+import: IMPORT QTEXT {
+ # importing files
+ # yuk, i hate keywords
+ # we'll probably have to have some kind of search path eventually
+ # but for now, just use a path relative to the file doing the importing
+ path = @lexer.file.sub(%r{[^/]+$},val[1])
+ parser = Puppet::Parser::Parser.new()
+ parser.stack = self.stack
+ Puppet.debug("importing %s" % path)
+ noimport = false
+ begin
+ parser.file = path
+ rescue Puppet::ImportError
+ Puppet.warning("Importing %s would result in an import loop" % path)
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ noimport = true
+ end
+ unless noimport
+ result = parser.parse
+ end
+}
+
+definition: DEFINE NAME argumentlist LBRACE statements RBRACE {
+ result = AST::CompDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[4]
+ )
+}
+
+hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE {
+ result = AST::ClassDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :parentclass => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[5]
+ )
+}
+
+#nodedef: NODE words LBRACE statements RBRACE {
+# result = AST::NodeDef.new(
+# :names => val[1],
+# :code => val[3]
+# )
+#}
+
+nothing: {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+}
+
+argumentlist: nothing
+ | LPAREN nothing RPAREN {
+ result = val[1]
+}
+ | LPAREN arguments RPAREN {
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ end
+}
+
+arguments: argument
+ | arguments COMMA argument {
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+}
+
+argument: name EQUALS rvalue {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+}
+ | name {
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+}
+
+parent: nothing
+ | INHERITS NAME {
+ result = AST::Name.new(
+ :value => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+}
+
+variable: VARIABLE {
+ name = val[0].sub(/^\$/,'')
+ result = AST::Variable.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => name
+ )
+}
+
+array: LBRACK rvalues RBRACK {
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new
+ result.push val[1]
+ end
+}
+
+comma: FARROW
+ | COMMA
+
+endcomma: # nothing
+ | COMMA { result = nil }
+
+end
+---- header ----
+require 'puppet'
+require 'puppet/parser/lexer'
+require 'puppet/parser/ast'
+#require 'puppet/parser/interpreter'
+
+module Puppet
+ # this exception class already has a :stack accessor
+ class ParseError < Puppet::Error
+ attr_accessor :line, :file
+ end
+
+ class ImportError < Racc::ParseError; end
+end
+
+Puppet[:typecheck] = true
+Puppet[:paramcheck] = true
+
+---- inner ----
+attr_writer :stack
+attr_reader :file
+
+def file=(file)
+ if self.stack.include?(file)
+ raise Puppet::ImportError.new("Import loop detected")
+ else
+ @lexer.file = file
+ end
+end
+
+def initialize
+ @lexer = Puppet::Parser::Lexer.new()
+ if Puppet[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ #error = "line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last]
+ error = "an error was found"
+
+ if Puppet[:debug]
+ puts stack.inspect
+ puts stack
+ end
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ except = Puppet::ParseError.new(error)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
+# how should I do error handling here?
+def parse
+ begin
+ yyparse(@lexer,:scan)
+ rescue Racc::ParseError => except
+ error = Puppet::ParseError.new(except)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ raise error
+ rescue Puppet::ParseError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::Error => except
+ # and this is a framework error
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= except.stack
+ raise except
+ rescue Puppet::DevError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= caller
+ if Puppet[:debug]
+ puts except.stack
+ end
+ raise except
+ rescue => except
+ error = Puppet::DevError.new(except.message)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ if Puppet[:debug]
+ puts caller
+ end
+ raise error
+ end
+end
+
+def stack
+ if defined? @stack and ! @stack.nil?
+ if @lexer.file
+ return [@stack,@lexer.file].flatten
+ else
+ return @stack
+ end
+ else
+ if @lexer.file
+ return [@lexer.file]
+ else
+ return []
+ end
+ end
+end
+
+def string=(string)
+ @lexer.string = string
+end
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
new file mode 100644
index 000000000..88ab5854e
--- /dev/null
+++ b/lib/puppet/parser/interpreter.rb
@@ -0,0 +1,135 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the interpreter
+#
+# this builds our virtual pinball machine, into which we'll place our host-specific
+# information and out of which we'll receive our host-specific configuration
+
+require 'puppet'
+require 'puppet/parser/parser'
+require 'puppet/parser/scope'
+
+
+module Puppet
+ module Parser
+ #---------------------------------------------------------------
+ class Interpreter
+ attr_accessor :ast, :topscope
+ # just shorten the constant path a bit, using what amounts to an alias
+ AST = Puppet::Parser::AST
+
+ #------------------------------------------------------------
+ def clear
+ TransObject.clear
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ #def callfunc(function,*args)
+ # #Puppet.debug("Calling %s on %s" % [function,@client])
+ # @client.callfunc(function,*args)
+ # #Puppet.debug("Finished %s" % function)
+ #end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # create our interpreter
+ def initialize(hash)
+ unless hash.include?(:ast)
+ raise ArgumentError.new("Must pass tree and client to Interpreter")
+ end
+ @ast = hash[:ast]
+ #@client = hash[:client]
+
+ @scope = Puppet::Parser::Scope.new() # no parent scope
+ @topscope = @scope
+ @scope.interp = self
+
+ if hash.include?(:facts)
+ facts = hash[:facts]
+ unless facts.is_a?(Hash)
+ raise ArgumentError.new("Facts must be a hash")
+ end
+
+ facts.each { |fact,value|
+ @scope.setvar(fact,value)
+ }
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # evaluate our whole tree
+ def run
+ # evaluate returns a value, but at the top level we only
+ # care about its side effects
+ # i think
+ unless @ast.is_a?(AST) or @ast.is_a?(AST::ASTArray)
+ Puppet.err "Received top-level non-ast '%s' of type %s" %
+ [@ast,@ast.class]
+ raise TypeError.new("Received non-ast '%s' of type %s" %
+ [@ast,@ast.class])
+ end
+
+ begin
+ @ast.evaluate(@scope)
+ rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except
+ #Puppet.err "File %s, line %s: %s" %
+ # [except.file, except.line, except.message]
+ if Puppet[:debug]
+ puts except.stack
+ end
+ #exit(1)
+ raise
+ rescue => except
+ error = Puppet::DevError.new("%s: %s" %
+ [except.class, except.message])
+ error.stack = caller
+ if Puppet[:debug]
+ puts error.stack
+ end
+ raise error
+ end
+
+ # okay, at this point we have a tree of scopes, and we want to
+ # unzip along that tree, building our structure of objects
+ # to pass to the client
+ # this will be heirarchical, and will (at this point) contain
+ # only TransObjects and TransSettings
+ @topscope.name = "top"
+ @topscope.type = "puppet"
+ begin
+ topbucket = @topscope.to_trans
+ rescue => detail
+ Puppet.warning detail
+ raise
+ end
+
+ # add our settings to the front of the array
+ # at least, for now
+ #@topscope.typesets.each { |setting|
+ # topbucket.unshift setting
+ #}
+
+ # guarantee that settings are at the very top
+ #topbucket.push settingbucket
+ #topbucket.push @scope.to_trans
+
+ #retlist = TransObject.list
+ #Puppet.debug "retobject length is %s" % retlist.length
+ #TransObject.clear
+ return topbucket
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def scope
+ return @scope
+ end
+ #------------------------------------------------------------
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb
new file mode 100644
index 000000000..d9d9d5f5c
--- /dev/null
+++ b/lib/puppet/parser/lexer.rb
@@ -0,0 +1,225 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the scanner/lexer
+
+require 'strscan'
+require 'puppet'
+
+
+module Puppet
+ class LexError < RuntimeError; end
+ module Parser
+ #---------------------------------------------------------------
+ class Lexer
+ attr_reader :line, :last, :file
+
+ #%r{\w+} => :WORD,
+ @@tokens = {
+ %r{#.*} => :COMMENT,
+ %r{\[} => :LBRACK,
+ %r{\]} => :RBRACK,
+ %r{\{} => :LBRACE,
+ %r{\}} => :RBRACE,
+ %r{\(} => :LPAREN,
+ %r{\)} => :RPAREN,
+ %r{"} => :DQUOTE,
+ %r{\n} => :RETURN,
+ %r{'} => :SQUOTE,
+ %r{=} => :EQUALS,
+ %r{==} => :ISEQUAL,
+ %r{>=} => :GREATEREQUAL,
+ %r{>} => :GREATERTHAN,
+ %r{<} => :LESSTHAN,
+ %r{<=} => :LESSEQUAL,
+ %r{!=} => :NOTEQUAL,
+ %r{,} => :COMMA,
+ %r{\.} => :DOT,
+ %r{:} => :COLON,
+ %r{;} => :SEMIC,
+ %r{\?} => :QMARK,
+ %r{\\} => :BACKSLASH,
+ %r{=>} => :FARROW,
+ %r{[a-z]\w*} => :NAME,
+ %r{[A-Z]\w*} => :TYPE,
+ %r{[0-9]+} => :NUMBER,
+ %r{\$\w+} => :VARIABLE
+ }
+
+ @@keywords = {
+ "if" => :IF,
+ "elsif" => :ELSIF,
+ "else" => :ELSE,
+ "import" => :IMPORT,
+ "class" => :CLASS,
+ "node" => :NODE,
+ "host" => :NODE,
+ "true" => :BOOLEAN,
+ "false" => :BOOLEAN,
+ "inherits" => :INHERITS,
+ "define" => :DEFINE
+ }
+
+ # scan the whole file
+ # basically just used for testing
+ def fullscan
+ array = []
+
+ self.scan { |token,str|
+ #Puppet.debug("got token '%s' => '%s'" % [token,str])
+ if token.nil?
+ return array
+ else
+ array.push([token,str])
+ end
+ }
+ return array
+ end
+
+ # this is probably pretty damned inefficient...
+ # it'd be nice not to have to load the whole file first...
+ def file=(file)
+ @file = file
+ @line = 1
+ File.open(file) { |of|
+ str = ""
+ of.each { |line| str += line }
+ @scanner = StringScanner.new(str)
+ }
+ end
+
+ def initialize
+ @line = 1
+ @last = ""
+ @scanner = nil
+ @file = nil
+ # AAARRGGGG! okay, regexes in ruby are bloody annoying
+ # no one else has "\n" =~ /\s/
+ @skip = %r{[ \t]+}
+ end
+
+ def rest
+ @scanner.rest
+ end
+
+ # this is the heart of the lexer
+ def scan
+ #Puppet.debug("entering scan")
+ if @scanner.nil?
+ raise TypeError.new("Invalid or empty string")
+ end
+
+ @scanner.skip(@skip)
+ until @scanner.eos? do
+ yielded = false
+ sendbreak = false # gah, this is a nasty hack
+ stoken = nil
+ sregex = nil
+ value = ""
+
+ # first find out which type of token we've got
+ @@tokens.each { |regex,token|
+ # we're just checking, which doesn't advance the scan
+ # pointer
+ tmp = @scanner.check(regex)
+ if tmp.nil?
+ #puppet.debug("did not match %s to '%s'" %
+ # [regex,@scanner.rest])
+ next
+ end
+
+ # find the longest match
+ if tmp.length > value.length
+ value = tmp
+ stoken = token
+ sregex = regex
+ else
+ # we've already got a longer match
+ next
+ end
+ }
+
+ # error out if we didn't match anything at all
+ if stoken.nil?
+ nword = nil
+ if @scanner.rest =~ /^(\S+)/
+ nword = $1
+ elsif@scanner.rest =~ /^(\s+)/
+ nword = $1
+ else
+ nword = @scanner.rest
+ end
+ raise "Could not match '%s'" % nword
+ end
+
+ value = @scanner.scan(sregex)
+
+ if value == ""
+ raise "Didn't match regex on token %s" % stoken
+ end
+
+ # token-specific operations
+ # if this gets much more complicated, it should
+ # be moved up to where the tokens themselves are defined
+ # which will get me about 75% of the way to a lexer generator
+ case stoken
+ when :NAME then
+ wtoken = stoken
+ # we're looking for keywords here
+ if @@keywords.include?(value)
+ wtoken = @@keywords[value]
+ #Puppet.debug("token '%s'" % wtoken)
+ end
+ yield [wtoken,value]
+ @last = value
+ when :NUMBER then
+ yield [:NAME,value]
+ # just throw comments away
+ when :COMMENT then
+ # just throw comments away
+ when :RETURN then
+ @line += 1
+ @scanner.skip(@skip)
+ when :DQUOTE then
+ #Puppet.debug("searching '%s' after '%s'" % [self.rest,value])
+ value = self.slurpstring(value)
+ yield [:QTEXT,value]
+ @last = value
+ #stoken = :QTEXT
+ #Puppet.debug("got string '%s' => '%s'" % [:QTEXT,value])
+ else
+ yield [stoken,value]
+ @last = value
+ #Puppet.debug("got token '%s' => '%s'" % [stoken,value])
+ end
+ @scanner.skip(@skip)
+ end
+ @scanner = nil
+ yield [false,false]
+ end
+
+ # we've encountered an opening quote...
+ # slurp in the rest of the string and return it
+ def slurpstring(quote)
+ #Puppet.debug("searching '%s'" % self.rest)
+ str = @scanner.scan_until(/[^\\]#{quote}/)
+ #str = @scanner.scan_until(/"/)
+ if str.nil?
+ raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" %
+ [self.last,self.rest])
+ else
+ str.sub!(/#{quote}$/,"")
+ str.gsub!(/\\#{quote}/,quote)
+ end
+
+ return str
+ end
+
+ def string=(string)
+ @scanner = StringScanner.new(string)
+ end
+ end
+ #---------------------------------------------------------------
+ end
+end
diff --git a/lib/puppet/parser/makefile b/lib/puppet/parser/makefile
new file mode 100644
index 000000000..9ec6265de
--- /dev/null
+++ b/lib/puppet/parser/makefile
@@ -0,0 +1,5 @@
+#parser.rb: grammar.ry
+# ryacc --output parser grammar
+
+parser.rb: grammar.ra
+ racc -v -o$@ grammar.ra
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
new file mode 100644
index 000000000..3b42bd8d7
--- /dev/null
+++ b/lib/puppet/parser/parser.rb
@@ -0,0 +1,1264 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by racc 1.4.4
+# from racc grammer file "grammar.ra".
+#
+
+require 'racc/parser'
+
+
+require 'puppet'
+require 'puppet/parser/lexer'
+require 'puppet/parser/ast'
+#require 'puppet/parser/interpreter'
+
+module Puppet
+ # this exception class already has a :stack accessor
+ class ParseError < Puppet::Error
+ attr_accessor :line, :file
+ end
+
+ class ImportError < Racc::ParseError; end
+end
+
+Puppet[:typecheck] = true
+Puppet[:paramcheck] = true
+
+
+module Puppet
+
+ module Parser
+
+ class Parser < Racc::Parser
+
+module_eval <<'..end grammar.ra modeval..ida89b5273aa', 'grammar.ra', 573
+attr_writer :stack
+attr_reader :file
+
+def file=(file)
+ if self.stack.include?(file)
+ raise Puppet::ImportError.new("Import loop detected")
+ else
+ @lexer.file = file
+ end
+end
+
+def initialize
+ @lexer = Puppet::Parser::Lexer.new()
+ if Puppet[:debug]
+ @yydebut = true
+ end
+end
+
+def on_error(token,value,stack)
+ #on '%s' at '%s' in\n'%s'" % [token,value,stack]
+ #error = "line %s: parse error after '%s'" %
+ # [@lexer.line,@lexer.last]
+ error = "an error was found"
+
+ if Puppet[:debug]
+ puts stack.inspect
+ puts stack
+ end
+ if @lexer.file
+ error += (" in '%s'" % @lexer.file)
+ end
+
+ except = Puppet::ParseError.new(error)
+ except.line = @lexer.line
+ if @lexer.file
+ except.file = @lexer.file
+ end
+
+ raise except
+end
+
+# how should I do error handling here?
+def parse
+ begin
+ yyparse(@lexer,:scan)
+ rescue Racc::ParseError => except
+ error = Puppet::ParseError.new(except)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ raise error
+ rescue Puppet::ParseError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ raise except
+ rescue Puppet::Error => except
+ # and this is a framework error
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= except.stack
+ raise except
+ rescue Puppet::DevError => except
+ except.line ||= @lexer.line
+ except.file ||= @lexer.file
+ except.stack ||= caller
+ if Puppet[:debug]
+ puts except.stack
+ end
+ raise except
+ rescue => except
+ error = Puppet::DevError.new(except.message)
+ error.line = @lexer.line
+ error.file = @lexer.file
+ error.stack = caller
+ if Puppet[:debug]
+ puts caller
+ end
+ raise error
+ end
+end
+
+def stack
+ if defined? @stack and ! @stack.nil?
+ if @lexer.file
+ return [@stack,@lexer.file].flatten
+ else
+ return @stack
+ end
+ else
+ if @lexer.file
+ return [@lexer.file]
+ else
+ return []
+ end
+ end
+end
+
+def string=(string)
+ @lexer.string = string
+end
+..end grammar.ra modeval..ida89b5273aa
+
+##### racc 1.4.4 generates ###
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 38, :_reduce_1,
+ 1, 39, :_reduce_none,
+ 2, 39, :_reduce_3,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 1, 40, :_reduce_none,
+ 5, 41, :_reduce_11,
+ 5, 41, :_reduce_12,
+ 5, 41, :_reduce_13,
+ 3, 54, :_reduce_14,
+ 1, 49, :_reduce_none,
+ 3, 49, :_reduce_16,
+ 0, 50, :_reduce_none,
+ 1, 50, :_reduce_none,
+ 1, 48, :_reduce_19,
+ 1, 53, :_reduce_20,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 1, 55, :_reduce_none,
+ 3, 42, :_reduce_26,
+ 0, 51, :_reduce_27,
+ 1, 51, :_reduce_28,
+ 3, 51, :_reduce_29,
+ 3, 60, :_reduce_30,
+ 1, 61, :_reduce_none,
+ 3, 61, :_reduce_32,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 59, :_reduce_none,
+ 1, 56, :_reduce_42,
+ 1, 63, :_reduce_43,
+ 4, 64, :_reduce_44,
+ 5, 44, :_reduce_45,
+ 10, 44, :_reduce_46,
+ 9, 44, :_reduce_47,
+ 5, 66, :_reduce_48,
+ 6, 66, :_reduce_49,
+ 1, 65, :_reduce_none,
+ 3, 65, :_reduce_51,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 1, 67, :_reduce_none,
+ 3, 43, :_reduce_58,
+ 1, 68, :_reduce_none,
+ 3, 68, :_reduce_60,
+ 1, 70, :_reduce_none,
+ 3, 70, :_reduce_62,
+ 3, 69, :_reduce_63,
+ 1, 71, :_reduce_none,
+ 1, 71, :_reduce_none,
+ 1, 71, :_reduce_none,
+ 2, 45, :_reduce_67,
+ 6, 46, :_reduce_68,
+ 7, 47, :_reduce_69,
+ 0, 74, :_reduce_70,
+ 1, 72, :_reduce_none,
+ 3, 72, :_reduce_72,
+ 3, 72, :_reduce_73,
+ 1, 75, :_reduce_none,
+ 3, 75, :_reduce_75,
+ 3, 76, :_reduce_76,
+ 1, 76, :_reduce_77,
+ 1, 73, :_reduce_none,
+ 2, 73, :_reduce_79,
+ 1, 57, :_reduce_80,
+ 3, 58, :_reduce_81,
+ 1, 62, :_reduce_none,
+ 1, 62, :_reduce_none,
+ 0, 52, :_reduce_none,
+ 1, 52, :_reduce_85 ]
+
+racc_reduce_n = 86
+
+racc_shift_n = 153
+
+racc_action_table = [
+ 22, 24, 22, 24, 1, 54, 5, 9, 39, 12,
+ 14, 22, 24, 22, 24, 82, 3, 7, 116, 84,
+ 85, 59, 22, 24, 115, 28, 126, 28, 96, 31,
+ 108, 31, 3, 7, 3, 7, 28, 59, 28, 62,
+ 31, 7, 31, 3, 7, 3, 7, 28, 22, 24,
+ 89, 31, 56, 57, 3, 7, 97, 22, 24, 22,
+ 24, 22, 24, 128, 22, 24, 129, 113, 56, 57,
+ 22, 24, 114, 28, 98, 22, 24, 31, 59, 103,
+ 3, 7, 28, 80, 28, 80, 28, 63, 31, 28,
+ 7, 3, 7, 31, 69, 28, 3, 7, 133, 31,
+ 28, 121, 3, 7, 31, 22, 24, 3, 7, 56,
+ 57, 122, 136, 80, 1, 137, 5, 9, 124, 12,
+ 14, 125, 111, 109, 84, 85, 3, 7, 102, 102,
+ 28, 78, 63, 55, 31, 44, 134, 3, 7, 1,
+ 140, 5, 9, 99, 12, 14, 98, 138, 37, 80,
+ 37, 3, 7, 141, 152, 44, 1, 42, 5, 9,
+ 144, 12, 14, 41, 146, 40, 39, 38, 3, 7,
+ 1, 151, 5, 9, 37, 12, 14, 36, 7, nil,
+ nil, nil, 3, 7, nil, 149, nil, 1, nil, 5,
+ 9, nil, 12, 14, nil, nil, nil, nil, nil, 3,
+ 7, 1, 147, 5, 9, nil, 12, 14, nil, nil,
+ nil, nil, nil, 3, 7, nil, nil, nil, 1, nil,
+ 5, 9, nil, 12, 14, nil, nil, nil, nil, nil,
+ 3, 7, 1, nil, 5, 9, nil, 12, 14, nil,
+ nil, nil, nil, nil, 3, 7, 1, nil, 5, 9,
+ nil, 12, 14, nil, nil, nil, nil, nil, 3, 7,
+ 1, nil, 5, 9, nil, 12, 14, nil, nil, nil,
+ nil, nil, 3, 7, 1, nil, 5, 9, nil, 12,
+ 14, nil, nil, nil, nil, nil, 3, 7, 1, nil,
+ 5, 9, nil, 12, 14, nil, nil, nil, nil, nil,
+ 3, 7, 1, nil, 5, 9, nil, 12, 14, nil,
+ nil, nil, nil, nil, 3, 7, 1, nil, 5, 9,
+ nil, 12, 14, nil, nil, nil, nil, nil, 3, 7,
+ 1, nil, 5, 9, nil, 12, 14, nil, nil, nil,
+ nil, nil, 3, 7, 48, 49, 50, 51, 52, 53 ]
+
+racc_action_check = [
+ 1, 1, 83, 83, 96, 27, 96, 96, 27, 96,
+ 96, 89, 89, 54, 54, 46, 96, 96, 94, 46,
+ 46, 112, 47, 47, 94, 1, 106, 83, 64, 1,
+ 87, 83, 1, 1, 83, 83, 89, 37, 54, 37,
+ 89, 63, 54, 89, 89, 54, 54, 47, 98, 98,
+ 61, 47, 112, 112, 47, 47, 66, 99, 99, 129,
+ 129, 39, 39, 109, 113, 113, 109, 92, 37, 37,
+ 137, 137, 93, 98, 80, 22, 22, 98, 62, 79,
+ 98, 98, 99, 97, 129, 44, 39, 42, 129, 113,
+ 99, 129, 129, 113, 39, 137, 113, 113, 117, 137,
+ 22, 100, 137, 137, 22, 40, 40, 22, 22, 62,
+ 62, 101, 127, 102, 117, 127, 117, 117, 103, 117,
+ 117, 104, 91, 88, 91, 91, 117, 117, 81, 74,
+ 40, 41, 38, 34, 40, 33, 118, 40, 40, 88,
+ 135, 88, 88, 73, 88, 88, 69, 128, 68, 134,
+ 23, 88, 88, 136, 150, 18, 135, 14, 135, 135,
+ 139, 135, 135, 13, 142, 12, 11, 9, 135, 135,
+ 150, 148, 150, 150, 6, 150, 150, 5, 116, nil,
+ nil, nil, 150, 150, nil, 145, nil, 148, nil, 148,
+ 148, nil, 148, 148, nil, nil, nil, nil, nil, 148,
+ 148, 145, 143, 145, 145, nil, 145, 145, nil, nil,
+ nil, nil, nil, 145, 145, nil, nil, nil, 143, nil,
+ 143, 143, nil, 143, 143, nil, nil, nil, nil, nil,
+ 143, 143, 144, nil, 144, 144, nil, 144, 144, nil,
+ nil, nil, nil, nil, 144, 144, 141, nil, 141, 141,
+ nil, 141, 141, nil, nil, nil, nil, nil, 141, 141,
+ 146, nil, 146, 146, nil, 146, 146, nil, nil, nil,
+ nil, nil, 146, 146, 55, nil, 55, 55, nil, 55,
+ 55, nil, nil, nil, nil, nil, 55, 55, 138, nil,
+ 138, 138, nil, 138, 138, nil, nil, nil, nil, nil,
+ 138, 138, 15, nil, 15, 15, nil, 15, 15, nil,
+ nil, nil, nil, nil, 15, 15, 125, nil, 125, 125,
+ nil, 125, 125, nil, nil, nil, nil, nil, 125, 125,
+ 0, nil, 0, 0, nil, 0, 0, nil, nil, nil,
+ nil, nil, 0, 0, 26, 26, 26, 26, 26, 26 ]
+
+racc_action_pointer = [
+ 308, -2, nil, nil, nil, 174, 161, nil, nil, 132,
+ nil, 161, 153, 163, 122, 280, nil, nil, 150, nil,
+ nil, nil, 73, 137, nil, nil, 328, 3, nil, nil,
+ nil, nil, nil, 130, 128, nil, nil, 34, 118, 59,
+ 103, 131, 73, nil, 50, nil, 11, 20, nil, nil,
+ nil, nil, nil, nil, 11, 252, nil, nil, nil, nil,
+ nil, 42, 75, 6, 23, nil, 23, nil, 135, 138,
+ nil, nil, nil, 107, 120, nil, nil, nil, nil, 50,
+ 66, 119, nil, 0, nil, nil, nil, 26, 117, 9,
+ nil, 116, 55, 57, 9, nil, -18, 48, 46, 55,
+ 95, 105, 78, 83, 116, nil, 20, nil, nil, 40,
+ nil, nil, 18, 62, nil, nil, 143, 92, 127, nil,
+ nil, nil, nil, nil, nil, 294, nil, 89, 142, 57,
+ nil, nil, nil, nil, 114, 134, 148, 68, 266, 155,
+ nil, 224, 159, 196, 210, 179, 238, nil, 165, nil,
+ 148, nil, nil ]
+
+racc_action_default = [
+ -86, -86, -7, -20, -8, -86, -86, -19, -9, -86,
+ -10, -86, -80, -86, -86, -1, -2, -4, -86, -5,
+ -6, -33, -86, -39, -42, -40, -50, -34, -80, -38,
+ -36, -43, -41, -35, -86, -37, -67, -86, -70, -27,
+ -86, -86, -70, -3, -27, -31, -86, -86, -52, -53,
+ -54, -55, -56, -57, -86, -86, -65, -64, -58, -66,
+ -59, -86, -86, -70, -86, -71, -86, -21, -24, -19,
+ -25, -22, -28, -17, -84, -23, -15, -26, 153, -70,
+ -86, -84, -81, -86, -82, -83, -51, -86, -86, -86,
+ -61, -86, -77, -86, -86, -74, -86, -27, -86, -18,
+ -86, -86, -85, -86, -86, -78, -86, -32, -44, -45,
+ -63, -60, -86, -86, -72, -73, -86, -86, -14, -30,
+ -16, -11, -12, -29, -79, -86, -13, -86, -86, -86,
+ -62, -76, -75, -68, -86, -86, -86, -86, -86, -86,
+ -69, -86, -86, -86, -86, -86, -86, -47, -86, -46,
+ -86, -48, -49 ]
+
+racc_goto_table = [
+ 15, 29, 76, 33, 60, 70, 23, 34, 67, 123,
+ 93, 74, 83, 95, 45, 64, 81, 58, 101, 79,
+ 47, 127, 29, 73, 33, 106, 105, 23, 91, 90,
+ 100, 104, 77, 46, 43, 94, 13, nil, nil, 86,
+ 29, 123, 33, nil, 68, 23, 87, 29, nil, 33,
+ nil, nil, 23, nil, 29, 88, 33, 112, nil, 23,
+ nil, nil, 120, 35, nil, 70, 132, nil, 67, 118,
+ nil, nil, nil, nil, nil, 107, nil, nil, nil, 130,
+ nil, 110, nil, 29, 35, 33, nil, nil, 23, 29,
+ 119, 33, nil, nil, 23, nil, 117, nil, 29, nil,
+ 33, 75, 35, 23, 68, 131, nil, 43, nil, 35,
+ nil, nil, nil, 29, nil, 33, 35, nil, 23, 27,
+ nil, nil, nil, nil, nil, 135, nil, nil, nil, 29,
+ nil, 33, nil, nil, 23, 139, 43, 29, 143, 33,
+ 27, 145, 23, 142, 148, 35, 150, nil, nil, nil,
+ nil, 35, nil, nil, 43, nil, nil, 71, 27, nil,
+ 35, 75, 43, nil, 43, 27, nil, 43, nil, 43,
+ nil, nil, 27, nil, nil, 35, nil, nil, nil, nil,
+ nil, 92, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 35, nil, nil, nil, nil, nil, nil, nil, 35,
+ nil, 27, nil, nil, nil, nil, nil, 27, nil, nil,
+ nil, nil, nil, nil, nil, nil, 27, 71, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 27, nil, nil, 92, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 27, nil, nil,
+ nil, nil, nil, nil, nil, 27 ]
+
+racc_goto_check = [
+ 2, 4, 17, 16, 32, 21, 20, 28, 19, 23,
+ 37, 14, 25, 39, 22, 35, 14, 31, 15, 35,
+ 30, 29, 4, 12, 16, 15, 37, 20, 33, 32,
+ 13, 36, 22, 24, 3, 38, 1, nil, nil, 22,
+ 4, 23, 16, nil, 20, 20, 22, 4, nil, 16,
+ nil, nil, 20, nil, 4, 2, 16, 25, nil, 20,
+ nil, nil, 17, 6, nil, 21, 39, nil, 19, 14,
+ nil, nil, nil, nil, nil, 22, nil, nil, nil, 32,
+ nil, 22, nil, 4, 6, 16, nil, nil, 20, 4,
+ 22, 16, nil, nil, 20, nil, 2, nil, 4, nil,
+ 16, 6, 6, 20, 20, 22, nil, 3, nil, 6,
+ nil, nil, nil, 4, nil, 16, 6, nil, 20, 11,
+ nil, nil, nil, nil, nil, 2, nil, nil, nil, 4,
+ nil, 16, nil, nil, 20, 28, 3, 4, 2, 16,
+ 11, 2, 20, 28, 2, 6, 2, nil, nil, nil,
+ nil, 6, nil, nil, 3, nil, nil, 11, 11, nil,
+ 6, 6, 3, nil, 3, 11, nil, 3, nil, 3,
+ nil, nil, 11, nil, nil, 6, nil, nil, nil, nil,
+ nil, 11, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 6, nil, nil, nil, nil, nil, nil, nil, 6,
+ nil, 11, nil, nil, nil, nil, nil, 11, nil, nil,
+ nil, nil, nil, nil, nil, nil, 11, 11, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
+ nil, 11, nil, nil, 11, nil, nil, nil, nil, nil,
+ nil, nil, nil, nil, nil, nil, nil, 11, nil, nil,
+ nil, nil, nil, nil, nil, 11 ]
+
+racc_goto_pointer = [
+ nil, 36, 0, 19, 0, nil, 62, nil, nil, nil,
+ nil, 118, -16, -43, -28, -56, 2, -37, nil, -31,
+ 5, -34, -8, -93, 11, -34, nil, nil, 6, -88,
+ -6, -20, -33, -34, nil, -23, -48, -53, -28, -50 ]
+
+racc_goto_default = [
+ nil, nil, nil, 16, 17, 19, 20, 2, 4, 8,
+ 10, 11, nil, nil, nil, nil, 18, nil, 66, 21,
+ 6, 25, 26, 72, nil, nil, 30, 32, nil, nil,
+ nil, nil, nil, nil, 61, nil, nil, 65, nil, nil ]
+
+racc_token_table = {
+ false => 0,
+ Object.new => 1,
+ :LBRACK => 2,
+ :QTEXT => 3,
+ :RBRACK => 4,
+ :LBRACE => 5,
+ :RBRACE => 6,
+ :SYMBOL => 7,
+ :FARROW => 8,
+ :COMMA => 9,
+ :TRUE => 10,
+ :FALSE => 11,
+ :EQUALS => 12,
+ :QMARK => 13,
+ :LPAREN => 14,
+ :RPAREN => 15,
+ :ISEQUAL => 16,
+ :GREATEREQUAL => 17,
+ :GREATERTHAN => 18,
+ :LESSTHAN => 19,
+ :LESSEQUAL => 20,
+ :NOTEQUAL => 21,
+ :IF => 22,
+ :ELSE => 23,
+ :IMPORT => 24,
+ :DEFINE => 25,
+ :ELSIF => 26,
+ :VARIABLE => 27,
+ :CLASS => 28,
+ :INHERITS => 29,
+ :NODE => 30,
+ :BOOLEAN => 31,
+ :DOT => 32,
+ :COLON => 33,
+ :TYPE => 34,
+ :NAME => 35,
+ :SEMIC => 36 }
+
+racc_use_result_var = true
+
+racc_nt_base = 37
+
+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',
+'QTEXT',
+'RBRACK',
+'LBRACE',
+'RBRACE',
+'SYMBOL',
+'FARROW',
+'COMMA',
+'TRUE',
+'FALSE',
+'EQUALS',
+'QMARK',
+'LPAREN',
+'RPAREN',
+'ISEQUAL',
+'GREATEREQUAL',
+'GREATERTHAN',
+'LESSTHAN',
+'LESSEQUAL',
+'NOTEQUAL',
+'IF',
+'ELSE',
+'IMPORT',
+'DEFINE',
+'ELSIF',
+'VARIABLE',
+'CLASS',
+'INHERITS',
+'NODE',
+'BOOLEAN',
+'DOT',
+'COLON',
+'TYPE',
+'NAME',
+'SEMIC',
+'$start',
+'program',
+'statements',
+'statement',
+'object',
+'assignment',
+'selector',
+'iftest',
+'import',
+'definition',
+'hostclass',
+'name',
+'objectinstances',
+'endsemi',
+'params',
+'endcomma',
+'type',
+'objectinst',
+'objectname',
+'quotedtext',
+'variable',
+'array',
+'rvalue',
+'param',
+'rvalues',
+'comma',
+'boolean',
+'objectref',
+'test',
+'elsifs',
+'testop',
+'svalues',
+'selectval',
+'sintvalues',
+'selectlhand',
+'argumentlist',
+'parent',
+'nothing',
+'arguments',
+'argument']
+
+Racc_debug_parser = false
+
+##### racc system variables end #####
+
+ # reduce 0 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 40
+ def _reduce_1( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :children => [val[0]]
+ )
+ end
+
+ # this is mainly so we can test the parser separately from the
+ # interpreter
+ if Puppet[:parseonly]
+ begin
+ if Puppet[:debug]
+ puts result.tree(0)
+ end
+ rescue NoMethodError => detail
+ Puppet.err detail
+ #exit(78)
+ end
+ #require 'puppet/parser/interpreter'
+ #result = Puppet::Server.new(result)
+ end
+ result
+ end
+.,.,
+
+ # reduce 2 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 54
+ def _reduce_3( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[1])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :children => [val[0],val[1]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 4 omitted
+
+ # reduce 5 omitted
+
+ # reduce 6 omitted
+
+ # reduce 7 omitted
+
+ # reduce 8 omitted
+
+ # reduce 9 omitted
+
+ # reduce 10 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 89
+ def _reduce_11( val, _values, result )
+ ary = val[2]
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ if ary[0].is_a?(AST::Leaf)
+ ary = [ary]
+ end
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file
+ )
+ ary.each { |instance|
+ Puppet.debug "Adding %s with name %s" % [val[0].value, instance[0].value]
+ result.push AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => instance[0],
+ :params => instance[1]
+ )
+ }
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 113
+ def _reduce_12( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid name"
+ raise Puppet::ParseError, "Invalid name"
+ end
+ # an object but without a name
+ # this cannot be an instance of a library type
+
+ Puppet.debug "Adding %s" % val[0].value
+ # make a unique name for bookkeeping purposes
+ name = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => [val[0].value, "-", val[0].object_id].join('')
+ )
+
+ result = AST::ObjectDef.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => name,
+ :params => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 126
+ def _reduce_13( val, _values, result )
+ # a template setting for a type
+ if val[0].is_a?(AST::ASTArray)
+ Puppet.notice "invalid type"
+ raise Puppet::ParseError, "Invalid type"
+ end
+ result = AST::TypeDefaults.new(
+ :pin => "{}",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :params => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 134
+ def _reduce_14( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ result
+ end
+.,.,
+
+ # reduce 15 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 148
+ def _reduce_16( val, _values, result )
+ if val[0][0].is_a?(AST::ASTArray)
+ val[0].push val[2]
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 17 omitted
+
+ # reduce 18 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 159
+ def _reduce_19( val, _values, result )
+ result = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 167
+ def _reduce_20( val, _values, result )
+ result = AST::Type.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+ # reduce 21 omitted
+
+ # reduce 22 omitted
+
+ # reduce 23 omitted
+
+ # reduce 24 omitted
+
+ # reduce 25 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 189
+ def _reduce_26( val, _values, result )
+ # this is distinct from referencing a variable
+ variable = AST::Name.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0].sub(/^\$/,'')
+ )
+ result = AST::VarDef.new(
+ :pin => "=",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :name => variable,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 198
+ def _reduce_27( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 198
+ def _reduce_28( val, _values, result )
+ result = val[0]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 211
+ def _reduce_29( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 226
+ def _reduce_30( val, _values, result )
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 31 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 239
+ def _reduce_32( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ result = val[0].push(val[2])
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 33 omitted
+
+ # reduce 34 omitted
+
+ # reduce 35 omitted
+
+ # reduce 36 omitted
+
+ # reduce 37 omitted
+
+ # reduce 38 omitted
+
+ # reduce 39 omitted
+
+ # reduce 40 omitted
+
+ # reduce 41 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 257
+ def _reduce_42( val, _values, result )
+ result = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 265
+ def _reduce_43( val, _values, result )
+ result = AST::Boolean.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 275
+ def _reduce_44( val, _values, result )
+ result = AST::ObjectRef.new(
+ :pin => '[]',
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :type => val[0],
+ :name => val[2]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 285
+ def _reduce_45( val, _values, result )
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :statements => val[3]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 307
+ def _reduce_46( val, _values, result )
+ # make sure our elsifs are an array, as it will save lots of
+ # effort later
+ unless val[5].is_a?(AST::ASTArray)
+ val[5] = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[5]]
+ )
+ end
+
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[8],
+ :elsif => val[5]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 318
+ def _reduce_47( val, _values, result )
+ result = AST::If.new(
+ :pin => "if",
+ :test => val[1],
+ :statements => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :else => val[7]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 328
+ def _reduce_48( val, _values, result )
+ result = AST::If.new(
+ :pin => "elseif",
+ :test => val[1],
+ :file => @lexer.file,
+ :statements => val[3],
+ :line => @lexer.line
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 348
+ def _reduce_49( val, _values, result )
+ second = AST::If.new(
+ :pin => "elsif",
+ :test => val[2],
+ :statements => val[4],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(second)
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],second]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 50 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 359
+ def _reduce_51( val, _values, result )
+ result = AST::Test.new(
+ :pin => val[1],
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :lhs => val[0],
+ :rhs => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 52 omitted
+
+ # reduce 53 omitted
+
+ # reduce 54 omitted
+
+ # reduce 55 omitted
+
+ # reduce 56 omitted
+
+ # reduce 57 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 376
+ def _reduce_58( val, _values, result )
+ result = AST::Selector.new(
+ :pin => "?",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => val[0],
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 59 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 378
+ def _reduce_60( val, _values, result )
+ result = val[1]
+ result
+ end
+.,.,
+
+ # reduce 61 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 393
+ def _reduce_62( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 408
+ def _reduce_63( val, _values, result )
+ leaf = AST::String.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => val[0]
+ )
+ result = AST::ObjectParam.new(
+ :pin => "=>",
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :param => leaf,
+ :value => val[2]
+ )
+ result
+ end
+.,.,
+
+ # reduce 64 omitted
+
+ # reduce 65 omitted
+
+ # reduce 66 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 437
+ def _reduce_67( val, _values, result )
+ # importing files
+ # yuk, i hate keywords
+ # we'll probably have to have some kind of search path eventually
+ # but for now, just use a path relative to the file doing the importing
+ path = @lexer.file.sub(%r{[^/]+$},val[1])
+ parser = Puppet::Parser::Parser.new()
+ parser.stack = self.stack
+ Puppet.debug("importing %s" % path)
+ noimport = false
+ begin
+ parser.file = path
+ rescue Puppet::ImportError
+ Puppet.warning("Importing %s would result in an import loop" % path)
+ result = AST::ASTArray.new(
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ noimport = true
+ end
+ unless noimport
+ result = parser.parse
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 447
+ def _reduce_68( val, _values, result )
+ result = AST::CompDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[4]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 458
+ def _reduce_69( val, _values, result )
+ result = AST::ClassDef.new(
+ :name => AST::Name.new(:value => val[1], :line => @lexer.line),
+ :args => val[2],
+ :parentclass => val[3],
+ :file => @lexer.file,
+ :line => @lexer.line,
+ :code => val[5]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 473
+ def _reduce_70( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => []
+ )
+ result
+ end
+.,.,
+
+ # reduce 71 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 478
+ def _reduce_72( val, _values, result )
+ result = val[1]
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 489
+ def _reduce_73( val, _values, result )
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ end
+ result
+ end
+.,.,
+
+ # reduce 74 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 503
+ def _reduce_75( val, _values, result )
+ if val[0].is_a?(AST::ASTArray)
+ val[0].push(val[2])
+ result = val[0]
+ else
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ end
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 511
+ def _reduce_76( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0],val[2]]
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 518
+ def _reduce_77( val, _values, result )
+ result = AST::ASTArray.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :children => [val[0]]
+ )
+ result
+ end
+.,.,
+
+ # reduce 78 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 527
+ def _reduce_79( val, _values, result )
+ result = AST::Name.new(
+ :value => val[1],
+ :file => @lexer.file,
+ :line => @lexer.line
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 536
+ def _reduce_80( val, _values, result )
+ name = val[0].sub(/^\$/,'')
+ result = AST::Variable.new(
+ :line => @lexer.line,
+ :file => @lexer.file,
+ :value => name
+ )
+ result
+ end
+.,.,
+
+module_eval <<'.,.,', 'grammar.ra', 545
+ def _reduce_81( val, _values, result )
+ if val[1].is_a?(AST::ASTArray)
+ result = val[1]
+ else
+ result = AST::ASTArray.new
+ result.push val[1]
+ end
+ result
+ end
+.,.,
+
+ # reduce 82 omitted
+
+ # reduce 83 omitted
+
+ # reduce 84 omitted
+
+module_eval <<'.,.,', 'grammar.ra', 550
+ def _reduce_85( val, _values, result )
+ result = nil
+ result
+ end
+.,.,
+
+ def _reduce_none( val, _values, result )
+ result
+ end
+
+ end # class Parser
+
+ end # module Parser
+
+end # module Puppet
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
new file mode 100644
index 000000000..dcefdbcae
--- /dev/null
+++ b/lib/puppet/parser/scope.rb
@@ -0,0 +1,427 @@
+#!/usr/local/bin/ruby -w
+
+# $Id$
+
+# the interpreter
+#
+# this builds our virtual pinball machine, into which we'll place our host-specific
+# information and out of which we'll receive our host-specific configuration
+
+require 'puppet/transportable'
+
+module Puppet
+ module Parser
+ class ScopeError < RuntimeError
+ attr_accessor :line, :file
+ end
+ #---------------------------------------------------------------
+ class Scope
+
+ attr_accessor :symtable, :objectable, :parent, :level, :interp
+ attr_accessor :name, :type
+
+ # i don't really know how to deal with a global scope yet, so
+ # i'm leaving it disabled
+ @@global = nil
+
+ @@hosttable = {}
+ @@settingtable = []
+ @@declarative = true
+
+ #------------------------------------------------------------
+ def Scope.declarative
+ return @@declarative
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def Scope.declarative=(val)
+ @@declarative = val
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def Scope.global
+ return @@global
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def child=(scope)
+ @children.push(scope)
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def declarative
+ return @@declarative
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def initialize(parent = nil, declarative = true)
+ @parent = parent
+ if @parent.nil?
+ @level = 1
+ @@declarative = declarative
+ else
+ @parent.child = self
+ @level = @parent.level + 1
+ @interp = @parent.interp
+ end
+
+ @children = []
+
+ @symtable = Hash.new(nil)
+ @typetable = Hash.new(nil)
+
+ # the defaultstable is a hash of hashes
+ @defaultstable = Hash.new { |dhash,type|
+ dhash[type] = Hash.new(nil)
+ }
+
+ @objectable = Hash.new { |typehash,typekey|
+ #hash[key] = TransObject.new(key)
+ typehash[typekey] = Hash.new { |namehash, namekey|
+ #Puppet.debug("Creating iobject with name %s and type %s" %
+ # [namekey,typekey])
+ namehash[namekey] = TransObject.new(namekey,typekey)
+ @children.push namehash[namekey]
+
+ # this has to be last, because the return value of the
+ # block is the actual hash
+ namehash[namekey]
+ }
+ }
+ @map = {
+ "variable" => @symtable,
+ "type" => @typetable,
+ "object" => @objectable,
+ "defaults" => @defaultstable
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # this method just abstracts the upwards-recursive nature of
+ # name resolution
+ # because different tables are different depths (e.g., flat, or
+ # hash of hashes), we pass in a code snippet that gets passed
+ # the table. It is assumed that the code snippet already has
+ # the name in it
+ def lookup(type,sub)
+ table = @map[type]
+ if table.nil?
+ error = Puppet::ParseError.new(
+ "Could not retrieve %s table at level %s" % [type,self.level]
+ )
+ error.stack = caller
+ raise error
+ end
+
+ if sub.is_a?(Proc) and obj = sub.call(table)
+ return obj
+ elsif table.include?(sub)
+ return table[sub]
+ elsif ! @parent.nil?
+ return @parent.lookup(type,sub)
+ else
+ return :undefined
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookuphost(name)
+ if @@hosttable.include?(name)
+ return @@hosttable[name]
+ else
+ return nil
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # collect all of the defaults set at any higher scopes
+ # this is a different type of lookup because it's additive --
+ # it collects all of the defaults, with defaults in closer scopes
+ # overriding those in later scopes
+ def lookupdefaults(type)
+ values = {}
+ # first collect the values from the parents
+ unless @parent.nil?
+ @parent.lookupdefaults(type).each { |var,value|
+ values[var] = value
+ }
+ end
+
+ # then override them with any current values
+ # this should probably be done differently
+ if @defaultstable.include?(type)
+ @defaultstable[type].each { |var,value|
+ values[var] = value
+ }
+ end
+ Puppet.debug "Got defaults for %s: %s" %
+ [type,values.inspect]
+ return values
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookuptype(name)
+ Puppet.debug "Looking up type %s" % name
+ value = self.lookup("type",name)
+ if value == :undefined
+ return nil
+ else
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # slightly different, because we're looking through a hash of hashes
+ def lookupobject(name,type)
+ Puppet.debug "Looking up object %s of type %s" % [name, type]
+ sub = proc { |table|
+ if table.include?(type)
+ if type[type].include?(name)
+ type[type][name]
+ end
+ else
+ nil
+ end
+ }
+ value = self.lookup("object",sub)
+ if value == :undefined
+ return nil
+ else
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def lookupvar(name)
+ Puppet.debug "Looking up variable %s" % name
+ value = self.lookup("variable", name)
+ if value == :undefined
+ error = Puppet::ParseError.new(
+ "Undefined variable '%s'" % name
+ )
+ error.stack = caller
+ raise error
+ else
+ #Puppet.debug "Value of '%s' is '%s'" % [name,value]
+ return value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def newscope
+ Puppet.debug "Creating new scope, level %s" % [self.level + 1]
+ return Puppet::Parser::Scope.new(self)
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def setdefaults(type,params)
+ table = @defaultstable[type]
+
+ # if we got a single param, it'll be in its own array
+ unless params[0].is_a?(Array)
+ params = [params]
+ end
+
+ params.each { |ary|
+ Puppet.debug "Default for %s is %s => %s" %
+ [type,ary[0].inspect,ary[1].inspect]
+ if @@declarative
+ if table.include?(ary[0])
+ error = Puppet::ParseError.new(
+ "Default already defined for %s { %s }" %
+ [type,ary[0]]
+ )
+ error.stack = caller
+ raise error
+ end
+ else
+ if table.include?(ary[0])
+ # we should maybe allow this warning to be turned off...
+ Puppet.warning "Replacing default for %s { %s }" %
+ [type,ary[0]]
+ end
+ end
+ table[ary[0]] = ary[1]
+ }
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def sethost(name,host)
+ @@hosttable[name] = host
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def settype(name,ltype)
+ @typetable[name] = ltype
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # when we have an 'eval' function, we should do that instead
+ # for now, we only support variables in strings
+ def strinterp(string)
+ newstring = string.dup
+ regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
+ #Puppet.debug("interpreting '%s'" % string)
+ while match = regex.match(newstring) do
+ if match[1]
+ newstring.sub!(regex,self.lookupvar(match[1]))
+ elsif match[2]
+ newstring.sub!(regex,self.lookupvar(match[2]))
+ else
+ raise 'fuh?'
+ end
+ end
+ #Puppet.debug("result is '%s'" % newstring)
+ return newstring
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # this is kind of quirky, because it doesn't differentiate between
+ # creating a new object and adding params to an existing object
+ # it doesn't solve the real problem, though: cases like file recursion,
+ # where one statement explicitly modifies an object, and another
+ # statement modifies it because of recursion
+ def setobject(type, name, params, file, line)
+ obj = self.lookupobject(name,type)
+ if obj == :undefined or obj.nil?
+ obj = @objectable[type][name]
+
+ # only set these if we've created the object, which is the
+ # most common case
+ obj.file = file
+ obj.line = line
+ end
+
+ # now add the params to whatever object we've found, whether
+ # it was in a higher scope or we just created it
+ # it will not be obvious where these parameters are from, that is,
+ # which file they're in or whatever
+ params.each { |var,value|
+ obj[var] = value
+ }
+ return obj
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ def setvar(name,value)
+ Puppet.debug "Setting '%s' to '%s' at level %s" %
+ [name.inspect,value,self.level]
+ if @@declarative and @symtable.include?(name)
+ error = Puppet::ParseError.new(
+ "Cannot reassign variable %s" % name
+ )
+ error.stack = caller
+ raise error
+ else
+ if @symtable.include?(name)
+ Puppet.warning "Reassigning %s to %s" % [name,value]
+ end
+ @symtable[name] = value
+ end
+ end
+ #------------------------------------------------------------
+
+ #------------------------------------------------------------
+ # I'm pretty sure this method could be obviated, but it doesn't
+ # really seem worth it
+ def to_trans
+ Puppet.debug "Translating scope %s at level %s" %
+ [self.object_id,self.level]
+
+ results = []
+
+ @children.each { |child|
+ if child.is_a?(Scope)
+ cresult = child.to_trans
+ Puppet.debug "Got %s from scope %s" %
+ [cresult.class,child.object_id]
+
+ # get rid of the arrayness
+ unless cresult.is_a?(TransBucket)
+ cresult.each { |result|
+ results.push(result)
+ }
+ else
+ results.push(cresult)
+ end
+ elsif child.is_a?(TransObject)
+ results.push(child)
+ else
+ error = Puppet::DevError.new(
+ "Puppet::Parse::Scope cannot handle objects of type %s" %
+ child.class
+ )
+ error.stack = caller
+ raise error
+ end
+ }
+ results = results.reject { |child|
+ # if a scope didn't result in any objects, we get some nils
+ # just get rid of them
+ 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 defined? @name
+ bucket = TransBucket.new
+ bucket.name = @name
+
+ # it'd be nice not to have to do this...
+ results.each { |result|
+ #Puppet.debug "Result type is %s" % result.class
+ bucket.push(result)
+ }
+ if defined? @type
+ bucket.type = @type
+ else
+ error = Puppet::ParseError.new(
+ "No type for scope %s" % @name
+ )
+ error.stack = caller
+ raise error
+ 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 "nameless scope; just returning a list"
+ return results
+ end
+ end
+ #------------------------------------------------------------
+ end
+ #---------------------------------------------------------------
+ end
+end