<commit_before>#
# Copyright 2010-2012, Mathematics and Mechanics faculty
#                   of Saint-Petersburg State University. All rights reserved.
# Copyright 2010-2012, Lanit-Tercom Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

#
# Object-oriented make.
#
#   Date: Dec 20, 2010
# Author: <NAME>
#

ifndef __core_object_mk
__core_object_mk := 1

##
# General overview.
#
#   This file provides an object-oriented superstructure for Make.
#
##
# Terminology.
#
#   Class
#     - a prototype of each instance (object), which defines members (fields
#       and methods) available for instances.
#
#   Object
#     - an instance of a class. Each object has its own set of values of
#       fields associated with the class of the object.
#
#   Object reference
#     - a value used to refer an object.
#
#   Method
#     - a function associated with a class, defines runtime behavior of an
#       instance of the class. Method is evaluated in a special context with
#      'this' pointing the current object, thus it has as access to other
#       members of the object.
#
#   Field
#     - a value associated with each object of a certain class, provides a
#       runtime state of the instance.
#
#   Property
#     - a special kind of member representing a runtime state of the instance
#       through getter and setter methods.
#
#   Reference field/property
#     - a member, that holds references to other objects.
#
##
# Class definition.
#
#   Each variable with name starting with 'class-' is considered as a new class
#   definition. The name of the class is everything after the dash.
#
#   	define class-clazz
#   		...
#   	endef
#
#   In the example above, a variable 'class-clazz' defines a new class
#   named 'clazz'. The name of the class is primarily used for object
#   instantiation and for class introspection.
#
#   So what can class actually define?
#
#
# Field definition.
#
#   A field is defined using special 'field' builtin function.
#
#   	$(field name[...][:{*|type}][,initializer...])
#
#   The first argument specifies the name of the field and whether the field
#   is a reference field. In the latter case one can also specify a type of
#   objects that can be held in this field.
#
#   If an initializer is specified in the second argument, it will be called
#   every time when a new object is created, and its return value will be
#   assigned to the field. Otherwise, the field gets empty initial value
#   by default.
#
#   	# A simple field for storing an arbitrary data.
#   	$(field a_field)
#
#   	# A field with an initializer. Note the usage of 'this'.
#   	$(field a_field_with_initializer,
#   		$(info initializing a field of '$(this)')
#   		initial value)
#
#   	# A field that holds references to any objects.
#   	$(field a_reference_field : *)
#
#   	# A reference fields that enforces the type of referenced objects to
#   	# be 'some_class'.
#   	$(field a_reference_field : some_class)
#
#
# Method definition.
#
#   As fields methods are defined using special builtin function, which has the
#   following syntax.
#
#   	$(method name[,implementation...])
#
#   The first argument is the name of the method.
#
#   If the second argument is not specified, then the method is considered
#   abstract. Abstract method are assumed to be implemented in some subclass.
#   To prevent invocation of an abstract method a special stub ($(error ...))
#   is assigned as its body.
#
#   	# A simple method which always returns 'result' string.
#   	$(method a_method,result)
#
#   	# A method with no implementation (abstract method).
#   	$(method an_abstract_method)
#
#   	# As a usual function method body have an access to all passed
#   	# arguments ($1 ..). A reference to the current object is available
#   	# in 'this' variable.
#   	$(method say_hello,
#   		$(info Hello from '$(this)', the first argument is '$1'))
#
#
# Property and its accessors definition.
#
#   The definition of a property is very similar to the fields except that
#   there is no initializer function.
#
#   	$(property name[...][:{*|type}])
#
#   As for fields the property can be defined to hold object references.
#
#
# Class inheritance.
#
#   Class can extend other classes, inheriting their members. Multiple
#   inheritance is supported, tracking an inheritance order and avoiding
#   MI issues (such as diamond problem) is the responsibility of user.
#
#   	$(super class_name[,arguments...])
#
#   The first argument is the name of a class to inherit. The specified class
#   must have already been defined. Arguments (if any) are passed to a
#   constructor of the super class.
#
#
# Constructor statements.
#
#   There is no dedicated structure for a constructor. Instead, the whole
#   class body is considered as a constructor. That is, you may include an
#   arbitrary code outside the builtins mentioned above.
#
#   Instantiation also involves field initialization and calling super
#   constructors, in the order they have been defined the class body.
#
#   	define class-zuper
#   		$(info Super constructor with argument '$1')
#
#   		$(field delegate : zuper)
#
#   	endef
#
#   	define class-clazz
#   		$(info This is executed first)
#
#   		$(super zuper,
#   			Hello from clazz)
#
#   		$(field foo,
#   			$(info Initializing field 'foo')
#   			initial value)
#
#   		$(method bar,
#   			$(info Calling method 'bar')
#   			hello world)
#
#   		$(info End of constructor)
#   	endef
#
#   In this example the following test will be printed during constructing
#   a new object:
#
#   	This is executed first
#   	Super constructor with argument 'Hello from clazz'
#   	Initializing field 'foo'
#   	End of constructor
#
##
# Runtime concepts.
#
#   After a class has been defined, one can create objects of such class,
#   invoke its methods and get/set field values.
#
#
# Object instantiation.
#
#   A new object is created using 'new' builtin function.
#
#   	$(new class_name[,arguments...])
#
#   The first argument specifies the name of a class being instantiated.
#
#   Note that this should be the name of the class itself, not the name of a
#   variable used to define a class. The name of the class defined in
#   variable 'class-clazz' is 'clazz', and a proper call to 'new' is the
#   following:
#
#   	$(new clazz)
#
#   Arguments specified after a class name are passed to the constructor.
#
#  'new' allocates a unique object identifier which acts as the object
#   reference and is accessible within the constructor through 'this' variable.
#   This identifier is then returned as the result value of 'new'.
#
#
# Accessing object members.
#
#   Member access includes method invocation and getting/setting field values.
#
#   	# Calls the method with given arguments and returns the result.
#   	$(invoke method[,arguments...])
#
#   	$(get-field  field)       # Retrieves the current field value.
#
#   	$(set-field  field,value) # Assigns a new value to the field.
#   	$(set-field+ field,value) # Appends a given value to the field.
#   	$(set-field- field,value) # Removes any occurrence of a value.
#
#   	$(get  property)          # Invokes getter of the specified property.
#
#   	$(set  property,value)    # Sets a new value by calling setter.
#   	$(set+ property,value)    # Append (multi-value properties only).
#   	$(set- property,value)    # Remove (multi-value properties only).
#
#   Related builtin functions allows one to specify a target member in three
#   different forms:
#
#   member
#     - Simple form for referring members of 'this' object.
#
#   <object>.member
#     - Accesses a member of the specified object.
#
#   	$(invoke $(new clazz).some_method)
#
#   	# A more real world example.
#   	$(foreach object,$(new clazz),
#   		$(invoke $(object).some_method))
#
#   <variable>->member
#     - The same as the previous one, but the object reference is computed
#       by expanding the specified variable. In fact, this form is a shorthand
#       for '$(<variable>).member'. Thus, the last of the examples above
#       could rewritten as follows:
#
#   	$(foreach object,$(new clazz),
#   		$(invoke object->some_method))
#
#   It is important to note that the latter two forms must include '.' (period)
#   and '->' (dash with closing angle bracket) literally. Think of them as a
#   kind of separator, such as ',' (comma) for arguments.
#
#
# A few words about object references.
#
#   A reference to an object is a string that is used to identify the object.
#   For example, 'new' returns a reference, 'this' contains a reference (in
#   certain contexts, of course: within constructors and methods).
#
#   There is, however, a special feature of references: prepending an
#   arbitrary prefix does not corrupt the reference. Moreover, one can extract
#   a prefix (if any) efficiently using $(basename <ref>) function.
#   In turn a normalized reference is available through $(suffix <ref>).
#
#   So, returning to an example of method invocation, one could modify it as
#   follows without changing the meaning:
#
#   	$(foreach object,foo/bar.baz!$(new clazz),
#   		$(invoke $(object).some_method))
#
##
# Internal representation.
#
#  TODO outdated. -- Eldar
#
#
#   During class definition or a new object instantiation a number of ancillary
#   variables are created.
#
#   Considering the code below as an example, we'll examine what variables
#   are created and when.
#
#   	define class-zuper
#   		$(info Super constructor with argument '$1')
#
#   		$(field delegate : zuper)
#
#   	endef
#
#   After defining class 'zuper' variable 'class-zuper' becomes a part
#   of its constructor. Also an initializer function for 'delegate' field, a
#   type checking setter method for it and three special variables are defined.
#
#   	# class-zuper = \
#   	# 	$(info Super constructor with argument '$1') \
#   	# 	$(eval $(this).delegate := $(value zuper.delegate))
#
#   	# zuper.super :=
#   	# zuper.fields := delegate.zuper
#   	# zuper.methods := set.delegate
#
#   	# zuper.field.delegate =
#   	# zuper.method.set.delegate = \
#   	# 	$(foreach 2,zuper,$(__field_setter_type_check))
#
#   Lets extend our class as follows:
#
#   	define class-clazz
#   		$(info This is executed first)
#
#   		$(super zuper,
#   			Hello from clazz)
#
#   		$(field foo,
#   			$(info Initializing field 'foo')
#   			initial value)
#
#   		$(method bar,
#   			$(info Calling method 'bar')
#   			hello world)
#
#   		$(info End of constructor)
#   	endef
#
#   Notice additional two variables for field 'foo' and method 'bar':
#
#   	# class-clazz = \
#   	# 	$(info This is executed first) \
#   	# 	$(call class-zuper,Hello from clazz) \
#   	# 	$(eval $(this).foo := $(value clazz.foo)) \
#   	# 	$(info End of constructor)
#
#   	# clazz.super := zuper
#   	# clazz.fields := delegate.zuper foo
#   	# clazz.methods := set.delegate bar
#
#   	# clazz.field.foo = \
#   	# 	$(info Initializing field 'foo')initial value
#   	# clazz.method.bar = \
#   	# 	$(foreach this,$(__this),$(info Calling method 'bar')hello world)
#   	# clazz.method.set.delegate = \
#   	# 	$(foreach 2,zuper,$(__field_setter_type_check))
#
#   Finally, we will instantiate 'clazz':
#
#   	inztance := $(call new,clazz)
#
#   And inspect newly defined variables:
#
#   	# inztance := .obj1
#
#   	# .obj1 := clazz
#   	# .obj1.delegate :=
#   	# .obj1.foo := initial value
#
#

