One comment below, otherwise +1
On Jul 26, 2009, at 9:03 AM, Brice Figureau wrote:
>
> This changeset introduces regexp in if expression with the use of the
> =~ (match) and !~ (not match) operator.
>
> Usage:
>
> if $uname =~ /Linux|Debian/ {
> ...
> }
>
> Moreover this patch creates ephemeral variables ($0 to $9) in the
> current
> scope which contains the regex captures:
> if $uname =~ /(Linux|Debian)/ {
> notice("this is a $1 system")
> }
>
> Signed-off-by: Brice Figureau <[email protected]>
> ---
> lib/puppet/parser/ast.rb | 1 +
> lib/puppet/parser/ast/ifstatement.rb | 17 +-
> lib/puppet/parser/ast/match_operator.rb | 31 +
> lib/puppet/parser/grammar.ra | 6 +
> lib/puppet/parser/parser.rb | 1272 +++++++++++++++
> +---------------
> spec/unit/parser/ast/ifstatement.rb | 75 ++
> spec/unit/parser/ast/match_operator.rb | 50 ++
> test/data/snippets/ifexpression.pp | 12 +
> test/data/snippets/ifexpression.rb | 6 -
> test/language/ast.rb | 5 +-
> test/language/snippets.rb | 4 +
> 11 files changed, 836 insertions(+), 643 deletions(-)
> create mode 100644 lib/puppet/parser/ast/match_operator.rb
> create mode 100755 spec/unit/parser/ast/ifstatement.rb
> create mode 100755 spec/unit/parser/ast/match_operator.rb
> create mode 100644 test/data/snippets/ifexpression.pp
> delete mode 100644 test/data/snippets/ifexpression.rb
>
> diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb
> index ab23dd1..ad8af74 100644
> --- a/lib/puppet/parser/ast.rb
> +++ b/lib/puppet/parser/ast.rb
> @@ -105,6 +105,7 @@ require 'puppet/parser/ast/function'
> require 'puppet/parser/ast/hostclass'
> require 'puppet/parser/ast/ifstatement'
> require 'puppet/parser/ast/leaf'
> +require 'puppet/parser/ast/match_operator'
> require 'puppet/parser/ast/minus'
> require 'puppet/parser/ast/node'
> require 'puppet/parser/ast/nop'
> diff --git a/lib/puppet/parser/ast/ifstatement.rb b/lib/puppet/
> parser/ast/ifstatement.rb
> index d216b7c..9d52123 100644
> --- a/lib/puppet/parser/ast/ifstatement.rb
> +++ b/lib/puppet/parser/ast/ifstatement.rb
> @@ -18,14 +18,19 @@ class Puppet::Parser::AST
> def evaluate(scope)
> value = @test.safeevaluate(scope)
>
> - if Puppet::Parser::Scope.true?(value)
> - return @statements.safeevaluate(scope)
> - else
> - if defined? @else
> - return @else.safeevaluate(scope)
> + # let's emulate a new scope for each branches
> + begin
> + if Puppet::Parser::Scope.true?(value)
> + return @statements.safeevaluate(scope)
> else
> - return nil
> + if defined? @else
> + return @else.safeevaluate(scope)
> + else
> + return nil
> + end
> end
> + ensure
> + scope.unset_ephemeral_var
> end
> end
> end
> diff --git a/lib/puppet/parser/ast/match_operator.rb b/lib/puppet/
> parser/ast/match_operator.rb
> new file mode 100644
> index 0000000..17e2782
> --- /dev/null
> +++ b/lib/puppet/parser/ast/match_operator.rb
> @@ -0,0 +1,31 @@
> +require 'puppet'
> +require 'puppet/parser/ast/branch'
> +
> +class Puppet::Parser::AST
> + class MatchOperator < AST::Branch
> +
> + attr_accessor :lval, :rval, :operator
> +
> + # Iterate across all of our children.
> + def each
> + [...@lval,@rval].each { |child| yield child }
> + end
> +
> + # Returns a boolean which is the result of the boolean
> operation
> + # of lval and rval operands
> + def evaluate(scope)
> + lval = @lval.safeevaluate(scope)
> +
> + return @operator == "=~" if rval.evaluate_match(lval,
> scope)
> + return @operator == "!~"
> + end
> +
> + def initialize(hash)
> + super
> +
> + unless %w{!~ =~}.include?(@operator)
> + raise ArgumentError, "Invalid regexp operator %s" %
> @operator
> + end
> + end
> + end
> +end
> diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/
> grammar.ra
> index 68acf35..2040ee8 100644
> --- a/lib/puppet/parser/grammar.ra
> +++ b/lib/puppet/parser/grammar.ra
> @@ -488,6 +488,12 @@ else: # nothing
> # per operator :-(
>
> expression: rvalue
> + | expression MATCH regex {
> + result = ast AST::MatchOperator, :operator => val[1]
> [:value], :lval => val[0], :rval => val[2]
> +}
> + | expression NOMATCH regex {
> + result = ast AST::MatchOperator, :operator => val[1]
> [:value], :lval => val[0], :rval => val[2]
> +}
> | expression PLUS expression {
> result = ast AST::ArithmeticOperator, :operator => val[1]
> [:value], :lval => val[0], :rval => val[2]
> }
> diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
> index ef08d6e..906304b 100644
> --- a/lib/puppet/parser/parser.rb
> +++ b/lib/puppet/parser/parser.rb
> <Patch Elided>
> diff --git a/spec/unit/parser/ast/ifstatement.rb b/spec/unit/parser/
> ast/ifstatement.rb
> new file mode 100755
> index 0000000..ab8379d
> --- /dev/null
> +++ b/spec/unit/parser/ast/ifstatement.rb
> @@ -0,0 +1,75 @@
> +#!/usr/bin/env ruby
> +
> +require File.dirname(__FILE__) + '/../../../spec_helper'
> +
> +describe Puppet::Parser::AST::IfStatement do
> + before :each do
> + @scope = Puppet::Parser::Scope.new()
> + end
> +
> + describe "when evaluating" do
> +
> + before :each do
> + @test = stub 'test'
> + @test.stubs(:safeevaluate).with(@scope)
> +
> + @stmt = stub 'stmt'
> + @stmt.stubs(:safeevaluate).with(@scope)
> +
> + @else = stub 'else'
> + @else.stubs(:safeevaluate).with(@scope)
> +
> + @ifstmt = Puppet::Parser::AST::IfStatement.new :test =>
> @test, :statements => @stmt
> + @ifelsestmt =
> Puppet::Parser::AST::IfStatement.new :test => @test, :statements =>
> @stmt, :else => @else
> + end
> +
> + it "should evaluate test" do
> + Puppet::Parser::Scope.stubs(:true?).returns(false)
> +
> + @test.expects(:safeevaluate).with(@scope)
> +
> + @ifstmt.evaluate(@scope)
> + end
> +
> + it "should evaluate if statements if test is true" do
> + Puppet::Parser::Scope.stubs(:true?).returns(true)
> +
> + @stmt.expects(:safeevaluate).with(@scope)
> +
> + @ifstmt.evaluate(@scope)
> + end
> +
> + it "should not evaluate if statements if test is true" do
s/true/false/
>
> + Puppet::Parser::Scope.stubs(:true?).returns(false)
> +
> + @stmt.expects(:safeevaluate).with(@scope).never
> +
> + @ifstmt.evaluate(@scope)
> + end
> +
> + it "should evaluate the else branch if test is false" do
> + Puppet::Parser::Scope.stubs(:true?).returns(false)
> +
> + @else.expects(:safeevaluate).with(@scope)
> +
> + @ifelsestmt.evaluate(@scope)
> + end
> +
> + it "should not evaluate the else branch if test is true" do
> + Puppet::Parser::Scope.stubs(:true?).returns(true)
> +
> + @else.expects(:safeevaluate).with(@scope).never
> +
> + @ifelsestmt.evaluate(@scope)
> + end
> +
> + it "should reset ephemeral statements after evaluation" do
> + Puppet::Parser::Scope.stubs(:true?).returns(true)
> +
> + @stmt.expects(:safeevaluate).with(@scope)
> + @scope.expects(:unset_ephemeral_var)
> +
> + @ifstmt.evaluate(@scope)
> + end
> + end
> +end
> diff --git a/spec/unit/parser/ast/match_operator.rb b/spec/unit/
> parser/ast/match_operator.rb
> new file mode 100755
> index 0000000..985cf60
> --- /dev/null
> +++ b/spec/unit/parser/ast/match_operator.rb
> @@ -0,0 +1,50 @@
> +#!/usr/bin/env ruby
> +
> +require File.dirname(__FILE__) + '/../../../spec_helper'
> +
> +describe Puppet::Parser::AST::MatchOperator do
> + before :each do
> + @scope = Puppet::Parser::Scope.new()
> +
> + @lval = stub 'lval'
> + @lval.stubs(:safeevaluate).with(@scope).returns("this is a
> string")
> +
> + @rval = stub 'rval'
> + @rval.stubs(:evaluate_match)
> +
> + @operator = Puppet::Parser::AST::MatchOperator.new :lval =>
> @lval, :rval => @rval, :operator => "=~"
> + end
> +
> + it "should evaluate the left operand" do
> + @lval.expects(:safeevaluate).with(@scope)
> +
> + @operator.evaluate(@scope)
> + end
> +
> + it "should fail for an unknown operator" do
> + lambda { operator =
> Puppet::Parser::AST::MatchOperator.new :lval => @lval, :operator =>
> "unknown", :rval => @rval }.should raise_error
> + end
> +
> + it "should evaluate_match the left operand" do
> + @rval.expects(:evaluate_match).with("this is a string",
> @scope).returns(:match)
> +
> + @operator.evaluate(@scope)
> + end
> +
> + { "=~" => true, "!~" => false }.each do |op, res|
> + it "should return #{res} if the regexp matches with #{op}" do
> + match = stub 'match'
> + @rval.stubs(:evaluate_match).with("this is a string",
> @scope).returns(match)
> +
> + operator = Puppet::Parser::AST::MatchOperator.new :lval
> => @lval, :rval => @rval, :operator => op
> + operator.evaluate(@scope).should == res
> + end
> +
> + it "should return #{!res} if the regexp doesn't match" do
> + @rval.stubs(:evaluate_match).with("this is a string",
> @scope).returns(nil)
> +
> + operator = Puppet::Parser::AST::MatchOperator.new :lval
> => @lval, :rval => @rval, :operator => op
> + operator.evaluate(@scope).should == !res
> + end
> + end
> +end
> diff --git a/test/data/snippets/ifexpression.pp b/test/data/snippets/
> ifexpression.pp
> new file mode 100644
> index 0000000..29a6372
> --- /dev/null
> +++ b/test/data/snippets/ifexpression.pp
> @@ -0,0 +1,12 @@
> +$one = 1
> +$two = 2
> +
> +if ($one < $two) and (($two < 3) or ($two == 2)) {
> + notice("True!")
> +}
> +
> +if "test regex" =~ /(.*) regex/ {
> + file {
> + "/tmp/${1}iftest": ensure => file, mode => 0755
> + }
> +}
> diff --git a/test/data/snippets/ifexpression.rb b/test/data/snippets/
> ifexpression.rb
> deleted file mode 100644
> index eea3b85..0000000
> --- a/test/data/snippets/ifexpression.rb
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -$one = 1
> -$two = 2
> -
> -if ($one < $two) and (($two < 3) or ($two == 2)) {
> - notice("True!")
> -}
> diff --git a/test/language/ast.rb b/test/language/ast.rb
> index 8c0f31a..ed11bc1 100755
> --- a/test/language/ast.rb
> +++ b/test/language/ast.rb
> @@ -15,6 +15,7 @@ class TestAST < Test::Unit::TestCase
> include PuppetTest::Support::Collection
>
> def test_if
> + scope = mkscope
> astif = nil
> astelse = nil
> fakeelse = FakeAST.new(:else)
> @@ -35,14 +36,14 @@ class TestAST < Test::Unit::TestCase
> # We initialized it to true, so we should get that first
> ret = nil
> assert_nothing_raised {
> - ret = astif.evaluate("yay")
> + ret = astif.evaluate(scope)
> }
> assert_equal(:if, ret)
>
> # Now set it to false and check that
> faketest.evaluate = false
> assert_nothing_raised {
> - ret = astif.evaluate("yay")
> + ret = astif.evaluate(scope)
> }
> assert_equal(:else, ret)
> end
> diff --git a/test/language/snippets.rb b/test/language/snippets.rb
> index 87ad9e7..cfca10e 100755
> --- a/test/language/snippets.rb
> +++ b/test/language/snippets.rb
> @@ -481,6 +481,10 @@ class TestSnippets < Test::Unit::TestCase
> assert_mode_equal(0600, path)
> end
>
> + def snippet_ifexpression
> + assert_file("/tmp/testiftest","if test");
> + end
> +
> # Iterate across each of the snippets and create a test.
> Dir.entries(snippetdir).sort.each { |file|
> next if file =~ /^\./
> --
> 1.6.0.2
>
>
> >
--
When a man tells you that he got rich through hard work, ask
him: 'Whose?' --Don Marquis
---------------------------------------------------------------------
Luke Kanies | http://reductivelabs.com | http://madstop.com
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Puppet Developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/puppet-dev?hl=en
-~----------~----~----~----~------~----~------~--~---