File: shoop.sh

package info (click to toggle)
shoop 0.1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 328 kB
  • ctags: 41
  • sloc: sh: 1,138; makefile: 209; perl: 44
file content (161 lines) | stat: -rw-r--r-- 5,779 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/bin/sh -e
# OOP in shell.
# LGPL copyright 2000 by Joey Hess <joey@kitenet.net>
#			 Adam Heath <doogie@debian.org>

_shoop () {
	local TRUEOBJ=$1 TRYOBJ=$2 METH=$3 TRUEMETH=$1_$3 TRYMETH=$2_$3 LASTMETH=$METH
	shift 3
	case "$1" in
		=|=q|=p|.=|.=q|.=p|.=qp|:|:p)
			local varmeth=$1 append="" quiet="" private=""; shift
			if [ "${varmeth%p}" != $varmeth ]; then private=1; varmeth=${varmeth%p}; fi
			# This block is for introspect.
			if [ "$_shoop_introspect" ] &&
			   eval [ -z \"\$_shooptype_$TRYMETH\$private\" ]; then
				eval "_shoopdefines_$TRUEOBJ=\"\$_shoopdefines_$TRUEOBJ $METH\""
			fi
			if [ -z "$_shoopnocache_" ]; then
				eval $_shoopcacheclear_
			fi
			# Some various assignment modifiers.
			if [ "${varmeth#.}" != $varmeth ]; then append=1 varmeth=${varmeth#.}; fi
			if [ "${varmeth%q}" != $varmeth ]; then quiet=1 varmeth=${varmeth%q}; fi
			if [ "$varmeth" = = ]; then
				if [ "$append" ];then set -- "$(eval eval "\$_shoop_$TRUEMETH") $@"; fi
				if [ ! "$quiet" ]; then echo -n $@; fi

				eval "_shoop_$TRUEMETH='echo -n $@'
				      _shooptype_$TRUEMETH=variable"
			else
				if [ "$quiet" ]; then echo "Invalid modifier(q) on assignment!($TRUEOBJ.$METH)" >&2; fi
				if [ "$append" ];then
					eval eval "_shoop_$TRUEMETH=\'\$_shoop_$TRUEMETH;\$@\'
					      _shooptype_$TRUEMETH=method"
				else
					eval "_shoop_$TRUEMETH='$@'
					      _shooptype_$TRUEMETH=method"
				fi
			fi
			return
		;;
	esac
	if eval [ \"\$_shooptype_$TRYMETH\" ]; then
		local THIS=$TRUEOBJ
		eval eval "\$_shoop_$TRYMETH"
		return
	else
		eval local P PARENTS=\"$(eval eval "\$_shoop_${TRYOBJ}_parent")\"\
			THIS=$TRUEOBJ GETMETH="" NEWPARENTS=""
		if [ -z "$_shoopnocache_" ]; then
			# If this object is found in the cache, than short-circuit
			# the resolving code.
			eval local CACHE=\"\$_shoopcache_link_$TRUEMETH\"
			if [ "$CACHE" ]; then
				eval eval \$$CACHE
				return
			fi
		fi
		# 1st stage resolver.  Look at the immediate parents.
		for P in $PARENTS; do
			eval GETMETH=\"\$_shoop_${P}_$METH\"
			if [ "$GETMETH" ]; then
				eval "$GETMETH"
				return
			fi
			# Save the parents of the current parents, for use in the
			# 2nd stage resolver.  Yes, this slows the 1st stage down,
			# but barely.  However, it greatly speeds up the 2nd stage,
			# which is where most of the time will be spent.  This
			# gave an 8% speedup in the 2nd stage, and only noise in
			# the first.
			NEWPARENTS="$NEWPARENTS $(eval eval "\$_shoop_${P}_parent")"
		done
		# 1st stage found no match, so resolve the inheritance tree,
		# starting at the second level, and loop over untested super
		# classes.
		local orgargs="$@"
		set -- $NEWPARENTS
		while [ $# -gt 0 ];do
			P=$1
			eval GETMETH="\$_shoop_${P}_$METH"
			if [ "$GETMETH" ]; then
				set -- $orgargs
				# Save a reference to the resolved object in the cache for the
				# true object.
				if [ -z "$_shoopnocache_" ]; then
					eval _shoopcache_link_${THIS}_$METH=_shoop_${P}_$METH\
					     _shoopcache_=\"\$_shoopcache_\
						  _shoopcache_method_$METH _shoopcache_link_${THIS}_$METH \"\
					     _shoopcache_method_$METH=\"\$_shoopcache_method_$METH\
						  _shoopcache_link_${THIS}_$METH\"\
					     _shoopcache_linkmethod_${P}_$METH=\"\$_shoopcache_linkmethod_${P}_$METH\
						  _shoopcache_link_${THIS}_$METH\"
				fi
				eval "$GETMETH"
				return
			fi
			shift
			set -- $(eval eval "\$_shoop_${P}_parent") "$@"
		done
		echo "\"$METH\" is undefined for $TRYOBJ." >&2
		return 1
	fi
}
# _shoopcache_link_DESCENDENT_counter=_shoop_OBJECT_counter
# _shoopcache_= _shoopcache_method_new _shoopcache_link_GRANDCHILD_new  _shoopcache_method_counter _shoopcache_link_DESCENDENT_counter 
# _shoopcache_method_counter= _shoopcache_link_DESCENDENT_counter
# _shoopcache_linkmethod_OBJECT_counter= _shoopcache_link_DESCENDENT_counter

IFS=" " _shoopcacheclear_="
	if eval [ \\\"\\\$_shoopcache_method_\$METH\\\" ]; then
		# Ok, the current METH is already in someone's cache.
		# Find out if it is THIS object that is referenced.
		if eval [ -z \\\"\\\$_shoopcache_linkmethod_\$TRUEMETH\\\" ]; then
			# Someone is referencing \$METH, and it isn't TRUEMETH, so
			# that means we have to erase all references for \$METH.
			#
			# TODO: Only erase if $TRUE was in the parent path of
			# \$_shoopcache_method_\$METH
			eval unset _shoopcache_method_\$METH\
				 \\\$_shoopcache_method_\$METH\
				   _shoopcache_linkmethod_\$TRUEMETH\
				 \\\$_shoopcache_linkmethod_\$TRUEMETH
		fi
	fi
"
# Temporarily turn on introspection, so the base object has everything 
# recorded about it as it is being created.
_shoop_introspect=1

# Create a method to create a new object.

# We clear the whole cache, whenever a new object is created.  This
# is sub-optimal, as it should really only dump cache chains that
# have traversed this object.
#
# The reason for this, is because we use lazy resolving.  You can
# set your parents to non-existant objects, and define those objects
# at a later time.  However, if the newer object contains a method
# that has already been resolved(and cached) by the first object,
# this will lead to a cache inconsistency.

IFS=" " _shoop OBJECT OBJECT new :p '
	local OBJNAME=$1
	eval "$OBJNAME () { shift; _shoop $OBJNAME $OBJNAME \"\$@\"; };"
	if [ $THIS != $OBJNAME ]; then
		_shoop $OBJNAME $OBJNAME parent = $THIS >/dev/null
	fi
	eval unset _shoopcache_ \$_shoopcache_ || true
'
# Create the base object via the method already defined on it.
_shoop OBJECT OBJECT new OBJECT

# Define the parent variable
OBJECT . parent = ""

# This method handles calling an overridden method of your parent.
OBJECT . super :p '_shoop $THIS $($THIS . parent) "$LASTMETH" "$@"; return'

# Now if you want introspection, you have to turn it back on.
unset _shoop_introspect