include mk/core/common.mk
include mk/core/string.mk
include mk/core/define.mk

include mk/util/var/assign.mk

#
# $(new class,args...)
#
define builtin_func-new
	$$(foreach __class__,$$(call __class_resolve,$1),
		$(def-ifdef OBJ_DEBUG,
			$$(foreach __obj_debug_args_nr,
				$(words $(nofirstword $(builtin_args_list))),
				$$(call __new,$(builtin_nofirstarg))
			),
			$(if $(multiword $(builtin_args_list)),
				$$(call __new,$(builtin_nofirstarg)),
				$$(call __new)
			)
		)
	)
endef
$(call def,builtin_func-new)

# Creates a new instance of the specified class.
# Only a single argument is passed to the constructor.
#   1. Class name.
#   2. An (optional) argument to pass to the constructor.
# Return:
#   Newly created object instance.
define new
	$(new $1,$(value 2))
endef

# Params:
#   1.. Args...
# Context:
#   '__class__'
# Return:
#   New object identifier.
define __new
	# It is mandatory for object references to start with a period.
	$(foreach this,.obj$(words $(__object_instance_cnt) x),
		$(def-ifdef OBJ_DEBUG,
			$(info $(this):	new    $(__class__): $(__obj_debug_args)))
		${eval \
			__object_instance_cnt += $(this:.obj%=%)
			$(\n)
			$(this) := $(__class__)
			$(\n)
			$$(and $(value class-$(__class__)),)
		}
		$(this)
	)
endef
__object_instance_cnt :=# Initially empty.

# Return:
#   Human-readable list of args from 1 up to '__obj_debug_args_nr',
#   or 'no args' if '__obj_debug_args_nr' is 0.
# Note:
#   Do not 'call'.
define __obj_debug_args
	$(or \
		$(foreach a,
			$(wordlist 1,$(__obj_debug_args_nr),1 2 3 4 5 6 7 8 9),
			$(\t)$$$a='$($a)'
		),
#		<no args>
	)
endef

# Params:
#   1. Class name.
define __class_resolve
	$(if $(findstring undefined,$(flavor class-$1)),
		$(call $(if $(value __def_var),builtin_)error,
				Class '$1' not found),
		$1
	)
endef

#
# Runtime member access: 'invoke', 'get' and 'set'.
#

# Params:
#   1. Member name to parse in form 'member', 'ref->member' or 'obj.member'.
#      It is assumed that there is no any commas outside parens.
#   2. Continuation with the following args:
#       1. Empty in case of referencing a member of 'this',
#          the target object otherwise.
#       2. The member being referenced.
#       3. Optional argument.
#   3. (optional) Argument to pass to the continuaion.
# Return:
#   Result of call to continuation in case of a valid reference,
#   otherwise it aborts using 'builtin_error'.
define __object_member_parse
	$(or \
		$(__object_member_try_parse),
		$(call builtin_error,
				Invalid first argument to '$(builtin_name)' function: '$1')
	)
endef

# Params:
#   See '__object_member_parse'.
# Return:
#   Result of call to continuation in case of a valid reference,
#   empty otherwise.
define __object_member_try_parse
	$(expand $$(call \
		$(lambda $(or \
			$(and \
				$(eq .,$6),$(nolastword $4),$(trim $5),
				# Invocation on some object.
				# Escaped reference is in $4. Escaped member name is in $5.
				$(call $1,
					# Optimize out explicit dereference of 'this'.
					$(if $(not $(eq this ->,$(strip $4))),
						$(call $(if $(eq ->,$(lastword $4)),$(lambda $$($1)),id),
							$(call $3,$(nolastword $4))
						)
					),
					$(call $3,$5),$2)
			),
			$(and \
				$(eq .,$5),$(trim $4),
				# Target object is implicitly 'this'.
				# Escaped member is in $4.
				$(call $1,,$(call $3,$4),$2)
			)
		)),

		# 1 and 2: The continuation with its argument.
		$$2,$$(value 3),

		# 3: Unescape function which restores '.' and '->' back.
		$(lambda \
			$(trim $(subst $(\s).$(\comma),.,$(subst $(\s)->$(\comma),->,$1)))
		),

		# 4 .. 5: Escaped member name with '.' and '->' repalaced by commas.
		$(subst .,$(\s).$(\comma),$(subst ->,$(\s)->$(\comma),
			$(subst $(\comma),$$(\comma),$(subst $$,$$$$,$1))
		)),

		# 5 or 6: End of args marker.
		.,
	))
endef

# Params:
#   1. The target object or empty in case of referencing a member of 'this'.
#   2. The code to wrap.
# Return:
#   The code (wrapped if needed).
define __object_member_access_wrap
	$$(foreach __this,$(if $1,
			$$(call __object_check,$1
					$(def-ifdef OBJ_DEBUG,$(\comma)$(subst $$,$$$$,$1))),
			$$(this)),
		$2
	)
endef

$(def_all)

# Params:
#   1. Object reference.
#   2. Original code (if OBJ_DEBUG).
# Return:
#   The trimmed argument if it is a single word, fails with an error otherwise.
define __object_check
	$(or \
		$(suffix $(singleword $1)),
		$(error \
				Invalid object reference: '$1'$(def-ifdef OBJ_DEBUG, ($2)))
	)
