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
-~----------~----~----~----~------~----~------~--~---

Reply via email to