From 9161ae8f215520cda16ba9345a8818de08b89ca0 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sat, 19 Jan 2008 11:45:09 -0800 Subject: Applying a fix for #998 -- I used a patch equivalent to bartv's, although I could not use his commit because it was against the 'master' branch instead of 0.24.x. --- lib/puppet/parser/compile.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index f76103a28..e1e230d48 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -309,6 +309,9 @@ class Puppet::Parser::Compile done = false if evaluate_collections done = false if evaluate_definitions break if done + + count += 1 + if count > 1000 raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" end -- cgit From f98be4a7198326b26f1072c401b3e337f340db40 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sat, 19 Jan 2008 13:40:40 -0800 Subject: Fixing #976 -- both the full name of qualified classes and the class parts are now added as tags. I've also created a Tagging module that we should push throughout the rest of the system that uses tags. --- lib/puppet/parser/resource.rb | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 3f346166e..7dc42ccec 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -3,18 +3,18 @@ class Puppet::Parser::Resource require 'puppet/parser/resource/param' require 'puppet/parser/resource/reference' + require 'puppet/util/tagging' include Puppet::Util include Puppet::Util::MethodHelper include Puppet::Util::Errors include Puppet::Util::Logging + include Puppet::Util::Tagging attr_accessor :source, :line, :file, :scope, :rails_id attr_accessor :virtual, :override, :translated attr_reader :exported, :evaluated, :params - attr_writer :tags - # Determine whether the provided parameter name is a relationship parameter. def self.relationship_parameter?(name) unless defined?(@relationship_names) @@ -86,6 +86,7 @@ class Puppet::Parser::Resource add_overrides() add_defaults() add_metaparams() + add_scope_tags() validate() end @@ -130,13 +131,8 @@ class Puppet::Parser::Resource raise ArgumentError, "Resources do not accept %s" % options.keys.collect { |k| k.to_s }.join(", ") end - @tags = [] tag(@ref.type) - tag(@ref.title) if @ref.title.to_s =~ /^[-\w]+$/ - - if scope.resource - @tags += scope.resource.tags - end + tag(@ref.title) if valid_tag?(@ref.title.to_s) end # Merge an override resource in. This will throw exceptions if @@ -223,23 +219,6 @@ class Puppet::Parser::Resource @ref.to_s end - # Add a tag to our current list. These tags will be added to all - # of the objects contained in this scope. - def tag(*ary) - ary.collect { |tag| tag.to_s.downcase }.collect { |tag| tag.split("::") }.flatten.each do |tag| - unless tag =~ /^\w[-\w]*$/ - fail Puppet::ParseError, "Invalid tag %s" % tag.inspect - end - unless @tags.include?(tag) - @tags << tag - end - end - end - - def tags - @tags.dup - end - def to_hash @params.inject({}) do |hash, ary| param = ary[1] @@ -374,6 +353,12 @@ class Puppet::Parser::Resource end end + def add_scope_tags + if scope_resource = scope.resource + tag(*scope_resource.tags) + end + end + # Accept a parameter from an override. def override_parameter(param) # This can happen if the override is defining a new parameter, rather -- cgit From b561ae64014630d9d2b93a6a6625fe67e9844c9e Mon Sep 17 00:00:00 2001 From: Bart Vanbrabant Date: Tue, 22 Jan 2008 16:29:38 +0100 Subject: Fix bug #997, only evaluate non-virtual definitions --- lib/puppet/parser/compile.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index e1e230d48..3dc7d3657 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -285,11 +285,15 @@ class Puppet::Parser::Compile def evaluate_definitions exceptwrap do if ary = unevaluated_resources + evaluated = false ary.each do |resource| - resource.evaluate + if not resource.virtual? + resource.evaluate + evaluated = true + end end # If we evaluated, let the loop know. - return true + return evaluated else return false end -- cgit From ee6ddc9c3bf76d4a6499fdbcb43f18daed247395 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 28 Jan 2008 16:48:35 +1100 Subject: Removing tons of unnecessary calls to "nil?" from the lexer. --- lib/puppet/parser/lexer.rb | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 086d82c09..09f263eb9 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -62,8 +62,6 @@ module Puppet "<<|" => "|>>" } - @@reverse_pairs = @@pairs.inject({}) { |hash, pair| hash[pair[1]] = pair[0]; hash } - @@keywords = { "case" => :CASE, "class" => :CLASS, @@ -105,15 +103,10 @@ module Puppet def fullscan array = [] - self.scan { |token,str| + self.scan { |token, str| # Ignore any definition nesting problems @indefine = false - #Puppet.debug("got token '%s' => '%s'" % [token,str]) - if token.nil? - return array - else - array.push([token,str]) - end + array.push([token,str]) } return array end @@ -182,9 +175,7 @@ module Puppet # this is the heart of the lexer def scan #Puppet.debug("entering scan") - if @scanner.nil? - raise TypeError.new("Invalid or empty string") - end + raise TypeError.new("Invalid or empty string") unless @scanner @scanner.skip(@skip) until @scanner.eos? do @@ -195,15 +186,10 @@ module Puppet value = "" # first find out which type of token we've got - @@tokens.each { |regex,token| + @@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 + next unless tmp = @scanner.check(regex) # find the longest match if tmp.length > value.length @@ -231,9 +217,7 @@ module Puppet value = @scanner.scan(sregex) - if value == "" - raise "Didn't match regex on token %s" % stoken - end + raise "Didn't match regex on token %s" % stoken if value == "" # token-specific operations # if this gets much more complicated, it should -- cgit From 6ff9423c7ebaf759fcee28a3009cd59bed3ea886 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Wed, 30 Jan 2008 23:28:07 +1100 Subject: Significantly refactoring the lexer, including adding Token and TokenList classes for managing how the tokens work. I also moved they tests to RSpec, but I didn't rewrite all of them. --- lib/puppet/parser/lexer.rb | 661 +++++++++++++++++++++--------------- lib/puppet/parser/parser_support.rb | 7 +- 2 files changed, 393 insertions(+), 275 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/lexer.rb b/lib/puppet/parser/lexer.rb index 09f263eb9..6661650ba 100644 --- a/lib/puppet/parser/lexer.rb +++ b/lib/puppet/parser/lexer.rb @@ -1,4 +1,3 @@ - # the scanner/lexer require 'strscan' @@ -7,310 +6,432 @@ require 'puppet' module Puppet class LexError < RuntimeError; end - module Parser - #--------------------------------------------------------------- - class Lexer - attr_reader :line, :last, :file - - attr_accessor :indefine - - #%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{!} => :NOT, - %r{,} => :COMMA, - %r{\.} => :DOT, - %r{:} => :COLON, - %r{@} => :AT, - %r{<<\|} => :LLCOLLECT, - %r{\|>>} => :RRCOLLECT, - %r{<\|} => :LCOLLECT, - %r{\|>} => :RCOLLECT, - %r{;} => :SEMIC, - %r{\?} => :QMARK, - %r{\\} => :BACKSLASH, - %r{=>} => :FARROW, - %r{\+>} => :PARROW, - %r{[a-z][-\w]*} => :NAME, - %r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME, - %r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF, - %r{[0-9]+} => :NUMBER, - %r{\$(\w*::)*\w+} => :VARIABLE - } - - @@pairs = { - "{" => "}", - "(" => ")", - "[" => "]", - "<|" => "|>", - "<<|" => "|>>" - } - - @@keywords = { - "case" => :CASE, - "class" => :CLASS, - "default" => :DEFAULT, - "define" => :DEFINE, - "false" => :BOOLEAN, - "import" => :IMPORT, - "if" => :IF, - "elsif" => :ELSIF, - "else" => :ELSE, - "inherits" => :INHERITS, - "node" => :NODE, - "true" => :BOOLEAN, - "and" => :AND, - "or" => :OR, - "undef" => :UNDEF - } - - def clear - initvars - end +end - def expected - if @expected.empty? - nil - else - token = @expected[-1] - @@tokens.each do |value, name| - if token == name - return value - end - end - return token - end - end +module Puppet::Parser; end - # scan the whole file - # basically just used for testing - def fullscan - array = [] - - self.scan { |token, str| - # Ignore any definition nesting problems - @indefine = false - array.push([token,str]) - } - return array - end +class Puppet::Parser::Lexer + attr_reader :last, :file + + attr_accessor :line, :indefine + + # Our base token class. + class Token + attr_accessor :regex, :name, :string, :skip, :incr_line, :skip_text - # 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) - } + def initialize(regex, name) + if regex.is_a?(String) + @name, @string = name, regex + @regex = Regexp.new(Regexp.escape(@string)) + else + @name, @regex = name, regex end + end - def indefine? - if defined? @indefine - @indefine - else - false - end + def skip? + self.skip + end + + def to_s + "Lexer token %s" % @name.to_s + end + end + + # Maintain a list of tokens. + class TokenList + attr_reader :regex_tokens, :string_tokens + + def [](name) + @tokens[name] + end + + # Create a new token. + def add_token(name, regex, options = {}, &block) + token = Token.new(regex, name) + raise(ArgumentError, "Token %s already exists" % name) if @tokens.include?(name) + @tokens[token.name] = token + if token.string + @string_tokens << token + @tokens_by_string[token.string] = token + else + @regex_tokens << token end - def initialize - initvars() + options.each do |name, option| + token.send(name.to_s + "=", option) end - def initvars - @line = 1 - @last = "" - @lasttoken = nil - @scanner = nil - @file = nil - # AAARRGGGG! okay, regexes in ruby are bloody annoying - # no one else has "\n" =~ /\s/ - @skip = %r{[ \t]+} + token.meta_def(:convert, &block) if block_given? - @namestack = [] - @indefine = false + token + end - @expected = [] - end + def initialize + @tokens = {} + @regex_tokens = [] + @string_tokens = [] + @tokens_by_string = {} + end + + # Look up a token by its value, rather than name. + def lookup(string) + @tokens_by_string[string] + end - # Go up one in the namespace. - def namepop - @namestack.pop + # Define more tokens. + def add_tokens(hash) + hash.each do |regex, name| + add_token(name, regex) end + end + + # Sort our tokens by length, so we know once we match, we're done. + # This helps us avoid the O(n^2) nature of token matching. + def sort_tokens + @string_tokens.sort! { |a, b| b.string.length <=> a.string.length } + end + end - # Collect the current namespace. - def namespace - @namestack.join("::") + TOKENS = TokenList.new + TOKENS.add_tokens( + '[' => :LBRACK, + ']' => :RBRACK, + '{' => :LBRACE, + '}' => :RBRACE, + '(' => :LPAREN, + ')' => :RPAREN, + '=' => :EQUALS, + '==' => :ISEQUAL, + '>=' => :GREATEREQUAL, + '>' => :GREATERTHAN, + '<' => :LESSTHAN, + '<=' => :LESSEQUAL, + '!=' => :NOTEQUAL, + '!' => :NOT, + ',' => :COMMA, + '.' => :DOT, + ':' => :COLON, + '@' => :AT, + '<<|' => :LLCOLLECT, + '|>>' => :RRCOLLECT, + '<|' => :LCOLLECT, + '|>' => :RCOLLECT, + ';' => :SEMIC, + '?' => :QMARK, + '\\' => :BACKSLASH, + '=>' => :FARROW, + '+>' => :PARROW, + %r{([a-z][-\w]*::)+[a-z][-\w]*} => :CLASSNAME, + %r{((::){0,1}[A-Z][-\w]*)+} => :CLASSREF + ) + + TOKENS.add_tokens "Whatever" => :DQTEXT, "Nomatter" => :SQTEXT, "alsonomatter" => :BOOLEAN + + TOKENS.add_token :NAME, %r{[a-z][-\w]*} do |lexer, value| + string_token = self + # we're looking for keywords here + if tmp = KEYWORDS.lookup(value) + string_token = tmp + if [:TRUE, :FALSE].include?(string_token.name) + value = eval(value) + string_token = TOKENS[:BOOLEAN] end + end + [string_token, value] + end + + TOKENS.add_token :NUMBER, %r{[0-9]+} do |lexer, value| + [TOKENS[:NAME], value] + end + + TOKENS.add_token :COMMENT, %r{#.*}, :skip => true + + TOKENS.add_token :RETURN, "\n", :skip => true, :incr_line => true, :skip_text => true + + TOKENS.add_token :SQUOTE, "'" do |lexer, value| + value = lexer.slurpstring(value) + [TOKENS[:SQTEXT], value] + end + + TOKENS.add_token :DQUOTE, '"' do |lexer, value| + value = lexer.slurpstring(value) + [TOKENS[:DQTEXT], value] + end + + TOKENS.add_token :VARIABLE, %r{\$(\w*::)*\w+} do |lexer, value| + value = value.sub(/^\$/, '') + [self, value] + end + + TOKENS.sort_tokens + + @@pairs = { + "{" => "}", + "(" => ")", + "[" => "]", + "<|" => "|>", + "<<|" => "|>>" + } + + KEYWORDS = TokenList.new + + KEYWORDS.add_tokens( + "case" => :CASE, + "class" => :CLASS, + "default" => :DEFAULT, + "define" => :DEFINE, + "import" => :IMPORT, + "if" => :IF, + "elsif" => :ELSIF, + "else" => :ELSE, + "inherits" => :INHERITS, + "node" => :NODE, + "and" => :AND, + "or" => :OR, + "undef" => :UNDEF, + "false" => :FALSE, + "true" => :TRUE + ) + + def clear + initvars + end + + def expected + return nil if @expected.empty? + name = @expected[-1] + raise "Could not find expected token %s" % name unless token = TOKENS.lookup(name) + + return token + end + + # scan the whole file + # basically just used for testing + def fullscan + array = [] + + self.scan { |token, str| + # Ignore any definition nesting problems + @indefine = false + array.push([token,str]) + } + 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 - # This value might have :: in it, but we don't care -- it'll be - # handled normally when joining, and when popping we want to pop - # this full value, however long the namespace is. - def namestack(value) - @namestack << value + def find_string_token + matched_token = value = nil + + # We know our longest string token is three chars, so try each size in turn + # until we either match or run out of chars. This way our worst-case is three + # tries, where it is otherwise the number of string chars we have. Also, + # the lookups are optimized hash lookups, instead of regex scans. + [3, 2, 1].each do |i| + str = @scanner.peek(i) + if matched_token = TOKENS.lookup(str) + value = @scanner.scan(matched_token.regex) + break end + end + + return matched_token, value + end - def rest - @scanner.rest + # Find the next token that matches a regex. We look for these first. + def find_regex_token + @regex += 1 + matched_token = nil + value = "" + length = 0 + + # I tried optimizing based on the first char, but it had + # a slightly negative affect and was a good bit more complicated. + TOKENS.regex_tokens.each do |token| + next unless match_length = @scanner.match?(token.regex) + + # We've found a longer match + if match_length > length + value = @scanner.scan(token.regex) + length = value.length + matched_token = token end + end - # this is the heart of the lexer - def scan - #Puppet.debug("entering scan") - raise TypeError.new("Invalid or empty string") unless @scanner - - @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 - next unless tmp = @scanner.check(regex) - - # 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 + return matched_token, value + end - value = @scanner.scan(sregex) - - raise "Didn't match regex on token %s" % stoken if value == "" - - # 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 - ptoken = stoken - 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) - if wtoken == :BOOLEAN - value = eval(value) - end - end - ptoken = wtoken - when :NUMBER then - ptoken = :NAME - when :COMMENT then - # just throw comments away - next - when :RETURN then - @line += 1 - @scanner.skip(@skip) - next - when :SQUOTE then - #Puppet.debug("searching '%s' after '%s'" % [self.rest,value]) - value = self.slurpstring(value) - ptoken = :SQTEXT - #Puppet.debug("got string '%s' => '%s'" % [:DQTEXT,value]) - when :DQUOTE then - value = self.slurpstring(value) - ptoken = :DQTEXT - when :VARIABLE then - value = value.sub(/^\$/, '') - end + # Find the next token, returning the string and the token. + def find_token + @find += 1 + matched_token, value = find_regex_token - if match = @@pairs[value] and ptoken != :DQUOTE and ptoken != :SQUOTE - @expected << match - elsif exp = @expected[-1] and exp == value and ptoken != :DQUOTE and ptoken != :SQUOTE - @expected.pop - end + unless matched_token + matched_token, value = find_string_token + end - yield [ptoken, value] + return matched_token, value + end - if @lasttoken == :CLASS - namestack(value) - end + def indefine? + if defined? @indefine + @indefine + else + false + end + end - if @lasttoken == :DEFINE - if indefine? - msg = "Cannot nest definition %s inside %s" % [value, @indefine] - self.indefine = false - raise Puppet::ParseError, msg - end + def initialize + @find = 0 + @regex = 0 + initvars() + end - @indefine = value - end + def initvars + @line = 1 + @previous_token = nil + @scanner = nil + @file = nil + # AAARRGGGG! okay, regexes in ruby are bloody annoying + # no one else has "\n" =~ /\s/ + @skip = %r{[ \t]+} + + @namestack = [] + @indefine = false + @expected = [] + end - @last = value - @lasttoken = ptoken + # Make any necessary changes to the token and/or value. + def munge_token(token, value) + @line += 1 if token.incr_line - @scanner.skip(@skip) - end - @scanner = nil - yield [false,false] - end + skip() if token.skip_text + + return if token.skip + + token, value = token.convert(self, value) if token.respond_to?(:convert) + + return unless token + + return token, value + end + + # Go up one in the namespace. + def namepop + @namestack.pop + end - # we've encountered an opening quote... - # slurp in the rest of the string and return it - def slurpstring(quote) - # we search for the next quote that isn't preceded by a - # backslash; the caret is there to match empty strings - str = @scanner.scan_until(/([^\\]|^)#{quote}/) - if str.nil? - raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" % - [self.last,self.rest]) + # Collect the current namespace. + def namespace + @namestack.join("::") + end + + # This value might have :: in it, but we don't care -- it'll be + # handled normally when joining, and when popping we want to pop + # this full value, however long the namespace is. + def namestack(value) + @namestack << value + end + + def rest + @scanner.rest + end + + # this is the heart of the lexer + def scan + #Puppet.debug("entering scan") + raise Puppet::LexError.new("Invalid or empty string") unless @scanner + + # Skip any initial whitespace. + skip() + + until @scanner.eos? do + yielded = false + matched_token, value = find_token + + # error out if we didn't match anything at all + if matched_token.nil? + nword = nil + # Try to pull a 'word' out of the remaining string. + if @scanner.rest =~ /^(\S+)/ + nword = $1 + elsif @scanner.rest =~ /^(\s+)/ + nword = $1 else - str.sub!(/#{quote}\Z/,"") - str.gsub!(/\\#{quote}/,quote) + nword = @scanner.rest end + raise "Could not match '%s'" % nword + end - return str + final_token, value = munge_token(matched_token, value) + + next unless final_token + + if match = @@pairs[value] and final_token.name != :DQUOTE and final_token.name != :SQUOTE + @expected << match + elsif exp = @expected[-1] and exp == value and final_token.name != :DQUOTE and final_token.name != :SQUOTE + @expected.pop end - # just parse a string, not a whole file - def string=(string) - @scanner = StringScanner.new(string) + yield [final_token.name, value] + + if @previous_token + namestack(value) if @previous_token.name == :CLASS + + if @previous_token.name == :DEFINE + if indefine? + msg = "Cannot nest definition %s inside %s" % [value, @indefine] + self.indefine = false + raise Puppet::ParseError, msg + end + + @indefine = value + end end + + @previous_token = final_token + skip() end - #--------------------------------------------------------------- + @scanner = nil + + # This indicates that we're done parsing. + yield [false,false] + end + + # Skip any skipchars in our remaining string. + def skip + @scanner.skip(@skip) end -end + # we've encountered an opening quote... + # slurp in the rest of the string and return it + def slurpstring(quote) + # we search for the next quote that isn't preceded by a + # backslash; the caret is there to match empty strings + str = @scanner.scan_until(/([^\\]|^)#{quote}/) + if str.nil? + raise Puppet::LexError.new("Unclosed quote after '%s' in '%s'" % + [self.last,self.rest]) + else + str.sub!(/#{quote}\Z/,"") + str.gsub!(/\\#{quote}/,quote) + end + + return str + end + + # just parse a string, not a whole file + def string=(string) + @scanner = StringScanner.new(string) + end +end diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index acf3c9f7c..b543cd3ec 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -47,11 +47,8 @@ class Puppet::Parser::Parser # Create an AST object, and automatically add the file and line information if # available. - def ast(klass, hash = nil) - hash ||= {} - unless hash.include?(:line) - hash[:line] = @lexer.line - end + def ast(klass, hash = {}) + hash[:line] = @lexer.line unless hash.include?(:line) unless hash.include?(:file) if file = @lexer.file -- cgit From ade9f3c7296db12eee6f3c9c3aa29b99da652960 Mon Sep 17 00:00:00 2001 From: Bart Vanbrabant Date: Sun, 3 Feb 2008 11:44:32 +0100 Subject: Store a resource before adding relations to it otherwise activerecord will complain. This fixes #933 --- lib/puppet/parser/resource.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 7dc42ccec..58667727f 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -239,6 +239,8 @@ class Puppet::Parser::Resource # Handle file specially db_resource.file = self.file + db_resource.save + @params.each { |name, param| param.to_rails(db_resource) } -- cgit From ed0c745c0f3b0f277968149ca25c78ce374bd2e4 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Sun, 20 Jan 2008 22:00:05 -0800 Subject: Fixing #1017 -- environment-specific modulepath is no longer ignored. (Cherry-picked from master.) --- lib/puppet/parser/parser_support.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb index b543cd3ec..ccfc4d48a 100644 --- a/lib/puppet/parser/parser_support.rb +++ b/lib/puppet/parser/parser_support.rb @@ -177,7 +177,7 @@ class Puppet::Parser::Parser "in file #{@lexer.file} at line #{@lexer.line}" ) end - files = Puppet::Module::find_manifests(pat, :cwd => dir) + files = Puppet::Module::find_manifests(pat, :cwd => dir, :environment => @environment) if files.size == 0 raise Puppet::ImportError.new("No file(s) found for import " + "of '#{pat}'") -- cgit From dbaffaeaafa60c7325f7522a76a84c5dd7df7c6b Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 8 Feb 2008 11:42:24 -0600 Subject: Ceasing autoloading ast files; loading them manually instead --- lib/puppet/parser/ast.rb | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 14b686e2f..880e406a8 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -107,13 +107,27 @@ class Puppet::Parser::AST @line = nil set_options(args) end - #--------------------------------------------------------------- - # Now autoload everything. - @autoloader = Puppet::Util::Autoload.new(self, - "puppet/parser/ast" - ) - @autoloader.loadall end +# And include all of the AST subclasses. +require 'puppet/parser/ast/astarray' +require 'puppet/parser/ast/branch' +require 'puppet/parser/ast/caseopt' +require 'puppet/parser/ast/casestatement' +require 'puppet/parser/ast/collection' +require 'puppet/parser/ast/collexpr' +require 'puppet/parser/ast/definition' +require 'puppet/parser/ast/else' +require 'puppet/parser/ast/function' +require 'puppet/parser/ast/hostclass' +require 'puppet/parser/ast/ifstatement' require 'puppet/parser/ast/leaf' - +require 'puppet/parser/ast/node' +require 'puppet/parser/ast/resource' +require 'puppet/parser/ast/resource_defaults' +require 'puppet/parser/ast/resource_override' +require 'puppet/parser/ast/resource_reference' +require 'puppet/parser/ast/resourceparam' +require 'puppet/parser/ast/selector' +require 'puppet/parser/ast/tag' +require 'puppet/parser/ast/vardef' -- cgit From 82720d5327b30839a29035ee0b498b940ffc7a5a Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 8 Feb 2008 13:24:39 -0600 Subject: Removing some obsolete code from the AST base class --- lib/puppet/parser/ast.rb | 42 +++--------------------------------------- 1 file changed, 3 insertions(+), 39 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 880e406a8..cac18fef4 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -14,30 +14,6 @@ class Puppet::Parser::AST include Puppet::Util::MethodHelper attr_accessor :line, :file, :parent, :scope - # Just used for 'tree', which is only used in debugging. - @@pink = "" - @@green = "" - @@yellow = "" - @@slate = "" - @@reset = "" - - # Just used for 'tree', which is only used in debugging. - @@indent = " " * 4 - @@indline = @@pink + ("-" * 4) + @@reset - @@midline = @@slate + ("-" * 4) + @@reset - - @@settypes = {} - - # Just used for 'tree', which is only used in debugging. - def AST.indention - return @@indent * @@indention - end - - # Just used for 'tree', which is only used in debugging. - def AST.midline - return @@midline - end - # Does this ast object set something? If so, it gets evaluated first. def self.settor? if defined? @settor @@ -47,16 +23,12 @@ class Puppet::Parser::AST end end - # Evaluate the current object. Basically just iterates across all + # Evaluate the current object. Just a stub method, since the subclass + # should override this method. # of the contained children and evaluates them in turn, returning a # list of all of the collected values, rejecting nil values def evaluate(args) - #Puppet.debug("Evaluating ast %s" % @name) - value = self.collect { |obj| - obj.safeevaluate(args) - }.reject { |obj| - obj.nil? - } + raise Puppet::DevError, "Did not override #evaluate in %s" % self.class end # Throw a parse error. @@ -90,14 +62,6 @@ class Puppet::Parser::AST end end - # Again, just used for printing out the parse tree. - 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 - # Initialize the object. Requires a hash as the argument, and # takes each of the parameters of the hash and calls the settor # method for them. This is probably pretty inefficient and should -- cgit From 5a0e34b4f8da22e1830ec7d0a730c3686665bceb Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 8 Feb 2008 14:25:52 -0600 Subject: Refactoring the AST classes just a bit. I realized that all of the evaluate() methods only ever accepted a scope, and sometimes one other option, so I switched them all to use named arguments instead of a hash. --- lib/puppet/parser/ast.rb | 6 +++--- lib/puppet/parser/ast/astarray.rb | 5 ++--- lib/puppet/parser/ast/caseopt.rb | 8 ++++---- lib/puppet/parser/ast/casestatement.rb | 9 ++++----- lib/puppet/parser/ast/collection.rb | 6 ++---- lib/puppet/parser/ast/collexpr.rb | 8 +++----- lib/puppet/parser/ast/definition.rb | 9 +++------ lib/puppet/parser/ast/else.rb | 5 ++--- lib/puppet/parser/ast/function.rb | 13 +++---------- lib/puppet/parser/ast/hostclass.rb | 12 +++++------- lib/puppet/parser/ast/ifstatement.rb | 9 ++++----- lib/puppet/parser/ast/leaf.rb | 12 ++++++------ lib/puppet/parser/ast/node.rb | 16 ++++------------ lib/puppet/parser/ast/resource.rb | 8 +++----- lib/puppet/parser/ast/resource_defaults.rb | 6 ++---- lib/puppet/parser/ast/resource_override.rb | 8 +++----- lib/puppet/parser/ast/resource_reference.rb | 6 ++---- lib/puppet/parser/ast/resourceparam.rb | 6 ++---- lib/puppet/parser/ast/selector.rb | 11 +++++------ lib/puppet/parser/ast/tag.rb | 6 ++---- lib/puppet/parser/ast/vardef.rb | 7 +++---- lib/puppet/parser/resource.rb | 2 +- 22 files changed, 68 insertions(+), 110 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index cac18fef4..c9bd7c9e8 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -27,7 +27,7 @@ class Puppet::Parser::AST # should override this method. # of the contained children and evaluates them in turn, returning a # list of all of the collected values, rejecting nil values - def evaluate(args) + def evaluate(*options) raise Puppet::DevError, "Did not override #evaluate in %s" % self.class end @@ -47,11 +47,11 @@ class Puppet::Parser::AST # correctly handles errors. It is critical to use this method because # it can enable you to catch the error where it happens, rather than # much higher up the stack. - def safeevaluate(options) + def safeevaluate(*options) # We duplicate code here, rather than using exceptwrap, because this # is called so many times during parsing. begin - return self.evaluate(options) + return self.evaluate(*options) rescue Puppet::Error => detail raise adderrorcontext(detail) rescue => detail diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb index 5f1e838d0..b66fd6bba 100644 --- a/lib/puppet/parser/ast/astarray.rb +++ b/lib/puppet/parser/ast/astarray.rb @@ -15,8 +15,7 @@ class Puppet::Parser::AST end # Evaluate our children. - def evaluate(hash) - scope = hash[:scope] + def evaluate(scope) rets = nil # We basically always operate declaratively, and when we # do we need to evaluate the settor-like statements first. This @@ -51,7 +50,7 @@ class Puppet::Parser::AST } rets = [settors, others].flatten.collect { |child| - child.safeevaluate(:scope => scope) + child.safeevaluate(scope) } return rets.reject { |o| o.nil? } end diff --git a/lib/puppet/parser/ast/caseopt.rb b/lib/puppet/parser/ast/caseopt.rb index d1d9d0e9c..824bde853 100644 --- a/lib/puppet/parser/ast/caseopt.rb +++ b/lib/puppet/parser/ast/caseopt.rb @@ -44,17 +44,17 @@ class Puppet::Parser::AST def eachvalue(scope) if @value.is_a?(AST::ASTArray) @value.each { |subval| - yield subval.evaluate(:scope => scope) + yield subval.safeevaluate(scope) } else - yield @value.evaluate(:scope => scope) + yield @value.safeevaluate(scope) end end # Evaluate the actual statements; this only gets called if # our option matched. - def evaluate(hash) - return @statements.safeevaluate(hash) + def evaluate(scope) + return @statements.safeevaluate(scope) end end end diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb index 3c6f9c7e2..aa03090de 100644 --- a/lib/puppet/parser/ast/casestatement.rb +++ b/lib/puppet/parser/ast/casestatement.rb @@ -8,9 +8,8 @@ class Puppet::Parser::AST # Short-curcuit evaluation. Return the value of the statements for # the first option that matches. - def evaluate(hash) - scope = hash[:scope] - value = @test.safeevaluate(:scope => scope) + def evaluate(scope) + value = @test.safeevaluate(scope) sensitive = Puppet[:casesensitive] value = value.downcase if ! sensitive and value.respond_to?(:downcase) @@ -30,7 +29,7 @@ class Puppet::Parser::AST if found # we found a matching option - retvalue = option.safeevaluate(:scope => scope) + retvalue = option.safeevaluate(scope) break end @@ -42,7 +41,7 @@ class Puppet::Parser::AST # Unless we found something, look for the default. unless found if default - retvalue = default.safeevaluate(:scope => scope) + retvalue = default.safeevaluate(scope) else Puppet.debug "No true answers and no default" retvalue = nil diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb index e05977a47..31f508929 100644 --- a/lib/puppet/parser/ast/collection.rb +++ b/lib/puppet/parser/ast/collection.rb @@ -9,11 +9,9 @@ class Collection < AST::Branch attr_accessor :type, :query, :form # We return an object that does a late-binding evaluation. - def evaluate(hash) - scope = hash[:scope] - + def evaluate(scope) if self.query - str, code = self.query.safeevaluate :scope => scope + str, code = self.query.safeevaluate scope else str = code = nil end diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb index 4a96d9c61..3e13d9400 100644 --- a/lib/puppet/parser/ast/collexpr.rb +++ b/lib/puppet/parser/ast/collexpr.rb @@ -9,9 +9,7 @@ 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] - + def evaluate(scope) # Make sure our contained expressions have all the info they need. [@test1, @test2].each do |t| if t.is_a?(self.class) @@ -21,8 +19,8 @@ class CollExpr < AST::Branch end # The code is only used for virtual lookups - str1, code1 = @test1.safeevaluate :scope => scope - str2, code2 = @test2.safeevaluate :scope => scope + str1, code1 = @test1.safeevaluate scope + str2, code2 = @test2.safeevaluate scope # First build up the virtual code. # If we're a conjunction operator, then we're calling code. I did diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index 3d6d6188c..2e39e7332 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -27,10 +27,7 @@ class Puppet::Parser::AST false end - def evaluate(options) - origscope = options[:scope] - resource = options[:resource] - + def evaluate(origscope, resource) # Create a new scope. scope = subscope(origscope, resource) @@ -49,7 +46,7 @@ class Puppet::Parser::AST set_resource_parameters(scope, resource) if self.code - return self.code.safeevaluate(:scope => scope) + return self.code.safeevaluate(scope) else return nil end @@ -181,7 +178,7 @@ class Puppet::Parser::AST arg = symbolize(arg) unless args.include?(arg) if defined? default and ! default.nil? - default = default.safeevaluate :scope => scope + default = default.safeevaluate scope args[arg] = default #Puppet.debug "Got default %s for %s in %s" % # [default.inspect, arg.inspect, @name.inspect] diff --git a/lib/puppet/parser/ast/else.rb b/lib/puppet/parser/ast/else.rb index e76051372..affac625d 100644 --- a/lib/puppet/parser/ast/else.rb +++ b/lib/puppet/parser/ast/else.rb @@ -12,9 +12,8 @@ class Puppet::Parser::AST # Evaluate the actual statements; this only gets called if # our test was true matched. - def evaluate(hash) - scope = hash[:scope] - return @statements.safeevaluate(:scope => scope) + def evaluate(scope) + return @statements.safeevaluate(scope) end end end diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb index 0cd1fff62..63d7c7abf 100644 --- a/lib/puppet/parser/ast/function.rb +++ b/lib/puppet/parser/ast/function.rb @@ -7,18 +7,11 @@ class Puppet::Parser::AST @settor = true - def evaluate(hash) + def evaluate(scope) # We don't need to evaluate the name, because it's plaintext + args = @arguments.safeevaluate(scope) - # Just evaluate the arguments - scope = hash[:scope] - - args = @arguments.safeevaluate(:scope => scope) - - #exceptwrap :message => "Failed to execute %s" % @name, - # :type => Puppet::ParseError do - return scope.send("function_" + @name, args) - #end + return scope.send("function_" + @name, args) end def initialize(hash) diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 63900d0e3..42d13f4b2 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -20,9 +20,7 @@ class Puppet::Parser::AST end # Evaluate the code associated with this class. - def evaluate(options) - scope = options[:scope] - raise(ArgumentError, "Classes require resources") unless options[:resource] + def evaluate(scope, resource) # Verify that we haven't already been evaluated. This is # what provides the singleton aspect. if existing_scope = scope.compile.class_scope(self) @@ -34,7 +32,7 @@ class Puppet::Parser::AST pnames = nil if pklass = self.parentobj - pklass.safeevaluate :scope => scope, :resource => options[:resource] + pklass.safeevaluate(scope, resource) scope = parent_scope(scope, pklass) pnames = scope.namespaces @@ -42,8 +40,8 @@ class Puppet::Parser::AST # Don't create a subscope for the top-level class, since it already # has its own scope. - unless options[:resource].title == :main - scope = subscope(scope, options[:resource]) + unless resource.title == :main + scope = subscope(scope, resource) end if pnames @@ -58,7 +56,7 @@ class Puppet::Parser::AST # Now evaluate our code, yo. if self.code - return self.code.evaluate(:scope => scope) + return self.code.safeevaluate(scope) else return nil end diff --git a/lib/puppet/parser/ast/ifstatement.rb b/lib/puppet/parser/ast/ifstatement.rb index 66a07b01f..afa2cd572 100644 --- a/lib/puppet/parser/ast/ifstatement.rb +++ b/lib/puppet/parser/ast/ifstatement.rb @@ -12,15 +12,14 @@ class Puppet::Parser::AST # Short-curcuit evaluation. If we're true, evaluate our statements, # else if there's an 'else' setting, evaluate it. # the first option that matches. - def evaluate(hash) - scope = hash[:scope] - value = @test.safeevaluate(:scope => scope) + def evaluate(scope) + value = @test.safeevaluate(scope) if Puppet::Parser::Scope.true?(value) - return @statements.safeevaluate(:scope => scope) + return @statements.safeevaluate(scope) else if defined? @else - return @else.safeevaluate(:scope => scope) + return @else.safeevaluate(scope) else return nil end diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb index 225253061..c545c1e47 100644 --- a/lib/puppet/parser/ast/leaf.rb +++ b/lib/puppet/parser/ast/leaf.rb @@ -6,7 +6,7 @@ class Puppet::Parser::AST attr_accessor :value, :type # Return our value. - def evaluate(hash) + def evaluate(scope) return @value end @@ -35,14 +35,14 @@ class Puppet::Parser::AST class String < AST::Leaf # Interpolate the string looking for variables, and then return # the result. - def evaluate(hash) - return hash[:scope].strinterp(@value, @file, @line) + def evaluate(scope) + return scope.strinterp(@value, @file, @line) end end # An uninterpreted string. class FlatString < AST::Leaf - def evaluate(hash) + def evaluate(scope) return @value end end @@ -81,9 +81,9 @@ class Puppet::Parser::AST class Variable < Name # Looks up the value of the object in the scope tree (does # not include syntactical constructs, like '$' and '{}'). - def evaluate(hash) + def evaluate(scope) parsewrap do - return hash[:scope].lookupvar(@value) + return scope.lookupvar(@value) end end end diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index a296e43ba..3c5d44d1b 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -7,23 +7,15 @@ class Puppet::Parser::AST @name = :node attr_accessor :name - def evaluate(options) - scope = options[:scope] - - #pscope = if ! Puppet[:lexical] or options[:asparent] - # @scope - #else - # origscope - #end - + def evaluate(scope, resource) # 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.parentobj - scope = parent.safeevaluate :scope => scope, :resource => options[:resource] + scope = parent.safeevaluate scope, resource end scope = scope.newscope( - :resource => options[:resource], + :resource => resource, :keyword => @keyword, :source => self, :namespace => "" # nodes are always in "" @@ -36,7 +28,7 @@ class Puppet::Parser::AST # And then evaluate our code if we have any if self.code - @code.safeevaluate(:scope => scope) + @code.safeevaluate(scope) end return scope diff --git a/lib/puppet/parser/ast/resource.rb b/lib/puppet/parser/ast/resource.rb index c53ab0a68..606beb537 100644 --- a/lib/puppet/parser/ast/resource.rb +++ b/lib/puppet/parser/ast/resource.rb @@ -9,15 +9,13 @@ class Resource < AST::ResourceReference # Does not actually return an object; instead sets an object # in the current scope. - def evaluate(options) - scope = options[:scope] - + def evaluate(scope) # Evaluate all of the specified params. paramobjects = @params.collect { |param| - param.safeevaluate(:scope => scope) + param.safeevaluate(scope) } - objtitles = @title.safeevaluate(:scope => scope) + objtitles = @title.safeevaluate(scope) # it's easier to always use an array, even for only one name unless objtitles.is_a?(Array) diff --git a/lib/puppet/parser/ast/resource_defaults.rb b/lib/puppet/parser/ast/resource_defaults.rb index 8f9c1b8df..4856f0594 100644 --- a/lib/puppet/parser/ast/resource_defaults.rb +++ b/lib/puppet/parser/ast/resource_defaults.rb @@ -8,13 +8,11 @@ class Puppet::Parser::AST # As opposed to ResourceDef, this stores each default for the given # object type. - def evaluate(hash) - scope = hash[:scope] - + def evaluate(scope) # Use a resource reference to canonize the type ref = Puppet::ResourceReference.new(@type, "whatever") type = ref.type - params = @params.safeevaluate(:scope => scope) + params = @params.safeevaluate(scope) parsewrap do scope.setdefaults(type, params) diff --git a/lib/puppet/parser/ast/resource_override.rb b/lib/puppet/parser/ast/resource_override.rb index 46c930902..d15f68608 100644 --- a/lib/puppet/parser/ast/resource_override.rb +++ b/lib/puppet/parser/ast/resource_override.rb @@ -17,17 +17,15 @@ class Puppet::Parser::AST # Does not actually return an object; instead sets an object # in the current scope. - def evaluate(hash) - scope = hash[:scope] - + def evaluate(scope) # Get our object reference. - object = @object.safeevaluate(:scope => scope) + object = @object.safeevaluate(scope) hash = {} # Evaluate all of the specified params. params = @params.collect { |param| - param.safeevaluate(:scope => scope) + param.safeevaluate(scope) } # Now we just create a normal resource, but we call a very different diff --git a/lib/puppet/parser/ast/resource_reference.rb b/lib/puppet/parser/ast/resource_reference.rb index b06ea17be..4bb41165a 100644 --- a/lib/puppet/parser/ast/resource_reference.rb +++ b/lib/puppet/parser/ast/resource_reference.rb @@ -22,10 +22,8 @@ class Puppet::Parser::AST # Evaluate our object, but just return a simple array of the type # and name. - def evaluate(hash) - scope = hash[:scope] - - title = @title.safeevaluate(:scope => scope) + def evaluate(scope) + title = @title.safeevaluate(scope) if @type.to_s.downcase == "class" objtype = "class" title = qualified_class(scope, title) diff --git a/lib/puppet/parser/ast/resourceparam.rb b/lib/puppet/parser/ast/resourceparam.rb index 8b1e7b367..c552a7ee5 100644 --- a/lib/puppet/parser/ast/resourceparam.rb +++ b/lib/puppet/parser/ast/resourceparam.rb @@ -10,12 +10,10 @@ class Puppet::Parser::AST end # Return the parameter and the value. - def evaluate(hash) - scope = hash[:scope] - + def evaluate(scope) return Puppet::Parser::Resource::Param.new( :name => @param, - :value => @value.safeevaluate(:scope => scope), + :value => @value.safeevaluate(scope), :source => scope.source, :line => self.line, :file => self.file, :add => self.add ) diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb index d363ab7e4..399d405a3 100644 --- a/lib/puppet/parser/ast/selector.rb +++ b/lib/puppet/parser/ast/selector.rb @@ -11,13 +11,12 @@ class Puppet::Parser::AST end # Find the value that corresponds with the test. - def evaluate(hash) - scope = hash[:scope] + def evaluate(scope) retvalue = nil found = nil # Get our parameter. - paramvalue = @param.safeevaluate(:scope => scope) + paramvalue = @param.safeevaluate(scope) sensitive = Puppet[:casesensitive] @@ -33,13 +32,13 @@ class Puppet::Parser::AST # Then look for a match in the options. @values.each { |obj| - param = obj.param.safeevaluate(:scope => scope) + param = obj.param.safeevaluate(scope) if ! sensitive && param.respond_to?(:downcase) param = param.downcase end if param == paramvalue # we found a matching option - retvalue = obj.value.safeevaluate(:scope => scope) + retvalue = obj.value.safeevaluate(scope) found = true break elsif obj.param.is_a?(Default) @@ -51,7 +50,7 @@ class Puppet::Parser::AST # Unless we found something, look for the default. unless found if default - retvalue = default.value.safeevaluate(:scope => scope) + retvalue = default.value.safeevaluate(scope) else self.fail Puppet::ParseError, "No matching value for selector param '%s'" % paramvalue diff --git a/lib/puppet/parser/ast/tag.rb b/lib/puppet/parser/ast/tag.rb index e2882d2f0..2909504a7 100644 --- a/lib/puppet/parser/ast/tag.rb +++ b/lib/puppet/parser/ast/tag.rb @@ -8,10 +8,8 @@ class Puppet::Parser::AST @name = :class attr_accessor :type - def evaluate(hash) - scope = hash[:scope] - - types = @type.safeevaluate(:scope => scope) + def evaluate(scope) + types = @type.safeevaluate(scope) types = [types] unless types.is_a? Array diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb index 1e7f874bc..ee79159d7 100644 --- a/lib/puppet/parser/ast/vardef.rb +++ b/lib/puppet/parser/ast/vardef.rb @@ -9,10 +9,9 @@ class Puppet::Parser::AST # Look up our name and value, and store them appropriately. The # lexer strips off the syntax stuff like '$'. - def evaluate(hash) - scope = hash[:scope] - name = @name.safeevaluate(:scope => scope) - value = @value.safeevaluate(:scope => scope) + def evaluate(scope) + name = @name.safeevaluate(scope) + value = @value.safeevaluate(scope) parsewrap do scope.setvar(name,value, @file, @line) diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 58667727f..ea6038299 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -59,7 +59,7 @@ class Puppet::Parser::Resource if klass = @ref.definedtype finish() scope.compile.delete_resource(self) - return klass.evaluate(:scope => scope, :resource => self) + return klass.evaluate(scope, self) elsif builtin? devfail "Cannot evaluate a builtin type" else -- cgit From fb4bdc0b02bba1291cb78ccd5c2a3198d3929d69 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Fri, 8 Feb 2008 13:54:53 -0800 Subject: More AST refactoring -- each of the code wrapping classes just returns a resource from its evaluate() method, and all of the work is done in the evaluate_code method. This makes the code cleaner, because it means 1) evaluate() has the same prototype as all of the other AST classes, 2) evaluate() is no longer called indirectly through the Parser Resource class, and 3) the classes themselves are responsible for creating the resources, rather than it being done in the Compile class. --- lib/puppet/parser/ast/definition.rb | 304 ++++++++++++++++++------------------ lib/puppet/parser/ast/hostclass.rb | 113 +++++++------- lib/puppet/parser/ast/node.rb | 96 ++++++------ lib/puppet/parser/resource.rb | 2 +- lib/puppet/parser/resource/param.rb | 2 +- lib/puppet/parser/scope.rb | 5 + 6 files changed, 256 insertions(+), 266 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index 2e39e7332..bf57942d7 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -1,152 +1,145 @@ 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 Definition < AST::Branch - include Puppet::Util - include Puppet::Util::Warnings - include Puppet::Util::MethodHelper - class << self - attr_accessor :name - end +require 'puppet/util/warnings' - # The class name - @name = :definition +# The AST class for defined types, which is also the base class +# nodes and classes. +class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch + include Puppet::Util::Warnings + class << self + attr_accessor :name + end - attr_accessor :classname, :arguments, :code, :scope, :keyword - attr_accessor :exported, :namespace, :parser, :virtual + # The class name + @name = :definition - # These are retrieved when looking up the superclass - attr_accessor :name + attr_accessor :classname, :arguments, :code, :scope, :keyword + attr_accessor :exported, :namespace, :parser, :virtual, :name - attr_reader :parentclass + attr_reader :parentclass - def child_of?(klass) - false - end + def child_of?(klass) + false + end - def evaluate(origscope, resource) - # Create a new scope. - scope = subscope(origscope, resource) + # Create a resource that knows how to evaluate our actual code. + def evaluate(scope) + resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source) - # Additionally, add a tag for whatever kind of class - # we are - if @classname != "" and ! @classname.nil? - @classname.split(/::/).each { |tag| scope.resource.tag(tag) } - end + scope.compile.store_resource(scope, resource) - [resource.name, resource.title].each do |str| - unless str.nil? or str =~ /[^\w]/ or str == "" - scope.resource.tag(str) - end - end + return resource + end - set_resource_parameters(scope, resource) + # Now evaluate the code associated with this class or definition. + def evaluate_code(resource) + # Create a new scope. + scope = subscope(resource.scope, resource) - if self.code - return self.code.safeevaluate(scope) - else - return nil - end + set_resource_parameters(scope, resource) + + if self.code + return self.code.safeevaluate(scope) + else + return nil end + end - def initialize(hash = {}) - @arguments = nil - @parentclass = nil - super + def initialize(hash = {}) + @arguments = nil + @parentclass = nil + super - # Convert the arguments to a hash for ease of later use. - if @arguments - unless @arguments.is_a? Array - @arguments = [@arguments] - end - oldargs = @arguments - @arguments = {} - oldargs.each do |arg, val| - @arguments[arg] = val - end - else - @arguments = {} + # Convert the arguments to a hash for ease of later use. + if @arguments + unless @arguments.is_a? Array + @arguments = [@arguments] end - - # Deal with metaparams in the argument list. - @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 resources" % arg - else - raise Puppet::ParseError, "%s is a metaparameter; please choose another parameter name in the %s definition" % [arg, self.classname] - end + oldargs = @arguments + @arguments = {} + oldargs.each do |arg, val| + @arguments[arg] = val end + else + @arguments = {} end - def find_parentclass - @parser.findclass(namespace, parentclass) + # Deal with metaparams in the argument list. + @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 resources" % arg + else + raise Puppet::ParseError, "%s is a metaparameter; please choose another parameter name in the %s definition" % [arg, self.classname] + end end + end - # Set our parent class, with a little check to avoid some potential - # weirdness. - def parentclass=(name) - if name == self.classname - parsefail "Parent classes must have dissimilar names" - end + def find_parentclass + @parser.findclass(namespace, parentclass) + end - @parentclass = name + # Set our parent class, with a little check to avoid some potential + # weirdness. + def parentclass=(name) + if name == self.classname + parsefail "Parent classes must have dissimilar names" end - # Hunt down our class object. - def parentobj - if @parentclass - # Cache our result, since it should never change. - unless defined?(@parentobj) - unless tmp = find_parentclass - parsefail "Could not find %s %s" % [self.class.name, @parentclass] - end + @parentclass = name + end - if tmp == self - parsefail "Parent classes must have dissimilar names" - end + # Hunt down our class object. + def parentobj + if @parentclass + # Cache our result, since it should never change. + unless defined?(@parentobj) + unless tmp = find_parentclass + parsefail "Could not find %s %s" % [self.class.name, @parentclass] + end - @parentobj = tmp + if tmp == self + parsefail "Parent classes must have dissimilar names" end - @parentobj - else - nil + + @parentobj = tmp end + @parentobj + else + nil end + end - # Create a new subscope in which to evaluate our code. - def subscope(scope, resource) - args = { - :resource => resource, - :keyword => self.keyword, - :namespace => self.namespace, - :source => self - } + # Create a new subscope in which to evaluate our code. + def subscope(scope, resource) + args = { + :resource => resource, + :keyword => self.keyword, + :namespace => self.namespace, + :source => self + } - oldscope = scope - scope = scope.newscope(args) - scope.source = self + oldscope = scope + scope = scope.newscope(args) + scope.source = self - return scope - end + return scope + end - def to_s - classname - end + def to_s + classname + end - # Check whether a given argument is valid. Searches up through - # any parent classes that might exist. - def validattr?(param) - param = param.to_s + # Check whether a given argument is valid. Searches up through + # any parent classes that might exist. + def validattr?(param) + param = param.to_s - if @arguments.include?(param) - # It's a valid arg for us - return true - elsif param == "name" - return true + if @arguments.include?(param) + # It's a valid arg for us + return true + elsif param == "name" + return true # elsif defined? @parentclass and @parentclass # # Else, check any existing parent # if parent = @scope.lookuptype(@parentclass) and parent != [] @@ -157,53 +150,52 @@ class Puppet::Parser::AST # raise Puppet::Error, "Could not find parent class %s" % # @parentclass # end - elsif Puppet::Type.metaparam?(param) - return true - else - # Or just return false - return false - end + elsif Puppet::Type.metaparam?(param) + return true + else + # Or just return false + return false end + end - private - - # Set any arguments passed by the resource as variables in the scope. - def set_resource_parameters(scope, resource) - args = symbolize_options(resource.to_hash || {}) - - # Verify that all required arguments are either present or - # have been provided with defaults. - if self.arguments - self.arguments.each { |arg, default| - arg = symbolize(arg) - unless args.include?(arg) - if defined? default and ! default.nil? - default = default.safeevaluate scope - args[arg] = default - #Puppet.debug "Got default %s for %s in %s" % - # [default.inspect, arg.inspect, @name.inspect] - else - parsefail "Must pass %s to %s of type %s" % - [arg, resource.title, @classname] - end + private + + # Set any arguments passed by the resource as variables in the scope. + def set_resource_parameters(scope, resource) + args = symbolize_options(resource.to_hash || {}) + + # Verify that all required arguments are either present or + # have been provided with defaults. + if self.arguments + self.arguments.each { |arg, default| + arg = arg.to_sym + unless args.include?(arg) + if defined? default and ! default.nil? + default = default.safeevaluate scope + args[arg] = default + #Puppet.debug "Got default %s for %s in %s" % + # [default.inspect, arg.inspect, @name.inspect] + else + parsefail "Must pass %s to %s of type %s" % + [arg, resource.title, @classname] end - } - end - - # Set each of the provided arguments as variables in the - # definition's scope. - args.each { |arg,value| - unless validattr?(arg) - parsefail "%s does not accept attribute %s" % [@classname, arg] - end - - exceptwrap do - scope.setvar(arg.to_s, args[arg]) end } - - scope.setvar("title", resource.title) unless args.include? :title - scope.setvar("name", resource.name) unless args.include? :name end + + # Set each of the provided arguments as variables in the + # definition's scope. + args.each { |arg,value| + unless validattr?(arg) + parsefail "%s does not accept attribute %s" % [@classname, arg] + end + + exceptwrap do + scope.setvar(arg.to_s, args[arg]) + end + } + + scope.setvar("title", resource.title) unless args.include? :title + scope.setvar("name", resource.name) unless args.include? :name end end diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 42d13f4b2..251d5eba6 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -1,78 +1,73 @@ require 'puppet/parser/ast/definition' -class Puppet::Parser::AST - # The code associated with a class. This is different from definitions - # in that each class is a singleton -- only one will exist for a given - # node. - class HostClass < AST::Definition - @name = :class +# The code associated with a class. This is different from definitions +# in that each class is a singleton -- only one will exist for a given +# node. +class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition + @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 + # 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.parentobj - return true - else - return self.parentobj.child_of?(klass) - end + if klass == self.parentobj + return true + else + return self.parentobj.child_of?(klass) end + end - # Evaluate the code associated with this class. - def evaluate(scope, resource) - # Verify that we haven't already been evaluated. This is - # what provides the singleton aspect. - if existing_scope = scope.compile.class_scope(self) - Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname) - return nil - end - - scope.compile.catalog.tag(self.classname) + # Evaluate the code associated with this class. + def evaluate_code(resource) + scope = resource.scope + # Verify that we haven't already been evaluated. This is + # what provides the singleton aspect. + if existing_scope = scope.compile.class_scope(self) + Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname) + return nil + end - pnames = nil - if pklass = self.parentobj - pklass.safeevaluate(scope, resource) + pnames = nil + if pklass = self.parentobj + pklass.evaluate_code(resource) - scope = parent_scope(scope, pklass) - pnames = scope.namespaces - end + scope = parent_scope(scope, pklass) + pnames = scope.namespaces + end - # Don't create a subscope for the top-level class, since it already - # has its own scope. - unless resource.title == :main - scope = subscope(scope, resource) - end + # Don't create a subscope for the top-level class, since it already + # has its own scope. + scope = subscope(scope, resource) unless resource.title == :main - if pnames - pnames.each do |ns| - scope.add_namespace(ns) - end + if pnames + pnames.each do |ns| + scope.add_namespace(ns) end + end - # Set the class before we do anything else, so that it's set - # during the evaluation and can be inspected. - scope.compile.class_set(self.classname, scope) + # Set the class before we do anything else, so that it's set + # during the evaluation and can be inspected. + scope.compile.class_set(self.classname, scope) - # Now evaluate our code, yo. - if self.code - return self.code.safeevaluate(scope) - else - return nil - end + # Now evaluate our code, yo. + if self.code + return self.code.safeevaluate(scope) + else + return nil end + end - def initialize(options) - @parentclass = nil - super - end + def initialize(options) + @parentclass = nil + super + end - def parent_scope(scope, klass) - if s = scope.compile.class_scope(klass) - return s - else - raise Puppet::DevError, "Could not find scope for %s" % klass.classname - end + def parent_scope(scope, klass) + if s = scope.compile.class_scope(klass) + return s + else + raise Puppet::DevError, "Could not find scope for %s" % klass.classname end end end diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index 3c5d44d1b..7ff7a18e1 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -1,59 +1,57 @@ 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 :name - - def evaluate(scope, resource) - # 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.parentobj - scope = parent.safeevaluate scope, resource - end - - scope = scope.newscope( - :resource => resource, - :keyword => @keyword, - :source => self, - :namespace => "" # nodes are always in "" - ) - - # 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.compile.class_set(self.classname, scope) - - # And then evaluate our code if we have any - if self.code - @code.safeevaluate(scope) - end - - return scope +# 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 Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass + @name = :node + + # Evaluate the code associated with our node definition. + def evaluate_code(resource) + scope = resource.scope + + # 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.parentobj + scope = parent.evaluate_code(resource) end - def initialize(options) - @parentclass = nil - super + scope = scope.newscope( + :resource => resource, + :keyword => @keyword, + :source => self, + :namespace => "" # nodes are always in "" + ) - # Do some validation on the node name - if @name =~ /[^-\w.]/ - raise Puppet::ParseError, "Invalid node name %s" % @name - end - end + # 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.compile.class_set(self.classname, scope) - # Make sure node scopes are marked as such. - def subscope(*args) - scope = super - scope.nodescope = true - end + # And then evaluate our code if we have any + @code.safeevaluate(scope) if self.code + + return scope + end + + def initialize(options) + @parentclass = nil + super - private - # Search for the object matching our parent class. - def find_parentclass - @parser.findnode(parentclass) + # Do some validation on the node name + if @name =~ /[^-\w.]/ + raise Puppet::ParseError, "Invalid node name %s" % @name end end + + # Make sure node scopes are marked as such. + def subscope(*args) + scope = super + scope.nodescope = true + end + + private + # Search for the object matching our parent class. + def find_parentclass + @parser.findnode(parentclass) + end end diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index ea6038299..f8701578c 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -59,7 +59,7 @@ class Puppet::Parser::Resource if klass = @ref.definedtype finish() scope.compile.delete_resource(self) - return klass.evaluate(scope, self) + return klass.evaluate_code(self) elsif builtin? devfail "Cannot evaluate a builtin type" else diff --git a/lib/puppet/parser/resource/param.rb b/lib/puppet/parser/resource/param.rb index 9352311d6..9dd3f26d2 100644 --- a/lib/puppet/parser/resource/param.rb +++ b/lib/puppet/parser/resource/param.rb @@ -12,7 +12,7 @@ class Puppet::Parser::Resource::Param end def inspect - "#<#{self.class} @name => #{self.name}, @value => #{self.value}, @source => #{self.source.name}>" + "#<#{self.class} @name => #{name}, @value => #{value}, @source => #{source.name}>" end def line_to_i diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 028414cc0..81d4ac71a 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -19,6 +19,11 @@ class Puppet::Parser::Scope attr_accessor :base, :keyword, :nodescope attr_accessor :top, :translated, :compile + # A demeterific shortcut to the catalog. + def catalog + compile.catalog + end + # Proxy accessors def host @compile.node.name -- cgit From 194e7309c9c481f7e67bd63b13e2fc80fe0a4f00 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 12:37:00 -0800 Subject: Moving all of the tests for Puppet::Parser::Compile to rspec, so I can refactor the class to more heavily rely on a Node::Catalog instead of doing its own resource container management. --- lib/puppet/parser/compile.rb | 21 ++++++++++++++++----- lib/puppet/parser/resource.rb | 14 -------------- lib/puppet/parser/resource/reference.rb | 2 +- 3 files changed, 17 insertions(+), 20 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index e1e230d48..42d229867 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -68,10 +68,10 @@ class Puppet::Parser::Compile evaluate_generators() - fail_on_unevaluated() - finish() + fail_on_unevaluated() + if Puppet[:storeconfigs] store() end @@ -200,8 +200,6 @@ class Puppet::Parser::Compile # Store a resource override. def store_override(override) - override.override = true - # If possible, merge the override in immediately. if resource = @resource_table[override.ref] resource.merge(override) @@ -383,7 +381,20 @@ class Puppet::Parser::Compile # Make sure all of our resources and such have done any last work # necessary. def finish - @resource_table.each { |name, resource| resource.finish if resource.respond_to?(:finish) } + @resource_table.each do |name, resource| + # Add in any resource overrides. + if overrides = resource_overrides(resource) + overrides.each do |over| + resource.merge(over) + end + + # Remove the overrides, so that the configuration knows there + # are none left. + overrides.clear + end + + resource.finish if resource.respond_to?(:finish) + end end # Initialize the top-level scope, class, and resource. diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index f8701578c..67428d5f3 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -83,7 +83,6 @@ class Puppet::Parser::Resource # Do any finishing work on this object, called before evaluation or # before storage/translation. def finish - add_overrides() add_defaults() add_metaparams() add_scope_tags() @@ -342,19 +341,6 @@ class Puppet::Parser::Resource end end - # Add any overrides for this object. - def add_overrides - if overrides = scope.compile.resource_overrides(self) - overrides.each do |over| - self.merge(over) - end - - # Remove the overrides, so that the configuration knows there - # are none left. - overrides.clear - end - end - def add_scope_tags if scope_resource = scope.resource tag(*scope_resource.tags) diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb index 6e70d23b7..ea53b421a 100644 --- a/lib/puppet/parser/resource/reference.rb +++ b/lib/puppet/parser/resource/reference.rb @@ -49,7 +49,7 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference if tmp @definedtype = tmp else - fail Puppet::ParseError, "Could not find resource '%s'" % self + fail Puppet::ParseError, "Could not find resource type '%s'" % self.type end end -- cgit From 3b740ff7a6ab7127ec5e4935782c33245687c429 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 13:59:25 -0800 Subject: Converting the Compile class to use a Node::Catalog instance as its resource container, instead of having its own behaviour around resource uniqueness. --- lib/puppet/parser/compile.rb | 67 +++++++------------------------------------ lib/puppet/parser/resource.rb | 1 - 2 files changed, 11 insertions(+), 57 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index 42d229867..46ce1cb9b 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -84,11 +84,6 @@ class Puppet::Parser::Compile @collections.delete(coll) if @collections.include?(coll) end - # LAK:FIXME There are no tests for this. - def delete_resource(resource) - @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref) - end - # Return the node's environment. def environment unless defined? @environment @@ -140,10 +135,8 @@ class Puppet::Parser::Compile end # Return a resource by either its ref or its type and title. - def findresource(string, name = nil) - string = "%s[%s]" % [string.capitalize, name] if name - - @resource_table[string] + def findresource(*args) + @catalog.resource(*args) end # Set up our compile. We require a parser @@ -195,13 +188,13 @@ class Puppet::Parser::Compile # Return a list of all resources. def resources - @resource_table.values + @catalog.vertices end # Store a resource override. def store_override(override) # If possible, merge the override in immediately. - if resource = @resource_table[override.ref] + if resource = @catalog.resource(override.ref) resource.merge(override) else # Otherwise, store the override for later; these @@ -212,11 +205,7 @@ class Puppet::Parser::Compile # Store a resource in our resource table. def store_resource(scope, resource) - # This might throw an exception - verify_uniqueness(resource) - - # Store it in the global table. - @resource_table[resource.ref] = resource + @catalog.add_resource(resource) # And in the resource graph. At some point, this might supercede # the global resource table, but the table is a lot faster @@ -323,9 +312,7 @@ class Puppet::Parser::Compile @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) @topscope.resource = @main_resource - @catalog.add_vertex!(@main_resource) - - @resource_table["Class[main]"] = @main_resource + @catalog.add_resource(@main_resource) @main_resource.evaluate end @@ -381,7 +368,9 @@ class Puppet::Parser::Compile # Make sure all of our resources and such have done any last work # necessary. def finish - @resource_table.each do |name, resource| + @catalog.resources.each do |name| + resource = @catalog.resource(name) + # Add in any resource overrides. if overrides = resource_overrides(resource) overrides.each do |over| @@ -410,9 +399,6 @@ class Puppet::Parser::Compile # be used by top scopes and node scopes. @class_scopes = {} - # The table for all defined resources. - @resource_table = {} - # The list of objects that will available for export. @exported_resources = {} @@ -457,7 +443,7 @@ class Puppet::Parser::Compile # We used to have hooks here for forking and saving, but I don't # think it's worth retaining at this point. - store_to_active_record(@node, @resource_table.values) + store_to_active_record(@node, @catalog.vertices) end # Do the actual storage. @@ -480,9 +466,7 @@ class Puppet::Parser::Compile # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources - ary = @resource_table.find_all do |name, object| - ! object.builtin? and ! object.evaluated? - end.collect { |name, object| object } + ary = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } if ary.empty? return nil @@ -490,33 +474,4 @@ class Puppet::Parser::Compile return ary end end - - # Verify that the given resource isn't defined elsewhere. - def verify_uniqueness(resource) - # Short-curcuit the common case, - unless existing_resource = @resource_table[resource.ref] - return true - end - - if typeclass = Puppet::Type.type(resource.type) and ! typeclass.isomorphic? - Puppet.info "Allowing duplicate %s" % typeclass.name - return true - end - - # Either it's a defined type, which are never - # isomorphic, or it's a non-isomorphic type, so - # we should throw an exception. - msg = "Duplicate definition: %s is already defined" % resource.ref - - if existing_resource.file and existing_resource.line - msg << " in file %s at line %s" % - [existing_resource.file, existing_resource.line] - end - - if resource.line or resource.file - msg << "; cannot redefine" - end - - raise Puppet::ParseError.new(msg) - end end diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb index 67428d5f3..fb0799011 100644 --- a/lib/puppet/parser/resource.rb +++ b/lib/puppet/parser/resource.rb @@ -58,7 +58,6 @@ class Puppet::Parser::Resource def evaluate if klass = @ref.definedtype finish() - scope.compile.delete_resource(self) return klass.evaluate_code(self) elsif builtin? devfail "Cannot evaluate a builtin type" -- cgit From 6a4cf6c978e8c8aebba4ed0f16d3de7bb31a0ce0 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 17:24:02 -0600 Subject: Fixed #1030 - class and definition evaluation has been significantly refactored, fixing this problem and making the whole interplay between the classes, definitions, and nodes, and the Compile class much cleaner. --- lib/puppet/parser/ast/definition.rb | 31 +++++++++++++++++-------------- lib/puppet/parser/ast/hostclass.rb | 14 +++++++++----- lib/puppet/parser/compile.rb | 10 +--------- 3 files changed, 27 insertions(+), 28 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index bf57942d7..e3f6414c3 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -24,7 +24,12 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch # Create a resource that knows how to evaluate our actual code. def evaluate(scope) - resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source) + # Do nothing if the resource already exists; this provides the singleton nature classes need. + return if scope.catalog.resource(:class, self.classname) + + resource = Puppet::Parser::Resource.new(:type => "class", :title => self.classname, :scope => scope, :source => scope.source) + + scope.catalog.tag(*resource.tags) scope.compile.store_resource(scope, resource) @@ -91,23 +96,21 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch # Hunt down our class object. def parentobj - if @parentclass - # Cache our result, since it should never change. - unless defined?(@parentobj) - unless tmp = find_parentclass - parsefail "Could not find %s %s" % [self.class.name, @parentclass] - end + return nil unless @parentclass - if tmp == self - parsefail "Parent classes must have dissimilar names" - end + # Cache our result, since it should never change. + unless defined?(@parentobj) + unless tmp = find_parentclass + parsefail "Could not find %s parent %s" % [self.class.name, @parentclass] + end - @parentobj = tmp + if tmp == self + parsefail "Parent classes must have dissimilar names" end - @parentobj - else - nil + + @parentobj = tmp end + @parentobj end # Create a new subscope in which to evaluate our code. diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 251d5eba6..4f2d00f0c 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -18,6 +18,15 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition end end + # Make sure our parent class has been evaluated, if we have one. + def evaluate(scope) + if parentclass and ! scope.catalog.resource(:class, parentclass) + resource = parentobj.evaluate(scope) + end + + super + end + # Evaluate the code associated with this class. def evaluate_code(resource) scope = resource.scope @@ -58,11 +67,6 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition end end - def initialize(options) - @parentclass = nil - super - end - def parent_scope(scope, klass) if s = scope.compile.class_scope(klass) return s diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index 46ce1cb9b..bceead271 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -115,16 +115,11 @@ class Puppet::Parser::Compile if klass = scope.findclass(name) found << name and next if class_scope(klass) - # Create a resource to model this class, and then add it to the list - # of resources. - resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source) - - store_resource(scope, resource) + resource = klass.evaluate(scope) # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. resource.evaluate unless lazy_evaluate - @catalog.tag(klass.classname) found << name else Puppet.info "Could not find class %s for %s" % [name, node.name] @@ -412,9 +407,6 @@ class Puppet::Parser::Compile # but they each refer back to the scope that created them. @collections = [] - # A list of tags we've generated; most class names. - @tags = [] - # A graph for maintaining scope relationships. @scope_graph = Puppet::SimpleGraph.new -- cgit From e247b56d9941b4a636d3a3d9935d6b9cd9b199ea Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 17:33:12 -0600 Subject: Changing some methods in the Compile class to be more internally consistent (switched store_resource to add_resource, and store_override to add_override). --- lib/puppet/parser/ast/definition.rb | 2 +- lib/puppet/parser/ast/resource.rb | 2 +- lib/puppet/parser/ast/resource_override.rb | 2 +- lib/puppet/parser/collector.rb | 2 +- lib/puppet/parser/compile.rb | 46 +++++++++++++++--------------- 5 files changed, 27 insertions(+), 27 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index e3f6414c3..992bb1f5e 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -31,7 +31,7 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch scope.catalog.tag(*resource.tags) - scope.compile.store_resource(scope, resource) + scope.compile.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/ast/resource.rb b/lib/puppet/parser/ast/resource.rb index 606beb537..2dadf9ed6 100644 --- a/lib/puppet/parser/ast/resource.rb +++ b/lib/puppet/parser/ast/resource.rb @@ -51,7 +51,7 @@ class Resource < AST::ResourceReference # And then store the resource in the compile. # At some point, we need to switch all of this to return # objects instead of storing them like this. - scope.compile.store_resource(scope, obj) + scope.compile.add_resource(scope, obj) obj end }.reject { |obj| obj.nil? } diff --git a/lib/puppet/parser/ast/resource_override.rb b/lib/puppet/parser/ast/resource_override.rb index d15f68608..db0986a8e 100644 --- a/lib/puppet/parser/ast/resource_override.rb +++ b/lib/puppet/parser/ast/resource_override.rb @@ -42,7 +42,7 @@ class Puppet::Parser::AST # Now we tell the scope that it's an override, and it behaves as # necessary. - scope.compile.store_override(obj) + scope.compile.add_override(obj) obj end diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb index b8165a84f..efd64a320 100644 --- a/lib/puppet/parser/collector.rb +++ b/lib/puppet/parser/collector.rb @@ -150,7 +150,7 @@ class Puppet::Parser::Collector resource.exported = false - scope.compile.store_resource(scope, resource) + scope.compile.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index bceead271..f68796843 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -17,6 +17,28 @@ class Puppet::Parser::Compile @collections << coll end + # Store a resource override. + def add_override(override) + # If possible, merge the override in immediately. + if resource = @catalog.resource(override.ref) + resource.merge(override) + else + # Otherwise, store the override for later; these + # get evaluated in Resource#finish. + @resource_overrides[override.ref] << override + end + end + + # Store a resource in our resource table. + def add_resource(scope, resource) + @catalog.add_resource(resource) + + # And in the resource graph. At some point, this might supercede + # the global resource table, but the table is a lot faster + # so it makes sense to maintain for now. + @catalog.add_edge!(scope.resource, resource) + end + # Do we use nodes found in the code, vs. the external node sources? def ast_nodes? parser.nodes.length > 0 @@ -186,28 +208,6 @@ class Puppet::Parser::Compile @catalog.vertices end - # Store a resource override. - def store_override(override) - # If possible, merge the override in immediately. - if resource = @catalog.resource(override.ref) - resource.merge(override) - else - # Otherwise, store the override for later; these - # get evaluated in Resource#finish. - @resource_overrides[override.ref] << override - end - end - - # Store a resource in our resource table. - def store_resource(scope, resource) - @catalog.add_resource(resource) - - # And in the resource graph. At some point, this might supercede - # the global resource table, but the table is a lot faster - # so it makes sense to maintain for now. - @catalog.add_edge!(scope.resource, resource) - end - # The top scope is usually the top-level scope, but if we're using AST nodes, # then it is instead the node's scope. def topscope @@ -233,7 +233,7 @@ class Puppet::Parser::Compile # Create a resource to model this node, and then add it to the list # of resources. resource = Puppet::Parser::Resource.new(:type => "node", :title => astnode.classname, :scope => topscope, :source => topscope.source) - store_resource(topscope, resource) + add_resource(topscope, resource) @catalog.tag(astnode.classname) resource.evaluate -- cgit From 5ebaa8953155d091ed5b5c68c3862c9f695f03c0 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 17:41:19 -0600 Subject: Refactoring the interface between the Compile class and the AST::Node class to match that to the definitions and AST classes. --- lib/puppet/parser/compile.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb index f68796843..2415fd5e8 100644 --- a/lib/puppet/parser/compile.rb +++ b/lib/puppet/parser/compile.rb @@ -232,9 +232,7 @@ class Puppet::Parser::Compile # Create a resource to model this node, and then add it to the list # of resources. - resource = Puppet::Parser::Resource.new(:type => "node", :title => astnode.classname, :scope => topscope, :source => topscope.source) - add_resource(topscope, resource) - @catalog.tag(astnode.classname) + resource = astnode.evaluate(topscope) resource.evaluate -- cgit From fd0c5cbddec8dc53196a3b84e33e1000c3c0720f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 17:59:34 -0600 Subject: Changing the name of the Compile class to Compiler, since it's stupid to have a class named after a verb. --- lib/puppet/parser/ast/collection.rb | 2 +- lib/puppet/parser/ast/definition.rb | 2 +- lib/puppet/parser/ast/hostclass.rb | 6 +- lib/puppet/parser/ast/node.rb | 2 +- lib/puppet/parser/ast/resource.rb | 4 +- lib/puppet/parser/ast/resource_override.rb | 2 +- lib/puppet/parser/collector.rb | 8 +- lib/puppet/parser/compile.rb | 467 ----------------------------- lib/puppet/parser/compiler.rb | 467 +++++++++++++++++++++++++++++ lib/puppet/parser/functions.rb | 6 +- lib/puppet/parser/interpreter.rb | 4 +- lib/puppet/parser/scope.rb | 16 +- lib/puppet/parser/templatewrapper.rb | 2 +- 13 files changed, 494 insertions(+), 494 deletions(-) delete mode 100644 lib/puppet/parser/compile.rb create mode 100644 lib/puppet/parser/compiler.rb (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb index 31f508929..9e795a33c 100644 --- a/lib/puppet/parser/ast/collection.rb +++ b/lib/puppet/parser/ast/collection.rb @@ -18,7 +18,7 @@ class Collection < AST::Branch newcoll = Puppet::Parser::Collector.new(scope, @type, str, code, self.form) - scope.compile.add_collection(newcoll) + scope.compiler.add_collection(newcoll) newcoll end diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index 992bb1f5e..b4a90016a 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -31,7 +31,7 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch scope.catalog.tag(*resource.tags) - scope.compile.add_resource(scope, resource) + scope.compiler.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 4f2d00f0c..f49016526 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -32,7 +32,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition scope = resource.scope # Verify that we haven't already been evaluated. This is # what provides the singleton aspect. - if existing_scope = scope.compile.class_scope(self) + if existing_scope = scope.compiler.class_scope(self) Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname) return nil end @@ -57,7 +57,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition # Set the class before we do anything else, so that it's set # during the evaluation and can be inspected. - scope.compile.class_set(self.classname, scope) + scope.compiler.class_set(self.classname, scope) # Now evaluate our code, yo. if self.code @@ -68,7 +68,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition end def parent_scope(scope, klass) - if s = scope.compile.class_scope(klass) + if s = scope.compiler.class_scope(klass) return s else raise Puppet::DevError, "Could not find scope for %s" % klass.classname diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index 7ff7a18e1..8cebac8a8 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -25,7 +25,7 @@ class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass # 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.compile.class_set(self.classname, scope) + scope.compiler.class_set(self.classname, scope) # And then evaluate our code if we have any @code.safeevaluate(scope) if self.code diff --git a/lib/puppet/parser/ast/resource.rb b/lib/puppet/parser/ast/resource.rb index 2dadf9ed6..8a60522a3 100644 --- a/lib/puppet/parser/ast/resource.rb +++ b/lib/puppet/parser/ast/resource.rb @@ -48,10 +48,10 @@ class Resource < AST::ResourceReference :scope => scope ) - # And then store the resource in the compile. + # And then store the resource in the compiler. # At some point, we need to switch all of this to return # objects instead of storing them like this. - scope.compile.add_resource(scope, obj) + scope.compiler.add_resource(scope, obj) obj end }.reject { |obj| obj.nil? } diff --git a/lib/puppet/parser/ast/resource_override.rb b/lib/puppet/parser/ast/resource_override.rb index db0986a8e..f9464acda 100644 --- a/lib/puppet/parser/ast/resource_override.rb +++ b/lib/puppet/parser/ast/resource_override.rb @@ -42,7 +42,7 @@ class Puppet::Parser::AST # Now we tell the scope that it's an override, and it behaves as # necessary. - scope.compile.add_override(obj) + scope.compiler.add_override(obj) obj end diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb index efd64a320..e0c37cd35 100644 --- a/lib/puppet/parser/collector.rb +++ b/lib/puppet/parser/collector.rb @@ -118,20 +118,20 @@ class Puppet::Parser::Collector # If there are no more resources to find, delete this from the list # of collections. if @resources.empty? - @scope.compile.delete_collection(self) + @scope.compiler.delete_collection(self) end return result end - # Collect just virtual objects, from our local compile. + # Collect just virtual objects, from our local compiler. def collect_virtual(exported = false) if exported method = :exported? else method = :virtual? end - scope.compile.resources.find_all do |resource| + scope.compiler.resources.find_all do |resource| resource.type == @type and resource.send(method) and match?(resource) end end @@ -150,7 +150,7 @@ class Puppet::Parser::Collector resource.exported = false - scope.compile.add_resource(scope, resource) + scope.compiler.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb deleted file mode 100644 index 2415fd5e8..000000000 --- a/lib/puppet/parser/compile.rb +++ /dev/null @@ -1,467 +0,0 @@ -# Created by Luke A. Kanies on 2007-08-13. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/node' -require 'puppet/node/catalog' -require 'puppet/util/errors' - -# Maintain a graph of scopes, along with a bunch of data -# about the individual catalog we're compiling. -class Puppet::Parser::Compile - include Puppet::Util - include Puppet::Util::Errors - attr_reader :parser, :node, :facts, :collections, :catalog, :node_scope - - # Add a collection to the global list. - def add_collection(coll) - @collections << coll - end - - # Store a resource override. - def add_override(override) - # If possible, merge the override in immediately. - if resource = @catalog.resource(override.ref) - resource.merge(override) - else - # Otherwise, store the override for later; these - # get evaluated in Resource#finish. - @resource_overrides[override.ref] << override - end - end - - # Store a resource in our resource table. - def add_resource(scope, resource) - @catalog.add_resource(resource) - - # And in the resource graph. At some point, this might supercede - # the global resource table, but the table is a lot faster - # so it makes sense to maintain for now. - @catalog.add_edge!(scope.resource, resource) - end - - # Do we use nodes found in the code, vs. the external node sources? - def ast_nodes? - parser.nodes.length > 0 - end - - # Store the fact that we've evaluated a class, and store a reference to - # the scope in which it was evaluated, so that we can look it up later. - def class_set(name, scope) - if existing = @class_scopes[name] - if existing.nodescope? or scope.nodescope? - raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" - else - raise Puppet::DevError, "Somehow evaluated the same class twice" - end - end - @class_scopes[name] = scope - @catalog.add_class(name) unless name == "" - end - - # Return the scope associated with a class. This is just here so - # that subclasses can set their parent scopes to be the scope of - # their parent class, and it's also used when looking up qualified - # variables. - def class_scope(klass) - # They might pass in either the class or class name - if klass.respond_to?(:classname) - @class_scopes[klass.classname] - else - @class_scopes[klass] - end - end - - # Return a list of all of the defined classes. - def classlist - return @catalog.classes - end - - # Compile our catalog. This mostly revolves around finding and evaluating classes. - # This is the main entry into our catalog. - def compile - # Set the client's parameters into the top scope. - set_node_parameters() - - evaluate_main() - - evaluate_ast_node() - - evaluate_node_classes() - - evaluate_generators() - - finish() - - fail_on_unevaluated() - - if Puppet[:storeconfigs] - store() - end - - return @catalog - end - - # LAK:FIXME There are no tests for this. - def delete_collection(coll) - @collections.delete(coll) if @collections.include?(coll) - end - - # Return the node's environment. - def environment - unless defined? @environment - if node.environment and node.environment != "" - @environment = node.environment - else - @environment = nil - end - end - @environment - end - - # Evaluate all of the classes specified by the node. - def evaluate_node_classes - evaluate_classes(@node.classes, topscope) - end - - # Evaluate each specified class in turn. If there are any classes we can't - # find, just tag the catalog and move on. This method really just - # creates resource objects that point back to the classes, and then the - # resources are themselves evaluated later in the process. - def evaluate_classes(classes, scope, lazy_evaluate = true) - unless scope.source - raise Puppet::DevError, "No source for scope passed to evaluate_classes" - end - found = [] - classes.each do |name| - # If we can find the class, then make a resource that will evaluate it. - if klass = scope.findclass(name) - found << name and next if class_scope(klass) - - resource = klass.evaluate(scope) - - # If they've disabled lazy evaluation (which the :include function does), - # then evaluate our resource immediately. - resource.evaluate unless lazy_evaluate - found << name - else - Puppet.info "Could not find class %s for %s" % [name, node.name] - @catalog.tag(name) - end - end - found - end - - # Return a resource by either its ref or its type and title. - def findresource(*args) - @catalog.resource(*args) - end - - # Set up our compile. We require a parser - # and a node object; the parser is so we can look up classes - # and AST nodes, and the node has all of the client's info, - # like facts and environment. - def initialize(node, parser, options = {}) - @node = node - @parser = parser - - options.each do |param, value| - begin - send(param.to_s + "=", value) - rescue NoMethodError - raise ArgumentError, "Compile objects do not accept %s" % param - end - end - - initvars() - init_main() - end - - # Create a new scope, with either a specified parent scope or - # using the top scope. Adds an edge between the scope and - # its parent to the graph. - def newscope(parent, options = {}) - parent ||= topscope - options[:compile] = self - options[:parser] ||= self.parser - scope = Puppet::Parser::Scope.new(options) - @scope_graph.add_edge!(parent, scope) - scope - end - - # Find the parent of a given scope. Assumes scopes only ever have - # one in edge, which will always be true. - def parent(scope) - if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0 - ary[0] - else - nil - end - end - - # Return any overrides for the given resource. - def resource_overrides(resource) - @resource_overrides[resource.ref] - end - - # Return a list of all resources. - def resources - @catalog.vertices - end - - # The top scope is usually the top-level scope, but if we're using AST nodes, - # then it is instead the node's scope. - def topscope - node_scope || @topscope - end - - private - - # If ast nodes are enabled, then see if we can find and evaluate one. - def evaluate_ast_node - return unless ast_nodes? - - # Now see if we can find the node. - astnode = nil - @node.names.each do |name| - break if astnode = @parser.nodes[name.to_s.downcase] - end - - unless (astnode ||= @parser.nodes["default"]) - raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") - end - - # Create a resource to model this node, and then add it to the list - # of resources. - resource = astnode.evaluate(topscope) - - resource.evaluate - - # Now set the node scope appropriately, so that :topscope can - # behave differently. - @node_scope = class_scope(astnode) - end - - # Evaluate our collections and return true if anything returned an object. - # The 'true' is used to continue a loop, so it's important. - def evaluate_collections - return false if @collections.empty? - - found_something = false - exceptwrap do - # We have to iterate over a dup of the array because - # collections can delete themselves from the list, which - # changes its length and causes some collections to get missed. - @collections.dup.each do |collection| - found_something = true if collection.evaluate - end - end - - return found_something - end - - # Make sure all of our resources have been evaluated into native resources. - # We return true if any resources have, so that we know to continue the - # evaluate_generators loop. - def evaluate_definitions - exceptwrap do - if ary = unevaluated_resources - ary.each do |resource| - resource.evaluate - end - # If we evaluated, let the loop know. - return true - else - return false - end - end - end - - # Iterate over collections and resources until we're sure that the whole - # compile is evaluated. This is necessary because both collections - # and defined resources can generate new resources, which themselves could - # be defined resources. - def evaluate_generators - count = 0 - loop do - done = true - - # Call collections first, then definitions. - done = false if evaluate_collections - done = false if evaluate_definitions - break if done - - count += 1 - - if count > 1000 - raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" - end - end - end - - # Find and evaluate our main object, if possible. - def evaluate_main - @main = @parser.findclass("", "") || @parser.newclass("") - @topscope.source = @main - @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) - @topscope.resource = @main_resource - - @catalog.add_resource(@main_resource) - - @main_resource.evaluate - end - - # Make sure the entire catalog is evaluated. - def fail_on_unevaluated - fail_on_unevaluated_overrides - fail_on_unevaluated_resource_collections - end - - # If there are any resource overrides remaining, then we could - # not find the resource they were supposed to override, so we - # want to throw an exception. - def fail_on_unevaluated_overrides - remaining = [] - @resource_overrides.each do |name, overrides| - remaining += overrides - end - - unless remaining.empty? - fail Puppet::ParseError, - "Could not find object(s) %s" % remaining.collect { |o| - o.ref - }.join(", ") - end - end - - # Make sure we don't have any remaining collections that specifically - # look for resources, because we want to consider those to be - # parse errors. - def fail_on_unevaluated_resource_collections - remaining = [] - @collections.each do |coll| - # We're only interested in the 'resource' collections, - # which result from direct calls of 'realize'. Anything - # else is allowed not to return resources. - # Collect all of them, so we have a useful error. - if r = coll.resources - if r.is_a?(Array) - remaining += r - else - remaining << r - end - end - end - - unless remaining.empty? - raise Puppet::ParseError, "Failed to realize virtual resources %s" % - remaining.join(', ') - end - end - - # Make sure all of our resources and such have done any last work - # necessary. - def finish - @catalog.resources.each do |name| - resource = @catalog.resource(name) - - # Add in any resource overrides. - if overrides = resource_overrides(resource) - overrides.each do |over| - resource.merge(over) - end - - # Remove the overrides, so that the configuration knows there - # are none left. - overrides.clear - end - - resource.finish if resource.respond_to?(:finish) - end - end - - # Initialize the top-level scope, class, and resource. - def init_main - # Create our initial scope and a resource that will evaluate main. - @topscope = Puppet::Parser::Scope.new(:compile => self, :parser => self.parser) - @scope_graph.add_vertex!(@topscope) - end - - # Set up all of our internal variables. - def initvars - # The table for storing class singletons. This will only actually - # be used by top scopes and node scopes. - @class_scopes = {} - - # The list of objects that will available for export. - @exported_resources = {} - - # The list of overrides. This is used to cache overrides on objects - # that don't exist yet. We store an array of each override. - @resource_overrides = Hash.new do |overs, ref| - overs[ref] = [] - end - - # The list of collections that have been created. This is a global list, - # but they each refer back to the scope that created them. - @collections = [] - - # A graph for maintaining scope relationships. - @scope_graph = Puppet::SimpleGraph.new - - # For maintaining the relationship between scopes and their resources. - @catalog = Puppet::Node::Catalog.new(@node.name) - @catalog.version = @parser.version - end - - # Set the node's parameters into the top-scope as variables. - def set_node_parameters - node.parameters.each do |param, value| - @topscope.setvar(param, value) - end - end - - # Store the catalog into the database. - def store - unless Puppet.features.rails? - raise Puppet::Error, - "storeconfigs is enabled but rails is unavailable" - end - - unless ActiveRecord::Base.connected? - Puppet::Rails.connect - end - - # We used to have hooks here for forking and saving, but I don't - # think it's worth retaining at this point. - store_to_active_record(@node, @catalog.vertices) - end - - # Do the actual storage. - def store_to_active_record(node, resources) - begin - # We store all of the objects, even the collectable ones - benchmark(:info, "Stored catalog for #{node.name}") do - Puppet::Rails::Host.transaction do - Puppet::Rails::Host.store(node, resources) - end - end - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Could not store configs: %s" % detail.to_s - end - end - - # Return an array of all of the unevaluated resources. These will be definitions, - # which need to get evaluated into native resources. - def unevaluated_resources - ary = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } - - if ary.empty? - return nil - else - return ary - end - end -end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb new file mode 100644 index 000000000..27860487a --- /dev/null +++ b/lib/puppet/parser/compiler.rb @@ -0,0 +1,467 @@ +# Created by Luke A. Kanies on 2007-08-13. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/node' +require 'puppet/node/catalog' +require 'puppet/util/errors' + +# Maintain a graph of scopes, along with a bunch of data +# about the individual catalog we're compiling. +class Puppet::Parser::Compiler + include Puppet::Util + include Puppet::Util::Errors + attr_reader :parser, :node, :facts, :collections, :catalog, :node_scope + + # Add a collection to the global list. + def add_collection(coll) + @collections << coll + end + + # Store a resource override. + def add_override(override) + # If possible, merge the override in immediately. + if resource = @catalog.resource(override.ref) + resource.merge(override) + else + # Otherwise, store the override for later; these + # get evaluated in Resource#finish. + @resource_overrides[override.ref] << override + end + end + + # Store a resource in our resource table. + def add_resource(scope, resource) + @catalog.add_resource(resource) + + # And in the resource graph. At some point, this might supercede + # the global resource table, but the table is a lot faster + # so it makes sense to maintain for now. + @catalog.add_edge!(scope.resource, resource) + end + + # Do we use nodes found in the code, vs. the external node sources? + def ast_nodes? + parser.nodes.length > 0 + end + + # Store the fact that we've evaluated a class, and store a reference to + # the scope in which it was evaluated, so that we can look it up later. + def class_set(name, scope) + if existing = @class_scopes[name] + if existing.nodescope? or scope.nodescope? + raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" + else + raise Puppet::DevError, "Somehow evaluated the same class twice" + end + end + @class_scopes[name] = scope + @catalog.add_class(name) unless name == "" + end + + # Return the scope associated with a class. This is just here so + # that subclasses can set their parent scopes to be the scope of + # their parent class, and it's also used when looking up qualified + # variables. + def class_scope(klass) + # They might pass in either the class or class name + if klass.respond_to?(:classname) + @class_scopes[klass.classname] + else + @class_scopes[klass] + end + end + + # Return a list of all of the defined classes. + def classlist + return @catalog.classes + end + + # Compiler our catalog. This mostly revolves around finding and evaluating classes. + # This is the main entry into our catalog. + def compile + # Set the client's parameters into the top scope. + set_node_parameters() + + evaluate_main() + + evaluate_ast_node() + + evaluate_node_classes() + + evaluate_generators() + + finish() + + fail_on_unevaluated() + + if Puppet[:storeconfigs] + store() + end + + return @catalog + end + + # LAK:FIXME There are no tests for this. + def delete_collection(coll) + @collections.delete(coll) if @collections.include?(coll) + end + + # Return the node's environment. + def environment + unless defined? @environment + if node.environment and node.environment != "" + @environment = node.environment + else + @environment = nil + end + end + @environment + end + + # Evaluate all of the classes specified by the node. + def evaluate_node_classes + evaluate_classes(@node.classes, topscope) + end + + # Evaluate each specified class in turn. If there are any classes we can't + # find, just tag the catalog and move on. This method really just + # creates resource objects that point back to the classes, and then the + # resources are themselves evaluated later in the process. + def evaluate_classes(classes, scope, lazy_evaluate = true) + unless scope.source + raise Puppet::DevError, "No source for scope passed to evaluate_classes" + end + found = [] + classes.each do |name| + # If we can find the class, then make a resource that will evaluate it. + if klass = scope.findclass(name) + found << name and next if class_scope(klass) + + resource = klass.evaluate(scope) + + # If they've disabled lazy evaluation (which the :include function does), + # then evaluate our resource immediately. + resource.evaluate unless lazy_evaluate + found << name + else + Puppet.info "Could not find class %s for %s" % [name, node.name] + @catalog.tag(name) + end + end + found + end + + # Return a resource by either its ref or its type and title. + def findresource(*args) + @catalog.resource(*args) + end + + # Set up our compile. We require a parser + # and a node object; the parser is so we can look up classes + # and AST nodes, and the node has all of the client's info, + # like facts and environment. + def initialize(node, parser, options = {}) + @node = node + @parser = parser + + options.each do |param, value| + begin + send(param.to_s + "=", value) + rescue NoMethodError + raise ArgumentError, "Compiler objects do not accept %s" % param + end + end + + initvars() + init_main() + end + + # Create a new scope, with either a specified parent scope or + # using the top scope. Adds an edge between the scope and + # its parent to the graph. + def newscope(parent, options = {}) + parent ||= topscope + options[:compiler] = self + options[:parser] ||= self.parser + scope = Puppet::Parser::Scope.new(options) + @scope_graph.add_edge!(parent, scope) + scope + end + + # Find the parent of a given scope. Assumes scopes only ever have + # one in edge, which will always be true. + def parent(scope) + if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0 + ary[0] + else + nil + end + end + + # Return any overrides for the given resource. + def resource_overrides(resource) + @resource_overrides[resource.ref] + end + + # Return a list of all resources. + def resources + @catalog.vertices + end + + # The top scope is usually the top-level scope, but if we're using AST nodes, + # then it is instead the node's scope. + def topscope + node_scope || @topscope + end + + private + + # If ast nodes are enabled, then see if we can find and evaluate one. + def evaluate_ast_node + return unless ast_nodes? + + # Now see if we can find the node. + astnode = nil + @node.names.each do |name| + break if astnode = @parser.nodes[name.to_s.downcase] + end + + unless (astnode ||= @parser.nodes["default"]) + raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") + end + + # Create a resource to model this node, and then add it to the list + # of resources. + resource = astnode.evaluate(topscope) + + resource.evaluate + + # Now set the node scope appropriately, so that :topscope can + # behave differently. + @node_scope = class_scope(astnode) + end + + # Evaluate our collections and return true if anything returned an object. + # The 'true' is used to continue a loop, so it's important. + def evaluate_collections + return false if @collections.empty? + + found_something = false + exceptwrap do + # We have to iterate over a dup of the array because + # collections can delete themselves from the list, which + # changes its length and causes some collections to get missed. + @collections.dup.each do |collection| + found_something = true if collection.evaluate + end + end + + return found_something + end + + # Make sure all of our resources have been evaluated into native resources. + # We return true if any resources have, so that we know to continue the + # evaluate_generators loop. + def evaluate_definitions + exceptwrap do + if ary = unevaluated_resources + ary.each do |resource| + resource.evaluate + end + # If we evaluated, let the loop know. + return true + else + return false + end + end + end + + # Iterate over collections and resources until we're sure that the whole + # compile is evaluated. This is necessary because both collections + # and defined resources can generate new resources, which themselves could + # be defined resources. + def evaluate_generators + count = 0 + loop do + done = true + + # Call collections first, then definitions. + done = false if evaluate_collections + done = false if evaluate_definitions + break if done + + count += 1 + + if count > 1000 + raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" + end + end + end + + # Find and evaluate our main object, if possible. + def evaluate_main + @main = @parser.findclass("", "") || @parser.newclass("") + @topscope.source = @main + @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) + @topscope.resource = @main_resource + + @catalog.add_resource(@main_resource) + + @main_resource.evaluate + end + + # Make sure the entire catalog is evaluated. + def fail_on_unevaluated + fail_on_unevaluated_overrides + fail_on_unevaluated_resource_collections + end + + # If there are any resource overrides remaining, then we could + # not find the resource they were supposed to override, so we + # want to throw an exception. + def fail_on_unevaluated_overrides + remaining = [] + @resource_overrides.each do |name, overrides| + remaining += overrides + end + + unless remaining.empty? + fail Puppet::ParseError, + "Could not find object(s) %s" % remaining.collect { |o| + o.ref + }.join(", ") + end + end + + # Make sure we don't have any remaining collections that specifically + # look for resources, because we want to consider those to be + # parse errors. + def fail_on_unevaluated_resource_collections + remaining = [] + @collections.each do |coll| + # We're only interested in the 'resource' collections, + # which result from direct calls of 'realize'. Anything + # else is allowed not to return resources. + # Collect all of them, so we have a useful error. + if r = coll.resources + if r.is_a?(Array) + remaining += r + else + remaining << r + end + end + end + + unless remaining.empty? + raise Puppet::ParseError, "Failed to realize virtual resources %s" % + remaining.join(', ') + end + end + + # Make sure all of our resources and such have done any last work + # necessary. + def finish + @catalog.resources.each do |name| + resource = @catalog.resource(name) + + # Add in any resource overrides. + if overrides = resource_overrides(resource) + overrides.each do |over| + resource.merge(over) + end + + # Remove the overrides, so that the configuration knows there + # are none left. + overrides.clear + end + + resource.finish if resource.respond_to?(:finish) + end + end + + # Initialize the top-level scope, class, and resource. + def init_main + # Create our initial scope and a resource that will evaluate main. + @topscope = Puppet::Parser::Scope.new(:compiler => self, :parser => self.parser) + @scope_graph.add_vertex!(@topscope) + end + + # Set up all of our internal variables. + def initvars + # The table for storing class singletons. This will only actually + # be used by top scopes and node scopes. + @class_scopes = {} + + # The list of objects that will available for export. + @exported_resources = {} + + # The list of overrides. This is used to cache overrides on objects + # that don't exist yet. We store an array of each override. + @resource_overrides = Hash.new do |overs, ref| + overs[ref] = [] + end + + # The list of collections that have been created. This is a global list, + # but they each refer back to the scope that created them. + @collections = [] + + # A graph for maintaining scope relationships. + @scope_graph = Puppet::SimpleGraph.new + + # For maintaining the relationship between scopes and their resources. + @catalog = Puppet::Node::Catalog.new(@node.name) + @catalog.version = @parser.version + end + + # Set the node's parameters into the top-scope as variables. + def set_node_parameters + node.parameters.each do |param, value| + @topscope.setvar(param, value) + end + end + + # Store the catalog into the database. + def store + unless Puppet.features.rails? + raise Puppet::Error, + "storeconfigs is enabled but rails is unavailable" + end + + unless ActiveRecord::Base.connected? + Puppet::Rails.connect + end + + # We used to have hooks here for forking and saving, but I don't + # think it's worth retaining at this point. + store_to_active_record(@node, @catalog.vertices) + end + + # Do the actual storage. + def store_to_active_record(node, resources) + begin + # We store all of the objects, even the collectable ones + benchmark(:info, "Stored catalog for #{node.name}") do + Puppet::Rails::Host.transaction do + Puppet::Rails::Host.store(node, resources) + end + end + rescue => detail + if Puppet[:trace] + puts detail.backtrace + end + Puppet.err "Could not store configs: %s" % detail.to_s + end + end + + # Return an array of all of the unevaluated resources. These will be definitions, + # which need to get evaluated into native resources. + def unevaluated_resources + ary = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } + + if ary.empty? + return nil + else + return ary + end + end +end diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 34b38b809..e0b60e161 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -111,7 +111,7 @@ module Functions vals = [vals] unless vals.is_a?(Array) # The 'false' disables lazy evaluation. - klasses = compile.evaluate_classes(vals, self, false) + klasses = compiler.evaluate_classes(vals, self, false) missing = vals.find_all do |klass| ! klasses.include?(klass) @@ -146,7 +146,7 @@ module Functions tells you whether the current container is tagged with the specified tags. The tags are ANDed, so that all of the specified tags must be included for the function to return true.") do |vals| - configtags = compile.catalog.tags + configtags = compiler.catalog.tags resourcetags = resource.tags retval = true @@ -235,7 +235,7 @@ module Functions vals = [vals] unless vals.is_a?(Array) coll.resources = vals - compile.add_collection(coll) + compiler.add_collection(coll) end newfunction(:search, :doc => "Add another namespace for this class to search. diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index e29e19944..1d93193dd 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -3,7 +3,7 @@ require 'timeout' require 'puppet/rails' require 'puppet/util/methodhelper' require 'puppet/parser/parser' -require 'puppet/parser/compile' +require 'puppet/parser/compiler' require 'puppet/parser/scope' # The interpreter is a very simple entry-point class that @@ -25,7 +25,7 @@ class Puppet::Parser::Interpreter # evaluate our whole tree def compile(node) raise Puppet::ParseError, "Could not parse configuration; cannot compile" unless env_parser = parser(node.environment) - return Puppet::Parser::Compile.new(node, env_parser).compile + return Puppet::Parser::Compiler.new(node, env_parser).compile end # create our interpreter diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 81d4ac71a..a6e43e7b3 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -17,20 +17,20 @@ class Puppet::Parser::Scope include Puppet::Util::Errors attr_accessor :parent, :level, :parser, :source, :resource attr_accessor :base, :keyword, :nodescope - attr_accessor :top, :translated, :compile + attr_accessor :top, :translated, :compiler # A demeterific shortcut to the catalog. def catalog - compile.catalog + compiler.catalog end # Proxy accessors def host - @compile.node.name + @compiler.node.name end def interpreter - @compile.interpreter + @compiler.interpreter end # Is the value true? This allows us to control the definition of truth @@ -77,7 +77,7 @@ class Puppet::Parser::Scope end def findresource(string, name = nil) - compile.findresource(string, name) + compiler.findresource(string, name) end # Initialize our new scope. Defaults to having no parent. @@ -152,7 +152,7 @@ class Puppet::Parser::Scope unless klass raise Puppet::ParseError, "Could not find class %s" % klassname end - unless kscope = compile.class_scope(klass) + unless kscope = compiler.class_scope(klass) raise Puppet::ParseError, "Class %s has not been evaluated so its variables cannot be referenced" % klass.classname end return kscope.lookupvar(shortname, usestring) @@ -189,7 +189,7 @@ class Puppet::Parser::Scope # Create a new scope and set these options. def newscope(options = {}) - compile.newscope(self, options) + compiler.newscope(self, options) end # Is this class for a node? This is used to make sure that @@ -204,7 +204,7 @@ class Puppet::Parser::Scope # than doing lots of queries. def parent unless defined?(@parent) - @parent = compile.parent(self) + @parent = compiler.parent(self) end @parent end diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 13823d483..7a8f74156 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -7,7 +7,7 @@ class Puppet::Parser::TemplateWrapper def initialize(scope, file) @scope = scope - @file = Puppet::Module::find_template(file, @scope.compile.environment) + @file = Puppet::Module::find_template(file, @scope.compiler.environment) unless FileTest.exists?(@file) raise Puppet::ParseError, -- cgit From 8b2fae019b31513becd002eb474e1b4803abde24 Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 18:27:49 -0600 Subject: Removing the last remaining vestiges of GRATR -- removing the bangs from 'add_vertex!' and 'add_edge!'. --- lib/puppet/parser/compiler.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 27860487a..26fdd3743 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -36,7 +36,7 @@ class Puppet::Parser::Compiler # And in the resource graph. At some point, this might supercede # the global resource table, but the table is a lot faster # so it makes sense to maintain for now. - @catalog.add_edge!(scope.resource, resource) + @catalog.add_edge(scope.resource, resource) end # Do we use nodes found in the code, vs. the external node sources? @@ -184,7 +184,7 @@ class Puppet::Parser::Compiler options[:compiler] = self options[:parser] ||= self.parser scope = Puppet::Parser::Scope.new(options) - @scope_graph.add_edge!(parent, scope) + @scope_graph.add_edge(parent, scope) scope end @@ -383,7 +383,7 @@ class Puppet::Parser::Compiler def init_main # Create our initial scope and a resource that will evaluate main. @topscope = Puppet::Parser::Scope.new(:compiler => self, :parser => self.parser) - @scope_graph.add_vertex!(@topscope) + @scope_graph.add_vertex(@topscope) end # Set up all of our internal variables. -- cgit From c8da318a2a4445e0ce10c76a7fbb64635b291ccd Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Tue, 12 Feb 2008 14:19:19 -0600 Subject: Moving the ast node tests to rspec (which I could have *sworn* I did this weekend). In the process, I fixed a couple of bugs related to differentiating between nodes and classes, and then cleaned up quite a few error messages. --- lib/puppet/parser/ast/definition.rb | 4 ++-- lib/puppet/parser/ast/hostclass.rb | 11 +++++++---- lib/puppet/parser/ast/node.rb | 34 ++++++--------------------------- lib/puppet/parser/compiler.rb | 5 +++-- lib/puppet/parser/resource/reference.rb | 8 ++++++-- 5 files changed, 24 insertions(+), 38 deletions(-) (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index b4a90016a..2b7506446 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -25,9 +25,9 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch # Create a resource that knows how to evaluate our actual code. def evaluate(scope) # Do nothing if the resource already exists; this provides the singleton nature classes need. - return if scope.catalog.resource(:class, self.classname) + return if scope.catalog.resource(self.class.name, self.classname) - resource = Puppet::Parser::Resource.new(:type => "class", :title => self.classname, :scope => scope, :source => scope.source) + resource = Puppet::Parser::Resource.new(:type => self.class.name, :title => self.classname, :scope => scope, :source => scope.source) scope.catalog.tag(*resource.tags) diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index f49016526..8d4d01660 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -20,7 +20,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition # Make sure our parent class has been evaluated, if we have one. def evaluate(scope) - if parentclass and ! scope.catalog.resource(:class, parentclass) + if parentclass and ! scope.catalog.resource(self.class.name, parentclass) resource = parentobj.evaluate(scope) end @@ -39,7 +39,9 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition pnames = nil if pklass = self.parentobj - pklass.evaluate_code(resource) + parent_resource = resource.scope.compiler.catalog.resource(self.class.name, pklass.classname) + # This shouldn't evaluate if the class has already been evaluated. + pklass.evaluate_code(parent_resource) scope = parent_scope(scope, pklass) pnames = scope.namespaces @@ -49,14 +51,15 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition # has its own scope. scope = subscope(scope, resource) unless resource.title == :main + # Add the parent scope namespaces to our own. if pnames pnames.each do |ns| scope.add_namespace(ns) end end - # Set the class before we do anything else, so that it's set - # during the evaluation and can be inspected. + # Set the class before we evaluate the code, so that it's set during + # the evaluation and can be inspected. scope.compiler.class_set(self.classname, scope) # Now evaluate our code, yo. diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index 8cebac8a8..2bf6c1882 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -5,34 +5,6 @@ require 'puppet/parser/ast/hostclass' class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass @name = :node - # Evaluate the code associated with our node definition. - def evaluate_code(resource) - scope = resource.scope - - # 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.parentobj - scope = parent.evaluate_code(resource) - end - - scope = scope.newscope( - :resource => resource, - :keyword => @keyword, - :source => self, - :namespace => "" # nodes are always in "" - ) - - # 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.compiler.class_set(self.classname, scope) - - # And then evaluate our code if we have any - @code.safeevaluate(scope) if self.code - - return scope - end - def initialize(options) @parentclass = nil super @@ -43,13 +15,19 @@ class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass end end + def namespace + "" + end + # Make sure node scopes are marked as such. def subscope(*args) scope = super scope.nodescope = true + scope end private + # Search for the object matching our parent class. def find_parentclass @parser.findnode(parentclass) diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index 26fdd3743..68c06e500 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -31,6 +31,7 @@ class Puppet::Parser::Compiler # Store a resource in our resource table. def add_resource(scope, resource) + # Note that this will fail if the resource is not unique. @catalog.add_resource(resource) # And in the resource graph. At some point, this might supercede @@ -48,10 +49,10 @@ class Puppet::Parser::Compiler # the scope in which it was evaluated, so that we can look it up later. def class_set(name, scope) if existing = @class_scopes[name] - if existing.nodescope? or scope.nodescope? + if existing.nodescope? != scope.nodescope? raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" else - raise Puppet::DevError, "Somehow evaluated the same class twice" + raise Puppet::DevError, "Somehow evaluated %s %s twice" % [ existing.nodescope? ? "node" : "class", name] end end @class_scopes[name] = scope diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb index ea53b421a..c59748049 100644 --- a/lib/puppet/parser/resource/reference.rb +++ b/lib/puppet/parser/resource/reference.rb @@ -37,10 +37,14 @@ class Puppet::Parser::Resource::Reference < Puppet::ResourceReference if self.title == :main tmp = @scope.findclass("") else - tmp = @scope.findclass(self.title) + unless tmp = @scope.findclass(self.title) + fail Puppet::ParseError, "Could not find class '%s'" % self.title + end end when "Node": # look for node definitions - tmp = @scope.parser.nodes[self.title] + unless tmp = @scope.parser.nodes[self.title] + fail Puppet::ParseError, "Could not find node '%s'" % self.title + end else # normal definitions # We have to swap these variables around so the errors are right. tmp = @scope.finddefine(self.type) -- cgit