endef

#
# Method invocation.
#

#
# $(invoke method,args...)
# $(invoke obj.method,args...)
# $(invoke ref->method,args...)
#
define builtin_func-invoke
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced method.
		# 3. Args...
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$(if $(nofirstword $(builtin_args_list)),
					$$(foreach __args_nr,
						$(words $(builtin_args_list)),
						$$(call __method_invoke_debug,$3,$2)
					),
					$$(foreach __args_nr,1,
						$$(call __method_invoke_debug,$2)
					)
				),
				$$(call $$($$(__this)).$2,$3)
			)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1..N: Args...
#   N+1:  Method
# Context:
#   '__class__'
ifdef OBJ_DEBUG
define __method_invoke_debug
	$(foreach __obj_debug_args_nr,$(word $(__args_nr),0 1 2 3 4 5 6 7 8 9),
		$(info \
				$(__this):	invoke $($(__this)).$($(__args_nr)): \
				$(__obj_debug_args))
	)
	$(foreach 0,$(or $(call var_recursive,$($(__this)).$($(__args_nr))),
			$(error \
					No method '$($(__args_nr))', \
					invoked on object '$(__this)' of type '$($(__this))')),
		$(expand $(value $0))
	)
endef
endif

$(def_all)

#
# Property access.
#

#
# $(get [{obj.|ref->}]property)
#
define builtin_func-get
	$(call builtin_check_max_arity,1)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Property.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __property_get_debug,$2),
				$$(call $$($$(__this)).$2.getter)
			)
		)
	))
endef

# Params:
#   1. Property name.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __property_get_debug
	$(info $(__this):	get    $($(__this)).$1)

	$(call $(or $(call var_recursive,$($(__this)).$1.getter),
			$(error \
					No property '$1', performing 'get' \
					on object '$(__this)' of type '$($(__this))'))
	)
endef
endif

#
# $(set  [{obj.|ref->}]property,value)
# $(set+ [{obj.|ref->}]property,value)
# $(set- [{obj.|ref->}]property,value)
#
builtin_func-set  = $(__builtin_func_set)
builtin_func-set+ = $(__builtin_func_set)
builtin_func-set- = $(__builtin_func_set)

# Expanded from  'setx' builtin context. It will generate a call
# to the corresponding property setter, 'x' is taken from builtin name.
define __builtin_func_set
	$(call builtin_check_min_arity,2)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Property.
		# 3. Value being set.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __property_set_debug,$2,$(builtin_name:set%=%),$3),
				$$(call $$($$(__this)).$2.setter$(builtin_name:set%=%),$3)
			)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1. Property name.
#   2. '+', '-', or empty.
#   3. Value.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __property_set_debug
	$(info $(__this):	set$(or $2,$(\s))   $($(__this)).$1: '$3')

	$(call $(or $(call var_recursive,$($(__this)).$1.setter$2),
			$(error \
					No property '$1', performing 'set$2' \
					on object '$(__this)' of type '$($(__this))')),
		$3
	)
endef
endif

#
# Field operations.
#

# Params:
#   1. Field name.
# Context:
#   '__this'
define __field_check
	$(if $(findstring simple,$(flavor $(__this).$1)),
		$1,
		$(error \
				No field '$1', \
				referenced on object '$(__this)' of type '$($(__this))')
	)
endef

#
# $(get-field field)
# $(get-field obj.field)
# $(get-field ref->field)
#
define builtin_func-get-field
	$(call builtin_check_max_arity,1)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced field.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __field_get_debug,$2),
				$$($$(__this).$$(call __field_check,$2))
			)
		)
	))
endef


# Params:
#   1. Field name.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __field_get_debug
	$(info $(__this):	get-field $($(__this)).$1= \
		'$($(__this).$(call __field_check,$1))')
	$($(__this).$(call __field_check,$1))
endef
endif

#
# $(set-field  [{obj.|ref->}]field,value)
# $(set-field+ [{obj.|ref->}]field,value)
# $(set-field- [{obj.|ref->}]field,value)
#
builtin_func-set-field  = $(__builtin_func_set_field)
builtin_func-set-field+ = $(__builtin_func_set_field)
builtin_func-set-field- = $(__builtin_func_set_field)

# Expanded from  'set-fieldx' builtin context. It will generate a call
# to '__field_setx', where 'x' is taken from builtin name.
define __builtin_func_set_field
	$(call builtin_check_min_arity,2)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced field.
		# 3. Value.
		$(call __object_member_access_wrap,$1,
			$$(call __field_set$(builtin_name:set-field%=%),$2,$3)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) := $$2
	}
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set+
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field+ $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) += $$2
	}
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set-
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field- $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) := \
			$(if $(findstring %,$2),
				$$(subst %%,%,$$(filter-out $$(subst %,%%,$2),
					$$(subst %,%%,$$($(__this).$1)))),
				$$(filter-out $$2,$$($(__this).$1))
			)
	}
endef

$(def_all)

#
# Object/class structure introspection.
#

# Tells whether the argument is a valid object reference.
#   1. Reference to check.
# Return:
#   The argument if true, empty otherwise.
define is_object
	$(if $(value class-$(value $(suffix $1))),$1)
endef
builtin_func-is-object = \
	$(foreach builtin_name,is_object,$(builtin_to_function_inline))

# Tells whether a given class has been defined.
#   1. Class name.
# Return:
#   The argument if true, empty otherwise.
define class_exists
	$(if $(value class-$1),$1)
endef
builtin_func-class-exists = \
	$(foreach builtin_name,class_exists,$(builtin_to_function_inline))

# Gets the class of the specified object.
#   1. Object to inspect.
# Return:
#   The class if the argument is a valid object, empty otherwise.
define class
	$(foreach c,$(singleword $(value $(suffix $1))),
			$(if $(value class-$c),$c))
endef
$(call def,class)
builtin_func-class = $(builtin_to_function_inline)

# Tells whether a given object is an instance of the specified class.
#   1. Reference to check.
#   2. Class name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define instance_of
	#$(foreach c,$(class $1),
	#		$(if $(filter $2,$c $($c.super)),$1))

	# Optimized.
	$(foreach c,$(singleword $(value $(suffix $1))),
			$(and $(value class-$c),$(filter $2,$c $($c.supers)),$1))
endef
builtin_func-instance-of = \
	$(foreach builtin_name,instance_of,$(builtin_to_function_inline))

# Get all instances of all classes
define get-instances
	$(__object_instance_cnt:%=.obj%)
endef


# Get all instances of speceifed classes
#   1. Class name (optional).
define get-instances-of
	$(if $(call value,1),
		$(info searching istances of $1)
		$(foreach i,$(call get-instances),
			$(instance-of $i,$1)
		)
		,
		$(call get-instances)
	)
endef

# Tells whether a given object has the specified field.
#   1. Reference to check.
#   2. Field name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_field
	$(if $(call class_has_field,$(class $1),$2),$1)
endef
builtin_func-has-field = \
	$(foreach builtin_name,has_field,$(builtin_to_function_inline))

# Tells whether a class has the given field.
#   1. Name of the class to check.
#   2. Field name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_field
	$(if $(filter $2 $(addsuffix [],$2),$(basename $(value $1.fields))),$1)
endef
builtin_func-class-has-field = \
	$(foreach builtin_name,class_has_field,$(builtin_to_function_inline))

# Tells whether a given object has the specified property.
#   1. Reference to check.
#   2. Property name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_property
	$(if $(call class_has_property,$(class $1),$2),$1)
endef
builtin_func-has-property = \
	$(foreach builtin_name,has_property,$(builtin_to_function_inline))

# Tells whether a class has the given property.
#   1. Name of the class to check.
#   2. Property name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_property
	$(if $(filter $2 $(addsuffix [],$2),$(basename $(value $1.properties))),$1)
endef
builtin_func-class-has-property = \
	$(foreach builtin_name,class_has_property,$(builtin_to_function_inline))

