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

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
139 def assign(target, value, o, scope)
140   @@assign_visitor.visit_this_3(self, target, value, o, scope)
141 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
 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_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
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
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
149 def lvalue(o, scope)
150   @@lvalue_visitor.visit_this_1(self, o, scope)
151 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
186 def match?(left, right)
187   @@compare_operator.match(left, right, nil)
188 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
159 def string(o, scope)
160   @@string_visitor.visit_this_1(self, o, scope)
161 end
type_calculator() click to toggle source

@api private

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

Protected Instance Methods

assign_Array(lvalues, values, o, scope) click to toggle source
    # 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
assign_Numeric(n, value, o, scope) click to toggle source
    # 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
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
232 def assign_Object(name, value, o, scope)
233   fail(Issues::ILLEGAL_ASSIGNMENT, o)
234 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
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
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
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
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
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
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
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
eval_AccessExpression(o, scope) click to toggle source

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

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
eval_AttributesOperation(o, scope) click to toggle source
    # 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
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
357 def eval_BinaryExpression o, scope
358   [ evaluate(o.left_expr, scope), evaluate(o.right_expr, scope) ]
359 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
678 def eval_BlockExpression o, scope
679   o.statements.reduce(nil) {|memo, s| evaluate(s, scope)}
680 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
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
eval_CallNamedFunctionExpression(o, scope) click to toggle source

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

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

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

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

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

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
eval_LiteralDefault(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
312 def eval_LiteralDefault(o, scope)
313   :default
314 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
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
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
664 def eval_LiteralList o, scope
665   unfold([], o.values, scope)
666 end
eval_LiteralUndef(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
316 def eval_LiteralUndef(o, scope)
317   nil
318 end
eval_LiteralValue(o, scope) click to toggle source

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
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
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
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
287 def eval_NilClass(o, scope)
288   nil
289 end
eval_Nop(o, scope) click to toggle source

Evaluates Nop to nil.

    # File lib/puppet/pops/evaluator/evaluator_impl.rb
292 def eval_Nop(o, scope)
293   nil
294 end
eval_NotExpression(o, scope) click to toggle source
    # 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
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
282 def eval_Object o, scope
283   o
284 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
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
eval_ParenthesizedExpression(o, scope) click to toggle source
    # File lib/puppet/pops/evaluator/evaluator_impl.rb
735 def eval_ParenthesizedExpression(o, scope)
736   evaluate(o.expr, scope)
737 end
eval_Program(o, scope) click to toggle source
    # 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
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
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
eval_RelationshipExpression(o, scope) click to toggle source

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

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

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

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

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
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
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
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
1286 def is_match?(left, right, o, option_expr, scope)
1287   @@compare_operator.match(left, right, scope)
1288 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
204 def lvalue_LiteralList(o, scope)
205   o.values.map {|x| lvalue(x, scope) }
206 end
lvalue_Object(o, scope) click to toggle source

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

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

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