diff options
| author | Brice Figureau <brice-puppet@daysofwonder.com> | 2008-09-26 22:54:42 +0200 |
|---|---|---|
| committer | Brice Figureau <brice@daysofwonder.com> | 2008-09-30 17:22:07 +0200 |
| commit | cfa230a2d7b0c5e57cc0379785bd2025520f1c35 (patch) | |
| tree | 046bc7c55a6a1a8ed9b36c5371a3901a89a779c4 | |
| parent | 850e0baf0fbe321f14d4b9d913ce7dea39c9aa27 (diff) | |
| download | puppet-cfa230a2d7b0c5e57cc0379785bd2025520f1c35.tar.gz puppet-cfa230a2d7b0c5e57cc0379785bd2025520f1c35.tar.xz puppet-cfa230a2d7b0c5e57cc0379785bd2025520f1c35.zip | |
Add arithmetic operators to AST
This changeset adds +,-,/,*,<< and >> computation and
AST parse nodes.
| -rw-r--r-- | lib/puppet/parser/ast.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/parser/ast/arithmetic_operator.rb | 41 | ||||
| -rw-r--r-- | lib/puppet/parser/ast/minus.rb | 23 | ||||
| -rw-r--r-- | lib/puppet/parser/scope.rb | 23 | ||||
| -rwxr-xr-x | spec/unit/parser/ast/arithmetic_operator.rb | 73 | ||||
| -rwxr-xr-x | spec/unit/parser/ast/minus.rb | 36 | ||||
| -rwxr-xr-x | spec/unit/parser/scope.rb | 50 |
7 files changed, 248 insertions, 0 deletions
diff --git a/lib/puppet/parser/ast.rb b/lib/puppet/parser/ast.rb index 572771201..5aa9f528a 100644 --- a/lib/puppet/parser/ast.rb +++ b/lib/puppet/parser/ast.rb @@ -74,6 +74,7 @@ class Puppet::Parser::AST end # And include all of the AST subclasses. +require 'puppet/parser/ast/arithmetic_operator' require 'puppet/parser/ast/astarray' require 'puppet/parser/ast/branch' require 'puppet/parser/ast/boolean_operator' @@ -88,6 +89,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/minus' require 'puppet/parser/ast/node' require 'puppet/parser/ast/not' require 'puppet/parser/ast/resource' diff --git a/lib/puppet/parser/ast/arithmetic_operator.rb b/lib/puppet/parser/ast/arithmetic_operator.rb new file mode 100644 index 000000000..8d9cef86a --- /dev/null +++ b/lib/puppet/parser/ast/arithmetic_operator.rb @@ -0,0 +1,41 @@ +require 'puppet' +require 'puppet/parser/ast/branch' + +class Puppet::Parser::AST + class ArithmeticOperator < AST::Branch + + attr_accessor :operator, :lval, :rval + + # Iterate across all of our children. + def each + [@lval,@rval,@operator].each { |child| yield child } + end + + # Returns a boolean which is the result of the boolean operation + # of lval and rval operands + def evaluate(scope) + # evaluate the operands, should return a boolean value + lval = @lval.safeevaluate(scope) + lval = Puppet::Parser::Scope.number?(lval) + if lval == nil + raise ArgumentError, "left operand of %s is not a number" % @operator + end + rval = @rval.safeevaluate(scope) + rval = Puppet::Parser::Scope.number?(rval) + if rval == nil + raise ArgumentError, "right operand of %s is not a number" % @operator + end + + # compute result + lval.send(@operator, rval) + end + + def initialize(hash) + super + + unless %w{+ - * / << >>}.include?(@operator) + raise ArgumentError, "Invalid arithmetic operator %s" % @operator + end + end + end +end diff --git a/lib/puppet/parser/ast/minus.rb b/lib/puppet/parser/ast/minus.rb new file mode 100644 index 000000000..b0779a8ee --- /dev/null +++ b/lib/puppet/parser/ast/minus.rb @@ -0,0 +1,23 @@ +require 'puppet' +require 'puppet/parser/ast/branch' + +# An object that returns a boolean which is the boolean not +# of the given value. +class Puppet::Parser::AST + class Minus < AST::Branch + attr_accessor :value + + def each + yield @value + end + + def evaluate(scope) + val = @value.safeevaluate(scope) + val = Puppet::Parser::Scope.number?(val) + if val == nil + raise ArgumentError, "minus operand %s is not a number" % val + end + return -val + end + end +end diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 1ff998d96..4acdf41c9 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -43,6 +43,29 @@ class Puppet::Parser::Scope end end + # Is the value a number?, return the correct object or nil if not a number + def self.number?(value) + unless value.is_a?(Fixnum) or value.is_a?(Bignum) or value.is_a?(Float) or value.is_a?(String) + return nil + end + + if value.is_a?(String) + if value =~ /^-?\d+(:?\.\d+|(:?\.\d+)?e\d+)$/ + return value.to_f + elsif value =~ /^0x\d+/i + return value.to_i(16) + elsif value =~ /^0\d+/i + return value.to_i(8) + elsif value =~ /^-?\d+/ + return value.to_i + else + return nil + end + end + # it is one of Fixnum,Bignum or Float + return value + end + # Add to our list of namespaces. def add_namespace(ns) return false if @namespaces.include?(ns) diff --git a/spec/unit/parser/ast/arithmetic_operator.rb b/spec/unit/parser/ast/arithmetic_operator.rb new file mode 100755 index 000000000..24d6ad47d --- /dev/null +++ b/spec/unit/parser/ast/arithmetic_operator.rb @@ -0,0 +1,73 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::ArithmeticOperator do + + AST = Puppet::Parser::AST + + before :each do + @scope = Puppet::Parser::Scope.new() + @one = stub 'lval', :safeevaluate => 1 + @two = stub 'rval', :safeevaluate => 2 + end + + it "should evaluate both branches" do + lval = stub "lval" + lval.expects(:safeevaluate).with(@scope).returns(1) + rval = stub "rval" + rval.expects(:safeevaluate).with(@scope).returns(2) + + operator = AST::ArithmeticOperator.new :rval => rval, :operator => "+", :lval => lval + operator.evaluate(@scope) + end + + it "should fail for an unknown operator" do + lambda { operator = AST::ArithmeticOperator.new :lval => @one, :operator => "%", :rval => @two }.should raise_error + end + + it "should call Puppet::Parser::Scope.number?" do + Puppet::Parser::Scope.expects(:number?).with(1).returns(1) + Puppet::Parser::Scope.expects(:number?).with(2).returns(2) + + AST::ArithmeticOperator.new(:lval => @one, :operator => "+", :rval => @two).evaluate(@scope) + end + + + %w{ + - * / << >>}.each do |op| + it "should call ruby Numeric '#{op}'" do + one = stub 'one' + two = stub 'two' + operator = AST::ArithmeticOperator.new :lval => @one, :operator => op, :rval => @two + Puppet::Parser::Scope.stubs(:number?).with(1).returns(one) + Puppet::Parser::Scope.stubs(:number?).with(2).returns(two) + one.expects(:send).with(op,two) + operator.evaluate(@scope) + end + end + + it "should work even with numbers embedded in strings" do + two = stub 'two', :safeevaluate => "2" + one = stub 'one', :safeevaluate => "1" + operator = AST::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 3 + end + + it "should work even with floats" do + two = stub 'two', :safeevaluate => 2.53 + one = stub 'one', :safeevaluate => 1.80 + operator = AST::ArithmeticOperator.new :lval => two, :operator => "+", :rval => one + operator.evaluate(@scope).should == 4.33 + end + + it "should work for variables too" do + @scope.expects(:lookupvar).with("one").returns(1) + @scope.expects(:lookupvar).with("two").returns(2) + one = AST::Variable.new( :value => "one" ) + two = AST::Variable.new( :value => "two" ) + + operator = AST::ArithmeticOperator.new :lval => one, :operator => "+", :rval => two + operator.evaluate(@scope).should == 3 + end + +end diff --git a/spec/unit/parser/ast/minus.rb b/spec/unit/parser/ast/minus.rb new file mode 100755 index 000000000..83bd92d0d --- /dev/null +++ b/spec/unit/parser/ast/minus.rb @@ -0,0 +1,36 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe Puppet::Parser::AST::Minus do + before :each do + @scope = Puppet::Parser::Scope.new() + end + + it "should evaluate its argument" do + value = stub "value" + value.expects(:safeevaluate).with(@scope).returns(123) + + operator = Puppet::Parser::AST::Minus.new :value => value + operator.evaluate(@scope) + end + + it "should fail if argument is not a string or integer" do + array_ast = stub 'array_ast', :safeevaluate => [2] + operator = Puppet::Parser::AST::Minus.new :value => array_ast + lambda { operator.evaluate(@scope) }.should raise_error + end + + it "should work with integer as string" do + string = stub 'string', :safeevaluate => "123" + operator = Puppet::Parser::AST::Minus.new :value => string + operator.evaluate(@scope).should == -123 + end + + it "should work with integers" do + int = stub 'int', :safeevaluate => 123 + operator = Puppet::Parser::AST::Minus.new :value => int + operator.evaluate(@scope).should == -123 + end + +end diff --git a/spec/unit/parser/scope.rb b/spec/unit/parser/scope.rb index ec8ab6d7d..fa76c4ff2 100755 --- a/spec/unit/parser/scope.rb +++ b/spec/unit/parser/scope.rb @@ -34,4 +34,54 @@ describe Puppet::Parser::Scope do end end + + describe Puppet::Parser::Scope, "when calling number?" do + + it "should return nil if called with anything not a number" do + Puppet::Parser::Scope.number?([2]).should be_nil + end + + it "should return a Fixnum for a Fixnum" do + Puppet::Parser::Scope.number?(2).should be_an_instance_of(Fixnum) + end + + it "should return a Float for a Float" do + Puppet::Parser::Scope.number?(2.34).should be_an_instance_of(Float) + end + + it "should return 234 for '234'" do + Puppet::Parser::Scope.number?("234").should == 234 + end + + it "should return nil for 'not a number'" do + Puppet::Parser::Scope.number?("not a number").should be_nil + end + + it "should return 23.4 for '23.4'" do + Puppet::Parser::Scope.number?("23.4").should == 23.4 + end + + it "should return 23.4e13 for '23.4e13'" do + Puppet::Parser::Scope.number?("23.4e13").should == 23.4e13 + end + + it "should understand negative numbers" do + Puppet::Parser::Scope.number?("-234").should == -234 + end + + it "should know how to convert exponential float numbers ala '23e13'" do + Puppet::Parser::Scope.number?("23e13").should == 23e13 + end + + it "should understand hexadecimal numbers" do + Puppet::Parser::Scope.number?("0x234").should == 0x234 + end + + it "should understand octal numbers" do + Puppet::Parser::Scope.number?("0755").should == 0755 + end + + + end + end |