# Tells whether a given object has the specified method.
#   1. Reference to check.
#   2. Method name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_method
	$(if $(call class_has_method,$(class $1),$2),$1)
endef
builtin_func-has-method = \
	$(foreach builtin_name,has_method,$(builtin_to_function_inline))

# Tells whether a class has the given method.
#   1. Name of the class to check.
#   2. Method name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_method
	$(if $(filter $2,$(value $1.methods)),$1)
endef
builtin_func-class-has-method = \
	$(foreach builtin_name,class_has_method,$(builtin_to_function_inline))

$(def_all)

#
# Class definition.
#

define __class_variable_value_provider
	$(assert $(filter class-%,$1))

	$(if $(not $(call __class_name_check,$(1:class-%=%))),
		$(error \
				Illegal class name: '$(1:class-%=%)')
	)

	$(if $(call def_is_done,$1),
		# Workaround for repeated def in case when the class has already been
		# defined from 'super' builtin of another one.
		$(value $1),
		$$(__class__ \
			$(value $1)$(\n)# \n protects us from a comment on the last line.
		)
	)
endef
$(call def,__class_variable_value_provider)
$(call def_register_value_provider,class-%,__class_variable_value_provider)

#
# $(__class__ fields/methods/supers...)
#
define builtin_macro-__class__
	$(call builtin_check_max_arity,1)

	$(foreach __class__,$(__def_var:class-%=%),
		$(call var_assign_simple,$(__class__),)

		$(expand $1)

		# Define four special variables needed for introspection.
		# TODO Backward-compatibility, should be replaced
		# by '__class_attr_xxx' API.
		$(call var_assign_simple,$(__class__).supers,
			$(notdir $(call __class_attr_query,super,%)))
		$(call var_assign_simple,$(__class__).methods,
			$(notdir $(call __class_attr_query,method%,%)))
		$(call var_assign_simple,$(__class__).properties,
			$(notdir $(call __class_attr_query,property,%)))
		$(call var_assign_simple,$(__class__).fields,
			$(notdir $(call __class_attr_query,field,%)))
	)

endef

# Params:
#   1. Class or member name to check.
# Returns:
#   The argument if it is a valid name, empty otherwise.
define __class_name_check
	$(if $(not $(or \
			$(filter field method super class,$1),
			$(findstring $(\\),$1),
			$(findstring $(\h),$1),
			$(findstring $$,$1),
			$(findstring  %,$1),
			$(findstring  .,$1),
			$(findstring  /,$1)
		)),
		$(singleword $1)
	)
endef

# Params:
#   1. Class or member name to check.
# Returns:
#   The argument if it is a valid name, fails with an error otherwise.
define __class_name_check_or_die
	$(or $(__class_name_check),
		$(call builtin_error,
				Illegal name: '$1')
	)
endef

# Params:
#   1. Type.
#   2. Name.
define __class_new_attr
	$(assert $(not $(findstring /,$1$2)))

	$(if $(__class_attr_query_self),
		$(call builtin_error,
			Redefinition of '$2' $1)
	)

	$(call var_assign_simple_append,
		$(__class__),$1/$2)
endef

# Params:
#   1. Type.
#   2. Name.
#   3. Body.
define __class_new_attr_func
	$(call __class_new_attr,$1,$2)
	$(call __class_new_func,$2,$3)
endef

# Params:
#   1. Name.
#   2. Body.
define __class_new_func
	$(foreach f,$(__class__).$1,
		$(if $(call var_undefined,$f),
			$(call def_exclude,$f))
		$(call var_assign_recursive_sl,$f,$2)
	)
endef

# Params:
#   1. Name.
#   2. Body.
define __class_new_func_weak
	$(foreach f,$(__class__).$1,
		$(if $(call var_undefined,$f),
			$(call def_exclude,$f)
			$(call var_assign_recursive_sl,$f,$2))
	)
endef

# Searches the current class and its super-type hierarchy (at the moment
# of call, of course) for members matching both of the specified patterns.
# Duplicates are removed.
#
# Params:
#   1. Type patterns.
#   2. Name patterns.
# Return:
#   Quering result in form 'type/name'.
__class_attr_query = $(sort $(__class_attr_query_nosort))
__class_attr_query_nosort = \
	$(__class_attr_query_self_nosort) $(__class_attr_query_supers_nosort)

# Queries for those members that have been defined in the class itself.
# See '__class_attr_query'.
__class_attr_query_self = $(sort $(__class_attr_query_self_nosort))
define __class_attr_query_self_nosort
	$(foreach __class__,$(__class__),# Just a little optimization.
		$(__class_attr_do_query)
	)
endef

# Queries for members defined in supers of the current class.
# See '__class_attr_query'.
__class_attr_query_supers = $(sort $(__class_attr_query_supers_nosort))
define __class_attr_query_supers_nosort
	$(foreach __class__,# Get list of super classes.
		$(sort $(call $(lambda \
			$(foreach __class__,$(notdir $(call __class_attr_do_query,super,%)),
				$(__class__) $(call $0))
		))),
		$(__class_attr_do_query)
	)
endef

# Internal method for retrieving members of the class defined by '__class__'.
# Result may include duplicates.
define __class_attr_do_query
	$(with $1,$(filter $2,$(notdir $($(__class__)))),
		$(filter $(foreach 1,$1,$(addprefix $1/,$2)),$($(__class__))))
endef

# Searches the specified class and its super-type hierarchy.
#   1. Target class.
#   2. Type patterns.
#   3. Name patterns.
__class_attr_query_in = \
	$(foreach __class__,$1,$(call __class_attr_query,$2,$3))

# See '__class_attr_query_in', '__class_attr_query_self'.
__class_attr_query_self_in = \
	$(foreach __class__,$1,$(call __class_attr_query_self,$2,$3))

# See '__class_attr_query_in', '__class_attr_query_supers'.
__class_attr_query_supers_in = \
	$(foreach __class__,$1,$(call __class_attr_query_supers,$2,$3))

