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

ARITHMETIC_OPERATORS
COLLECTION_OPERATORS
COMMA_SEPARATOR
Issues

Reference to Issues name space makes it easier to refer to issues (Issues are shared with the validator).

Public Class Methods

new() click to toggle source
   # 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

assign(target, value, o, scope) click to toggle source

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
evaluate(target, scope) click to toggle source

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_block_with_bindings(scope, variable_bindings, block_expr) click to toggle source

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
lvalue(o, scope) click to toggle source

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
match?(left, right) click to toggle source

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
string(o, scope) click to toggle source

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
type_calculator() click to toggle source

@api private

   # File lib/puppet/pops/evaluator/evaluator_impl.rb
67 def type_calculator
68   @@type_calculator
69 end

Protected Instance Methods

assign_Array(lvalues, values, o, scope) click to toggle source
    # 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
assign_Numeric(n, value, o, scope) click to toggle source
    # 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
assign_Object(name, value, o, scope) click to toggle source

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_String(name, value, o, scope) click to toggle source

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
calculate(left, right, bin_expr, scope) click to toggle source

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
concatenate(x, y) click to toggle source

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
delete(x, y) click to toggle source

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
eval_AccessExpression(o, scope) click to toggle source

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
eval_AndExpression(o, scope) click to toggle source

@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
eval_ApplyExpression(o, scope) click to toggle source
    # 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
eval_ArithmeticExpression(o, scope) click to toggle source

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
eval_AssignmentExpression(o, scope) click to toggle source

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
eval_AttributeOperation(o, scope) click to toggle source

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
eval_AttributesOperation(o, scope) click to toggle source
    # 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
eval_BinaryExpression(o, scope) click to toggle source

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
eval_BlockExpression(o, scope) click to toggle source

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
eval_CallMethodExpression(o, scope) click to toggle source

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
eval_CallNamedFunctionExpression(o, scope) click to toggle source

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
eval_CaseExpression(o, scope) click to toggle source

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
eval_CollectExpression(o, scope) click to toggle source

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
eval_ComparisonExpression(o, scope) click to toggle source

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
eval_ConcatenatedString(o, scope) click to toggle source

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
eval_Definition(o, scope) click to toggle source

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
eval_EppExpression(o, scope) click to toggle source
    # 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
eval_Factory(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
278 def eval_Factory(o, scope)
279   evaluate(o.model, scope)
280 end
eval_HeredocExpression(o, scope) click to toggle source

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
eval_IfExpression(o, scope) click to toggle source

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
eval_InExpression(o, scope) click to toggle source

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
eval_LiteralDefault(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
313 def eval_LiteralDefault(o, scope)
314   :default
315 end
eval_LiteralHash(o, scope) click to toggle source

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
eval_LiteralList(o, scope) click to toggle source

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
eval_LiteralUndef(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
317 def eval_LiteralUndef(o, scope)
318   nil
319 end
eval_LiteralValue(o, scope) click to toggle source

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
eval_MatchExpression(o, scope) click to toggle source

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
eval_NilClass(o, scope) click to toggle source

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
eval_Nop(o, scope) click to toggle source

Evaluates Nop to nil.

    # File lib/puppet/pops/evaluator/evaluator_impl.rb
293 def eval_Nop(o, scope)
294   nil
295 end
eval_NotExpression(o, scope) click to toggle source
    # 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
eval_Object(o, scope) click to toggle source

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
eval_OrExpression(o, scope) click to toggle source

@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
eval_ParenthesizedExpression(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
736 def eval_ParenthesizedExpression(o, scope)
737   evaluate(o.expr, scope)
738 end
eval_Program(o, scope) click to toggle source
    # 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
eval_QualifiedReference(o, scope) click to toggle source

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
eval_RelationshipExpression(o, scope) click to toggle source

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
eval_RenderExpression(o, scope) click to toggle source
    # 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
eval_RenderStringExpression(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
486 def eval_RenderStringExpression(o, scope)
487   scope["@epp"] << o.value.dup
488   nil
489 end
eval_ReservedWord(o, scope) click to toggle source

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
eval_ResourceDefaultsExpression(o, scope) click to toggle source

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
eval_ResourceExpression(o, scope) click to toggle source

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
eval_ResourceOverrideExpression(o, scope) click to toggle source
    # 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
eval_SelectorExpression(o, scope) click to toggle source

@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
eval_TextExpression(o, scope) click to toggle source

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
eval_UnaryMinusExpression(o, scope) click to toggle source
    # 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
eval_UnfoldExpression(o, scope) click to toggle source
    # 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
eval_UnlessExpression(o, scope) click to toggle source

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
eval_VariableExpression(o, scope) click to toggle source

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
is_match?(left, right, o, option_expr, scope) click to toggle source

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
lvalue_LiteralList(o, scope) click to toggle source

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
lvalue_Object(o, scope) click to toggle source

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
lvalue_VariableExpression(o, scope) click to toggle source
    # 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
string_Array(o, scope) click to toggle source
     # 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
string_Hash(o, scope) click to toggle source
     # 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
string_Object(o, scope) click to toggle source
     # File lib/puppet/pops/evaluator/evaluator_impl.rb
1123 def string_Object(o, scope)
1124   o.to_s
1125 end
string_PAnyType(o, scope) click to toggle source
     # File lib/puppet/pops/evaluator/evaluator_impl.rb
1147 def string_PAnyType(o, scope)
1148   o.to_s
1149 end
string_Regexp(o, scope) click to toggle source
     # 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
string_Symbol(o, scope) click to toggle source
     # 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

call_function_with_block(name, evaluated_arguments, o, scope) click to toggle source
     # 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
proc_from_lambda(lambda, scope) click to toggle source
     # 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
static_initialize() click to toggle source

@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
unfold(result, array, scope) click to toggle source

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
unwind_parentheses(o) click to toggle source
     # 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