File: tcl86.test

package info (click to toggle)
nsf 2.4.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 13,208 kB
  • sloc: ansic: 32,687; tcl: 10,723; sh: 660; pascal: 176; javascript: 135; lisp: 41; makefile: 24
file content (433 lines) | stat: -rw-r--r-- 9,928 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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# -*- Tcl -*-

package prefer latest

package require nx
package require nx::test

# just 8.6 or newer
if {[info command yield] eq ""} return

#
# Test coroutine / yield
#
nx::test case number-generator {
  nx::Object create ::numbers {
    # set instance variable used in coroutine
    set :delta 2
    :public object method ++ {} {
      yield
      set i 0
      while 1 {
	yield $i
	incr i ${:delta}
      }
    }
  }
  # create coroutine
  coroutine nextNumber ::numbers ++
  set ::j 0
  # use coroutine
  for {set i 0} {$i < 10} {incr i} {
    incr ::j [nextNumber]
  }

  # remove coroutine
  rename nextNumber {}

  ? {set ::j} 90
}

#
# Test coroutine / yield 
#
nx::test case enumerator1 {

  #
  # enumerator with yield in a single class
  #
  nx::Class create Enumerator {
    :property members:0..n
    :public method yielder {} {
      yield [info coroutine]
      foreach m ${:members} {
	yield $m
      }
      return -level 2 -code break
    }
    :public method next {} {${:coro}}
    :method init {} {
      :require namespace
      set :coro [coroutine [self]::coro [self] yielder]
    }
  }
  
  #
  # Some application class using the enumerator (just used for easy
  # testing)
  #
  nx::Class create Foo {
    :public method sum {} {
      set sum 0
      set e [Enumerator new -members {1 2 3}]
      while 1 {
	incr sum [$e next]
      }
      return $sum
    }
    :create f1
  }
  
  ? {f1 sum} 6
}


nx::test case enumerator2 {
  
  #
  # Define separate classes for Yielder and Enumerator
  #
  nx::Class create Yielder {
    :property {block ";"}
    :variable continuation ""

    #
    # make apply available as a method
    #
    :public alias apply ::apply

    #
    # The method "yielder" is the working horse for next. We need this
    # since the interface of Tcl's coroutines is based on a separate
    # cmd for continuation in the coroutine. The block can be
    # configured by application classes.
    #
    :public method yielder {} {
      yield [info coroutine]
      eval ${:block}
      return -level 2 -code break
    }

    #
    # The method "next" simply forwards to the continuation
    #
    :public method next {} {${:continuation}}

    #
    # The method "each" is based on the method "next" and applies the
    # value returned by next to the lambda expression
    #
    :public method each {var body} {
      while 1 {
	uplevel [list set $var [:next]]
	uplevel $body
      }
    }

    #
    # When a yielder is generated, we create automatically a coroutine
    # for it. The coroutine is placed under the current object, this
    # ensures simple cleanup (but is most probably not the fastest
    # variant, since we have to require a namespace).
    #
    :method init {} {
      :require namespace
      set :continuation [coroutine [self]::coro [self] yielder]
    }
  }

  #
  # The class "Enumerator" provides some application logic for the
  # class "Yielder". We use here a list of elements as base
  # representation.
  #
  nx::Class create Enumerator -superclass Yielder {
    :property members:0..n
    :property {block {
      foreach m ${:members} { yield $m }
    }}
  }

  #
  # Some application class using the enumerator (just used for easy
  # testing)
  #
  nx::Class create Foo {

    # test Enumerator.next
    :public method sum {} {
      set sum 0
      set e [Enumerator new -members {1 2 3}]
      while 1 { incr sum [$e next] }
      return $sum
    }
    
    :public method set {var} {
      set :$var
    }

    # test Enumerator.each
    :public method concat {} {
      set string "-"
      set i 0
      set e [Enumerator new -members {a be bu}]
      $e each x { append string $x-([incr i])- }
      return $string
    }
    
    :create f1
  }
  
  ? {f1 sum} 6
  ? {f1 concat} "-a-(1)-be-(2)-bu-(3)-"


  #
  # Define a class ATeam that uses "Enumerator", refines the method
  # "each" and adds another method "concat"
  #
  nx::Class create ATeam -superclass Enumerator {
    #
    # Overload "each" to show overloading. Here, we simply capitalize
    # the members in the "each" method.
    #
    :public method each {var body} {
      while 1 {
	set value [string totitle [:next]]
	uplevel [list set $var $value]
	uplevel $body
      }
    }
    # Define some arbitrary method using ATeam.each
    :public method concat {} {
      set string "-"
      :each x { append string $x- }
      return $string
    }
  }
  ATeam create a1 -members {alice bob caesar}
  ? {a1 concat } "-Alice-Bob-Caesar-"
}

#
# apply
#
nx::test case apply {

  # Register apply as an alias
  ::nx::Object public alias apply ::apply

  ::nx::Object create o {
    # Set an object variable
    set :delta 100

    # Define a standard map function based on apply
    :public object method map {lambda values} {
      set result {}
      foreach value $values {
	lappend result [:apply $lambda $value]
      }
      return $result
    }

    :object method foo {x} {return $x-$x}
  }

  # Two examples from the apply man page
  ? {o map {x {return [string length $x]:$x}} {a bb ccc dddd}} \
      "1:a 2:bb 3:ccc 4:dddd"
  ? {o map {x {expr {$x**2 + 3*$x - 2}}} {-4 -3 -2 -1 0 1 2 3 4}} \
      "2 -2 -4 -4 -2 2 8 16 26"

  ## Test case accessing object specific variable
  #? {o map {x {::nsf::__db_show_stack; return [expr {$x * ${:delta}}]}} {-4 -3 -2 -1 0 1 2 3 4}} \
  #    "-400 -300 -200 -100 0 100 200 300 400"

  # Test case accessing object specific variable
  ? {o map {x {expr {$x * ${:delta}}}} {-4 -3 -2 -1 0 1 2 3 4}} \
      "-400 -300 -200 -100 0 100 200 300 400"

  # Test case calling own method via apply
  ? {o map {x {:foo $x}} {hello world}} \
      "hello-hello world-world"
}