#
# $(super ancestor,args...)
#
define builtin_func-super
	$(foreach s,class-$(call __class_resolve,$1),
		$(if $(or \
				$(call def_in_progress,$s),
				$(and \
					$(call def_is_done,$s),
					$(call __class_attr_query_in,$1,super,$(__class__))
				)),
			$(call builtin_error,
				Class '$(__class__)' can't extend '$1' \
				because of inheritance loop)
		)

		$(if $(not $(call def_is_done,$s)),$(call def,$s))

		# Emit a call to a super constructor.
		$(if $(multiword $(builtin_args_list)),
			$$(call $s,$(builtin_nofirstarg)),
			$$(call $s)
		)
	)

	# Must add it after querying the parent for its supers
	# because '__class_attr_query' assumes there is no inheritance loops.
	$(call __class_new_attr,super,$1)

	# Copy function table from super class, but not override functions, that
	# have already been defined in the current class - they always take
	# a precedence over inherited ones.
	$(foreach f,
		$(notdir $(call __class_attr_query_in,$1,method% xetter%,%)),
		$(call __class_new_func_weak,$f,$(value $1.$f))
	)
endef

#
# $(method name,body...)
#
define builtin_func-method
	$(if $(multiword $(builtin_args_list)),

		# Method with an implementation.
		$(call __class_new_attr_func,method,$(trim $1),<commit_msg>Fix a few bugs in object.mk<commit_after>#
# Copyright 2010-2012, Mathematics and Mechanics faculty
#                   of Saint-Petersburg State University. All rights reserved.
# Copyright 2010-2012, Lanit-Tercom Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

#
# Object-oriented make.
#
#   Date: Dec 20, 2010
# Author: <NAME>
#

ifndef __core_object_mk
__core_object_mk := 1

##
# General overview.
#
#   This file provides an object-oriented superstructure for Make.
#
##
# Terminology.
#
#   Class
#     - a prototype of each instance (object), which defines members (fields
#       and methods) available for instances.
#
#   Object
#     - an instance of a class. Each object has its own set of values of
#       fields associated with the class of the object.
#
#   Object reference
#     - a value used to refer an object.
#
#   Method
#     - a function associated with a class, defines runtime behavior of an
#       instance of the class. Method is evaluated in a special context with
#      'this' pointing the current object, thus it has as access to other
#       members of the object.
#
#   Field
#     - a value associated with each object of a certain class, provides a
#       runtime state of the instance.
#
#   Property
#     - a special kind of member representing a runtime state of the instance
#       through getter and setter methods.
#
#   Reference field/property
#     - a member, that holds references to other objects.
#
##
# Class definition.
#
#   Each variable with name starting with 'class-' is considered as a new class
#   definition. The name of the class is everything after the dash.
#
#   	define class-clazz
#   		...
#   	endef
#
#   In the example above, a variable 'class-clazz' defines a new class
#   named 'clazz'. The name of the class is primarily used for object
#   instantiation and for class introspection.
#
#   So what can class actually define?
#
#
# Field definition.
#
#   A field is defined using special 'field' builtin function.
#
#   	$(field name[...][:{*|type}][,initializer...])
#
#   The first argument specifies the name of the field and whether the field
#   is a reference field. In the latter case one can also specify a type of
#   objects that can be held in this field.
#
#   If an initializer is specified in the second argument, it will be called
#   every time when a new object is created, and its return value will be
#   assigned to the field. Otherwise, the field gets empty initial value
#   by default.
#
#   	# A simple field for storing an arbitrary data.
#   	$(field a_field)
#
#   	# A field with an initializer. Note the usage of 'this'.
#   	$(field a_field_with_initializer,
#   		$(info initializing a field of '$(this)')
#   		initial value)
#
#   	# A field that holds references to any objects.
#   	$(field a_reference_field : *)
#
#   	# A reference fields that enforces the type of referenced objects to
#   	# be 'some_class'.
#   	$(field a_reference_field : some_class)
#
#
# Method definition.
#
#   As fields methods are defined using special builtin function, which has the
#   following syntax.
#
#   	$(method name[,implementation...])
#
#   The first argument is the name of the method.
#
#   If the second argument is not specified, then the method is considered
#   abstract. Abstract method are assumed to be implemented in some subclass.
#   To prevent invocation of an abstract method a special stub ($(error ...))
#   is assigned as its body.
#
#   	# A simple method which always returns 'result' string.
#   	$(method a_method,result)
#
#   	# A method with no implementation (abstract method).
#   	$(method an_abstract_method)
#
#   	# As a usual function method body have an access to all passed
#   	# arguments ($1 ..). A reference to the current object is available
#   	# in 'this' variable.
#   	$(method say_hello,
#   		$(info Hello from '$(this)', the first argument is '$1'))
#
#
# Property and its accessors definition.
#
#   The definition of a property is very similar to the fields except that
#   there is no initializer function.
#
#   	$(property name[...][:{*|type}])
#
#   As for fields the property can be defined to hold object references.
#
#
# Class inheritance.
#
#   Class can extend other classes, inheriting their members. Multiple
#   inheritance is supported, tracking an inheritance order and avoiding
#   MI issues (such as diamond problem) is the responsibility of user.
#
#   	$(super class_name[,arguments...])
#
#   The first argument is the name of a class to inherit. The specified class
#   must have already been defined. Arguments (if any) are passed to a
#   constructor of the super class.
#
#
# Constructor statements.
#
#   There is no dedicated structure for a constructor. Instead, the whole
#   class body is considered as a constructor. That is, you may include an
#   arbitrary code outside the builtins mentioned above.
#
#   Instantiation also involves field initialization and calling super
#   constructors, in the order they have been defined the class body.
#
#   	define class-zuper
#   		$(info Super constructor with argument '$1')
#
#   		$(field delegate : zuper)
#
#   	endef
#
#   	define class-clazz
#   		$(info This is executed first)
#
#   		$(super zuper,
#   			Hello from clazz)
#
#   		$(field foo,
#   			$(info Initializing field 'foo')
#   			initial value)
#
#   		$(method bar,
#   			$(info Calling method 'bar')
#   			hello world)
#
#   		$(info End of constructor)
#   	endef
#
#   In this example the following test will be printed during constructing
#   a new object:
#
#   	This is executed first
#   	Super constructor with argument 'Hello from clazz'
#   	Initializing field 'foo'
#   	End of constructor
#
##
# Runtime concepts.
#
#   After a class has been defined, one can create objects of such class,
#   invoke its methods and get/set field values.
#
#
# Object instantiation.
#
#   A new object is created using 'new' builtin function.
#
#   	$(new class_name[,arguments...])
#
#   The first argument specifies the name of a class being instantiated.
#
#   Note that this should be the name of the class itself, not the name of a
#   variable used to define a class. The name of the class defined in
#   variable 'class-clazz' is 'clazz', and a proper call to 'new' is the
#   following:
#
#   	$(new clazz)
#
#   Arguments specified after a class name are passed to the constructor.
#
#  'new' allocates a unique object identifier which acts as the object
#   reference and is accessible within the constructor through 'this' variable.
#   This identifier is then returned as the result value of 'new'.
#
#
# Accessing object members.
#
#   Member access includes method invocation and getting/setting field values.
#
#   	# Calls the method with given arguments and returns the result.
#   	$(invoke method[,arguments...])
#
#   	$(get-field  field)       # Retrieves the current field value.
#
#   	$(set-field  field,value) # Assigns a new value to the field.
#   	$(set-field+ field,value) # Appends a given value to the field.
#   	$(set-field- field,value) # Removes any occurrence of a value.
#
#   	$(get  property)          # Invokes getter of the specified property.
#
#   	$(set  property,value)    # Sets a new value by calling setter.
#   	$(set+ property,value)    # Append (multi-value properties only).
#   	$(set- property,value)    # Remove (multi-value properties only).
#
#   Related builtin functions allows one to specify a target member in three
#   different forms:
#
#   member
#     - Simple form for referring members of 'this' object.
#
#   <object>.member
#     - Accesses a member of the specified object.
#
#   	$(invoke $(new clazz).some_method)
#
#   	# A more real world example.
#   	$(foreach object,$(new clazz),
#   		$(invoke $(object).some_method))
#
#   <variable>->member
#     - The same as the previous one, but the object reference is computed
#       by expanding the specified variable. In fact, this form is a shorthand
#       for '$(<variable>).member'. Thus, the last of the examples above
#       could rewritten as follows:
#
#   	$(foreach object,$(new clazz),
#   		$(invoke object->some_method))
#
#   It is important to note that the latter two forms must include '.' (period)
#   and '->' (dash with closing angle bracket) literally. Think of them as a
#   kind of separator, such as ',' (comma) for arguments.
#
#
# A few words about object references.
#
#   A reference to an object is a string that is used to identify the object.
#   For example, 'new' returns a reference, 'this' contains a reference (in
#   certain contexts, of course: within constructors and methods).
#
#   There is, however, a special feature of references: prepending an
#   arbitrary prefix does not corrupt the reference. Moreover, one can extract
#   a prefix (if any) efficiently using $(basename <ref>) function.
#   In turn a normalized reference is available through $(suffix <ref>).
#
#   So, returning to an example of method invocation, one could modify it as
#   follows without changing the meaning:
#
#   	$(foreach object,foo/bar.baz!$(new clazz),
#   		$(invoke $(object).some_method))
#
##
# Internal representation.
#
#  TODO outdated. -- Eldar
#
#
#   During class definition or a new object instantiation a number of ancillary
#   variables are created.
#
#   Considering the code below as an example, we'll examine what variables
#   are created and when.
#
#   	define class-zuper
#   		$(info Super constructor with argument '$1')
#
#   		$(field delegate : zuper)
#
#   	endef
#
#   After defining class 'zuper' variable 'class-zuper' becomes a part
#   of its constructor. Also an initializer function for 'delegate' field, a
#   type checking setter method for it and three special variables are defined.
#
#   	# class-zuper = \
#   	# 	$(info Super constructor with argument '$1') \
#   	# 	$(eval $(this).delegate := $(value zuper.delegate))
#
#   	# zuper.super :=
#   	# zuper.fields := delegate.zuper
#   	# zuper.methods := set.delegate
#
#   	# zuper.field.delegate =
#   	# zuper.method.set.delegate = \
#   	# 	$(foreach 2,zuper,$(__field_setter_type_check))
#
#   Lets extend our class as follows:
#
#   	define class-clazz
#   		$(info This is executed first)
#
#   		$(super zuper,
#   			Hello from clazz)
#
#   		$(field foo,
#   			$(info Initializing field 'foo')
#   			initial value)
#
#   		$(method bar,
#   			$(info Calling method 'bar')
#   			hello world)
#
#   		$(info End of constructor)
#   	endef
#
#   Notice additional two variables for field 'foo' and method 'bar':
#
#   	# class-clazz = \
#   	# 	$(info This is executed first) \
#   	# 	$(call class-zuper,Hello from clazz) \
#   	# 	$(eval $(this).foo := $(value clazz.foo)) \
#   	# 	$(info End of constructor)
#
#   	# clazz.super := zuper
#   	# clazz.fields := delegate.zuper foo
#   	# clazz.methods := set.delegate bar
#
#   	# clazz.field.foo = \
#   	# 	$(info Initializing field 'foo')initial value
#   	# clazz.method.bar = \
#   	# 	$(foreach this,$(__this),$(info Calling method 'bar')hello world)
#   	# clazz.method.set.delegate = \
#   	# 	$(foreach 2,zuper,$(__field_setter_type_check))
#
#   Finally, we will instantiate 'clazz':
#
#   	inztance := $(call new,clazz)
#
#   And inspect newly defined variables:
#
#   	# inztance := .obj1
#
#   	# .obj1 := clazz
#   	# .obj1.delegate :=
#   	# .obj1.foo := initial value
#
#

include mk/core/common.mk
include mk/core/string.mk
include mk/core/define.mk

include mk/util/var/assign.mk

#
# $(new class,args...)
#
define builtin_func-new
	$$(foreach __class__,$$(call __class_resolve,$1),
		$(def-ifdef OBJ_DEBUG,
			$$(foreach __obj_debug_args_nr,
				$(words $(nofirstword $(builtin_args_list))),
				$$(call __new,$(builtin_nofirstarg))
			),
			$(if $(multiword $(builtin_args_list)),
				$$(call __new,$(builtin_nofirstarg)),
				$$(call __new)
			)
		)
	)
endef
$(call def,builtin_func-new)

# Creates a new instance of the specified class.
# Only a single argument is passed to the constructor.
#   1. Class name.
#   2. An (optional) argument to pass to the constructor.
# Return:
#   Newly created object instance.
define new
	$(new $1,$(value 2))
endef

# Params:
#   1.. Args...
# Context:
#   '__class__'
# Return:
#   New object identifier.
define __new
	# It is mandatory for object references to start with a period.
	$(foreach this,.obj$(words $(__object_instance_cnt) x),
		$(def-ifdef OBJ_DEBUG,
			$(info $(this):	new    $(__class__): $(__obj_debug_args)))
		${eval \
			__object_instance_cnt += $(this:.obj%=%)
			$(\n)
			$(this) := $(__class__)
			$(\n)
			$$(and $(value class-$(__class__)),)
		}
		$(this)
	)
endef
__object_instance_cnt :=# Initially empty.

# Return:
#   Human-readable list of args from 1 up to '__obj_debug_args_nr',
#   or 'no args' if '__obj_debug_args_nr' is 0.
# Note:
#   Do not 'call'.
define __obj_debug_args
	$(or \
		$(foreach a,
			$(wordlist 1,$(__obj_debug_args_nr),1 2 3 4 5 6 7 8 9),
			$(\t)$$$a='$($a)'
		),
#		<no args>
	)
endef

# Params:
#   1. Class name.
define __class_resolve
	$(if $(findstring undefined,$(flavor class-$1)),
		$(call $(if $(value __def_var),builtin_)error,
				Class '$1' not found),
		$1
	)
endef

#
# Runtime member access: 'invoke', 'get' and 'set'.
#

# Params:
#   1. Member name to parse in form 'member', 'ref->member' or 'obj.member'.
#      It is assumed that there is no any commas outside parens.
#   2. Continuation with the following args:
#       1. Empty in case of referencing a member of 'this',
#          the target object otherwise.
#       2. The member being referenced.
#       3. Optional argument.
#   3. (optional) Argument to pass to the continuaion.
# Return:
#   Result of call to continuation in case of a valid reference,
#   otherwise it aborts using 'builtin_error'.
define __object_member_parse
	$(or \
		$(__object_member_try_parse),
		$(call builtin_error,
				Invalid first argument to '$(builtin_name)' function: '$1')
	)
endef

# Params:
#   See '__object_member_parse'.
# Return:
#   Result of call to continuation in case of a valid reference,
#   empty otherwise.
define __object_member_try_parse
	$(expand $$(call \
		$(lambda $(or \
			$(and \
				$(eq .,$6),$(nolastword $4),$(trim $5),
				# Invocation on some object.
				# Escaped reference is in $4. Escaped member name is in $5.
				$(call $1,
					# Optimize out explicit dereference of 'this'.
					$(if $(not $(eq this ->,$(strip $4))),
						$(call $(if $(eq ->,$(lastword $4)),$(lambda $$($1)),id),
							$(call $3,$(nolastword $4))
						)
					),
					$(call $3,$5),$2)
			),
			$(and \
				$(eq .,$5),$(trim $4),
				# Target object is implicitly 'this'.
				# Escaped member is in $4.
				$(call $1,,$(call $3,$4),$2)
			)
		)),

		# 1 and 2: The continuation with its argument.
		$$2,$$(value 3),

		# 3: Unescape function which restores '.' and '->' back.
		$(lambda \
			$(trim $(subst $(\s).$(\comma),.,$(subst $(\s)->$(\comma),->,$1)))
		),

		# 4 .. 5: Escaped member name with '.' and '->' repalaced by commas.
		$(subst .,$(\s).$(\comma),$(subst ->,$(\s)->$(\comma),
			$(subst $(\comma),$$(\comma),$(subst $$,$$$$,$1))
		)),

		# 5 or 6: End of args marker.
		.,
	))
endef

# Params:
#   1. The target object or empty in case of referencing a member of 'this'.
#   2. The code to wrap.
# Return:
#   The code (wrapped if needed).
define __object_member_access_wrap
	$$(foreach __this,$(if $1,
			$$(call __object_check,$1
					$(def-ifdef OBJ_DEBUG,$(\comma)$(subst $$,$$$$,$1))),
			$$(this)),
		$2
	)
endef

$(def_all)

# Params:
#   1. Object reference.
#   2. Original code (if OBJ_DEBUG).
# Return:
#   The trimmed argument if it is a single word, fails with an error otherwise.
define __object_check
	$(or \
		$(suffix $(singleword $1)),
		$(error \
				Invalid object reference: '$1'$(def-ifdef OBJ_DEBUG, ($2)))
	)
endef

#
# Method invocation.
#

#
# $(invoke method,args...)
# $(invoke obj.method,args...)
# $(invoke ref->method,args...)
#
define builtin_func-invoke
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced method.
		# 3. Args...
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$(if $(nofirstword $(builtin_args_list)),
					$$(foreach __args_nr,
						$(words $(builtin_args_list)),
						$$(call __method_invoke_debug,$3,$2)
					),
					$$(foreach __args_nr,1,
						$$(call __method_invoke_debug,$2)
					)
				),
				$$(call $$($$(__this)).$2,$3)
			)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1..N: Args...
#   N+1:  Method
# Context:
#   '__class__'
ifdef OBJ_DEBUG
define __method_invoke_debug
	$(foreach __obj_debug_args_nr,$(word $(__args_nr),0 1 2 3 4 5 6 7 8 9),
		$(info \
				$(__this):	invoke $($(__this)).$($(__args_nr)): \
				$(__obj_debug_args))
	)
	$(foreach 0,$(or $(call var_recursive,$($(__this)).$($(__args_nr))),
			$(error \
					No method '$($(__args_nr))', \
					invoked on object '$(__this)' of type '$($(__this))')),
		$(expand $(value $0))
	)
endef
endif

$(def_all)

#
# Property access.
#

#
# $(get [{obj.|ref->}]property)
#
define builtin_func-get
	$(call builtin_check_max_arity,1)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Property.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __property_get_debug,$2),
				$$(call $$($$(__this)).$2.getter)
			)
		)
	))
