1 /* testoxy.i
2  * $Id: testoxy.i,v 1.2 2010-07-17 22:44:56 dhmunro Exp $
3  * test object extensions to yorick
4  */
5 /* Copyright (c) 2010, David H. Munro.
6  * All rights reserved.
7  * This file is part of yorick (http://yorick.sourceforge.net).
8  * Read the accompanying LICENSE file for details.
9  */
10 
11 /* intentionally create some items which conflict with object members */
test1(x)12 func test1(x)
13 {
14   goof = (data1 != 27);
15   d = x(data1);
16   if (d != -10) goof |= 2;
17   x, test1;
18   if (x("data1")!=-9 || data1!=27) goof |= 4;
19   x, "test1";
20   if (x(data1)!=-8 || data1!=27) goof |= 8;
21   data2 = x(5);
22   save, x, "data1", d, data2;
23   if (x(data1)!=-10 || data1!=27) goof |= 0x10;
24   if (x(*)!=6 ||
25       anyof(x(*,)!=["test1", "test2", "test3", "data1", "data2", "data3"]) ||
26       x(*,5)!="data2" || anyof(x(*,3::-2)!=["test3","test1"]) ||
27       x(*,0)!="data3" || anyof(x(*,[-1,3,5])!=["data2","test3","data2"]) ||
28       x(*,"data1")!=4 || anyof(x(*,["test1","data3","data1"])!=[1,6,4]))
29     goof |= 0x20;
30   d = x(data2,-,::-1);
31   a = x(-1,-,::-1);
32   if (anyof(dimsof(d)!=[2,1,5]) || anyof(d!=indgen(5:1:-1)(-,)) ||
33       anyof(dimsof(a)!=[2,1,5]) || anyof(a!=indgen(5:1:-1)(-,)))
34     goof |= 0x40;
35   a = x([-1,2]);
36   save,a, [], x(2:4);
37   if (anyof(a(*,)!=["data2","test2","test3","data1"])) goof |= 0x80;
38   save,a, , span(0,1,3), string(0), x;
39   if (anyof(a(*,)!=["data2","test2","test3",
40                     "data1",string(0),string(0)])) goof |= 0x100;
41   if (is_obj(d) || !is_obj(a) || is_obj(a)!=3 ||
42       anyof(is_obj(a,)!=[0,0,0,0,0,3]) || is_obj(a,"test2") ||
43       anyof(is_obj(a,0:2:-2)!=[3,0,0]) || anyof(is_obj(a,[0,-5,6])!=[3,0,3]) ||
44       anyof(is_obj(a,["test2","data1"])))
45     goof |= 0x200;
46   if (is_obj(d,"x",1)!=-2 || anyof(is_obj(a,[0,-5,6],1)!=[3,0,3]) ||
47       is_obj(a,[0,-5,10],1)!=-1 ||is_obj(a,"oops",1)!=-1|| is_obj(a,9,1)!=-1 ||
48       anyof(is_obj(a,["test2","oops","data1"],1)!=[0,-1,0]))
49     goof |= 0x200;
50   b = x();
51   if (b(*)!=6 || anyof(b(*,)!=x(*,))) goof |= 0x400;
52   b = x(0:2:-2);
53   if (b(*)!=3 || anyof(b(*,)!=["data3","data1","test2"])) goof |= 0x400;
54   b = x([6,-2,0]);  /* note repeat handling feature ?? */
55   if (b(*)!=2 || anyof(b(*,)!=["data3","data1"])) goof |= 0x400;
56   b = x(["data3","data1","data3"]);
57   if (b(*)!=2 || anyof(b(*,)!=["data3","data1"])) goof |= 0x400;
58   if (is_func(x(data3)) != 4) goof |= 0x800;
59   b = x(data3,d,data2);
60   if (anyof(vunpack(b,-)!=d) || anyof(vunpack(b,-)!=data2)) goof |= 0x800;
61   b = x.data3(d,data2);
62   if (anyof(vunpack(b,-)!=d) || anyof(vunpack(b,-)!=data2)) goof |= 0x800;
63   b = x(4:6);
64   save, b, y=cos, data1=[4.5,-1.5];
65   if (b(*)!=4 || anyof(b(*,)!=["data1","data2","data3","y"]) ||
66       anyof(b(data1)!=[4.5,-1.5]) || b(y)!=cos) goof |= 0x1000;
67   b, data1=b(data1,2), z=tan;
68   if (b(*)!=5 || anyof(b(*,)!=["data1","data2","data3","y","z"]) ||
69       b(data1)!=-1.5 || b(z)!=tan) goof |= 0x1000;
70   if (!goof) write, "PASSED oxy test1";
71   else write, format="***FAILED oxy test1, goof=%04lx\n", goof;
72 }
73 data1 = 27;
74 
75 /* begin yorick idiom for creating an object
76  * first save saves the original values of testoxy members to be defined here
77  * second save save original values of incidental variables used to construct
78  *   the data members of the object, but not part of the object itself
79  */
80 testoxy = save(test1, test2, test3, data1, data2, data3);
81 scratch = save(scratch, x, y, z);
82 /* now define the object members */
83 
84 data1 = -10;
85 data2 = indgen(5);
86 data3 = vpack;      /* try something not an array */
87 
88 func test1
89 {
90   use, data1;
91   data1++;
92 }
93 
94 func test2(x, &data3, z)
95 {
96   if (x==use(data1)) restore, use, data3;
97   if (z==use(4)) save, use, data1=use("data1")+12;
98   return save(wow=use(3:5), , use([1,2]));
99 }
100 
101 func test3(a, .., key=)
102 {
103   while (more_args()) save, a, string(0), next_arg();
104   return (key==use(data1));
105 }
106 
107 /* first restore puts back scratch variables (including scratch object)
108  * second restore puts back original member values, creating the new
109  *   object from the values we just defined
110  * this completes yorick idiom for creating an object
111  */
112 restore, scratch;
113 testoxy = restore(testoxy);
114 
115 test1, testoxy;
116 y = 7;
117 x = testoxy(test2, -10, y, -10);
118 if (y!=vpack || testoxy.data1!=2 || x(*)!=3 ||
119     anyof(x(*,)!=["wow","test1","test2"])) {
120     write, format="***FAILED oxy test2%s\n", "";
121 } else {
122   write, "PASSED oxy test2";
123 }
124 y = save(cos);
125 x = x(wow,test3, key=2, y,6,7,8);
126 if (!x || y(*)!=4 || y(cos)!=cos || anyof([y(2),y(3),y(4)]!=[6,7,8])) {
127     write, format="***FAILED oxy test3%s\n", "";
128 } else {
129   write, "PASSED oxy test3";
130 }
131 
132 /* ---------------------- test closures */
133 
cl_assert(test,msg)134 func cl_assert(test, msg) { if (!test) error, msg; }
135 errs2caller, cl_assert;
136 
cl_op_extern1(data,arg)137 func cl_op_extern1(data, arg)
138 {
139   result = data;
140   if (!is_void(arg)) result += arg;
141   if (am_subroutine()) write, result;
142   else return result;
143 }
144 
cl_op_extern2(data,arg)145 func cl_op_extern2(data, arg)
146 {
147   result = data + arg + 1;
148   if (am_subroutine()) write, result;
149   else return result;
150 }
151 
cl_op_extern3(data,arg)152 func cl_op_extern3(data, arg)
153 {
154   return data + arg + 1;
155 }
156 
157 func cl_test1
158 {
159   /* check that closure properly unreferences its "data" */
160   dat1 = m = "PASSED closure called with no arguments";
161   c = closure("cl_op_extern1", dat1);
162   dat1 = [1,2,-5];
163   c;
164   cl_assert,c.data == m, "expected static data";
165 
166   /* check that closure properly solve its "function" at runtime */
167   c = closure("cl_op1", 42.0);
168   f = c.function;
169   cl_assert, is_void(f), "expecting a nil result";
170   cl_assert, is_closure(c) == 2, "expecting a runtime closure";
171   cl_op1 = cl_op_extern1;
172   cl_assert, is_func(c.function), "expecting a function";
173   cl_assert, c(1) == 43, "unexpected result";
174   cl_op1 = cl_op_extern2;
175   cl_assert, c(1) == 44, "unexpected result";
176   c = closure(cl_op_extern1, 42.0);
177   cl_assert, c.function_name == string(0), "bad function_name";
178   cl_assert, is_closure(c) == 1, "expecting an immutable closure";
179   cl_assert, c.data == 42.0, "bad data extract";
180 
181   /* check closure with object argument */
182   data2 = -1;
183   c = closure(testoxy, data2);
184   cl_assert, is_closure(c) == 1, "expecting an immutable closure";
185   cl_assert, allof(c() == indgen(5)), "unexpected result";
186   cl_assert, allof(c.data == -1), "expected runtime data";
187   c = closure("o:testoxy", data2);
188   cl_assert, is_closure(c) == 2, "expecting a runtime closure";
189   cl_assert, c.data_name == "data2", "bad data name";
190   cl_assert, allof(c() == indgen(5)), "unexpected result";
191   cl_assert, allof(c.data == -1), "expected runtime data";
192   c = closure("testoxy", data2);
193   cl_assert, allof(c() == indgen(5)), "unexpected result";
194 
195   write, "PASSED all closure tests";
196 }
197 
198 /* normally, is_func(x)==5 should be adequate (even that rarely needed) */
is_closure(x)199 func is_closure(x)
200 {
201   if (is_func(x) != 5) return 0n;
202   if (x.function_name) return 2n;
203   return 1n;
204 }
205 
206 if (is_func(closure)==5) closure = closure.data;
207 cl_test1;
208 
209 func cl_test2
210 {
211   /* check that closure properly solve its "function" at runtime */
212   local cl_op1;
213   c = closure(cl_op1, 42.0);
214   f = c.function;
215   cl_assert, is_void(f), "expecting a nil result";
216   cl_assert, is_closure(c) == 2, "expecting a runtime closure";
217   cl_op1 = cl_op_extern1;
218   cl_assert, is_func(c.function), "expecting a function";
219   cl_assert, c(1) == 43, "unexpected result";
220   cl_op1 = cl_op_extern2;
221   cl_assert, c(1) == 44, "unexpected result";
222   c = closure(cl_op_extern1, 42.0);
223   cl_assert, c.function_name == "cl_op_extern1", "bad function_name";
224   cl_assert, is_closure(c) == 2, "expecting a runtime closure";
225   cl_assert, c.data == 42.0, "bad data extract";
226   write, "PASSED alternate closure tests";
227 }
228 
229 /* create an alternate form of closure for which
230  * first argument simple variable reference is treated as
231  *   resolve at runtime
232  * - may be useful for debugging
233  */
234 c = save(c, closure);
closure(args)235 func closure(args)
236 {
237   return args(1)((args(0,2)? args(2) : args(-,2)), args(3));
238 }
239 wrap_args, closure;
240 if (is_func(c(closure))==2) {  /* be sure c(closure) is original builtin */
241   closure = c(closure, closure, c(closure));      /* you gotta love this */
242   restore, c, c;
243  } else { /* ... otherwise abort */
244   restore, c;
245 }
246 
247 cl_test2;
248 
249 /* put back built-in closure */
250 closure = closure.data;
251 
252 /* ---------------------- test friend and sibling calls */
253 
254 func test5
255 {
256   use, data1;
257   data1++;
258 }
259 save, testoxy, test5;
260 
261 func test4(&x)
262 {
263   use, data1;
264   x = data1;
265   use_method, test5;
266   return data1++;
267 }
268 
269 testoxy, data1=12;
270 x = testoxy(noop(test4), y);
271 if (y!=12 || x!=13 || testoxy(data1)!=14) {
272     write, format="***FAILED oxy %s test\n", "friend";
273 } else {
274   write, "PASSED oxy friend test";
275 }
276 
277 save, testoxy, test4;
278 testoxy, data1=12;
279 x = testoxy(test4, y);
280 if (y!=12 || x!=13 || testoxy(data1)!=14) {
281     write, format="***FAILED oxy %s test\n", "sibling";
282 } else {
283   write, "PASSED oxy sibling test";
284 }
285