#
# The corrected cmd-literal semantics regarding cmd resolvers will
# only be released starting from and including 8.6.7.
#
if {![package vsatisfies [package req Tcl] 8.6.7]} {return}

set tcl87 [package vsatisfies [package req Tcl] 8.7-]

nx::test case bug-3418547 {
  #
  # See https://core.tcl-lang.org/tcl/tktview?name=3418547fff
  # 

  ? {info commands "::@"} ""

  proc getType {x} {dict get [::nsf::__db_get_obj @] type}

  ? {getType @} ""            ;# "@" has no type
  ? {namespace which @} ""
  if {!$::tcl87} {
      ? {getType @} "cmdName"     ;# "@" is of type "cmdName"
  }
  
  ? {@} {invalid command name "@"}
  ? {getType @} "bytecode"    ;# "@" is of type "bytecode"  

  
  #
  # 1) Provide @ for interp resolver in NX root namespace
  #
  proc ::nx::@ {} {
    return ::nx::@
  }

  nx::Object create ::o {
    :public object method foo {} {
      @; # Should resolve against ::nx::@ (by interp resolver)
    }
  }

  ? {getType @} "bytecode"        ;# "@" is still of type "bytecode"
  ::o foo
  ? {getType @} "bytecode"        ;# "@" is still of type "bytecode"  (byte code compilation should not leak)

  ? {::o foo} ::nx::@             ;# "@" is resolved in the nx context, therefore, we get nx::@
  
  #
  # 2) Provide alternative @
  #
  proc ::@ {} {
    return ::@
  }
  
  ? {info commands ::@} "::@"
  ? {::@} ::@
  ? {getType @} "bytecode"        ;# "@" is still of type "bytecode"

  set x [@]                       ;# execute "@" in an nx environment ("eval" of the test case)
  ? {getType @} "cmdName"         ;# "@" is of type "cmdName"

  ? [list $x] ::nx::@
  
  ? @ ::@                         ;# proc "?" interprets "@" as a script and turns "@"
                                  ;# into type "bytecode". The proc leaves the nx context
                                  ;# by using a "namespace eval", therefore, we see ::@
  ? {getType @} "bytecode"        ;# "@" is of type "bytecode"
  
  ? {namespace eval :: @} ::@     ;# exercise the same "namespace eval" as described above
  ? {namespace eval :: ::@} ::@   ;# the same with the global namespace qualifier
  
  ? {getType @} "bytecode"        ;# "@" is of type "bytecode"
  ? {getType ::@} "bytecode"      ;# "::@" is of type "bytecode"
  
  ? {namespace origin @} ::@      ;# "namespace origin" converts literal "@" from "bytecode" to "cmdName"
  ? {getType @} "cmdName"

  ? {namespace origin ::@} ::@
  ? {getType @}   "cmdName"
  ? {getType ::@} "cmdName"
  
  ? {@} ::@                   ;# the result is still the same as everywhere, since we are in an nx context XXX
}

#
# Without nx context
#
nx::test case bug-3418547-no-context
proc getType {x} {dict get [::nsf::__db_get_obj @] type}

# delete the commands
rename @ ""
rename ::nx::@ ""

? {info commands "::@"} ""

? {getType @}   ""
? {namespace which @} ""
if {!$::tcl87} {
    ? {getType @}   "cmdName"
}

? {@} {invalid command name "@"}

#
# 1) Provide proc @
#
proc ::@ {} {
  return ::@
}

? {@} ::@
if {!$::tcl87} {
    ? {getType @}   "cmdName"
}

#
# 2) Provide @ for interp resolver in NX root namespace
#
proc ::nx::@ {} {
  return ::nx::@
}

set r [@]       ;# "@" is not executed in an nx environment (no testcase eval), therefore, resolved globally
? {set r} ::@
if {!$::tcl87} {
    ? {getType @}   "cmdName"
}

nx::Object create ::o {
  :public object method foo {} {
    @          ; # resolve against ::nx::@ (via interp resolver)
  }
}

set r [::o foo]
? {set r} ::nx::@
if {!$::tcl87} {
    ? {getType @}   "cmdName"
}

? {::o foo} ::nx::@

set r [@]       ;# "@" is not executed in an nx environment (no testcase eval), therefore, resolves globally
? {set r} ::@
? {@} ::@       ;# "@" is executed in an "namespace eval ::", therefore, no nx context

# cleanup
rename ::nx::@ ""
rename @ ""


#
# Try to reconstruct test case of Tcl's resolver.test 1.6
#
nx::test case resolver-1.6

proc ::@@ {} {return ::@@}
proc ::nx::@ {} {
  return ::nx::@
}

nx::Object create ::o {
  :public object method foo {} {
    @          ; # resolve against ::nx::@ (via interp resolver)
  }
}

set r [::o foo]
? {set r} ::nx::@

interp alias {} ::nx::@ {} ::@@

# call the new aliased definition
? {::nx::@} ::@@

# see consistent results from method foo
set r [::o foo]
? {set r} ::@@