endef

# Params:
#   1. Property name.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __property_get_debug
	$(info $(__this):	get    $($(__this)).$1)

	$(call $(or $(call var_recursive,$($(__this)).$1.getter),
			$(error \
					No property '$1', performing 'get' \
					on object '$(__this)' of type '$($(__this))'))
	)
endef
endif

#
# $(set  [{obj.|ref->}]property,value)
# $(set+ [{obj.|ref->}]property,value)
# $(set- [{obj.|ref->}]property,value)
#
builtin_func-set  = $(__builtin_func_set)
builtin_func-set+ = $(__builtin_func_set)
builtin_func-set- = $(__builtin_func_set)

# Expanded from  'setx' builtin context. It will generate a call
# to the corresponding property setter, 'x' is taken from builtin name.
define __builtin_func_set
	$(call builtin_check_min_arity,2)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Property.
		# 3. Value being set.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __property_set_debug,$2,$(builtin_name:set%=%),$3),
				$$(call $$($$(__this)).$2.setter$(builtin_name:set%=%),$3)
			)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1. Property name.
#   2. '+', '-', or empty.
#   3. Value.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __property_set_debug
	$(info $(__this):	set$(or $2,$(\s))   $($(__this)).$1: '$3')

	$(call $(or $(call var_recursive,$($(__this)).$1.setter$2),
			$(error \
					No property '$1', performing 'set$2' \
					on object '$(__this)' of type '$($(__this))')),
		$3
	)
