module Puppet::Pops
module Evaluator
# AccessOperator handles operator []
# This operator is part of evaluation.
#
class AccessOperator
# Provides access to the Puppet 3.x runtime (scope, etc.)
# This separation has been made to make it easier to later migrate the evaluator to an improved runtime.
#
include Runtime3Support
attr_reader :semantic
# Initialize with AccessExpression to enable reporting issues
# @param access_expression [Model::AccessExpression] the semantic object being evaluated
# @return [void]
#
def initialize(access_expression)
@@access_visitor ||= Visitor.new(self, "access", 2, nil)
@semantic = access_expression
end
def access(o, scope, *keys)
@@access_visitor.visit_this_2(self, o, scope, keys)
end
protected
def access_Object(o, scope, keys)
type = Puppet::Pops::Types::TypeCalculator.infer_callable_methods_t(o)
if type.is_a?(Puppet::Pops::Types::TypeWithMembers)
access_func = type['[]']
return access_func.invoke(o, scope, keys) unless access_func.nil?
end
fail(Issues::OPERATOR_NOT_APPLICABLE, @semantic.left_expr, :operator=>'[]', :left_value => o)
end
def access_Binary(o, scope, keys)
Puppet::Pops::Types::PBinaryType::Binary.from_binary_string(access_String(o.binary_buffer, scope, keys))
end
def access_String(o, scope, keys)
keys.flatten!
result = case keys.size
when 0
fail(Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size})
when 1
# Note that Ruby 1.8.7 requires a length of 1 to produce a String
k1 = Utils.to_n(keys[0])
bad_string_access_key_type(o, 0, k1.nil? ? keys[0] : k1) unless k1.is_a?(Integer)
k2 = 1
k1 = k1 < 0 ? o.length + k1 : k1 # abs pos
# if k1 is outside, a length of 1 always produces an empty string
if k1 < 0
EMPTY_STRING
else
o[ k1, k2 ]
end
when 2
k1 = Utils.to_n(keys[0])
k2 = Utils.to_n(keys[1])
[k1, k2].each_with_index { |k,i| bad_string_access_key_type(o, i, k.nil? ? keys[i] : k) unless k.is_a?(Integer) }
k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end)
k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2 # abs length (negative k2 is length from pos to end count)
# if k1 is outside, adjust to first position, and adjust length
if k1 < 0
k2 = k2 + k1
k1 = 0
end
o[ k1, k2 ]
else
fail(Issues::BAD_STRING_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size})
end
# Specified as: an index outside of range, or empty result == empty string
(result.nil? || result.empty?) ? EMPTY_STRING : result
end
# Parameterizes a PRegexp Type with a pattern string or r ruby egexp
#
def access_PRegexpType(o, scope, keys)
keys.flatten!
unless keys.size == 1
blamed = keys.size == 0 ? @semantic : @semantic.keys[1]
fail(Issues::BAD_TYPE_SLICE_ARITY, blamed, :base_type => o, :min=>1, :actual => keys.size)
end
assert_keys(keys, o, 1, 1, String, Regexp)
Types::TypeFactory.regexp(*keys)
end
# Evaluates <ary>[] with 1 or 2 arguments. One argument is an index lookup, two arguments is a slice from/to.
#
def access_Array(o, scope, keys)
keys.flatten!
case keys.size
when 0
fail(Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size})
when 1
key = coerce_numeric(keys[0], @semantic.keys[0], scope)
unless key.is_a?(Integer)
bad_access_key_type(o, 0, key, Integer)
end
o[key]
when 2
# A slice [from, to] with support for -1 to mean start, or end respectively.
k1 = coerce_numeric(keys[0], @semantic.keys[0], scope)
k2 = coerce_numeric(keys[1], @semantic.keys[1], scope)
[k1, k2].each_with_index { |k,i| bad_access_key_type(o, i, k, Integer) unless k.is_a?(Integer) }
# Help confused Ruby do the right thing (it truncates to the right, but negative index + length can never overlap
# the available range.
k1 = k1 < 0 ? o.length + k1 : k1 # abs pos (negative is count from end)
k2 = k2 < 0 ? o.length - k1 + k2 + 1 : k2 # abs length (negative k2 is length from pos to end count)
# if k1 is outside, adjust to first position, and adjust length
if k1 < 0
k2 = k2 + k1
k1 = 0
end
# Help ruby always return empty array when asking for a sub array
result = o[ k1, k2 ]
result.nil? ? [] : result
else
fail(Issues::BAD_ARRAY_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size})
end
end
# Evaluates <hsh>[] with support for one or more arguments. If more than one argument is used, the result
# is an array with each lookup.
# @note
# Does not flatten its keys to enable looking up with a structure
#
def access_Hash(o, scope, keys)
# Look up key in hash, if key is nil, try alternate form (:undef) before giving up.
# This is done because the hash may have been produced by 3x logic and may thus contain :undef.
result = keys.collect do |k|
o.fetch(k) { |key| key.nil? ? o[:undef] : nil }
end
case result.size
when 0
fail(Issues::BAD_HASH_SLICE_ARITY, @semantic.left_expr, {:actual => keys.size})
when 1
result.pop
else
# remove nil elements and return
result.compact!
result
end
end
def access_PBooleanType(o, scope, keys)
keys.flatten!
assert_keys(keys, o, 1, 1, TrueClass, FalseClass)
Types::TypeFactory.boolean(keys[0])
end
def access_PEnumType(o, scope, keys)
keys.flatten!
last = keys.last
case_insensitive = false
if last == true || last == false
keys = keys[0...-1]
case_insensitive = last
end
assert_keys(keys, o, 1, Float::INFINITY, String)
Types::PEnumType.new(keys, case_insensitive)
end
def access_PVariantType(o, scope, keys)
keys.flatten!
assert_keys(keys, o, 1, Float::INFINITY, Types::PAnyType)
Types::TypeFactory.variant(*keys)
end
def access_PSemVerType(o, scope, keys)
keys.flatten!
assert_keys(keys, o, 1, Float::INFINITY, String, SemanticPuppet::VersionRange)
Types::TypeFactory.sem_ver(*keys)
end
def access_PTimestampType(o, scope, keys)
keys.flatten!
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min=>0, :max => 2, :actual => keys.size) if keys.size > 2
Types::TypeFactory.timestamp(*keys)
end
def access_PTimespanType(o, scope, keys)
keys.flatten!
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min=>0, :max => 2, :actual => keys.size) if keys.size > 2
Types::TypeFactory.timespan(*keys)
end
def access_PTupleType(o, scope, keys)
keys.flatten!
if Types::TypeFactory.is_range_parameter?(keys[-2]) && Types::TypeFactory.is_range_parameter?(keys[-1])
size_type = Types::TypeFactory.range(keys[-2], keys[-1])
keys = keys[0, keys.size - 2]
elsif Types::TypeFactory.is_range_parameter?(keys[-1])
size_type = Types::TypeFactory.range(keys[-1], :default)
keys = keys[0, keys.size - 1]
end
assert_keys(keys, o, 1, Float::INFINITY, Types::PAnyType)
Types::TypeFactory.tuple(keys, size_type)
end
def access_PCallableType(o, scope, keys)
if keys.size > 0 && keys[0].is_a?(Array)
unless keys.size == 2
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min=>2, :max => 2, :actual => keys.size)
end
unless keys[1].is_a?(Types::PAnyType)
bad_type_specialization_key_type(o, 1, k, Types::PAnyType)
end
end
Types::TypeFactory.callable(*keys)
end
def access_PStructType(o, scope, keys)
assert_keys(keys, o, 1, 1, Hash)
Types::TypeFactory.struct(keys[0])
end
def access_PStringType(o, scope, keys)
keys.flatten!
case keys.size
when 1
size_t = collection_size_t(0, keys[0])
when 2
size_t = collection_size_t(0, keys[0], keys[1])
else
fail(Issues::BAD_STRING_SLICE_ARITY, @semantic, {:actual => keys.size})
end
Types::TypeFactory.string(size_t)
end
# Asserts type of each key and calls fail with BAD_TYPE_SPECIFICATION
# @param keys [Array<Object>] the evaluated keys
# @param o [Object] evaluated LHS reported as :base_type
# @param min [Integer] the minimum number of keys (typically 1)
# @param max [Numeric] the maximum number of keys (use same as min, specific number, or Float::INFINITY)
# @param allowed_classes [Class] a variable number of classes that each key must be an instance of (any)
# @api private
#
def assert_keys(keys, o, min, max, *allowed_classes)
size = keys.size
unless size.between?(min, max || Float::INFINITY)
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, :base_type => o, :min=>1, :max => max, :actual => keys.size)
end
keys.each_with_index do |k, i|
unless allowed_classes.any? {|clazz| k.is_a?(clazz) }
bad_type_specialization_key_type(o, i, k, *allowed_classes)
end
end
end
def bad_access_key_type(lhs, key_index, actual, *expected_classes)
fail(Issues::BAD_SLICE_KEY_TYPE, @semantic.keys[key_index], {
:left_value => lhs,
:actual => bad_key_type_name(actual),
:expected_classes => expected_classes
})
end
def bad_string_access_key_type(lhs, key_index, actual)
fail(Issues::BAD_STRING_SLICE_KEY_TYPE, @semantic.keys[key_index], {
:left_value => lhs,
:actual_type => bad_key_type_name(actual),
})
end
def bad_key_type_name(actual)
case actual
when nil
'Undef'
when :default
'Default'
else
Types::TypeCalculator.generalize(Types::TypeCalculator.infer(actual)).to_s
end
end
def bad_type_specialization_key_type(type, key_index, actual, *expected_classes)
label_provider = Model::ModelLabelProvider.new()
expected = expected_classes.map {|c| label_provider.label(c) }.join(' or ')
fail(Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[key_index], {
:type => type,
:message => _("Cannot use %{key} where %{expected} is expected") % { key: bad_key_type_name(actual), expected: expected }
})
end
def access_PPatternType(o, scope, keys)
keys.flatten!
assert_keys(keys, o, 1, Float::INFINITY, String, Regexp, Types::PPatternType, Types::PRegexpType)
Types::TypeFactory.pattern(*keys)
end
def access_PURIType(o, scope, keys)
keys.flatten!
if keys.size == 1
param = keys[0]
unless Types::PURIType::TYPE_URI_PARAM_TYPE.instance?(param)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'URI-Type', :actual => param.class})
end
Types::PURIType.new(param)
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'URI-Type', :min => 1, :actual => keys.size})
end
end
def access_POptionalType(o, scope, keys)
keys.flatten!
if keys.size == 1
type = keys[0]
unless type.is_a?(Types::PAnyType)
if type.is_a?(String)
type = Types::TypeFactory.string(type)
else
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Optional-Type', :actual => type.class})
end
end
Types::POptionalType.new(type)
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Optional-Type', :min => 1, :actual => keys.size})
end
end
def access_PSensitiveType(o, scope, keys)
keys.flatten!
if keys.size == 1
type = keys[0]
unless type.is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Sensitive-Type', :actual => type.class})
end
Types::PSensitiveType.new(type)
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Sensitive-Type', :min => 1, :actual => keys.size})
end
end
def access_PObjectType(o, scope, keys)
keys.flatten!
if o.resolved? && !o.name.nil?
Types::PObjectTypeExtension.create(o, keys)
else
if keys.size == 1
Types::TypeFactory.object(keys[0])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Object-Type', :min => 1, :actual => keys.size})
end
end
end
def access_PTypeSetType(o, scope, keys)
keys.flatten!
if keys.size == 1
Types::TypeFactory.type_set(keys[0])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'TypeSet-Type', :min => 1, :actual => keys.size})
end
end
def access_PNotUndefType(o, scope, keys)
keys.flatten!
case keys.size
when 0
Types::TypeFactory.not_undef
when 1
type = keys[0]
case type
when String
type = Types::TypeFactory.string(type)
when Types::PAnyType
type = nil if type.class == Types::PAnyType
else
fail(Issues::BAD_NOT_UNDEF_SLICE_TYPE, @semantic.keys[0], {:base_type => 'NotUndef-Type', :actual => type.class})
end
Types::TypeFactory.not_undef(type)
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'NotUndef-Type', :min => 0, :max => 1, :actual => keys.size})
end
end
def access_PTypeType(o, scope, keys)
keys.flatten!
if keys.size == 1
unless keys[0].is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Type-Type', :actual => keys[0].class})
end
Types::PTypeType.new(keys[0])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Type-Type', :min => 1, :actual => keys.size})
end
end
def access_PInitType(o, scope, keys)
unless keys[0].is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Init-Type', :actual => keys[0].class})
end
Types::TypeFactory.init(*keys)
end
def access_PIterableType(o, scope, keys)
keys.flatten!
if keys.size == 1
unless keys[0].is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Iterable-Type', :actual => keys[0].class})
end
Types::PIterableType.new(keys[0])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Iterable-Type', :min => 1, :actual => keys.size})
end
end
def access_PIteratorType(o, scope, keys)
keys.flatten!
if keys.size == 1
unless keys[0].is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Iterator-Type', :actual => keys[0].class})
end
Types::PIteratorType.new(keys[0])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {:base_type => 'Iterator-Type', :min => 1, :actual => keys.size})
end
end
def access_PRuntimeType(o, scope, keys)
keys.flatten!
assert_keys(keys, o, 2, 2, String, String)
# create runtime type based on runtime and name of class, (not inference of key's type)
Types::TypeFactory.runtime(*keys)
end
def access_PIntegerType(o, scope, keys)
keys.flatten!
unless keys.size.between?(1, 2)
fail(Issues::BAD_INTEGER_SLICE_ARITY, @semantic, {:actual => keys.size})
end
keys.each_with_index do |x, index|
fail(Issues::BAD_INTEGER_SLICE_TYPE, @semantic.keys[index],
{:actual => x.class}) unless (x.is_a?(Integer) || x == :default)
end
Types::PIntegerType.new(*keys)
end
def access_PFloatType(o, scope, keys)
keys.flatten!
unless keys.size.between?(1, 2)
fail(Issues::BAD_FLOAT_SLICE_ARITY, @semantic, {:actual => keys.size})
end
keys.each_with_index do |x, index|
fail(Issues::BAD_FLOAT_SLICE_TYPE, @semantic.keys[index],
{:actual => x.class}) unless (x.is_a?(Float) || x.is_a?(Integer) || x == :default)
end
from, to = keys
from = from == :default || from.nil? ? nil : Float(from)
to = to == :default || to.nil? ? nil : Float(to)
Types::PFloatType.new(from, to)
end
# A Hash can create a new Hash type, one arg sets value type, two args sets key and value type in new type.
# With 3 or 4 arguments, these are used to create a size constraint.
# It is not possible to create a collection of Hash types directly.
#
def access_PHashType(o, scope, keys)
keys.flatten!
if keys.size == 2 && keys[0].is_a?(Integer) && keys[1].is_a?(Integer)
return Types::PHashType.new(nil, nil, Types::PIntegerType.new(*keys))
end
keys[0,2].each_with_index do |k, index|
unless k.is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[index], {:base_type => 'Hash-Type', :actual => k.class})
end
end
case keys.size
when 2
size_t = nil
when 3
size_t = keys[2]
size_t = Types::PIntegerType.new(size_t) unless size_t.is_a?(Types::PIntegerType)
when 4
size_t = collection_size_t(2, keys[2], keys[3])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic, {
:base_type => 'Hash-Type', :min => 2, :max => 4, :actual => keys.size
})
end
Types::PHashType.new(keys[0], keys[1], size_t)
end
# CollectionType is parameterized with a range
def access_PCollectionType(o, scope, keys)
keys.flatten!
case keys.size
when 1
size_t = collection_size_t(0, keys[0])
when 2
size_t = collection_size_t(0, keys[0], keys[1])
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,
{:base_type => 'Collection-Type', :min => 1, :max => 2, :actual => keys.size})
end
Types::PCollectionType.new(size_t)
end
# An Array can create a new Array type. It is not possible to create a collection of Array types.
#
def access_PArrayType(o, scope, keys)
keys.flatten!
case keys.size
when 1
unless keys[0].is_a?(Types::PAnyType)
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class})
end
type = keys[0]
size_t = nil
when 2
if keys[0].is_a?(Types::PAnyType)
size_t = collection_size_t(1, keys[1])
type = keys[0]
else
size_t = collection_size_t(0, keys[0], keys[1])
type = nil
end
when 3
if keys[0].is_a?(Types::PAnyType)
size_t = collection_size_t(1, keys[1], keys[2])
type = keys[0]
else
fail(Issues::BAD_TYPE_SLICE_TYPE, @semantic.keys[0], {:base_type => 'Array-Type', :actual => keys[0].class})
end
else
fail(Issues::BAD_TYPE_SLICE_ARITY, @semantic,
{:base_type => 'Array-Type', :min => 1, :max => 3, :actual => keys.size})
end
Types::PArrayType.new(type, size_t)
end
# Produces an PIntegerType (range) given one or two keys.
def collection_size_t(start_index, *keys)
if keys.size == 1 && keys[0].is_a?(Types::PIntegerType)
keys[0]
else
keys.each_with_index do |x, index|
fail(Issues::BAD_COLLECTION_SLICE_TYPE, @semantic.keys[start_index + index],
{:actual => x.class}) unless (x.is_a?(Integer) || x == :default)
end
Types::PIntegerType.new(*keys)
end
end
# A Puppet::Resource represents either just a type (no title), or is a fully qualified type/title.
#
def access_Resource(o, scope, keys)
# To access a Puppet::Resource as if it was a PResourceType, simply infer it, and take the type of
# the parameterized meta type (i.e. Type[Resource[the_resource_type, the_resource_title]])
t = Types::TypeCalculator.infer(o).type
# must map "undefined title" from resource to nil
t.title = nil if t.title == EMPTY_STRING
access(t, scope, *keys)
end
# If a type reference is encountered here, it's an error
def access_PTypeReferenceType(o, scope, keys)
fail(Issues::UNKNOWN_RESOURCE_TYPE, @semantic, {:type_name => o.type_string })
end
# A Resource can create a new more specific Resource type, and/or an array of resource types
# If the given type has title set, it can not be specified further.
# @example
# Resource[File] # => File
# Resource[File, 'foo'] # => File[foo]
# Resource[File. 'foo', 'bar'] # => [File[foo], File[bar]]
# File['foo', 'bar'] # => [File[foo], File[bar]]
# File['foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource
# Resource[File]['foo', 'bar'] # => [File[Foo], File[bar]]
# Resource[File, 'foo', 'bar'] # => [File[foo], File[bar]]
# Resource[File, 'foo']['bar'] # => Value of the 'bar' parameter in the File['foo'] resource
#
def access_PResourceType(o, scope, keys)
blamed = keys.size == 0 ? @semantic : @semantic.keys[0]
if keys.size == 0
fail(Issues::BAD_TYPE_SLICE_ARITY, blamed,
:base_type => o.to_s, :min => 1, :max => -1, :actual => 0)
end
# Must know which concrete resource type to operate on in all cases.
# It is not allowed to specify the type in an array arg - e.g. Resource[[File, 'foo']]
# type_name is LHS type_name if set, else the first given arg
type_name = o.type_name || Types::TypeFormatter.singleton.capitalize_segments(keys.shift)
type_name = case type_name
when Types::PResourceType
type_name.type_name
when String
type_name
else
# blame given left expression if it defined the type, else the first given key expression
blame = o.type_name.nil? ? @semantic.keys[0] : @semantic.left_expr
fail(Issues::ILLEGAL_RESOURCE_SPECIALIZATION, blame, {:actual => bad_key_type_name(type_name)})
end
# type name must conform
if type_name !~ Patterns::CLASSREF_EXT
fail(Issues::ILLEGAL_CLASSREF, blamed, {:name=>type_name})
end
# The result is an array if multiple titles are given, or if titles are specified with an array
# (possibly multiple arrays, and nested arrays).
result_type_array = keys.size > 1 || keys[0].is_a?(Array)
keys_orig_size = keys.size
keys.flatten!
keys.compact!
# If given keys that were just a mix of empty/nil with empty array as a result.
# As opposed to calling the function the wrong way (without any arguments), (configurable issue),
# Return an empty array
#
if keys.empty? && keys_orig_size > 0
optionally_fail(Issues::EMPTY_RESOURCE_SPECIALIZATION, blamed)
return result_type_array ? [] : nil
end
if !o.title.nil?
# lookup resource and return one or more parameter values
resource = find_resource(scope, o.type_name, o.title)
unless resource
fail(Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => o.type_name, :title => o.title})
end
result = keys.map do |k|
unless is_parameter_of_resource?(scope, resource, k)
fail(Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,
{:type_name => o.type_name, :title => o.title, :param_name=>k})
end
get_resource_parameter_value(scope, resource, k)
end
return result_type_array ? result : result.pop
end
keys = [:no_title] if keys.size < 1 # if there was only a type_name and it was consumed
result = keys.each_with_index.map do |t, i|
unless t.is_a?(String) || t == :no_title
index = keys_orig_size != keys.size ? i+1 : i
fail(Issues::BAD_TYPE_SPECIALIZATION, @semantic.keys[index], {
:type => o,
:message => "Cannot use #{bad_key_type_name(t)} where a resource title String is expected"
})
end
Types::PResourceType.new(type_name, t == :no_title ? nil : t)
end
# returns single type if request was for a single entity, else an array of types (possibly empty)
return result_type_array ? result : result.pop
end
NS = '::'.freeze
def access_PClassType(o, scope, keys)
blamed = keys.size == 0 ? @semantic : @semantic.keys[0]
keys_orig_size = keys.size
if keys_orig_size == 0
fail(Issues::BAD_TYPE_SLICE_ARITY, blamed,
:base_type => o.to_s, :min => 1, :max => -1, :actual => 0)
end
# The result is an array if multiple classnames are given, or if classnames are specified with an array
# (possibly multiple arrays, and nested arrays).
result_type_array = keys.size > 1 || keys[0].is_a?(Array)
keys.flatten!
keys.compact!
# If given keys that were just a mix of empty/nil with empty array as a result.
# As opposed to calling the function the wrong way (without any arguments), (configurable issue),
# Return an empty array
#
if keys.empty? && keys_orig_size > 0
optionally_fail(Issues::EMPTY_RESOURCE_SPECIALIZATION, blamed)
return result_type_array ? [] : nil
end
if o.class_name.nil?
result = keys.each_with_index.map do |c, i|
fail(Issues::ILLEGAL_HOSTCLASS_NAME, @semantic.keys[i], {:name => c}) unless c.is_a?(String)
name = c.downcase
# Remove leading '::' since all references are global, and 3x runtime does the wrong thing
name = name[2..-1] if name[0,2] == NS
fail(Issues::ILLEGAL_NAME, @semantic.keys[i], {:name=>c}) unless name =~ Patterns::NAME
Types::PClassType.new(name)
end
else
# lookup class resource and return one or more parameter values
resource = find_resource(scope, 'class', o.class_name)
if resource
result = keys.map do |k|
if is_parameter_of_resource?(scope, resource, k)
get_resource_parameter_value(scope, resource, k)
else
fail(Issues::UNKNOWN_RESOURCE_PARAMETER, @semantic,
{:type_name => 'Class', :title => o.class_name, :param_name=>k})
end
end
else
fail(Issues::UNKNOWN_RESOURCE, @semantic, {:type_name => 'Class', :title => o.class_name})
end
end
# returns single type as type, else an array of types
return result_type_array ? result : result.pop
end
end
end
end
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]