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