endef
endif

#
# Field operations.
#

# Params:
#   1. Field name.
# Context:
#   '__this'
define __field_check
	$(if $(findstring simple,$(flavor $(__this).$1)),
		$1,
		$(error \
				No field '$1', \
				referenced on object '$(__this)' of type '$($(__this))')
	)
endef

#
# $(get-field field)
# $(get-field obj.field)
# $(get-field ref->field)
#
define builtin_func-get-field
	$(call builtin_check_max_arity,1)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced field.
		$(call __object_member_access_wrap,$1,
			$(def-ifdef OBJ_DEBUG,
				$$(call __field_get_debug,$2),
				$$($$(__this).$$(call __field_check,$2))
			)
		)
	))
endef


# Params:
#   1. Field name.
# Context:
#   '__this'
ifdef OBJ_DEBUG
define __field_get_debug
	$(info $(__this):	get-field $($(__this)).$1= \
		'$($(__this).$(call __field_check,$1))')
	$($(__this).$(call __field_check,$1))
endef
endif

#
# $(set-field  [{obj.|ref->}]field,value)
# $(set-field+ [{obj.|ref->}]field,value)
# $(set-field- [{obj.|ref->}]field,value)
#
builtin_func-set-field  = $(__builtin_func_set_field)
builtin_func-set-field+ = $(__builtin_func_set_field)
builtin_func-set-field- = $(__builtin_func_set_field)

# Expanded from  'set-fieldx' builtin context. It will generate a call
# to '__field_setx', where 'x' is taken from builtin name.
define __builtin_func_set_field
	$(call builtin_check_min_arity,2)
	$(call __object_member_parse,$1,$(lambda \
		# 1. Empty for 'this', target object otherwise.
		# 2. Referenced field.
		# 3. Value.
		$(call __object_member_access_wrap,$1,
			$$(call __field_set$(builtin_name:set-field%=%),$2,$3)
		)
	),$(builtin_nofirstarg))
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) := $$2
	}
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set+
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field+ $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) += $$2
	}
endef

# Params:
#   1. Field name.
#   2. Value.
# Context:
#   '__this'
define __field_set-
	$(def-ifdef OBJ_DEBUG,$(info $(__this):	set-field- $($(__this)).$1: '$2'))

	${eval \
		override $(__this).$(__field_check) := \
			$(if $(findstring %,$2),
				$$(subst %%,%,$$(filter-out $$(subst %,%%,$2),
					$$(subst %,%%,$$($(__this).$1)))),
				$$(filter-out $$2,$$($(__this).$1))
			)
	}
endef

$(def_all)

#
# Object/class structure introspection.
#

# Tells whether the argument is a valid object reference.
#   1. Reference to check.
# Return:
#   The argument if true, empty otherwise.
define is_object
	$(if $(value class-$(value $(suffix $1))),$1)
endef
builtin_func-is-object = \
	$(foreach builtin_name,is_object,$(builtin_to_function_inline))

# Tells whether a given class has been defined.
#   1. Class name.
# Return:
#   The argument if true, empty otherwise.
define class_exists
	$(if $(value class-$1),$1)
endef
builtin_func-class-exists = \
	$(foreach builtin_name,class_exists,$(builtin_to_function_inline))

# Gets the class of the specified object.
#   1. Object to inspect.
# Return:
#   The class if the argument is a valid object, empty otherwise.
define class
	$(foreach c,$(singleword $(value $(suffix $1))),
			$(if $(value class-$c),$c))
endef
$(call def,class)
builtin_func-class = $(builtin_to_function_inline)

# Tells whether a given object is an instance of the specified class.
#   1. Reference to check.
#   2. Class name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define instance_of
	#$(foreach c,$(class $1),
	#		$(if $(filter $2,$c $($c.super)),$1))

	# Optimized.
	$(foreach c,$(singleword $(value $(suffix $1))),
			$(and $(value class-$c),$(filter $2,$c $($c.supers)),$1))
endef
builtin_func-instance-of = \
	$(foreach builtin_name,instance_of,$(builtin_to_function_inline))

# Get all instances of all classes
define get-instances
	$(__object_instance_cnt:%=.obj%)
endef


# Get all instances of speceifed classes
#   1. Class name (optional).
define get-instances-of
	$(if $(call value,1),
		$(info searching istances of $1)
		$(foreach i,$(call get-instances),
			$(instance-of $i,$1)
		)
		,
		$(call get-instances)
	)
endef

# Tells whether a given object has the specified field.
#   1. Reference to check.
#   2. Field name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_field
	$(if $(call class_has_field,$(class $1),$2),$1)
endef
builtin_func-has-field = \
	$(foreach builtin_name,has_field,$(builtin_to_function_inline))

# Tells whether a class has the given field.
#   1. Name of the class to check.
#   2. Field name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_field
	$(if $(filter $2 $(addsuffix [],$2),$(basename $(value $1.fields))),$1)
endef
builtin_func-class-has-field = \
	$(foreach builtin_name,class_has_field,$(builtin_to_function_inline))

# Tells whether a given object has the specified property.
#   1. Reference to check.
#   2. Property name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_property
	$(if $(call class_has_property,$(class $1),$2),$1)
endef
builtin_func-has-property = \
	$(foreach builtin_name,has_property,$(builtin_to_function_inline))

# Tells whether a class has the given property.
#   1. Name of the class to check.
#   2. Property name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_property
	$(if $(filter $2 $(addsuffix [],$2),$(basename $(value $1.properties))),$1)
