class Puppet::Pops::Evaluator::EvaluatorImpl
This implementation of {Evaluator} performs evaluation using the puppet 3.x runtime system in a manner largely compatible with Puppet 3.x, but adds new features and introduces constraints.
The evaluation uses _polymorphic dispatch_ which works by dispatching to the first found method named after the class or one of its super-classes. The EvaluatorImpl itself mainly deals with evaluation (it currently also handles assignment), and it uses a delegation pattern to more specialized handlers of some operators that in turn use polymorphic dispatch; this to not clutter EvaluatorImpl with too much responsibility).
Since a pattern is used, only the main entry points are fully documented. The parameters o and scope are the same in all the polymorphic methods, (the type of the parameter o is reflected in the method's name; either the actual class, or one of its super classes). The scope parameter is always the scope in which the evaluation takes place. If nothing else is mentioned, the return is always the result of evaluation.
See {Visitable} and {Visitor} for more information about polymorphic calling.
Constants
Public Class Methods
# File lib/puppet/pops/evaluator/evaluator_impl.rb 44 def initialize 45 @@initialized ||= static_initialize 46 47 # Use null migration checker unless given in context 48 @migration_checker = Puppet.lookup(:migration_checker) { Migration::MigrationChecker.singleton } 49 end
Public Instance Methods
Assigns the given value to the given target. The additional argument o is the instruction that produced the target/value tuple and it is used to set the origin of the result.
@param target [Object] assignment target - see methods on the pattern assign_TYPE for actual supported types. @param value [Object] the value to assign to `target` @param o [Model::PopsObject] originating instruction @param scope [Object] the runtime specific scope where evaluation should take place
@api private
# File lib/puppet/pops/evaluator/evaluator_impl.rb 140 def assign(target, value, o, scope) 141 @@assign_visitor.visit_this_3(self, target, value, o, scope) 142 end
Evaluates the given target object in the given scope.
@overload evaluate(target, scope) @param target [Object] evaluation target - see methods on the pattern assign_TYPE for actual supported types. @param scope [Object] the runtime specific scope class where evaluation should take place @return [Object] the result of the evaluation
@api public
# File lib/puppet/pops/evaluator/evaluator_impl.rb 80 def evaluate(target, scope) 81 begin 82 @@eval_visitor.visit_this_1(self, target, scope) 83 84 rescue SemanticError => e 85 # A raised issue may not know the semantic target, use errors call stack, but fill in the 86 # rest from a supplied semantic object, or the target instruction if there is not semantic 87 # object. 88 # 89 fail(e.issue, e.semantic || target, e.options, e) 90 91 rescue Puppet::PreformattedError => e 92 # Already formatted with location information, and with the wanted call stack. 93 # Note this is currently a specialized ParseError, so rescue-order is important 94 # 95 raise e 96 97 rescue Puppet::ParseError => e 98 # ParseError may be raised in ruby code without knowing the location 99 # in puppet code. 100 # Accept a ParseError that has file or line information available 101 # as an error that should be used verbatim. (Tests typically run without 102 # setting a file name). 103 # ParseError can supply an original - it is impossible to determine which 104 # call stack that should be propagated, using the ParseError's backtrace. 105 # 106 if e.file || e.line 107 raise e 108 else 109 # Since it had no location information, treat it as user intended a general purpose 110 # error. Pass on its call stack. 111 fail(Issues::RUNTIME_ERROR, target, {:detail => e.message}, e) 112 end 113 114 115 rescue Puppet::Error => e 116 # PuppetError has the ability to wrap an exception, if so, use the wrapped exception's 117 # call stack instead 118 fail(Issues::RUNTIME_ERROR, target, {:detail => e.message}, e.original || e) 119 120 rescue StopIteration => e 121 # Ensure these are not rescued as StandardError 122 raise e 123 124 rescue StandardError => e 125 # All other errors, use its message and call stack 126 fail(Issues::RUNTIME_ERROR, target, {:detail => e.message}, e) 127 end 128 end
Evaluate a BlockExpression in a new scope with variables bound to the given values.
@param scope [Puppet::Parser::Scope] the parent scope @param variable_bindings [Hash{String => Object}] the variable names and values to bind (names are keys, bound values are values) @param block [Model::BlockExpression] the sequence of expressions to evaluate in the new scope
@api private
# File lib/puppet/pops/evaluator/evaluator_impl.rb 173 def evaluate_block_with_bindings(scope, variable_bindings, block_expr) 174 scope.with_guarded_scope do 175 # change to create local scope_from - cannot give it file and line - 176 # that is the place of the call, not "here" 177 create_local_scope_from(variable_bindings, scope) 178 evaluate(block_expr, scope) 179 end 180 end
Computes a value that can be used as the LHS in an assignment. @param o [Object] the expression to evaluate as a left (assignable) entity @param scope [Object] the runtime specific scope where evaluation should take place
@api private
# File lib/puppet/pops/evaluator/evaluator_impl.rb 150 def lvalue(o, scope) 151 @@lvalue_visitor.visit_this_1(self, o, scope) 152 end
Implementation of case option matching.
This is the type of matching performed in a case option, using == for every type of value except regular expression where a match is performed.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 187 def match?(left, right) 188 @@compare_operator.match(left, right, nil) 189 end
Produces a String representation of the given object o as used in interpolation. @param o [Object] the expression of which a string representation is wanted @param scope [Object] the runtime specific scope where evaluation should take place
@api public
# File lib/puppet/pops/evaluator/evaluator_impl.rb 160 def string(o, scope) 161 @@string_visitor.visit_this_1(self, o, scope) 162 end
@api private
# File lib/puppet/pops/evaluator/evaluator_impl.rb 67 def type_calculator 68 @@type_calculator 69 end
Protected Instance Methods
# File lib/puppet/pops/evaluator/evaluator_impl.rb 237 def assign_Array(lvalues, values, o, scope) 238 if values.is_a?(Hash) 239 lvalues.map do |lval| 240 assign(lval, 241 values.fetch(lval) {|k| fail(Issues::MISSING_MULTI_ASSIGNMENT_KEY, o, :key =>k)}, 242 o, scope) 243 end 244 elsif values.is_a?(Puppet::Pops::Types::PClassType) 245 if Puppet[:tasks] 246 fail(Issues::CATALOG_OPERATION_NOT_SUPPORTED_WHEN_SCRIPTING, o, {:operation => _('multi var assignment from class')}) 247 end 248 # assign variables from class variables 249 # lookup class resource and return one or more parameter values 250 # TODO: behavior when class_name is nil 251 resource = find_resource(scope, 'class', values.class_name) 252 if resource 253 base_name = "#{values.class_name.downcase}::" 254 idx = -1 255 result = lvalues.map do |lval| 256 idx += 1 257 varname = "#{base_name}#{lval}" 258 if variable_exists?(varname, scope) 259 result = get_variable_value(varname, o, scope) 260 assign(lval, result, o, scope) 261 else 262 fail(Puppet::Pops::Issues::MISSING_MULTI_ASSIGNMENT_VARIABLE, o.left_expr.values[idx], {:name => varname}) 263 end 264 end 265 else 266 fail(Issues::UNKNOWN_RESOURCE, o.right_expr, {:type_name => 'Class', :title => values.class_name}) 267 end 268 269 else 270 values = [values] unless values.is_a?(Array) 271 if values.size != lvalues.size 272 fail(Issues::ILLEGAL_MULTI_ASSIGNMENT_SIZE, o, :expected =>lvalues.size, :actual => values.size) 273 end 274 lvalues.zip(values).map { |lval, val| assign(lval, val, o, scope) } 275 end 276 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 227 def assign_Numeric(n, value, o, scope) 228 fail(Issues::ILLEGAL_NUMERIC_ASSIGNMENT, o.left_expr, {:varname => n.to_s}) 229 end
Catches all illegal assignment (e.g. 1 = 2, {'a'=>1} = 2, etc)
# File lib/puppet/pops/evaluator/evaluator_impl.rb 233 def assign_Object(name, value, o, scope) 234 fail(Issues::ILLEGAL_ASSIGNMENT, o) 235 end
Assign value to named variable. The '$' sign is never part of the name. @example In Puppet DSL
$name = value
@param name [String] name of variable without $ @param value [Object] value to assign to the variable @param o [Model::PopsObject] originating instruction @param scope [Object] the runtime specific scope where evaluation should take place @return [value<Object>]
# File lib/puppet/pops/evaluator/evaluator_impl.rb 219 def assign_String(name, value, o, scope) 220 if name =~ /::/ 221 fail(Issues::CROSS_SCOPE_ASSIGNMENT, o.left_expr, {:name => name}) 222 end 223 set_variable(name, value, o, scope) 224 value 225 end
Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >>
# File lib/puppet/pops/evaluator/evaluator_impl.rb 401 def calculate(left, right, bin_expr, scope) 402 operator = bin_expr.operator 403 unless ARITHMETIC_OPERATORS.include?(operator) 404 fail(Issues::UNSUPPORTED_OPERATOR, bin_expr, {:operator => operator}) 405 end 406 407 left_o = bin_expr.left_expr 408 if (left.is_a?(URI) || left.is_a?(Types::PBinaryType::Binary)) && operator == '+' 409 concatenate(left, right) 410 elsif (left.is_a?(Array) || left.is_a?(Hash)) && COLLECTION_OPERATORS.include?(operator) 411 # Handle operation on collections 412 case operator 413 when '+' 414 concatenate(left, right) 415 when '-' 416 delete(left, right) 417 when '<<' 418 unless left.is_a?(Array) 419 fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) 420 end 421 left + [right] 422 end 423 else 424 # Handle operation on numeric 425 left = coerce_numeric(left, left_o, scope) 426 right = coerce_numeric(right, bin_expr.right_expr, scope) 427 begin 428 if operator == '%' && (left.is_a?(Float) || right.is_a?(Float)) 429 # Deny users the fun of seeing severe rounding errors and confusing results 430 fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) if left.is_a?(Float) 431 fail(Issues::OPERATOR_NOT_APPLICABLE_WHEN, left_o, {:operator => operator, :left_value => left, :right_value => right}) 432 end 433 if right.is_a?(Time::TimeData) && !left.is_a?(Time::TimeData) 434 if operator == '+' || operator == '*' && right.is_a?(Time::Timespan) 435 # Switch places. Let the TimeData do the arithmetic 436 x = left 437 left = right 438 right = x 439 elsif operator == '-' && right.is_a?(Time::Timespan) 440 left = Time::Timespan.new((left * Time::NSECS_PER_SEC).to_i) 441 else 442 fail(Issues::OPERATOR_NOT_APPLICABLE_WHEN, left_o, {:operator => operator, :left_value => left, :right_value => right}) 443 end 444 end 445 result = left.send(operator, right) 446 rescue NoMethodError 447 fail(Issues::OPERATOR_NOT_APPLICABLE, left_o, {:operator => operator, :left_value => left}) 448 rescue ZeroDivisionError 449 fail(Issues::DIV_BY_ZERO, bin_expr.right_expr) 450 end 451 case result 452 when Float 453 if result == Float::INFINITY || result == -Float::INFINITY 454 fail(Issues::RESULT_IS_INFINITY, left_o, {:operator => operator}) 455 end 456 when Integer 457 if result < MIN_INTEGER || result > MAX_INTEGER 458 fail(Issues::NUMERIC_OVERFLOW, bin_expr, {:value => result}) 459 end 460 end 461 result 462 end 463 end
Produces concatenation / merge of x and y.
When x is an Array, y of type produces:
-
Array => concatenation `[1,2], [3,4] => [1,2,3,4]`
-
Hash => concatenation of hash as array `[key, value, key, value, …]`
-
any other => concatenation of single value
When x is a Hash, y of type produces:
-
Array => merge of array interpreted as `[key, value, key, value,…]`
-
Hash => a merge, where entries in `y` overrides
-
any other => error
When x is a URI, y of type produces:
-
String => merge of URI interpreted x + URI(y) using URI merge semantics
-
URI => merge of URI interpreted x + y using URI merge semantics
-
any other => error
When x is nil, an empty array is used instead.
@note to concatenate an Array, nest the array - i.e. `[1,2], [[2,3]]`
@overload concatenate(obj_x, obj_y)
@param obj_x [Object] object to wrap in an array and concatenate to; see other overloaded methods for return type @param ary_y [Object] array to concatenate at end of `ary_x` @return [Object] wraps obj_x in array before using other overloaded option based on type of obj_y
@overload concatenate(ary_x, ary_y)
@param ary_x [Array] array to concatenate to @param ary_y [Array] array to concatenate at end of `ary_x` @return [Array] new array with `ary_x` + `ary_y`
@overload concatenate(ary_x, hsh_y)
@param ary_x [Array] array to concatenate to @param hsh_y [Hash] converted to array form, and concatenated to array @return [Array] new array with `ary_x` + `hsh_y` converted to array
@overload concatenate (ary_x, obj_y)
@param ary_x [Array] array to concatenate to @param obj_y [Object] non array or hash object to add to array @return [Array] new array with `ary_x` + `obj_y` added as last entry
@overload concatenate(hsh_x, ary_y)
@param hsh_x [Hash] the hash to merge with @param ary_y [Array] array interpreted as even numbered sequence of key, value merged with `hsh_x` @return [Hash] new hash with `hsh_x` merged with `ary_y` interpreted as hash in array form
@overload concatenate(hsh_x, hsh_y)
@param hsh_x [Hash] the hash to merge to @param hsh_y [Hash] hash merged with `hsh_x` @return [Hash] new hash with `hsh_x` merged with `hsh_y`
@overload concatenate(uri_x, uri_y)
@param uri_x [URI] the uri to merge to @param uri_y [URI] uri to merged with `uri_x` @return [URI] new uri with `uri_x` merged with `uri_y`
@overload concatenate(uri_x, string_y)
@param uri_x [URI] the uri to merge to @param string_y [String] string to merge with `uri_x` @return [URI] new uri with `uri_x` merged with `string_y`
@raise [ArgumentError] when `xxx_x` is neither an Array nor a Hash @raise [ArgumentError] when `xxx_x` is a Hash, and `xxx_y` is neither Array nor Hash.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1210 def concatenate(x, y) 1211 case x 1212 when Array 1213 y = case y 1214 when Array then y 1215 when Hash then y.to_a 1216 else 1217 [y] 1218 end 1219 x + y # new array with concatenation 1220 when Hash 1221 y = case y 1222 when Hash then y 1223 when Array 1224 # Hash[[a, 1, b, 2]] => {} 1225 # Hash[a,1,b,2] => {a => 1, b => 2} 1226 # Hash[[a,1], [b,2]] => {[a,1] => [b,2]} 1227 # Hash[[[a,1], [b,2]]] => {a => 1, b => 2} 1228 # Use type calculator to determine if array is Array[Array[?]], and if so use second form 1229 # of call 1230 t = @@type_calculator.infer(y) 1231 if t.element_type.is_a? Types::PArrayType 1232 Hash[y] 1233 else 1234 Hash[*y] 1235 end 1236 else 1237 raise ArgumentError.new(_('Can only append Array or Hash to a Hash')) 1238 end 1239 x.merge y # new hash with overwrite 1240 when URI 1241 raise ArgumentError.new(_('An URI can only be merged with an URI or String')) unless y.is_a?(String) || y.is_a?(URI) 1242 x + y 1243 when Types::PBinaryType::Binary 1244 raise ArgumentError.new(_('Can only append Binary to a Binary')) unless y.is_a?(Types::PBinaryType::Binary) 1245 Types::PBinaryType::Binary.from_binary_string(x.binary_buffer + y.binary_buffer) 1246 else 1247 concatenate([x], y) 1248 end 1249 end
Produces the result x \ y (set difference) When `x` is an Array, `y` is transformed to an array and then all matching elements removed from x. When `x` is a Hash, all contained keys are removed from x as listed in `y` if it is an Array, or all its keys if it is a Hash. The difference is returned. The given `x` and `y` are not modified by this operation. @raise [ArgumentError] when `x` is neither an Array nor a Hash
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1257 def delete(x, y) 1258 result = x.dup 1259 case x 1260 when Array 1261 y = case y 1262 when Array then y 1263 when Hash then y.to_a 1264 else 1265 [y] 1266 end 1267 y.each {|e| result.delete(e) } 1268 when Hash 1269 y = case y 1270 when Array then y 1271 when Hash then y.keys 1272 else 1273 [y] 1274 end 1275 y.each {|e| result.delete(e) } 1276 else 1277 raise ArgumentError.new(_("Can only delete from an Array or Hash.")) 1278 end 1279 result 1280 end
Evaluates x[key, key, …]
# File lib/puppet/pops/evaluator/evaluator_impl.rb 513 def eval_AccessExpression(o, scope) 514 left = evaluate(o.left_expr, scope) 515 keys = o.keys || [] 516 if left.is_a?(Types::PClassType) 517 # Evaluate qualified references without errors no undefined types 518 keys = keys.map {|key| key.is_a?(Model::QualifiedReference) ? Types::TypeParser.singleton.interpret(key) : evaluate(key, scope) } 519 else 520 keys = keys.map {|key| evaluate(key, scope) } 521 # Resource[File] becomes File 522 return keys[0] if Types::PResourceType::DEFAULT == left && keys.size == 1 && keys[0].is_a?(Types::PResourceType) 523 end 524 AccessOperator.new(o).access(left, scope, *keys) 525 end
@example
$a and $b
b is only evaluated if a is true
# File lib/puppet/pops/evaluator/evaluator_impl.rb 649 def eval_AndExpression o, scope 650 is_true?(evaluate(o.left_expr, scope), o.left_expr) ? is_true?(evaluate(o.right_expr, scope), o.right_expr) : false 651 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 896 def eval_ApplyExpression(o, scope) 897 # All expressions are wrapped in an ApplyBlockExpression so we can identify the contents of 898 # that block. However we don't want to serialize the block expression, so unwrap here. 899 body = if o.body.statements.count == 1 900 o.body.statements[0] 901 else 902 Model::BlockExpression.from_asserted_hash(o.body._pcore_init_hash) 903 end 904 905 Puppet.lookup(:apply_executor).apply(unfold([], o.arguments, scope), body, scope) 906 end
Handles binary expression where lhs and rhs are array/hash or numeric and operator is +, - , *, % / << >>
# File lib/puppet/pops/evaluator/evaluator_impl.rb 386 def eval_ArithmeticExpression(o, scope) 387 left = evaluate(o.left_expr, scope) 388 right = evaluate(o.right_expr, scope) 389 390 begin 391 result = calculate(left, right, o, scope) 392 rescue ArgumentError => e 393 fail(Issues::RUNTIME_ERROR, o, {:detail => e.message}, e) 394 end 395 result 396 end
Evaluates assignment with operators =, +=, -= and
@example Puppet DSL
$a = 1 $a += 1 $a -= 1
# File lib/puppet/pops/evaluator/evaluator_impl.rb 369 def eval_AssignmentExpression(o, scope) 370 name = lvalue(o.left_expr, scope) 371 value = evaluate(o.right_expr, scope) 372 373 if o.operator == '=' 374 assign(name, value, o, scope) 375 else 376 fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) 377 end 378 value 379 end
Produces 3x parameter
# File lib/puppet/pops/evaluator/evaluator_impl.rb 909 def eval_AttributeOperation(o, scope) 910 create_resource_parameter(o, scope, o.attribute_name, evaluate(o.value_expr, scope), o.operator) 911 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 913 def eval_AttributesOperation(o, scope) 914 hashed_params = evaluate(o.expr, scope) 915 unless hashed_params.is_a?(Hash) 916 actual = type_calculator.generalize(type_calculator.infer(hashed_params)).to_s 917 fail(Issues::TYPE_MISMATCH, o.expr, {:expected => 'Hash', :actual => actual}) 918 end 919 hashed_params.map { |k,v| create_resource_parameter(o, scope, k, v, '=>') } 920 end
Abstract evaluation, returns array [left, right] with the evaluated result of left_expr and right_expr @return <Array<Object, Object>> array with result of evaluating left and right expressions
# File lib/puppet/pops/evaluator/evaluator_impl.rb 358 def eval_BinaryExpression o, scope 359 [ evaluate(o.left_expr, scope), evaluate(o.right_expr, scope) ] 360 end
Evaluates all statements and produces the last evaluated value
# File lib/puppet/pops/evaluator/evaluator_impl.rb 679 def eval_BlockExpression o, scope 680 o.statements.reduce(nil) {|memo, s| evaluate(s, scope)} 681 end
Evaluation of CallMethodExpression handles a NamedAccessExpression functor (receiver.function_name)
# File lib/puppet/pops/evaluator/evaluator_impl.rb 970 def eval_CallMethodExpression(o, scope) 971 unless o.functor_expr.is_a? Model::NamedAccessExpression 972 fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function accessor', :container => o}) 973 end 974 receiver = unfold([], [o.functor_expr.left_expr], scope) 975 name = o.functor_expr.right_expr 976 unless name.is_a? Model::QualifiedName 977 fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) 978 end 979 name = name.value # the string function name 980 981 obj = receiver[0] 982 receiver_type = Types::TypeCalculator.infer_callable_methods_t(obj) 983 if receiver_type.is_a?(Types::TypeWithMembers) 984 member = receiver_type[name] 985 unless member.nil? 986 args = unfold([], o.arguments || [], scope) 987 return o.lambda.nil? ? member.invoke(obj, scope, args) : member.invoke(obj, scope, args, &proc_from_lambda(o.lambda, scope)) 988 end 989 end 990 991 call_function_with_block(name, unfold(receiver, o.arguments || [], scope), o, scope) 992 end
Evaluates function call by name.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 941 def eval_CallNamedFunctionExpression(o, scope) 942 # If LHS is a type (i.e. Integer, or Integer[...] 943 # the call is taken as an instantiation of the given type 944 # 945 functor = o.functor_expr 946 if functor.is_a?(Model::QualifiedReference) || 947 functor.is_a?(Model::AccessExpression) && functor.left_expr.is_a?(Model::QualifiedReference) 948 # instantiation 949 type = evaluate(functor, scope) 950 return call_function_with_block('new', unfold([type], o.arguments || [], scope), o, scope) 951 end 952 953 # The functor expression is not evaluated, it is not possible to select the function to call 954 # via an expression like $a() 955 case functor 956 when Model::QualifiedName 957 # ok 958 when Model::RenderStringExpression 959 # helpful to point out this easy to make Epp error 960 fail(Issues::ILLEGAL_EPP_PARAMETERS, o) 961 else 962 fail(Issues::ILLEGAL_EXPRESSION, o.functor_expr, {:feature=>'function name', :container => o}) 963 end 964 name = o.functor_expr.value 965 call_function_with_block(name, unfold([], o.arguments, scope), o, scope) 966 end
Performs optimized search over case option values, lazily evaluating each until there is a match. If no match is found, the case expression's default expression is evaluated (it may be nil or Nop if there is no default, thus producing nil). If an option matches, the result of evaluating that option is returned. @return [Object, nil] what a matched option returns, or nil if nothing matched.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 689 def eval_CaseExpression(o, scope) 690 # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars 691 # to expressions after the case expression. 692 # 693 scope.with_guarded_scope do 694 test = evaluate(o.test, scope) 695 696 result = nil 697 the_default = nil 698 if o.options.find do |co| 699 # the first case option that matches 700 if co.values.find do |c| 701 c = unwind_parentheses(c) 702 case c 703 when Model::LiteralDefault 704 the_default = co.then_expr 705 next false 706 when Model::UnfoldExpression 707 # not ideal for error reporting, since it is not known which unfolded result 708 # that caused an error - the entire unfold expression is blamed (i.e. the var c, passed to is_match?) 709 evaluate(c, scope).any? {|v| is_match?(test, v, c, co, scope) } 710 else 711 is_match?(test, evaluate(c, scope), c, co, scope) 712 end 713 end 714 result = evaluate(co.then_expr, scope) 715 true # the option was picked 716 end 717 end 718 result # an option was picked, and produced a result 719 else 720 evaluate(the_default, scope) # evaluate the default (should be a nop/nil) if there is no default). 721 end 722 end 723 end
Evaluates a CollectExpression by creating a collector transformer. The transformer will evaluate the collection, create the appropriate collector, and hand it off to the compiler to collect the resources specified by the query.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 729 def eval_CollectExpression o, scope 730 if o.query.is_a?(Model::ExportedQuery) 731 optionally_fail(Issues::RT_NO_STORECONFIGS, o); 732 end 733 CollectorTransformer.new().transform(o,scope) 734 end
Evaluates <, <=, >, >=, and ==
# File lib/puppet/pops/evaluator/evaluator_impl.rb 529 def eval_ComparisonExpression o, scope 530 left = evaluate(o.left_expr, scope) 531 right = evaluate(o.right_expr, scope) 532 533 begin 534 # Left is a type 535 if left.is_a?(Types::PAnyType) 536 case o.operator 537 when '==' 538 @@type_calculator.equals(left,right) 539 540 when '!=' 541 !@@type_calculator.equals(left,right) 542 543 when '<' 544 # left can be assigned to right, but they are not equal 545 @@type_calculator.assignable?(right, left) && ! @@type_calculator.equals(left,right) 546 when '<=' 547 # left can be assigned to right 548 @@type_calculator.assignable?(right, left) 549 when '>' 550 # right can be assigned to left, but they are not equal 551 @@type_calculator.assignable?(left,right) && ! @@type_calculator.equals(left,right) 552 when '>=' 553 # right can be assigned to left 554 @@type_calculator.assignable?(left, right) 555 else 556 fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) 557 end 558 else 559 case o.operator 560 when '==' 561 @@compare_operator.equals(left,right) 562 when '!=' 563 ! @@compare_operator.equals(left,right) 564 when '<' 565 @@compare_operator.compare(left,right) < 0 566 when '<=' 567 @@compare_operator.compare(left,right) <= 0 568 when '>' 569 @@compare_operator.compare(left,right) > 0 570 when '>=' 571 @@compare_operator.compare(left,right) >= 0 572 else 573 fail(Issues::UNSUPPORTED_OPERATOR, o, {:operator => o.operator}) 574 end 575 end 576 rescue ArgumentError => e 577 fail(Issues::COMPARISON_NOT_POSSIBLE, o, { 578 :operator => o.operator, 579 :left_value => left, 580 :right_value => right, 581 :detail => e.message}, e) 582 end 583 end
Evaluates double quoted strings that may contain interpolation
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1100 def eval_ConcatenatedString o, scope 1101 o.segments.collect {|expr| string(evaluate(expr, scope), scope)}.join 1102 end
This evaluates classes, nodes and resource type definitions to nil, since 3x: instantiates them, and evaluates their parameters and body. This is achieved by providing bridge AST classes in Puppet::Parser::AST::PopsBridge that bridges a Pops Program and a Pops Expression.
Since all Definitions are handled “out of band”, they are treated as a no-op when evaluated.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 748 def eval_Definition(o, scope) 749 nil 750 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 465 def eval_EppExpression(o, scope) 466 contains_sensitive = false 467 468 scope["@epp"] = [] 469 evaluate(o.body, scope) 470 result = scope["@epp"].map do |r| 471 if r.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive) 472 contains_sensitive = true 473 string(r.unwrap, scope) 474 else 475 r 476 end 477 end.join 478 479 if contains_sensitive 480 Puppet::Pops::Types::PSensitiveType::Sensitive.new(result) 481 else 482 result 483 end 484 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 278 def eval_Factory(o, scope) 279 evaluate(o.model, scope) 280 end
Evaluates Puppet DSL Heredoc
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1045 def eval_HeredocExpression o, scope 1046 expr = o.text_expr 1047 result = evaluate(o.text_expr, scope) 1048 unless expr.is_a?(Model::LiteralString) 1049 # When expr is a LiteralString, validation has already validated this 1050 assert_external_syntax(scope, result, o.syntax, o.text_expr) 1051 end 1052 result 1053 end
Evaluates Puppet DSL `if`
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1056 def eval_IfExpression o, scope 1057 scope.with_guarded_scope do 1058 if is_true?(evaluate(o.test, scope), o.test) 1059 evaluate(o.then_expr, scope) 1060 else 1061 evaluate(o.else_expr, scope) 1062 end 1063 end 1064 end
Evaluates Puppet DSL `in` expression
# File lib/puppet/pops/evaluator/evaluator_impl.rb 639 def eval_InExpression o, scope 640 left = evaluate(o.left_expr, scope) 641 right = evaluate(o.right_expr, scope) 642 @@compare_operator.include?(right, left, scope) 643 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 313 def eval_LiteralDefault(o, scope) 314 :default 315 end
Evaluates each entry of the literal hash and creates a new Hash. @return [Hash] with the evaluated content
# File lib/puppet/pops/evaluator/evaluator_impl.rb 672 def eval_LiteralHash o, scope 673 # optimized 674 o.entries.reduce({}) {|h,entry| h[evaluate(entry.key, scope)] = evaluate(entry.value, scope); h } 675 end
Evaluates each entry of the literal list and creates a new Array Supports unfolding of entries @return [Array] with the evaluated content
# File lib/puppet/pops/evaluator/evaluator_impl.rb 665 def eval_LiteralList o, scope 666 unfold([], o.values, scope) 667 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 317 def eval_LiteralUndef(o, scope) 318 nil 319 end
Captures all LiteralValues not handled elsewhere.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 299 def eval_LiteralValue(o, scope) 300 o.value 301 end
Evaluates matching expressions with type, string or regexp rhs expression. If RHS is a type, the =~ matches compatible (instance? of) type.
@example
x =~ /abc.*/
@example
x =~ "abc.*/"
@example
y = "abc" x =~ "${y}.*"
@example
[1,2,3] =~ Array[Integer[1,10]]
Note that a string is not instance? of Regexp, only Regular expressions are. The Pattern type should instead be used as it is specified as subtype of String.
@return [Boolean] if a match was made or not. Also sets $0..$n to matchdata in current scope.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 603 def eval_MatchExpression o, scope 604 left = evaluate(o.left_expr, scope) 605 pattern = evaluate(o.right_expr, scope) 606 607 # matches RHS types as instance of for all types except a parameterized Regexp[R] 608 if pattern.is_a?(Types::PAnyType) 609 # evaluate as instance? of type check 610 matched = pattern.instance?(left) 611 # convert match result to Boolean true, or false 612 return o.operator == '=~' ? !!matched : !matched 613 end 614 615 if pattern.is_a?(SemanticPuppet::VersionRange) 616 # evaluate if range includes version 617 matched = Types::PSemVerRangeType.include?(pattern, left) 618 return o.operator == '=~' ? matched : !matched 619 end 620 621 begin 622 pattern = Regexp.new(pattern) unless pattern.is_a?(Regexp) 623 rescue StandardError => e 624 fail(Issues::MATCH_NOT_REGEXP, o.right_expr, {:detail => e.message}, e) 625 end 626 unless left.is_a?(String) 627 fail(Issues::MATCH_NOT_STRING, o.left_expr, {:left_value => left}) 628 end 629 630 matched = pattern.match(left) # nil, or MatchData 631 set_match_data(matched,scope) # creates ephemeral 632 633 # convert match result to Boolean true, or false 634 o.operator == '=~' ? !!matched : !matched 635 end
Allows nil to be used as a Nop, Evaluates to nil
# File lib/puppet/pops/evaluator/evaluator_impl.rb 288 def eval_NilClass(o, scope) 289 nil 290 end
Evaluates Nop to nil.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 293 def eval_Nop(o, scope) 294 nil 295 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 329 def eval_NotExpression(o, scope) 330 ! is_true?(evaluate(o.expr, scope), o.expr) 331 end
Evaluates any object not evaluated to something else to itself.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 283 def eval_Object o, scope 284 o 285 end
@example
a or b
b is only evaluated if a is false
# File lib/puppet/pops/evaluator/evaluator_impl.rb 657 def eval_OrExpression o, scope 658 is_true?(evaluate(o.left_expr, scope), o.left_expr) ? true : is_true?(evaluate(o.right_expr, scope), o.right_expr) 659 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 736 def eval_ParenthesizedExpression(o, scope) 737 evaluate(o.expr, scope) 738 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 752 def eval_Program(o, scope) 753 begin 754 file = o.locator.file 755 line = 0 756 # Add stack frame for "top scope" logic. See Puppet::Pops::PuppetStack 757 return Puppet::Pops::PuppetStack.stack(file, line, self, 'evaluate', [o.body, scope]) 758 #evaluate(o.body, scope) 759 rescue Puppet::Pops::Evaluator::PuppetStopIteration => ex 760 # breaking out of a file level program is not allowed 761 #TRANSLATOR break() is a method that should not be translated 762 raise Puppet::ParseError.new(_("break() from context where this is illegal"), ex.file, ex.line) 763 end 764 end
A QualifiedReference (i.e. a capitalized qualified name such as Foo, or Foo::Bar) evaluates to a PTypeType
# File lib/puppet/pops/evaluator/evaluator_impl.rb 323 def eval_QualifiedReference(o, scope) 324 type = Types::TypeParser.singleton.interpret(o) 325 fail(Issues::UNKNOWN_RESOURCE_TYPE, o, {:type_name => type.type_string }) if type.is_a?(Types::PTypeReferenceType) 326 type 327 end
Evaluates Puppet DSL ->, ~>, <-, and <~
# File lib/puppet/pops/evaluator/evaluator_impl.rb 502 def eval_RelationshipExpression(o, scope) 503 # First level evaluation, reduction to basic data types or puppet types, the relationship operator then translates this 504 # to the final set of references (turning strings into references, which can not naturally be done by the main evaluator since 505 # all strings should not be turned into references. 506 # 507 real = eval_BinaryExpression(o, scope) 508 @@relationship_operator.evaluate(real, o, scope) 509 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 491 def eval_RenderExpression(o, scope) 492 result = evaluate(o.expr, scope) 493 if result.instance_of?(Puppet::Pops::Types::PSensitiveType::Sensitive) 494 scope["@epp"] << result 495 else 496 scope["@epp"] << string(result, scope) 497 end 498 nil 499 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 486 def eval_RenderStringExpression(o, scope) 487 scope["@epp"] << o.value.dup 488 nil 489 end
Reserved Words fail to evaluate
# File lib/puppet/pops/evaluator/evaluator_impl.rb 305 def eval_ReservedWord(o, scope) 306 if !o.future 307 fail(Issues::RESERVED_WORD, o, {:word => o.word}) 308 else 309 o.word 310 end 311 end
Sets default parameter values for a type, produces the type
# File lib/puppet/pops/evaluator/evaluator_impl.rb 924 def eval_ResourceDefaultsExpression(o, scope) 925 type = evaluate(o.type_ref, scope) 926 type_name = 927 if type.is_a?(Types::PResourceType) && !type.type_name.nil? && type.title.nil? 928 type.type_name # assume it is a valid name 929 else 930 actual = type_calculator.generalize(type_calculator.infer(type)) 931 fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_ref, {:actual => actual}) 932 end 933 evaluated_parameters = o.operations.map {|op| evaluate(op, scope) } 934 create_resource_defaults(o, scope, type_name, evaluated_parameters) 935 # Produce the type 936 type 937 end
Produces Array, an array of resource references
# File lib/puppet/pops/evaluator/evaluator_impl.rb 768 def eval_ResourceExpression(o, scope) 769 exported = o.exported 770 virtual = o.virtual 771 772 # Get the type name 773 type_name = 774 if (tmp_name = o.type_name).is_a?(Model::QualifiedName) 775 tmp_name.value # already validated as a name 776 else 777 type_name_acceptable = 778 case o.type_name 779 when Model::QualifiedReference 780 true 781 when Model::AccessExpression 782 o.type_name.left_expr.is_a?(Model::QualifiedReference) 783 end 784 785 evaluated_name = evaluate(tmp_name, scope) 786 unless type_name_acceptable 787 actual = type_calculator.generalize(type_calculator.infer(evaluated_name)).to_s 788 fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, {:actual => actual}) 789 end 790 791 # must be a CatalogEntry subtype 792 case evaluated_name 793 when Types::PClassType 794 unless evaluated_name.class_name.nil? 795 fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, {:actual=> evaluated_name.to_s}) 796 end 797 'class' 798 799 when Types::PResourceType 800 unless evaluated_name.title().nil? 801 fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, {:actual=> evaluated_name.to_s}) 802 end 803 evaluated_name.type_name # assume validated 804 805 when Types::PTypeReferenceType 806 fail(Issues::UNKNOWN_RESOURCE_TYPE, o.type_string, {:type_name => evaluated_name.to_s}) 807 808 else 809 actual = type_calculator.generalize(type_calculator.infer(evaluated_name)).to_s 810 fail(Issues::ILLEGAL_RESOURCE_TYPE, o.type_name, {:actual=>actual}) 811 end 812 end 813 814 # This is a runtime check - the model is valid, but will have runtime issues when evaluated 815 # and storeconfigs is not set. 816 if(o.exported) 817 optionally_fail(Issues::RT_NO_STORECONFIGS_EXPORT, o); 818 end 819 820 titles_to_body = {} 821 body_to_titles = {} 822 body_to_params = {} 823 824 # titles are evaluated before attribute operations 825 o.bodies.map do | body | 826 titles = evaluate(body.title, scope) 827 828 # Title may not be nil 829 # Titles may be given as an array, it is ok if it is empty, but not if it contains nil entries 830 # Titles may not be an empty String 831 # Titles must be unique in the same resource expression 832 # There may be a :default entry, its entries apply with lower precedence 833 # 834 if titles.nil? 835 fail(Issues::MISSING_TITLE, body.title) 836 end 837 titles = [titles].flatten 838 839 # Check types of evaluated titles and duplicate entries 840 titles.each_with_index do |title, index| 841 if title.nil? 842 fail(Issues::MISSING_TITLE_AT, body.title, {:index => index}) 843 844 elsif !title.is_a?(String) && title != :default 845 actual = type_calculator.generalize(type_calculator.infer(title)).to_s 846 fail(Issues::ILLEGAL_TITLE_TYPE_AT, body.title, {:index => index, :actual => actual}) 847 848 elsif title == EMPTY_STRING 849 fail(Issues::EMPTY_STRING_TITLE_AT, body.title, {:index => index}) 850 851 elsif titles_to_body[title] 852 fail(Issues::DUPLICATE_TITLE, o, {:title => title}) 853 end 854 titles_to_body[title] = body 855 end 856 857 # Do not create a real instance from the :default case 858 titles.delete(:default) 859 860 body_to_titles[body] = titles 861 862 # Store evaluated parameters in a hash associated with the body, but do not yet create resource 863 # since the entry containing :defaults may appear later 864 body_to_params[body] = body.operations.reduce({}) do |param_memo, op| 865 params = evaluate(op, scope) 866 params = [params] unless params.is_a?(Array) 867 params.each do |p| 868 if param_memo.include? p.name 869 fail(Issues::DUPLICATE_ATTRIBUTE, o, {:attribute => p.name}) 870 end 871 param_memo[p.name] = p 872 end 873 param_memo 874 end 875 end 876 877 # Titles and Operations have now been evaluated and resources can be created 878 # Each production is a PResource, and an array of all is produced as the result of 879 # evaluating the ResourceExpression. 880 # 881 defaults_hash = body_to_params[titles_to_body[:default]] || {} 882 o.bodies.map do | body | 883 titles = body_to_titles[body] 884 params = defaults_hash.merge(body_to_params[body] || {}) 885 create_resources(o, scope, virtual, exported, type_name, titles, params.values) 886 end.flatten.compact 887 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 889 def eval_ResourceOverrideExpression(o, scope) 890 evaluated_resources = evaluate(o.resources, scope) 891 evaluated_parameters = o.operations.map { |op| evaluate(op, scope) } 892 create_resource_overrides(o, scope, [evaluated_resources].flatten, evaluated_parameters) 893 evaluated_resources 894 end
@example
$x ? { 10 => true, 20 => false, default => 0 }
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1012 def eval_SelectorExpression o, scope 1013 # memo scope level before evaluating test - don't want a match in the case test to leak $n match vars 1014 # to expressions after the selector expression. 1015 # 1016 scope.with_guarded_scope do 1017 test = evaluate(o.left_expr, scope) 1018 1019 the_default = nil 1020 selected = o.selectors.find do |s| 1021 me = unwind_parentheses(s.matching_expr) 1022 case me 1023 when Model::LiteralDefault 1024 the_default = s.value_expr 1025 false 1026 when Model::UnfoldExpression 1027 # not ideal for error reporting, since it is not known which unfolded result 1028 # that caused an error - the entire unfold expression is blamed (i.e. the var c, passed to is_match?) 1029 evaluate(me, scope).any? {|v| is_match?(test, v, me, s, scope) } 1030 else 1031 is_match?(test, evaluate(me, scope), me, s, scope) 1032 end 1033 end 1034 if selected 1035 evaluate(selected.value_expr, scope) 1036 elsif the_default 1037 evaluate(the_default, scope) 1038 else 1039 fail(Issues::UNMATCHED_SELECTOR, o.left_expr, :param_value => test) 1040 end 1041 end 1042 end
If the wrapped expression is a QualifiedName, it is taken as the name of a variable in scope. Note that this is different from the 3.x implementation, where an initial qualified name is accepted. (e.g. `“—${var + 1}—”` is legal. This implementation requires such concrete syntax to be expressed in a model as `(TextExpression (+ (Variable var) 1)` - i.e. moving the decision to the parser.
Semantics; the result of an expression is turned into a string, nil is silently transformed to empty string. @return [String] the interpolated result
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1115 def eval_TextExpression o, scope 1116 if o.expr.is_a?(Model::QualifiedName) 1117 string(get_variable_value(o.expr.value, o, scope), scope) 1118 else 1119 string(evaluate(o.expr, scope), scope) 1120 end 1121 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 333 def eval_UnaryMinusExpression(o, scope) 334 - coerce_numeric(evaluate(o.expr, scope), o, scope) 335 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 337 def eval_UnfoldExpression(o, scope) 338 candidate = evaluate(o.expr, scope) 339 case candidate 340 when nil 341 [] 342 when Array 343 candidate 344 when Hash 345 candidate.to_a 346 when Puppet::Pops::Types::Iterable 347 candidate.to_a 348 else 349 # turns anything else into an array (so result can be unfolded) 350 [candidate] 351 end 352 end
Evaluates Puppet DSL `unless`
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1067 def eval_UnlessExpression o, scope 1068 scope.with_guarded_scope do 1069 unless is_true?(evaluate(o.test, scope), o.test) 1070 evaluate(o.then_expr, scope) 1071 else 1072 evaluate(o.else_expr, scope) 1073 end 1074 end 1075 end
Evaluates a variable (getting its value) The evaluator is lenient; any expression producing a String is used as a name of a variable.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1081 def eval_VariableExpression o, scope 1082 # Evaluator is not too fussy about what constitutes a name as long as the result 1083 # is a String and a valid variable name 1084 # 1085 name = evaluate(o.expr, scope) 1086 1087 # Should be caught by validation, but make this explicit here as well, or mysterious evaluation issues 1088 # may occur for some evaluation use cases. 1089 case name 1090 when String 1091 when Numeric 1092 else 1093 fail(Issues::ILLEGAL_VARIABLE_EXPRESSION, o.expr) 1094 end 1095 get_variable_value(name, o, scope) 1096 end
Implementation of case option matching.
This is the type of matching performed in a case option, using == for every type of value except regular expression where a match is performed.
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1287 def is_match?(left, right, o, option_expr, scope) 1288 @@compare_operator.match(left, right, scope) 1289 end
An array is assignable if all entries are lvalues
# File lib/puppet/pops/evaluator/evaluator_impl.rb 205 def lvalue_LiteralList(o, scope) 206 o.values.map {|x| lvalue(x, scope) } 207 end
Catches all illegal lvalues
# File lib/puppet/pops/evaluator/evaluator_impl.rb 200 def lvalue_Object(o, scope) 201 fail(Issues::ILLEGAL_ASSIGNMENT, o) 202 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 193 def lvalue_VariableExpression(o, scope) 194 # evaluate the name 195 evaluate(o.expr, scope) 196 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1135 def string_Array(o, scope) 1136 "[#{o.map {|e| string(e, scope)}.join(COMMA_SEPARATOR)}]" 1137 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1139 def string_Hash(o, scope) 1140 "{#{o.map {|k,v| "#{string(k, scope)} => #{string(v, scope)}"}.join(COMMA_SEPARATOR)}}" 1141 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1123 def string_Object(o, scope) 1124 o.to_s 1125 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1147 def string_PAnyType(o, scope) 1148 o.to_s 1149 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1143 def string_Regexp(o, scope) 1144 Types::PRegexpType.regexp_to_s_with_delimiters(o) 1145 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1127 def string_Symbol(o, scope) 1128 if :undef == o # optimized comparison 1.44 vs 1.95 1129 EMPTY_STRING 1130 else 1131 o.to_s 1132 end 1133 end
Private Instance Methods
# File lib/puppet/pops/evaluator/evaluator_impl.rb 994 def call_function_with_block(name, evaluated_arguments, o, scope) 995 if o.lambda.nil? 996 call_function(name, evaluated_arguments, o, scope) 997 else 998 call_function(name, evaluated_arguments, o, scope, &proc_from_lambda(o.lambda, scope)) 999 end 1000 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1003 def proc_from_lambda(lambda, scope) 1004 closure = Closure::Dynamic.new(self, lambda, scope) 1005 PuppetProc.new(closure) { |*args| closure.call(*args) } 1006 end
@api private
# File lib/puppet/pops/evaluator/evaluator_impl.rb 52 def static_initialize 53 @@eval_visitor ||= Visitor.new(self, "eval", 1, 1) 54 @@lvalue_visitor ||= Visitor.new(self, "lvalue", 1, 1) 55 @@assign_visitor ||= Visitor.new(self, "assign", 3, 3) 56 @@string_visitor ||= Visitor.new(self, "string", 1, 1) 57 58 @@type_calculator ||= Types::TypeCalculator.singleton 59 60 @@compare_operator ||= CompareOperator.new 61 @@relationship_operator ||= RelationshipOperator.new 62 true 63 end
Maps the expression in the given array to their product except for UnfoldExpressions which are first unfolded. The result is added to the given result Array. @param result [Array] Where to add the result (may contain information to add to) @param array [Array the expressions to map @param scope [Puppet::Parser::Scope] the scope to evaluate in @return [Array] the given result array with content added from the operation
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1298 def unfold(result, array, scope) 1299 array.each do |x| 1300 x = unwind_parentheses(x) 1301 if x.is_a?(Model::UnfoldExpression) 1302 result.concat(evaluate(x, scope)) 1303 else 1304 result << evaluate(x, scope) 1305 end 1306 end 1307 result 1308 end
# File lib/puppet/pops/evaluator/evaluator_impl.rb 1311 def unwind_parentheses(o) 1312 return o unless o.is_a?(Model::ParenthesizedExpression) 1313 unwind_parentheses(o.expr) 1314 end