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