endef
builtin_func-class-has-property = \
	$(foreach builtin_name,class_has_property,$(builtin_to_function_inline))

# Tells whether a given object has the specified method.
#   1. Reference to check.
#   2. Method name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define has_method
	$(if $(call class_has_method,$(class $1),$2),$1)
endef
builtin_func-has-method = \
	$(foreach builtin_name,has_method,$(builtin_to_function_inline))

# Tells whether a class has the given method.
#   1. Name of the class to check.
#   2. Method name.
# Return:
#   The first argument if the answer is true, empty otherwise.
define class_has_method
	$(if $(filter $2,$(value $1.methods)),$1)
endef
builtin_func-class-has-method = \
	$(foreach builtin_name,class_has_method,$(builtin_to_function_inline))

$(def_all)

#
# Class definition.
#

define __class_variable_value_provider
	$(assert $(filter class-%,$1))

	$(if $(not $(call __class_name_check,$(1:class-%=%))),
		$(error \
				Illegal class name: '$(1:class-%=%)')
	)

	$(if $(call def_is_done,$1),
		# Workaround for repeated def in case when the class has already been
		# defined from 'super' builtin of another one.
		$(value $1),
		$$(__class__ \
			$(value $1)$(\n)# \n protects us from a comment on the last line.
		)
	)
endef
$(call def,__class_variable_value_provider)
$(call def_register_value_provider,class-%,__class_variable_value_provider)

#
# $(__class__ fields/methods/supers...)
#
define builtin_macro-__class__
	$(call builtin_check_max_arity,1)

	$(foreach __class__,$(__def_var:class-%=%),
		$(call var_assign_simple,$(__class__),)

		$(expand $1)$$(\0)# XXX

		# Define four special variables needed for introspection.
		# TODO Backward-compatibility, should be replaced
		# by '__class_attr_xxx' API.
		$(call var_assign_simple,$(__class__).supers,
			$(notdir $(call __class_attr_query,super,%)))
		$(call var_assign_simple,$(__class__).methods,
			$(notdir $(call __class_attr_query,method%,%)))
		$(call var_assign_simple,$(__class__).properties,
			$(notdir $(call __class_attr_query,property,%)))
		$(call var_assign_simple,$(__class__).fields,
			$(notdir $(call __class_attr_query,field,%)))
	)

endef

# Params:
#   1. Class or member name to check.
# Returns:
#   The argument if it is a valid name, empty otherwise.
define __class_name_check
	$(if $(not $(or \
			$(filter field method super class,$1),
			$(findstring $(\\),$1),
			$(findstring $(\h),$1),
			$(findstring $$,$1),
			$(findstring  %,$1),
			$(findstring  .,$1),
			$(findstring  /,$1)
		)),
		$(singleword $1)
	)
endef

# Params:
#   1. Class or member name to check.
# Returns:
#   The argument if it is a valid name, fails with an error otherwise.
define __class_name_check_or_die
	$(or $(__class_name_check),
		$(call builtin_error,
				Illegal name: '$1')
	)
endef

# Params:
#   1. Type.
#   2. Name.
define __class_new_attr
	$(assert $(not $(findstring /,$1$2)))

	$(if $(__class_attr_query_self),
		$(call builtin_error,
			Redefinition of '$2' $1)
	)

	$(call var_assign_simple_append,
		$(__class__),$1/$2)
endef

# Params:
#   1. Type.
#   2. Name.
#   3. Body.
define __class_new_attr_func
	$(call __class_new_attr,$1,$2)
	$(call __class_new_func,$2,$3)
endef

# Params:
#   1. Name.
#   2. Body.
define __class_new_func
	$(foreach f,$(__class__).$1,
		$(if $(call var_undefined,$f),
			$(call def_exclude,$f))
		$(call var_assign_recursive_sl,$f,$2)
	)
endef

# Params:
#   1. Name.
#   2. Body.
define __class_new_func_weak
	$(foreach f,$(__class__).$1,
		$(if $(call var_undefined,$f),
			$(call def_exclude,$f)
			$(call var_assign_recursive_sl,$f,$2))
	)
endef

# Searches the current class and its super-type hierarchy (at the moment
# of call, of course) for members matching both of the specified patterns.
# Duplicates are removed.
#
# Params:
#   1. Type patterns.
#   2. Name patterns.
# Return:
#   Quering result in form 'type/name'.
__class_attr_query = $(sort $(__class_attr_query_nosort))
__class_attr_query_nosort = \
	$(__class_attr_query_self_nosort) $(__class_attr_query_supers_nosort)

# Queries for those members that have been defined in the class itself.
# See '__class_attr_query'.
__class_attr_query_self = $(sort $(__class_attr_query_self_nosort))
define __class_attr_query_self_nosort
	$(foreach __class__,$(__class__),# Just a little optimization.
		$(__class_attr_do_query)
	)
endef

# Queries for members defined in supers of the current class.
# See '__class_attr_query'.
__class_attr_query_supers = $(sort $(__class_attr_query_supers_nosort))
define __class_attr_query_supers_nosort
	$(foreach __class__,# Get list of super classes.
		$(sort $(call $(lambda \
			$(foreach __class__,$(notdir $(call __class_attr_do_query,super,%)),
				$(__class__) $(call $0))
		))),
		$(__class_attr_do_query)
	)
endef

# Internal method for retrieving members of the class defined by '__class__'.
# Result may include duplicates.
define __class_attr_do_query
	$(with $1,$(filter $2,$(notdir $($(__class__)))),
		$(filter $(foreach 1,$1,$(addprefix $1/,$2)),$($(__class__))))
endef

# Searches the specified class and its super-type hierarchy.
#   1. Target class.
#   2. Type patterns.
#   3. Name patterns.
__class_attr_query_in = \
	$(foreach __class__,$1,$(call __class_attr_query,$2,$3))

# See '__class_attr_query_in', '__class_attr_query_self'.
__class_attr_query_self_in = \
	$(foreach __class__,$1,$(call __class_attr_query_self,$2,$3))

# See '__class_attr_query_in', '__class_attr_query_supers'.
__class_attr_query_supers_in = \
	$(foreach __class__,$1,$(call __class_attr_query_supers,$2,$3))

#
# $(super ancestor,args...)
#
define builtin_func-super
	$(foreach s,class-$(call __class_resolve,$1),
		$(if $(or \
				$(call def_in_progress,$s),
				$(and \
					$(call def_is_done,$s),
					$(call __class_attr_query_in,$1,super,$(__class__))
				)),
			$(call builtin_error,
				Class '$(__class__)' can't extend '$1' \
				because of inheritance loop)
		)

		$(if $(not $(call def_is_done,$s)),$(call def,$s))

		# Emit a call to a super constructor.
		$(if $(multiword $(builtin_args_list)),
			$$(call $s,$(builtin_nofirstarg)),
			$$(call $s)
		)
	)

	# Must add it after querying the parent for its supers
	# because '__class_attr_query' assumes there is no inheritance loops.
	$(call __class_new_attr,super,$1)

	# Copy function table from super class, but not override functions, that
	# have already been defined in the current class - they always take
	# a precedence over inherited ones.
	$(foreach f,
		$(notdir $(call __class_attr_query_in,$1,method% xetter%,%)),
		# XXX There used to be a call to '__class_new_func_weak'... -- Eldar
		$(call __class_new_func,$f,$(value $1.$f))
	)
endef

#
# $(method name,body...)
#
define builtin_func-method
	$(if $(multiword $(builtin_args_list)),

		# Method with an implementation.
		$(call __class_new_attr_func,method,$(trim $1),
{'fraction_non_alphanumeric': 0.14227301161069833, 'fraction_numerical': 0.01005446805913675, 'mean_word_length': 3.696474634565778, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7613608', 'n_tokens_mistral': 23410, 'n_tokens_neox': 22147, 'n_words': 6991}