~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/vmbiftad.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef RCSID
 
2
static char RCSid[] =
 
3
"$Header: d:/cvsroot/tads/tads3/VMBIFTAD.CPP,v 1.3 1999/07/11 00:46:58 MJRoberts Exp $";
 
4
#endif
 
5
 
 
6
/* 
 
7
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
 
8
 *   
 
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
10
 *   on using and copying this software.  
 
11
 */
 
12
/*
 
13
Name
 
14
  vmbiftad.cpp - TADS built-in function set for T3 VM
 
15
Function
 
16
  
 
17
Notes
 
18
  
 
19
Modified
 
20
  04/05/99 MJRoberts  - Creation
 
21
*/
 
22
 
 
23
#include <stdio.h>
 
24
#include <string.h>
 
25
#include <time.h>
 
26
 
 
27
#include "t3std.h"
 
28
#include "os.h"
 
29
#include "utf8.h"
 
30
#include "vmuni.h"
 
31
#include "vmbiftad.h"
 
32
#include "vmstack.h"
 
33
#include "vmerr.h"
 
34
#include "vmerrnum.h"
 
35
#include "vmglob.h"
 
36
#include "vmpool.h"
 
37
#include "vmobj.h"
 
38
#include "vmstr.h"
 
39
#include "vmlst.h"
 
40
#include "vmrun.h"
 
41
#include "vmregex.h"
 
42
#include "vmundo.h"
 
43
#include "vmfile.h"
 
44
#include "vmsave.h"
 
45
#include "vmbignum.h"
 
46
#include "vmfunc.h"
 
47
#include "vmpat.h"
 
48
#include "vmtobj.h"
 
49
#include "vmvec.h"
 
50
#include "vmpredef.h"
 
51
 
 
52
 
 
53
/* ------------------------------------------------------------------------ */
 
54
/*
 
55
 *   forward statics 
 
56
 */
 
57
 
 
58
#ifdef VMBIFTADS_RNG_ISAAC
 
59
static void isaac_init(isaacctx *ctx, int flag);
 
60
#endif /* VMBIFTADS_RNG_ISAAC */
 
61
 
 
62
 
 
63
/* ------------------------------------------------------------------------ */
 
64
/*
 
65
 *   Initialize the TADS intrinsics global state 
 
66
 */
 
67
CVmBifTADSGlobals::CVmBifTADSGlobals(VMG0_)
 
68
{
 
69
    /* allocate our regular expression parser */
 
70
    rex_parser = new CRegexParser();
 
71
    rex_searcher = new CRegexSearcherSimple(rex_parser);
 
72
 
 
73
    /* 
 
74
     *   Allocate a global variable to hold the most recent regular
 
75
     *   expression search string.  We need this in a global so that the last
 
76
     *   search string is always protected from garbage collection; we must
 
77
     *   keep the string because we might need it to extract a group-match
 
78
     *   substring.  
 
79
     */
 
80
    last_rex_str = G_obj_table->create_global_var();
 
81
 
 
82
#ifdef VMBIFTADS_RNG_LCG
 
83
    /* 
 
84
     *   Set the random number seed to a fixed starting value (this value
 
85
     *   is arbitrary; we chose it by throwing dice).  If the program
 
86
     *   wants another sequence, it can manually change this by calling
 
87
     *   the randomize() intrinsic in our function set, which seeds the
 
88
     *   generator with an OS-dependent starting value (usually based on
 
89
     *   the system's real-time clock, to ensure that each run will use a
 
90
     *   different starting value).  
 
91
     */
 
92
    rand_seed = 024136543305;
 
93
#endif
 
94
 
 
95
#ifdef VMBIFTADS_RNG_ISAAC
 
96
    /* create the ISAAC context structure */
 
97
    isaac_ctx = (struct isaacctx *)t3malloc(sizeof(struct isaacctx));
 
98
 
 
99
    /* initialize with a fixed seed vector */
 
100
    isaac_init(isaac_ctx, FALSE);
 
101
#endif
 
102
}
 
103
 
 
104
/*
 
105
 *   delete the TADS intrinsics global state 
 
106
 */
 
107
CVmBifTADSGlobals::~CVmBifTADSGlobals()
 
108
{
 
109
    /* delete our regular expression searcher and parser */
 
110
    delete rex_searcher;
 
111
    delete rex_parser;
 
112
 
 
113
    /* 
 
114
     *   note that we leave our last_rex_str global variable undeleted here,
 
115
     *   as we don't have access to G_obj_table (as there's no VMG_ to a
 
116
     *   destructor); this is okay, since the object table will take care of
 
117
     *   deleting the variable for us when the object table itself is deleted
 
118
     */
 
119
 
 
120
#ifdef VMBIFTADS_RNG_ISAAC
 
121
    /* delete the ISAAC context */
 
122
    t3free(isaac_ctx);
 
123
#endif
 
124
}
 
125
 
 
126
/* ------------------------------------------------------------------------ */
 
127
/*
 
128
 *   datatype - get the datatype of a given value
 
129
 */
 
130
void CVmBifTADS::datatype(VMG_ uint argc)
 
131
{
 
132
    vm_val_t val;
 
133
    vm_val_t retval;
 
134
 
 
135
    /* check arguments */
 
136
    check_argc(vmg_ argc, 1);
 
137
 
 
138
    /* pop the value */
 
139
    G_stk->pop(&val);
 
140
 
 
141
    /* return the appropriate value for this type */
 
142
    retval.set_datatype(vmg_ &val);
 
143
    retval_int(vmg_ retval.val.intval);
 
144
}
 
145
 
 
146
/* ------------------------------------------------------------------------ */
 
147
/*
 
148
 *   getarg - get the given argument to the current procedure 
 
149
 */
 
150
void CVmBifTADS::getarg(VMG_ uint argc)
 
151
{
 
152
    int idx;
 
153
 
 
154
    /* check arguments */
 
155
    check_argc(vmg_ argc, 1);
 
156
 
 
157
    /* get the argument index value */
 
158
    idx = pop_int_val(vmg0_);
 
159
 
 
160
    /* if the argument index is out of range, throw an error */
 
161
    if (idx < 1 || idx > G_interpreter->get_cur_argc(vmg0_))
 
162
        err_throw(VMERR_BAD_VAL_BIF);
 
163
 
 
164
    /* push the parameter value */
 
165
    *G_interpreter->get_r0() = *G_interpreter->get_param(vmg_ idx - 1);
 
166
}
 
167
 
 
168
/* ------------------------------------------------------------------------ */
 
169
/*
 
170
 *   firstobj - get the first object instance
 
171
 */
 
172
void CVmBifTADS::firstobj(VMG_ uint argc)
 
173
{
 
174
    /* check arguments */
 
175
    check_argc_range(vmg_ argc, 0, 2);
 
176
 
 
177
    /* enumerate objects starting with object 1 in the master object table */
 
178
    enum_objects(vmg_ argc, (vm_obj_id_t)1);
 
179
}
 
180
 
 
181
/*
 
182
 *   nextobj - get the next object instance after a given object
 
183
 */
 
184
void CVmBifTADS::nextobj(VMG_ uint argc)
 
185
{
 
186
    vm_val_t val;
 
187
    vm_obj_id_t prv_obj;
 
188
 
 
189
    /* check arguments */
 
190
    check_argc_range(vmg_ argc, 1, 3);
 
191
 
 
192
    /* get the previous object */
 
193
    G_interpreter->pop_obj(vmg_ &val);
 
194
    prv_obj = val.val.obj;
 
195
 
 
196
    /* 
 
197
     *   Enumerate objects starting with the next object in the master
 
198
     *   object table after the given object.  Reduce the argument count by
 
199
     *   one, since we've removed the preceding object.  
 
200
     */
 
201
    enum_objects(vmg_ argc - 1, prv_obj + 1);
 
202
}
 
203
 
 
204
/* enum_objects flags */
 
205
#define VMBIFTADS_ENUM_INSTANCES  0x0001
 
206
#define VMBIFTADS_ENUM_CLASSES    0x0002
 
207
 
 
208
/*
 
209
 *   Common handler for firstobj/nextobj object iteration
 
210
 */
 
211
void CVmBifTADS::enum_objects(VMG_ uint argc, vm_obj_id_t start_obj)
 
212
{
 
213
    vm_val_t val;
 
214
    vm_obj_id_t sc;
 
215
    vm_obj_id_t obj;
 
216
    unsigned long flags;
 
217
 
 
218
    /* presume no superclass filter will be specified */
 
219
    sc = VM_INVALID_OBJ;
 
220
 
 
221
    /* presume we're enumerating instances only */
 
222
    flags = VMBIFTADS_ENUM_INSTANCES;
 
223
 
 
224
    /* 
 
225
     *   check arguments - we can optionally have two more arguments: a
 
226
     *   superclass whose instances/subclasses we are to enumerate, and an
 
227
     *   integer giving flag bits 
 
228
     */
 
229
    if (argc == 2)
 
230
    {
 
231
        /* pop the object */
 
232
        G_interpreter->pop_obj(vmg_ &val);
 
233
        sc = val.val.obj;
 
234
 
 
235
        /* pop the flags */
 
236
        flags = pop_long_val(vmg0_);
 
237
    }
 
238
    else if (argc == 1)
 
239
    {
 
240
        /* check to see if it's an object or the flags integer */
 
241
        switch (G_stk->get(0)->typ)
 
242
        {
 
243
        case VM_INT:
 
244
            /* it's the flags */
 
245
            flags = pop_long_val(vmg0_);
 
246
            break;
 
247
 
 
248
        case VM_OBJ:
 
249
            /* it's the superclass filter */
 
250
            G_interpreter->pop_obj(vmg_ &val);
 
251
            sc = val.val.obj;
 
252
            break;
 
253
 
 
254
        default:
 
255
            /* invalid argument type */
 
256
            err_throw(VMERR_BAD_TYPE_BIF);
 
257
        }
 
258
    }
 
259
 
 
260
    /* presume we won't find anything */
 
261
    retval_nil(vmg0_);
 
262
 
 
263
    /* 
 
264
     *   starting with the given object, scan objects until we find one
 
265
     *   that's valid and matches our superclass, if one was provided 
 
266
     */
 
267
    for (obj = start_obj ; obj < G_obj_table->get_max_used_obj_id() ; ++obj)
 
268
    {
 
269
        /* 
 
270
         *   If it's valid, and it's not an intrinsic class modifier object,
 
271
         *   consider it further.  Skip intrinsic class modifiers, since
 
272
         *   they're not really separate objects; they're really part of the
 
273
         *   intrinsic class they modify, and all of the properties and
 
274
         *   methods of a modifier object are reachable through the base
 
275
         *   intrinsic class.  
 
276
         */
 
277
        if (G_obj_table->is_obj_id_valid(obj)
 
278
            && !CVmObjIntClsMod::is_intcls_mod_obj(vmg_ obj))
 
279
        {
 
280
            /* 
 
281
             *   if it's a class, skip it if the flags indicate classes are
 
282
             *   not wanted; if it's an instance, skip it if the flags
 
283
             *   indicate that instances are not wanted 
 
284
             */
 
285
            if (vm_objp(vmg_ obj)->is_class_object(vmg_ obj))
 
286
            {
 
287
                /* it's a class - skip it if classes are not wanted */
 
288
                if ((flags & VMBIFTADS_ENUM_CLASSES) == 0)
 
289
                    continue;
 
290
            }
 
291
            else
 
292
            {
 
293
                /* it's an instance - skip it if instances are not wanted */
 
294
                if ((flags & VMBIFTADS_ENUM_INSTANCES) == 0)
 
295
                    continue;
 
296
            }
 
297
 
 
298
            /* 
 
299
             *   if a superclass was specified, and it matches, we have a
 
300
             *   winner 
 
301
             */
 
302
            if (sc != VM_INVALID_OBJ)
 
303
            {
 
304
                /* if the object matches, return it */
 
305
                if (vm_objp(vmg_ obj)->is_instance_of(vmg_ sc))
 
306
                {
 
307
                    retval_obj(vmg_ obj);
 
308
                    break;
 
309
                }
 
310
            }
 
311
            else
 
312
            {
 
313
                /* 
 
314
                 *   We're enumerating all objects - but skip List and String
 
315
                 *   object, as we expose these are special types.  
 
316
                 */
 
317
                if (vm_objp(vmg_ obj)->get_as_list() == 0
 
318
                    && vm_objp(vmg_ obj)->get_as_string(vmg0_) == 0)
 
319
                {
 
320
                    retval_obj(vmg_ obj);
 
321
                    break;
 
322
                }
 
323
            }
 
324
        }
 
325
    }
 
326
}
 
327
 
 
328
/* ------------------------------------------------------------------------ */
 
329
/*
 
330
 *   Random number generators.  Define one of the following configuration
 
331
 *   variables to select a random number generation algorithm:
 
332
 *   
 
333
 *   VMBIFTADS_RNG_LCG - linear congruential generator
 
334
 *.  VMBIFTADS_RNG_ISAAC - ISAAC (cryptographic hash generator) 
 
335
 */
 
336
 
 
337
/* ------------------------------------------------------------------------ */
 
338
/*
 
339
 *   Linear Congruential Random-Number Generator.  This generator uses an
 
340
 *   algorithm from Knuth, The Art of Computer Programming, Volume 2, p.
 
341
 *   170, with parameters chosen from the same book for their good
 
342
 *   statistical properties and efficiency on 32-bit hardware.  
 
343
 */
 
344
#ifdef VMBIFTADS_RNG_LCG
 
345
/*
 
346
 *   randomize - seed the random-number generator 
 
347
 */
 
348
void CVmBifTADS::randomize(VMG_ uint argc)
 
349
{
 
350
    /* check arguments */
 
351
    check_argc(vmg_ argc, 0);
 
352
 
 
353
    /* seed the generator */
 
354
    os_rand(&G_bif_tads_globals->rand_seed);
 
355
}
 
356
 
 
357
/*
 
358
 *   generate the next random number - linear congruential generator 
 
359
 */
 
360
static ulong rng_next(VMG0_)
 
361
{
 
362
    const ulong a = 1664525L;
 
363
    const ulong c = 1;
 
364
 
 
365
    /* 
 
366
     *   Generate the next random value using the linear congruential
 
367
     *   method described in Knuth, The Art of Computer Programming,
 
368
     *   volume 2, p170.
 
369
     *   
 
370
     *   Use 2^32 as m, hence (n mod m) == (n & 0xFFFFFFFF).  This is
 
371
     *   efficient and is well-suited to 32-bit machines, works fine on
 
372
     *   larger machines, and will even work on 16-bit machines as long as
 
373
     *   the compiler can provide us with 32-bit arithmetic (which we
 
374
     *   assume extensively elsewhere anyway).
 
375
     *   
 
376
     *   We use a = 1664525, a multiplier which has very good results with
 
377
     *   the Spectral Test (see Knuth p102) with our choice of m.
 
378
     *   
 
379
     *   Use c = 1, since this trivially satisfies Knuth's requirements
 
380
     *   about common factors.
 
381
     *   
 
382
     *   Note that the result of the multiplication might overflow a
 
383
     *   32-bit ulong for values of rand_seed that are not small.  This
 
384
     *   doesn't matter, since if it does, the machine will naturally
 
385
     *   truncate high-order bits to yield the result mod 2^32.  So, on a
 
386
     *   32-bit machine, the (&0xFFFFFFFF) part is superfluous, but it's
 
387
     *   harmless and is needed for machines with a larger word size.  
 
388
     */
 
389
    G_bif_tads_globals->rand_seed =
 
390
        (long)(((a * (ulong)G_bif_tads_globals->rand_seed) + 1) & 0xFFFFFFFF);
 
391
    return (ulong)G_bif_tads_globals->rand_seed;
 
392
}
 
393
#endif /* VMBIFTADS_RNG_LCG */
 
394
 
 
395
/* ------------------------------------------------------------------------ */
 
396
/*
 
397
 *   ISAAC random number generator. 
 
398
 */
 
399
 
 
400
#ifdef VMBIFTADS_RNG_ISAAC
 
401
 
 
402
/* service macros for ISAAC random number generator */
 
403
#define isaac_ind(mm,x)  ((mm)[(x>>2)&(ISAAC_RANDSIZ-1)])
 
404
#define isaac_step(mix,a,b,mm,m,m2,r,x) \
 
405
{ \
 
406
    x = *m;  \
 
407
    a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
 
408
    *(m++) = y = (isaac_ind(mm,x) + a + b) & 0xffffffff; \
 
409
    *(r++) = b = (isaac_ind(mm,y>>ISAAC_RANDSIZL) + x) & 0xffffffff; \
 
410
}
 
411
#define isaac_rand(r) \
 
412
    ((r)->cnt-- == 0 ? \
 
413
    (isaac_gen_group(r), (r)->cnt=ISAAC_RANDSIZ-1, (r)->rsl[(r)->cnt]) : \
 
414
    (r)->rsl[(r)->cnt])
 
415
 
 
416
#define isaac_mix(a,b,c,d,e,f,g,h) \
 
417
{ \
 
418
    a^=b<<11; d+=a; b+=c; \
 
419
    b^=c>>2;  e+=b; c+=d; \
 
420
    c^=d<<8;  f+=c; d+=e; \
 
421
    d^=e>>16; g+=d; e+=f; \
 
422
    e^=f<<10; h+=e; f+=g; \
 
423
    f^=g>>4;  a+=f; g+=h; \
 
424
    g^=h<<8;  b+=g; h+=a; \
 
425
    h^=a>>9;  c+=h; a+=b; \
 
426
}
 
427
 
 
428
/* generate the group of numbers */
 
429
static void isaac_gen_group(isaacctx *ctx)
 
430
{
 
431
    ulong a;
 
432
    ulong b;
 
433
    ulong x;
 
434
    ulong y;
 
435
    ulong *m;
 
436
    ulong *mm;
 
437
    ulong *m2;
 
438
    ulong *r;
 
439
    ulong *mend;
 
440
    
 
441
    mm = ctx->mem;
 
442
    r = ctx->rsl;
 
443
    a = ctx->a;
 
444
    b = (ctx->b + (++ctx->c)) & 0xffffffff;
 
445
    for (m = mm, mend = m2 = m + (ISAAC_RANDSIZ/2) ; m<mend ; )
 
446
    {
 
447
        isaac_step(a<<13, a, b, mm, m, m2, r, x);
 
448
        isaac_step(a>>6,  a, b, mm, m, m2, r, x);
 
449
        isaac_step(a<<2,  a, b, mm, m, m2, r, x);
 
450
        isaac_step(a>>16, a, b, mm, m, m2, r, x);
 
451
    }
 
452
    for (m2 = mm; m2<mend; )
 
453
    {
 
454
        isaac_step(a<<13, a, b, mm, m, m2, r, x);
 
455
        isaac_step(a>>6,  a, b, mm, m, m2, r, x);
 
456
        isaac_step(a<<2,  a, b, mm, m, m2, r, x);
 
457
        isaac_step(a>>16, a, b, mm, m, m2, r, x);
 
458
    }
 
459
    ctx->b = b;
 
460
    ctx->a = a;
 
461
}
 
462
 
 
463
/* 
 
464
 *   Initialize.  If flag is true, then use the contents of ctx->rsl[] to
 
465
 *   initialize ctx->mm[]; otherwise, we'll use a fixed starting
 
466
 *   configuration.  
 
467
 */
 
468
static void isaac_init(isaacctx *ctx, int flag)
 
469
{
 
470
    int i;
 
471
    ulong a;
 
472
    ulong b;
 
473
    ulong c;
 
474
    ulong d;
 
475
    ulong e;
 
476
    ulong f;
 
477
    ulong g;
 
478
    ulong h;
 
479
    ulong *m;
 
480
    ulong *r;
 
481
    
 
482
    ctx->a = ctx->b = ctx->c = 0;
 
483
    m = ctx->mem;
 
484
    r = ctx->rsl;
 
485
    a = b = c = d = e = f = g = h = 0x9e3779b9;         /* the golden ratio */
 
486
    
 
487
    /* scramble the initial settings */
 
488
    for (i = 0 ; i < 4 ; ++i)
 
489
    {
 
490
        isaac_mix(a, b, c, d, e, f, g, h);
 
491
    }
 
492
 
 
493
    if (flag) 
 
494
    {
 
495
        /* initialize using the contents of ctx->rsl[] as the seed */
 
496
        for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8)
 
497
        {
 
498
            a += r[i];   b += r[i+1]; c += r[i+2]; d += r[i+3];
 
499
            e += r[i+4]; f += r[i+5]; g += r[i+6]; h += r[i+7];
 
500
            isaac_mix(a, b, c, d, e, f, g, h);
 
501
            m[i] = a;   m[i+1] = b; m[i+2] = c; m[i+3] = d;
 
502
            m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h;
 
503
        }
 
504
 
 
505
        /* do a second pass to make all of the seed affect all of m */
 
506
        for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8)
 
507
        {
 
508
            a += m[i];   b += m[i+1]; c += m[i+2]; d += m[i+3];
 
509
            e += m[i+4]; f += m[i+5]; g += m[i+6]; h += m[i+7];
 
510
            isaac_mix(a, b, c, d, e, f, g, h);
 
511
            m[i] = a;   m[i+1] = b; m[i+2] = c; m[i+3] = d;
 
512
            m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h;
 
513
        }
 
514
    }
 
515
    else
 
516
    {
 
517
        /* initialize using fixed initial settings */
 
518
        for (i = 0 ; i < ISAAC_RANDSIZ ; i += 8)
 
519
        {
 
520
            isaac_mix(a, b, c, d, e, f, g, h);
 
521
            m[i] = a; m[i+1] = b; m[i+2] = c; m[i+3] = d;
 
522
            m[i+4] = e; m[i+5] = f; m[i+6] = g; m[i+7] = h;
 
523
        }
 
524
    }
 
525
 
 
526
    /* fill in the first set of results */
 
527
    isaac_gen_group(ctx);
 
528
 
 
529
    /* prepare to use the first set of results */    
 
530
    ctx->cnt = ISAAC_RANDSIZ;
 
531
}
 
532
 
 
533
/*
 
534
 *   seed the rng 
 
535
 */
 
536
void CVmBifTADS::randomize(VMG_ uint argc)
 
537
{
 
538
    int i;
 
539
    long seed;
 
540
    
 
541
    /* check arguments */
 
542
    check_argc(vmg_ argc, 0);
 
543
 
 
544
    /* seed the generator */
 
545
    os_rand(&seed);
 
546
 
 
547
    /* 
 
548
     *   Fill in rsl[] with the seed. It doesn't do a lot of good to call
 
549
     *   os_rand() repeatedly, since this function might simply return the
 
550
     *   real-time clock value.  So, use the os_rand() seed value as the
 
551
     *   first rsl[] value, then use a simple linear congruential
 
552
     *   generator to fill in the rest of rsl[].  
 
553
     */
 
554
    for (i = 0 ; i < ISAAC_RANDSIZ ; ++i)
 
555
    {
 
556
        const ulong a = 1664525L;
 
557
 
 
558
        /* fill in this value from the previous seed value */
 
559
        G_bif_tads_globals->isaac_ctx->rsl[i] = (ulong)seed;
 
560
 
 
561
        /* generate the next lcg value */
 
562
        seed = (long)(((a * (ulong)seed) + 1) & 0xFFFFFFFF);
 
563
    }
 
564
 
 
565
    /* initialize with this rsl[] array */
 
566
    isaac_init(G_bif_tads_globals->isaac_ctx, TRUE);
 
567
}
 
568
 
 
569
 
 
570
/*
 
571
 *   generate the next random number - ISAAC (by Bob Jenkins,
 
572
 *   http://ourworld.compuserve.com/homepages/bob_jenkins/isaacafa.htm) 
 
573
 */
 
574
static ulong rng_next(VMG0_)
 
575
{
 
576
    /* return the next number */
 
577
    return isaac_rand(G_bif_tads_globals->isaac_ctx);
 
578
}
 
579
#endif /* VMBIFTADS_RNG_ISAAC */
 
580
 
 
581
/* ------------------------------------------------------------------------ */
 
582
/*
 
583
 *   rand - generate a random number, or choose an element randomly from a
 
584
 *   list of values or from our list of arguments.
 
585
 *   
 
586
 *   With one integer argument N, we choose a random number from 0 to N-1.
 
587
 *   
 
588
 *   With one list argument, we choose a random element of the list.
 
589
 *   
 
590
 *   With multiple arguments, we choose one argument at random and return
 
591
 *   its value.  Note that, because this is an ordinary built-in function,
 
592
 *   all of our arguments will be fully evaluated.  
 
593
 */
 
594
void CVmBifTADS::rand(VMG_ uint argc)
 
595
{
 
596
    ulong range;
 
597
    int use_range;
 
598
    int choose_an_arg;
 
599
    const char *listp;
 
600
    ulong rand_val;
 
601
    CVmObjVector *vec = 0;
 
602
    vm_obj_id_t vecid = VM_INVALID_OBJ;
 
603
 
 
604
    /* presume we're not going to choose from our arguments or from a list */
 
605
    choose_an_arg = FALSE;
 
606
    listp = 0;
 
607
 
 
608
    /* determine the desired range of values based on the arguments */
 
609
    if (argc == 0)
 
610
    {
 
611
        /* 
 
612
         *   if no argument is given, produce a random number in our full
 
613
         *   range - clear the 'use_range' flag to so indicate
 
614
         */
 
615
        use_range = FALSE;
 
616
    }
 
617
    else if (argc == 1 && G_stk->get(0)->typ == VM_INT)
 
618
    {
 
619
        /* we're returning a number in the range 0..(arg-1) */
 
620
        range = G_stk->get(0)->val.intval;
 
621
        use_range = TRUE;
 
622
 
 
623
        /* discard the argument */
 
624
        G_stk->discard();
 
625
    }
 
626
    else if (argc == 1)
 
627
    {
 
628
        /* check for a vector or a list */
 
629
        if (G_stk->get(0)->typ == VM_OBJ
 
630
            && CVmObjVector::is_vector_obj(vmg_ G_stk->get(0)->val.obj))
 
631
        {
 
632
            /* 
 
633
             *   it's a vector - get the object pointer, but leave it on the
 
634
             *   stack for GC protection for now 
 
635
             */
 
636
            vecid = G_stk->get(0)->val.obj;
 
637
            vec = (CVmObjVector *)vm_objp(vmg_ vecid);
 
638
 
 
639
            /* the range is 0..(vector_length-1) */
 
640
            range = vec->get_element_count();
 
641
            use_range = TRUE;
 
642
        }
 
643
        else
 
644
        {
 
645
            /* it must be a list - pop the list value */
 
646
            listp = pop_list_val(vmg0_);
 
647
 
 
648
            /* our range is 0..(list_element_count-1) */
 
649
            range = vmb_get_len(listp);
 
650
            use_range = TRUE;
 
651
        }
 
652
    }
 
653
    else
 
654
    {
 
655
        /* 
 
656
         *   produce a random number in the range 0..(argc-1) so that we
 
657
         *   can select one of our arguments 
 
658
         */
 
659
        range = argc;
 
660
        use_range = TRUE;
 
661
 
 
662
        /* note that we should choose an argument value */
 
663
        choose_an_arg = TRUE;
 
664
    }
 
665
 
 
666
    /* get the next random number */
 
667
    rand_val = rng_next(vmg0_);
 
668
 
 
669
    /*
 
670
     *   Calculate our random value in the range 0..(range-1).  If range
 
671
     *   == 0, simply choose a value across our full range.  
 
672
     */
 
673
    if (use_range)
 
674
    {
 
675
        unsigned long hi;
 
676
        unsigned long lo;
 
677
        
 
678
        /* 
 
679
         *   A range was specified, so choose in our range.  As Knuth
 
680
         *   suggests, don't simply take the low-order bits from the value,
 
681
         *   since these are the least random part.  Instead, use the method
 
682
         *   Knuth describes in TAOCP Vol 2 section 3.4.2.
 
683
         *   
 
684
         *   Avoid floating point arithmetic - use an integer calculation
 
685
         *   instead.  This code performs a 64-bit fixed-point calculation
 
686
         *   using 32-bit values.
 
687
         *   
 
688
         *   The calculation we're really performing is this:
 
689
         *   
 
690
         *   rand_val = (ulong)((((double)rand_val) / 4294967296.0)
 
691
         *.             * (double)range); 
 
692
         */
 
693
 
 
694
        /* calculate the high-order 32 bits of (rand_val / 2^32 * range) */
 
695
        hi = (((rand_val >> 16) & 0xffff) * ((range >> 16) & 0xffff))
 
696
             + ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) >> 16)
 
697
             + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) >> 16);
 
698
 
 
699
        /* calculate the low-order 32 bits */
 
700
        lo = ((((rand_val >> 16) & 0xffff) * (range & 0xffff)) & 0xffff)
 
701
             + (((rand_val & 0xffff) * ((range >> 16) & 0xffff)) & 0xffff)
 
702
             + ((((rand_val & 0xffff) * (range & 0xffff)) >> 16) & 0xffff);
 
703
 
 
704
        /* 
 
705
         *   add the carry from the low part into the high part to get the
 
706
         *   result 
 
707
         */
 
708
        rand_val = hi + (lo >> 16);
 
709
    }
 
710
 
 
711
    /*
 
712
     *   Return the appropriate value, depending on our argument list 
 
713
     */
 
714
    if (choose_an_arg)
 
715
    {
 
716
        /* return the selected argument */
 
717
        retval(vmg_ G_stk->get((int)rand_val));
 
718
 
 
719
        /* discard all of the arguments */
 
720
        G_stk->discard(argc);
 
721
    }
 
722
    else if (vec != 0)
 
723
    {
 
724
        vm_val_t val;
 
725
 
 
726
        /* get the selected element */
 
727
        if (range == 0)
 
728
        {
 
729
            /* there are no elements to choose from, so return nil */
 
730
            val.set_nil();
 
731
        }
 
732
        else
 
733
        {
 
734
            vm_val_t idxval;
 
735
 
 
736
            /* get the selected vector element */
 
737
            idxval.set_int(rand_val + 1);
 
738
            vec->index_val(vmg_ &val, vecid, &idxval);
 
739
        }
 
740
 
 
741
        /* set the result */
 
742
        retval(vmg_ &val);
 
743
 
 
744
        /* discard our gc protection */
 
745
        G_stk->discard();
 
746
    }
 
747
    else if (listp != 0)
 
748
    {
 
749
        vm_val_t val;
 
750
 
 
751
        /* as a special case, if the list has zero elements, return nil */
 
752
        if (vmb_get_len(listp) == 0)
 
753
        {
 
754
            /* there are no elements to choose from, so return nil */
 
755
            val.set_nil();
 
756
        }
 
757
        else
 
758
        {
 
759
            /* get the selected list element */
 
760
            vmb_get_dh(listp + VMB_LEN
 
761
                       + (size_t)((rand_val * VMB_DATAHOLDER)), &val);
 
762
        }
 
763
            
 
764
        /* set the result */
 
765
        retval(vmg_ &val);
 
766
    }
 
767
    else
 
768
    {
 
769
        /* simply return the random number */
 
770
        retval_int(vmg_ (long)rand_val);
 
771
    }
 
772
}
 
773
 
 
774
/* ------------------------------------------------------------------------ */
 
775
/*
 
776
 *   Bit-shift generator.  This is from Knuth, The Art of Computer
 
777
 *   Programming, volume 2.  This generator is designed to produce random
 
778
 *   strings of bits and is not suitable for use as a general-purpose RNG.
 
779
 *   
 
780
 *   Linear congruential generators are not ideal for generating random
 
781
 *   bits; their statistical properties seem better suited for generating
 
782
 *   values over a wider range.  This generator is specially designed to
 
783
 *   produce random bits, so it could be a useful complement to an LCG RNG.
 
784
 *   
 
785
 *   This code should not be enabled in its present state; it's retained
 
786
 *   in case we want in the future to implement a generator exclusively
 
787
 *   for random bits.  The ISAAC generator seems to be a good source of
 
788
 *   random bits as well as random numbers, so it seems unlikely that
 
789
 *   we'll need a separate random bit generator.  
 
790
 */
 
791
 
 
792
#ifdef VMBIFTADS_RNG_BITSHIFT
 
793
void CVmBifTADS::randbit(VMG_ uint argc)
 
794
{
 
795
    int top_bit;
 
796
    
 
797
    /* check arguments */
 
798
    check_argc(vmg_ argc, 0);
 
799
 
 
800
    top_bit = (G_bif_tads_globals->rand_seed & 0x8000000);
 
801
    G_bif_tads_globals->rand_seed <<= 1;
 
802
    if (top_bit)
 
803
        G_bif_tads_globals->rand_seed ^= 035604231625;
 
804
 
 
805
    retval_int(vmg_ (long)(G_bif_tads_globals->rand_seed & 1));
 
806
}
 
807
#endif /* VMBIFTADS_RNG_BITSHIFT */
 
808
 
 
809
 
 
810
/* ------------------------------------------------------------------------ */
 
811
/*
 
812
 *   cvtstr (toString) - convert to string 
 
813
 */
 
814
void CVmBifTADS::cvtstr(VMG_ uint argc)
 
815
{
 
816
    const char *p;
 
817
    char buf[50];
 
818
    vm_val_t val;
 
819
    int radix;
 
820
    vm_val_t new_str;
 
821
    
 
822
    /* check arguments */
 
823
    check_argc_range(vmg_ argc, 1, 2);
 
824
 
 
825
    /* pop the argument */
 
826
    G_stk->pop(&val);
 
827
 
 
828
    /* if there's a radix specified, pop it as well */
 
829
    if (argc == 2)
 
830
    {
 
831
        /* get the radix from the stack */
 
832
        radix = pop_int_val(vmg0_);
 
833
    }
 
834
    else
 
835
    {
 
836
        /* use decimal by default */
 
837
        radix = 10;
 
838
    }
 
839
 
 
840
    /* convert the value */
 
841
    p = CVmObjString::cvt_to_str(vmg_ &new_str,
 
842
                                 buf, sizeof(buf), &val, radix);
 
843
 
 
844
    /* save the new string on the stack to protect from garbage collection */
 
845
    G_stk->push(&new_str);
 
846
 
 
847
    /* create and return a string from our new value */
 
848
    retval_obj(vmg_ CVmObjString::create(vmg_ FALSE,
 
849
                                         p + VMB_LEN, vmb_get_len(p)));
 
850
 
 
851
    /* done with the new string */
 
852
    G_stk->discard();
 
853
}
 
854
 
 
855
/*
 
856
 *   cvtnum (toInteger) - convert to an integer
 
857
 */
 
858
void CVmBifTADS::cvtnum(VMG_ uint argc)
 
859
{
 
860
    const char *strp;
 
861
    size_t len;
 
862
    int radix;
 
863
    vm_val_t *valp;
 
864
        
 
865
    /* check arguments */
 
866
    check_argc_range(vmg_ argc, 1, 2);
 
867
 
 
868
    /* 
 
869
     *   check for a BigNumber and convert it (not very object-oriented,
 
870
     *   but this is a type-conversion routine, so special awareness of
 
871
     *   individual types isn't that weird) 
 
872
     */
 
873
    valp = G_stk->get(0);
 
874
    if (valp->typ == VM_OBJ
 
875
        && CVmObjBigNum::is_bignum_obj(vmg_ valp->val.obj))
 
876
    {
 
877
        long intval;
 
878
        
 
879
        /* convert it as a BigNumber */
 
880
        intval = ((CVmObjBigNum *)vm_objp(vmg_ valp->val.obj))
 
881
                 ->convert_to_int();
 
882
 
 
883
        /* discard arguments (ignore the radix in this case) */
 
884
        G_stk->discard(argc);
 
885
 
 
886
        /* return the integer value */
 
887
        retval_int(vmg_ intval);
 
888
        return;
 
889
    }
 
890
 
 
891
    /* if it's already an integer, just return the same value */
 
892
    if (valp->typ == VM_INT)
 
893
    {
 
894
        /* just return the argument value */
 
895
        retval_int(vmg_ valp->val.intval);
 
896
 
 
897
        /* discard arguments (ignore the radix in this case) */
 
898
        G_stk->discard(argc);
 
899
 
 
900
        /* done */
 
901
        return;
 
902
    }
 
903
 
 
904
    /* otherwise, it must be a string */
 
905
    strp = pop_str_val(vmg0_);
 
906
    len = vmb_get_len(strp);
 
907
 
 
908
    /* if there's a radix specified, pop it as well */
 
909
    if (argc == 2)
 
910
    {
 
911
        /* get the radix from the stack */
 
912
        radix = pop_int_val(vmg0_);
 
913
 
 
914
        /* make sure the radix is valid */
 
915
        switch(radix)
 
916
        {
 
917
        case 2:
 
918
        case 8:
 
919
        case 10:
 
920
        case 16:
 
921
            /* it's okay - proceed */
 
922
            break;
 
923
 
 
924
        default:
 
925
            /* other radix values are invalid */
 
926
            err_throw(VMERR_BAD_VAL_BIF);
 
927
        }
 
928
    }
 
929
    else
 
930
    {
 
931
        /* the default radix is decimal */
 
932
        radix = 10;
 
933
    }
 
934
 
 
935
    /* parse the value */
 
936
    if (len == 3 && memcmp(strp + VMB_LEN, "nil", 3) == 0)
 
937
    {
 
938
        /* the value is the constant 'nil' */
 
939
        retval_nil(vmg0_);
 
940
    }
 
941
    else if (len == 4 && memcmp(strp + VMB_LEN, "true", 3) == 0)
 
942
    {
 
943
        /* the value is the constant 'true' */
 
944
        retval_true(vmg0_);
 
945
    }
 
946
    else
 
947
    {
 
948
        utf8_ptr p;
 
949
        size_t rem;
 
950
        int is_neg;
 
951
        ulong acc;
 
952
 
 
953
        /* scan past leading spaces */
 
954
        for (p.set((char *)strp + VMB_LEN), rem = len ;
 
955
             rem != 0 && is_space(p.getch()) ; p.inc(&rem)) ;
 
956
 
 
957
        /* presume it's positive */
 
958
        is_neg = FALSE;
 
959
 
 
960
        /* if the radix is 10, check for a leading + or - */
 
961
        if (radix == 10 && rem != 0)
 
962
        {
 
963
            if (p.getch() == '-')
 
964
            {
 
965
                /* note the sign and skip the character */
 
966
                is_neg = TRUE;
 
967
                p.inc(&rem);
 
968
            }
 
969
            else if (p.getch() == '+')
 
970
            {
 
971
                /* skip the character */
 
972
                p.inc(&rem);
 
973
            }
 
974
        }
 
975
 
 
976
        /* clear the accumulator */
 
977
        acc = 0;
 
978
 
 
979
        /* scan the digits */
 
980
        switch (radix)
 
981
        {
 
982
        case 2:
 
983
            for ( ; rem != 0 && (p.getch() == '0' || p.getch() == '1') ;
 
984
                  p.inc(&rem))
 
985
            {
 
986
                acc <<= 1;
 
987
                if (p.getch() == '1')
 
988
                    acc += 1;
 
989
            }
 
990
            break;
 
991
            
 
992
        case 8:
 
993
            for ( ; rem != 0 && is_odigit(p.getch()) ; p.inc(&rem))
 
994
            {
 
995
                acc <<= 3;
 
996
                acc += value_of_odigit(p.getch());
 
997
            }
 
998
            break;
 
999
 
 
1000
        case 10:
 
1001
            for ( ; rem != 0 && is_digit(p.getch()) ; p.inc(&rem))
 
1002
            {
 
1003
                acc *= 10;
 
1004
                acc += value_of_digit(p.getch());
 
1005
            }
 
1006
            break;
 
1007
 
 
1008
        case 16:
 
1009
            for ( ; rem != 0 && is_xdigit(p.getch()) ; p.inc(&rem))
 
1010
            {
 
1011
                acc <<= 4;
 
1012
                acc += value_of_xdigit(p.getch());
 
1013
            }
 
1014
            break;
 
1015
        }
 
1016
 
 
1017
        /* apply the sign, if appropriate, and set the return value */
 
1018
        if (is_neg)
 
1019
            retval_int(vmg_ -(long)acc);
 
1020
        else
 
1021
            retval_int(vmg_ (long)acc);
 
1022
    }
 
1023
}
 
1024
 
 
1025
/* ------------------------------------------------------------------------ */
 
1026
/*
 
1027
 *   put an integer value in a constant list, advancing the list write
 
1028
 *   pointer 
 
1029
 */
 
1030
static void put_list_int(char **dstp, long intval)
 
1031
{
 
1032
    vm_val_t val;
 
1033
 
 
1034
    /* set up the integer value */
 
1035
    val.set_int(intval);
 
1036
 
 
1037
    /* write it to the list */
 
1038
    vmb_put_dh(*dstp, &val);
 
1039
 
 
1040
    /* advance the output pointer */
 
1041
    *dstp += VMB_DATAHOLDER;
 
1042
}
 
1043
 
 
1044
/*
 
1045
 *   put an object value in a constant list, advancing the list write
 
1046
 *   pointer 
 
1047
 */
 
1048
static void put_list_obj(char **dstp, vm_obj_id_t objval)
 
1049
{
 
1050
    vm_val_t val;
 
1051
 
 
1052
    /* set up the integer value */
 
1053
    val.set_obj(objval);
 
1054
 
 
1055
    /* write it to the list */
 
1056
    vmb_put_dh(*dstp, &val);
 
1057
 
 
1058
    /* advance the output pointer */
 
1059
    *dstp += VMB_DATAHOLDER;
 
1060
}
 
1061
 
 
1062
 
 
1063
/*
 
1064
 *   get the current time 
 
1065
 */
 
1066
void CVmBifTADS::gettime(VMG_ uint argc)
 
1067
{
 
1068
    int typ;
 
1069
    time_t timer;
 
1070
    struct tm *tblock;
 
1071
    char buf[80];
 
1072
    char *dst;
 
1073
    
 
1074
    /* check arguments */
 
1075
    check_argc_range(vmg_ argc, 0, 1);
 
1076
 
 
1077
    /* if there's an argument, get the type of time value to return */
 
1078
    if (argc == 1)
 
1079
    {
 
1080
        /* get the time type code */
 
1081
        typ = pop_int_val(vmg0_);
 
1082
    }
 
1083
    else
 
1084
    {
 
1085
        /* use the default type */
 
1086
        typ = 1;
 
1087
    }
 
1088
 
 
1089
    /* check the type */
 
1090
    switch(typ)
 
1091
    {
 
1092
    case 1:
 
1093
        /* 
 
1094
         *   default information - return the current time and date 
 
1095
         */
 
1096
 
 
1097
        /* make sure the time zone is set up properly */
 
1098
        os_tzset();
 
1099
 
 
1100
        /* get the local time information */
 
1101
        timer = time(NULL);
 
1102
        tblock = localtime(&timer);
 
1103
 
 
1104
        /* adjust values for return format */
 
1105
        tblock->tm_year += 1900;
 
1106
        tblock->tm_mon++;
 
1107
        tblock->tm_wday++;
 
1108
        tblock->tm_yday++;
 
1109
 
 
1110
        /*   
 
1111
         *   build the return list: [year, month, day, day-of-week,
 
1112
         *   day-of-year, hour, minute, second, seconds-since-1970] 
 
1113
         */
 
1114
        vmb_put_len(buf, 9);
 
1115
        dst = buf + VMB_LEN;
 
1116
 
 
1117
        /* build return list value */
 
1118
        put_list_int(&dst, tblock->tm_year);
 
1119
        put_list_int(&dst, tblock->tm_mon);
 
1120
        put_list_int(&dst, tblock->tm_mday);
 
1121
        put_list_int(&dst, tblock->tm_wday);
 
1122
        put_list_int(&dst, tblock->tm_yday);
 
1123
        put_list_int(&dst, tblock->tm_hour);
 
1124
        put_list_int(&dst, tblock->tm_min);
 
1125
        put_list_int(&dst, tblock->tm_sec);
 
1126
        put_list_int(&dst, (long)timer);
 
1127
 
 
1128
        /* allocate and return the list value */
 
1129
        retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf));
 
1130
 
 
1131
        /* done */
 
1132
        break;
 
1133
 
 
1134
    case 2:
 
1135
        /* 
 
1136
         *   They want the high-precision system timer value, which returns
 
1137
         *   the time in milliseconds from an arbitrary zero point.  
 
1138
         */
 
1139
        {
 
1140
            unsigned long t;
 
1141
            static unsigned long t_zero;
 
1142
            static int t_zero_set = FALSE;
 
1143
 
 
1144
            /* retrieve the raw time from the operating system */
 
1145
            t = os_get_sys_clock_ms();
 
1146
 
 
1147
            /* 
 
1148
             *   We only have 31 bits of precision in our result (since we
 
1149
             *   must fit the value into a signed integer), so we can only
 
1150
             *   represent time differences of about 23 days.  Now, the
 
1151
             *   value from the OS could be at any arbitrary point in our
 
1152
             *   23-day range, so there's a nontrivial probability that the
 
1153
             *   raw OS value is near enough to the wrapping point that a
 
1154
             *   future call to this same function during the current
 
1155
             *   session could encounter the wrap condition.  The caller is
 
1156
             *   likely to be confused by this, because the time difference
 
1157
             *   from this call to that future call would appear to be
 
1158
             *   negative.
 
1159
             *   
 
1160
             *   There's obviously no way we can eliminate the possibility
 
1161
             *   of a negative time difference if the current program
 
1162
             *   session lasts more than 23 days of continuous execution.
 
1163
             *   Fortunately, it seems unlikely that most sessions will be
 
1164
             *   so long, which gives us a way to reduce the likelihood that
 
1165
             *   the program will encounter a wrapped timer: we can adjust
 
1166
             *   the zero point of the timer to the time of the first call
 
1167
             *   to this function.  That way, the timer will wrap only if
 
1168
             *   the program session runs continuously until the timer's
 
1169
             *   range is exhausted.  
 
1170
             */
 
1171
            if (!t_zero_set)
 
1172
            {
 
1173
                /* this is the first call - remember the zero point */
 
1174
                t_zero = t;
 
1175
                t_zero_set = TRUE;
 
1176
            }
 
1177
 
 
1178
            /* 
 
1179
             *   Adjust the time by subtracting the zero point from the raw
 
1180
             *   OS timer.  This will give us the number of milliseconds
 
1181
             *   from our zero point.
 
1182
             *   
 
1183
             *   If the system timer has wrapped since our zero point, we'll
 
1184
             *   get what looks like a negative number; but what we really
 
1185
             *   have is a large positive number with a borrow from an
 
1186
             *   unrepresented higher-precision portion, so the fact that
 
1187
             *   this value is negative doesn't matter - it will still be
 
1188
             *   sequential when treated as unsigned.  
 
1189
             */
 
1190
            t -= t_zero;
 
1191
 
 
1192
            /* 
 
1193
             *   whatever we got, keep only the low-order 31 bits, since we
 
1194
             *   only have 31 bits in which to represent an unsigned value
 
1195
             */
 
1196
            t &= 0x7fffffff;
 
1197
 
 
1198
            /* return the value we've calculated */
 
1199
            retval_int(vmg_ t);
 
1200
        }
 
1201
        break;
 
1202
 
 
1203
    default:
 
1204
        err_throw(VMERR_BAD_VAL_BIF);
 
1205
    }
 
1206
}
 
1207
 
 
1208
/* ------------------------------------------------------------------------ */
 
1209
/*
 
1210
 *   re_match - match a regular expression to a string
 
1211
 */
 
1212
void CVmBifTADS::re_match(VMG_ uint argc)
 
1213
{
 
1214
    const char *str;
 
1215
    utf8_ptr p;
 
1216
    size_t len;
 
1217
    int match_len;
 
1218
    vm_val_t *v1, *v2, *v3;
 
1219
    int start_idx;
 
1220
    CVmObjPattern *pat_obj = 0;
 
1221
    const char *pat_str = 0;
 
1222
    
 
1223
    /* check arguments */
 
1224
    check_argc_range(vmg_ argc, 2, 3);
 
1225
 
 
1226
    /* 
 
1227
     *   make copies of the arguments, so we can pop the values without
 
1228
     *   actually removing them from the stack - leave the originals on the
 
1229
     *   stack for gc protection 
 
1230
     */
 
1231
    v1 = G_stk->get(0);
 
1232
    v2 = G_stk->get(1);
 
1233
    v3 = (argc >= 3 ? G_stk->get(2) : 0);
 
1234
    G_stk->push(v2);
 
1235
    G_stk->push(v1);
 
1236
 
 
1237
    /* note the starting index, if given */
 
1238
    start_idx = 0;
 
1239
    if (v3 != 0)
 
1240
    {
 
1241
        /* check the type */
 
1242
        if (v3->typ != VM_INT)
 
1243
            err_throw(VMERR_BAD_TYPE_BIF);
 
1244
 
 
1245
        /* get the value */
 
1246
        start_idx = (int)v3->val.intval - 1;
 
1247
 
 
1248
        /* make sure it's in range */
 
1249
        if (start_idx < 0)
 
1250
            start_idx = 0;
 
1251
    }
 
1252
 
 
1253
    /* remember the last search string (the second argument) */
 
1254
    G_bif_tads_globals->last_rex_str->val = *v2;
 
1255
 
 
1256
    /* 
 
1257
     *   check what we have for the pattern - we could have either a string
 
1258
     *   giving the regular expression, or a RexPattern object with the
 
1259
     *   compiled pattern 
 
1260
     */
 
1261
    if (G_stk->get(0)->typ == VM_OBJ
 
1262
        && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj))
 
1263
    {
 
1264
        vm_val_t pat_val;
 
1265
 
 
1266
        /* get the pattern object */
 
1267
        G_stk->pop(&pat_val);
 
1268
        pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj);
 
1269
    }
 
1270
    else
 
1271
    {
 
1272
        /* get the pattern string */
 
1273
        pat_str = pop_str_val(vmg0_);
 
1274
    }
 
1275
 
 
1276
    /* get the string to match */
 
1277
    str = pop_str_val(vmg0_);
 
1278
    len = vmb_get_len(str);
 
1279
    p.set((char *)str + VMB_LEN);
 
1280
 
 
1281
    /* skip to the starting index */
 
1282
    for ( ; start_idx > 0 && len != 0 ; --start_idx, p.inc(&len)) ;
 
1283
 
 
1284
    /* match the pattern */
 
1285
    if (pat_obj != 0)
 
1286
    {
 
1287
        /* match the compiled pattern object */
 
1288
        match_len = G_bif_tads_globals->rex_searcher->
 
1289
                    match_pattern(pat_obj->get_pattern(vmg0_),
 
1290
                                  str + VMB_LEN, p.getptr(), len);
 
1291
    }
 
1292
    else
 
1293
    {
 
1294
        /* match the pattern to the regular expression string */
 
1295
        match_len = G_bif_tads_globals->rex_searcher->
 
1296
                    compile_and_match(pat_str + VMB_LEN, vmb_get_len(pat_str),
 
1297
                                      str + VMB_LEN, p.getptr(), len);
 
1298
    }
 
1299
 
 
1300
    /* check for a match */
 
1301
    if (match_len >= 0)
 
1302
    {
 
1303
        /* we got a match - calculate the character length of the match */
 
1304
        retval_int(vmg_ (long)p.len(match_len));
 
1305
    }
 
1306
    else
 
1307
    {
 
1308
        /* no match - return nil */
 
1309
        retval_nil(vmg0_);
 
1310
    }
 
1311
 
 
1312
    /* discard the arguments */
 
1313
    G_stk->discard(argc);
 
1314
}
 
1315
 
 
1316
/*
 
1317
 *   re_search - search for a substring matching a regular expression
 
1318
 *   within a string 
 
1319
 */
 
1320
void CVmBifTADS::re_search(VMG_ uint argc)
 
1321
{
 
1322
    const char *str;
 
1323
    utf8_ptr p;
 
1324
    size_t len;
 
1325
    int match_idx;
 
1326
    int match_len;
 
1327
    vm_val_t *v1, *v2, *v3;
 
1328
    int start_idx;
 
1329
    int i;
 
1330
    CVmObjPattern *pat_obj = 0;
 
1331
    const char *pat_str = 0;
 
1332
 
 
1333
    /* check arguments */
 
1334
    check_argc_range(vmg_ argc, 2, 3);
 
1335
 
 
1336
    /* 
 
1337
     *   make copies of the arguments, so we can pop the values without
 
1338
     *   actually removing them from the stack - leave the originals on the
 
1339
     *   stack for gc protection 
 
1340
     */
 
1341
    v1 = G_stk->get(0);
 
1342
    v2 = G_stk->get(1);
 
1343
    v3 = (argc >= 3 ? G_stk->get(2) : 0);
 
1344
    G_stk->push(v2);
 
1345
    G_stk->push(v1);
 
1346
 
 
1347
    /* note the starting index, if given */
 
1348
    start_idx = 0;
 
1349
    if (v3 != 0)
 
1350
    {
 
1351
        /* check the type */
 
1352
        if (v3->typ != VM_INT)
 
1353
            err_throw(VMERR_BAD_TYPE_BIF);
 
1354
 
 
1355
        /* get the value */
 
1356
        start_idx = (int)v3->val.intval - 1;
 
1357
 
 
1358
        /* make sure it's in range */
 
1359
        if (start_idx < 0)
 
1360
            start_idx = 0;
 
1361
    }
 
1362
 
 
1363
    /* remember the last search string (the second argument) */
 
1364
    G_bif_tads_globals->last_rex_str->val = *v2;
 
1365
 
 
1366
    /* check to see if we have a RexPattern object or an uncompiled string */
 
1367
    if (G_stk->get(0)->typ == VM_OBJ
 
1368
        && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj))
 
1369
    {
 
1370
        vm_val_t pat_val;
 
1371
 
 
1372
        /* get the pattern object */
 
1373
        G_stk->pop(&pat_val);
 
1374
        pat_obj = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj);
 
1375
    }
 
1376
    else
 
1377
    {
 
1378
        /* get the pattern string */
 
1379
        pat_str = pop_str_val(vmg0_);
 
1380
    }
 
1381
    
 
1382
    /* get the string to search for the pattern */
 
1383
    str = pop_str_val(vmg0_);
 
1384
    p.set((char *)str + VMB_LEN);
 
1385
    len = vmb_get_len(str);
 
1386
 
 
1387
    /* skip to the starting index */
 
1388
    for (i = start_idx ; i > 0 && len != 0 ; --i, p.inc(&len)) ;
 
1389
 
 
1390
    /* search for the pattern */
 
1391
    if (pat_obj != 0)
 
1392
    {
 
1393
        /* try finding the compiled pattern */
 
1394
        match_idx = G_bif_tads_globals->rex_searcher->search_for_pattern(
 
1395
            pat_obj->get_pattern(vmg0_),
 
1396
            str + VMB_LEN, p.getptr(), len, &match_len);
 
1397
    }
 
1398
    else
 
1399
    {
 
1400
        /* try finding the regular expression string pattern */
 
1401
        match_idx = G_bif_tads_globals->rex_searcher->compile_and_search(
 
1402
            pat_str + VMB_LEN, vmb_get_len(pat_str),
 
1403
            str + VMB_LEN, p.getptr(), len, &match_len);
 
1404
    }
 
1405
 
 
1406
    /* check for a match */
 
1407
    if (match_idx >= 0)
 
1408
    {
 
1409
        utf8_ptr matchp;
 
1410
        size_t char_idx;
 
1411
        size_t char_len;
 
1412
        vm_obj_id_t match_str_obj;
 
1413
        char *dst;
 
1414
        char buf[VMB_LEN + VMB_DATAHOLDER * 3];
 
1415
 
 
1416
        /* 
 
1417
         *   We got a match - calculate the character index of the match
 
1418
         *   offset, adjusted to a 1-base.  The character index is simply the
 
1419
         *   number of characters in the part of the string up to the match
 
1420
         *   index.  Note that we have to add the starting index to get the
 
1421
         *   actual index in the overall string, since 'p' points to the
 
1422
         *   character at the starting index.  
 
1423
         */
 
1424
        char_idx = p.len(match_idx) + start_idx + 1;
 
1425
 
 
1426
        /* calculate the character length of the match */
 
1427
        matchp.set(p.getptr() + match_idx);
 
1428
        char_len = matchp.len(match_len);
 
1429
 
 
1430
        /* allocate a string containing the match */
 
1431
        match_str_obj =
 
1432
            CVmObjString::create(vmg_ FALSE, matchp.getptr(), match_len);
 
1433
 
 
1434
        /* push it momentarily as protection against garbage collection */
 
1435
        G_stk->push()->set_obj(match_str_obj);
 
1436
 
 
1437
        /* 
 
1438
         *   set up a 3-element list to contain the return value:
 
1439
         *   [match_start_index, match_length, match_string] 
 
1440
         */
 
1441
        vmb_put_len(buf, 3);
 
1442
        dst = buf + VMB_LEN;
 
1443
        put_list_int(&dst, (long)char_idx);
 
1444
        put_list_int(&dst, (long)char_len);
 
1445
        put_list_obj(&dst, match_str_obj);
 
1446
 
 
1447
        /* allocate and return the list */
 
1448
        retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf));
 
1449
 
 
1450
        /* we no longer need the garbage collection protection */
 
1451
        G_stk->discard();
 
1452
    }
 
1453
    else
 
1454
    {
 
1455
        /* no match - return nil */
 
1456
        retval_nil(vmg0_);
 
1457
    }
 
1458
 
 
1459
    /* discard the arguments */
 
1460
    G_stk->discard(argc);
 
1461
}
 
1462
 
 
1463
/*
 
1464
 *   re_group - get the string matching a group in the most recent regular
 
1465
 *   expression search or match 
 
1466
 */
 
1467
void CVmBifTADS::re_group(VMG_ uint argc)
 
1468
{
 
1469
    int groupno;
 
1470
    const re_group_register *reg;
 
1471
    char buf[VMB_LEN + 3*VMB_DATAHOLDER];
 
1472
    char *dst;
 
1473
    utf8_ptr p;
 
1474
    vm_obj_id_t strobj;
 
1475
    const char *last_str;
 
1476
    int start_byte_ofs;
 
1477
    
 
1478
    /* check arguments */
 
1479
    check_argc(vmg_ argc, 1);
 
1480
 
 
1481
    /* get the group number to retrieve */
 
1482
    groupno = pop_int_val(vmg0_);
 
1483
 
 
1484
    /* make sure it's in range */
 
1485
    if (groupno < 1 || groupno > RE_GROUP_REG_CNT)
 
1486
        err_throw(VMERR_BAD_VAL_BIF);
 
1487
 
 
1488
    /* adjust from a 1-base to a 0-base */
 
1489
    --groupno;
 
1490
 
 
1491
    /* if the group doesn't exist in the pattern, return nil */
 
1492
    if (groupno >= G_bif_tads_globals->rex_searcher->get_group_cnt())
 
1493
    {
 
1494
        retval_nil(vmg0_);
 
1495
        return;
 
1496
    }
 
1497
 
 
1498
    /* 
 
1499
     *   get the previous search string - get a pointer directly to the
 
1500
     *   contents of the string
 
1501
     */
 
1502
    last_str = G_bif_tads_globals->last_rex_str->val.get_as_string(vmg0_);
 
1503
 
 
1504
    /* get the register */
 
1505
    reg = G_bif_tads_globals->rex_searcher->get_group_reg(groupno);
 
1506
 
 
1507
    /* if the group wasn't set, or there's no last string, return nil */
 
1508
    if (last_str == 0 || reg->start_ofs == -1 || reg->end_ofs == -1)
 
1509
    {
 
1510
        retval_nil(vmg0_);
 
1511
        return;
 
1512
    }
 
1513
    
 
1514
    /* set up for a list with three elements */
 
1515
    vmb_put_len(buf, 3);
 
1516
    dst = buf + VMB_LEN;
 
1517
 
 
1518
    /* get the starting offset from the group register */
 
1519
    start_byte_ofs = reg->start_ofs;
 
1520
 
 
1521
    /* 
 
1522
     *   The first element is the character index of the group text in the
 
1523
     *   source string.  Calculate the character index by adding 1 to the
 
1524
     *   character length of the text preceding the group; calculate the
 
1525
     *   character length from the byte length of that string.  Note that the
 
1526
     *   starting in the group register is stored from the starting point of
 
1527
     *   the search, not the start of the string, so we need to add in the
 
1528
     *   starting point in the search.  
 
1529
     */
 
1530
    p.set((char *)last_str + VMB_LEN);
 
1531
    put_list_int(&dst, p.len(start_byte_ofs) + 1);
 
1532
 
 
1533
    /* 
 
1534
     *   The second element is the character length of the group text.
 
1535
     *   Calculate the character length from the byte length. 
 
1536
     */
 
1537
    p.set(p.getptr() + start_byte_ofs);
 
1538
    put_list_int(&dst, p.len(reg->end_ofs - reg->start_ofs));
 
1539
 
 
1540
    /*
 
1541
     *   The third element is the string itself.  Create a new string
 
1542
     *   containing the matching substring. 
 
1543
     */
 
1544
    strobj = CVmObjString::create(vmg_ FALSE, p.getptr(),
 
1545
                                  reg->end_ofs - reg->start_ofs);
 
1546
    put_list_obj(&dst, strobj);
 
1547
 
 
1548
    /* save the string on the stack momentarily to protect against GC */
 
1549
    G_stk->push()->set_obj(strobj);
 
1550
 
 
1551
    /* create and return the list value */
 
1552
    retval_obj(vmg_ CVmObjList::create(vmg_ FALSE, buf));
 
1553
 
 
1554
    /* we no longer need the garbage collector protection */
 
1555
    G_stk->discard();
 
1556
}
 
1557
 
 
1558
/*
 
1559
 *   re_replace flags 
 
1560
 */
 
1561
#define VMBIFTADS_REPLACE_ALL  0x0001
 
1562
 
 
1563
/*
 
1564
 *   re_replace - search for a pattern in a string, and apply a
 
1565
 *   replacement pattern
 
1566
 */
 
1567
void CVmBifTADS::re_replace(VMG_ uint argc)
 
1568
{
 
1569
    vm_val_t patval, rplval;
 
1570
    const char *str;
 
1571
    const char *rpl;
 
1572
    ulong flags;
 
1573
    vm_val_t search_val;
 
1574
    int match_idx;
 
1575
    int match_len;
 
1576
    size_t new_len;
 
1577
    utf8_ptr p;
 
1578
    size_t rem;
 
1579
    int groupno;
 
1580
    const re_group_register *reg;
 
1581
    vm_obj_id_t ret_obj;
 
1582
    utf8_ptr dstp;
 
1583
    int match_cnt;
 
1584
    int start_idx;
 
1585
    re_compiled_pattern *cpat;
 
1586
    int cpat_is_ours;
 
1587
    int group_cnt;
 
1588
    int start_char_idx;
 
1589
    int skip_bytes;
 
1590
 
 
1591
    /* check arguments */
 
1592
    check_argc_range(vmg_ argc, 4, 5);
 
1593
 
 
1594
    /* remember the pattern and replacement string values */
 
1595
    patval = *G_stk->get(0);
 
1596
    rplval = *G_stk->get(2);
 
1597
 
 
1598
    /* retrieve the compiled RexPattern or uncompiled pattern string */
 
1599
    if (G_stk->get(0)->typ == VM_OBJ
 
1600
        && CVmObjPattern::is_pattern_obj(vmg_ G_stk->get(0)->val.obj))
 
1601
    {
 
1602
        vm_val_t pat_val;
 
1603
        CVmObjPattern *pat;
 
1604
 
 
1605
        /* get the pattern object */
 
1606
        G_stk->pop(&pat_val);
 
1607
        pat = (CVmObjPattern *)vm_objp(vmg_ pat_val.val.obj);
 
1608
 
 
1609
        /* get the compiled pattern structure */
 
1610
        cpat = pat->get_pattern(vmg0_);
 
1611
 
 
1612
        /* the pattern isn't ours, so we don't need to delete it */
 
1613
        cpat_is_ours = FALSE;
 
1614
    }
 
1615
    else
 
1616
    {
 
1617
        re_status_t stat;
 
1618
        const char *pat_str;
 
1619
 
 
1620
        /* pop the pattern string */
 
1621
        pat_str = pop_str_val(vmg0_);
 
1622
 
 
1623
        /* since we'll need it multiple times, compile it */
 
1624
        stat = G_bif_tads_globals->rex_parser->compile_pattern(
 
1625
            pat_str + VMB_LEN, vmb_get_len(pat_str), &cpat);
 
1626
 
 
1627
        /* if that failed, we don't have a pattern */
 
1628
        if (stat != RE_STATUS_SUCCESS)
 
1629
            cpat = 0;
 
1630
 
 
1631
        /* note that we allocated the pattern, so we have to delete it */
 
1632
        cpat_is_ours = TRUE;
 
1633
    }
 
1634
 
 
1635
    /* 
 
1636
     *   Pop the search string and the replacement string.  Note that we want
 
1637
     *   to retain the original value information for the search string,
 
1638
     *   since we'll end up returning it unchanged if we don't find the
 
1639
     *   pattern.  
 
1640
     */
 
1641
    G_stk->pop(&search_val);
 
1642
    rpl = pop_str_val(vmg0_);
 
1643
 
 
1644
    /* remember the last search string */
 
1645
    G_bif_tads_globals->last_rex_str->val = search_val;
 
1646
 
 
1647
    /* pop the flags */
 
1648
    flags = pop_long_val(vmg0_);
 
1649
 
 
1650
    /* pop the starting index if given */
 
1651
    start_char_idx = (argc >= 5 ? pop_int_val(vmg0_) - 1 : 0);
 
1652
 
 
1653
    /* make sure it's in range */
 
1654
    if (start_char_idx < 0)
 
1655
        start_char_idx = 0;
 
1656
 
 
1657
    /* 
 
1658
     *   put the pattern, replacement string, and search string values back
 
1659
     *   on the stack as protection against garbage collection 
 
1660
     */
 
1661
    G_stk->push(&patval);
 
1662
    G_stk->push(&rplval);
 
1663
    G_stk->push(&search_val);
 
1664
 
 
1665
    /* make sure the search string is indeed a string */
 
1666
    str = search_val.get_as_string(vmg0_);
 
1667
    if (str == 0)
 
1668
        err_throw(VMERR_STRING_VAL_REQD);
 
1669
 
 
1670
    /* 
 
1671
     *   figure out how many bytes at the start of the string to skip before
 
1672
     *   our first replacement 
 
1673
     */
 
1674
    for (p.set((char *)str + VMB_LEN), rem = vmb_get_len(str) ;
 
1675
         start_char_idx > 0 && rem != 0 ; --start_char_idx, p.inc(&rem)) ;
 
1676
 
 
1677
    /* the current offset in the string is the byte skip offset */
 
1678
    skip_bytes = p.getptr() - (str + VMB_LEN);
 
1679
 
 
1680
    /* 
 
1681
     *   if we don't have a compiled pattern at this point, we're not going
 
1682
     *   to be able to match anything, so we can just stop now and return the
 
1683
     *   original string unchanged 
 
1684
     */
 
1685
    if (cpat == 0)
 
1686
    {
 
1687
        /* return the original search string */
 
1688
        retval(vmg_ &search_val);
 
1689
        goto done;
 
1690
    }
 
1691
 
 
1692
    /* note the group count in the compiled pattern */
 
1693
    group_cnt = cpat->group_cnt;
 
1694
 
 
1695
    /*
 
1696
     *   First, determine how long the result string will be.  Search
 
1697
     *   repeatedly if the REPLACE_ALL flag (0x0001) is set.
 
1698
     */
 
1699
    for (new_len = skip_bytes, match_cnt = 0, start_idx = skip_bytes ;
 
1700
         (size_t)start_idx < vmb_get_len(str) ; ++match_cnt)
 
1701
    {
 
1702
        const char *last_str;
 
1703
 
 
1704
        /* figure out where the next search starts */
 
1705
        last_str = str + VMB_LEN + start_idx;
 
1706
 
 
1707
        /* search for the pattern in the search string */
 
1708
        match_idx = G_bif_tads_globals->rex_searcher->search_for_pattern(
 
1709
            cpat, str + VMB_LEN, last_str, vmb_get_len(str) - start_idx,
 
1710
            &match_len);
 
1711
 
 
1712
        /* if there was no match, there is no more replacing to do */
 
1713
        if (match_idx == -1)
 
1714
        {
 
1715
            /* 
 
1716
             *   if we haven't found a match before, there's no
 
1717
             *   replacement at all to do -- just return the original
 
1718
             *   string unchanged 
 
1719
             */
 
1720
            if (match_cnt == 0)
 
1721
            {
 
1722
                /* no replacement - return the original search string */
 
1723
                retval(vmg_ &search_val);
 
1724
                goto done;
 
1725
            }
 
1726
            else
 
1727
            {
 
1728
                /* we've found all of our matches - stop searching */
 
1729
                break;
 
1730
            }
 
1731
        }
 
1732
 
 
1733
        /*
 
1734
         *   We've found a match to replace.  Determine how much space we
 
1735
         *   need for the replacement pattern with its substitution
 
1736
         *   parameters replaced with the original string's matching text.
 
1737
         *   
 
1738
         *   First, add in the length of the part from the start of this
 
1739
         *   segment of the search to the matched substring.  
 
1740
         */
 
1741
        new_len += match_idx;
 
1742
 
 
1743
        /* 
 
1744
         *   now, scan the replacement string and add in its length and
 
1745
         *   the lengths of substitution parameters 
 
1746
         */
 
1747
        for (p.set((char *)rpl + VMB_LEN), rem = vmb_get_len(rpl) ;
 
1748
             rem != 0 ; p.inc(&rem))
 
1749
        {
 
1750
            /* check for '%' sequences */
 
1751
            if (p.getch() == '%')
 
1752
            {
 
1753
                /* skip the '%' */
 
1754
                p.inc(&rem);
 
1755
                
 
1756
                /* if there's anything left, see what we have */
 
1757
                if (rem != 0)
 
1758
                {
 
1759
                    switch(p.getch())
 
1760
                    {
 
1761
                    case '1':
 
1762
                    case '2':
 
1763
                    case '3':
 
1764
                    case '4':
 
1765
                    case '5':
 
1766
                    case '6':
 
1767
                    case '7':
 
1768
                    case '8':
 
1769
                    case '9':
 
1770
                        /* get the group number */
 
1771
                        groupno = value_of_digit(p.getch()) - 1;
 
1772
                        
 
1773
                        /* if this group is valid, add its length */
 
1774
                        if (groupno < group_cnt)
 
1775
                        {
 
1776
                            /* get the register */
 
1777
                            reg = G_bif_tads_globals->rex_searcher
 
1778
                                  ->get_group_reg(groupno);
 
1779
                            
 
1780
                            /* if it's been set, add its length */
 
1781
                            if (reg->start_ofs != -1 && reg->end_ofs != -1)
 
1782
                                new_len += reg->end_ofs - reg->start_ofs;
 
1783
                        }
 
1784
                        break;
 
1785
                        
 
1786
                    case '*':
 
1787
                        /* add the entire match size */
 
1788
                        new_len += match_len;
 
1789
                        break;
 
1790
                        
 
1791
                    case '%':
 
1792
                        /* add a single '%' */
 
1793
                        ++new_len;
 
1794
                        break;
 
1795
                        
 
1796
                    default:
 
1797
                        /* add the entire sequence unchanged */
 
1798
                        new_len += 2;
 
1799
                        break;
 
1800
                    }
 
1801
                }
 
1802
            }
 
1803
            else
 
1804
            {
 
1805
                /* count this character literally */
 
1806
                new_len += p.charsize();
 
1807
            }
 
1808
        }
 
1809
 
 
1810
        /* start the next search after the end of this match */
 
1811
        start_idx += match_idx + match_len;
 
1812
 
 
1813
        /* 
 
1814
         *   if the match length was zero, skip one more character - a zero
 
1815
         *   length match will just match again at the same spot forever, so
 
1816
         *   once we replace it once we need to move on to avoid an infinite
 
1817
         *   loop 
 
1818
         */
 
1819
        if (match_len == 0)
 
1820
        {
 
1821
            /* move past the input */
 
1822
            start_idx += 1;
 
1823
 
 
1824
            /* we'll copy this character to the output, so make room for it */
 
1825
            new_len += 1;
 
1826
        }
 
1827
 
 
1828
        /* 
 
1829
         *   if we're only replacing a single match, stop now; otherwise,
 
1830
         *   continue looking 
 
1831
         */
 
1832
        if (!(flags & VMBIFTADS_REPLACE_ALL))
 
1833
            break;
 
1834
    }
 
1835
 
 
1836
    /* add in the size of the remainder of the string after the last match */
 
1837
    new_len += vmb_get_len(str) - start_idx;
 
1838
 
 
1839
    /* allocate the result string */
 
1840
    ret_obj = CVmObjString::create(vmg_ FALSE, new_len);
 
1841
 
 
1842
    /* get a pointer to the result buffer */
 
1843
    dstp.set(((CVmObjString *)vm_objp(vmg_ ret_obj))->cons_get_buf());
 
1844
 
 
1845
    /* copy the initial part that we're skipping */
 
1846
    if (skip_bytes != 0)
 
1847
    {
 
1848
        memcpy(dstp.getptr(), str + VMB_LEN, skip_bytes);
 
1849
        dstp.set(dstp.getptr() + skip_bytes);
 
1850
    }
 
1851
 
 
1852
    /*
 
1853
     *   Once again, start searching from the beginning of the string.
 
1854
     *   This time, build the result string as we go. 
 
1855
     */
 
1856
    for (start_idx = skip_bytes ; (size_t)start_idx < vmb_get_len(str) ; )
 
1857
    {
 
1858
        const char *last_str;
 
1859
 
 
1860
        /* figure out where the next search starts */
 
1861
        last_str = str + VMB_LEN + start_idx;
 
1862
 
 
1863
        /* search for the pattern */
 
1864
        match_idx = G_bif_tads_globals->rex_searcher->search_for_pattern(
 
1865
            cpat, str + VMB_LEN, last_str, vmb_get_len(str) - start_idx,
 
1866
            &match_len);
 
1867
 
 
1868
        /* stop if we can't find another match */
 
1869
        if (match_idx < 0)
 
1870
            break;
 
1871
        
 
1872
        /* copy the part up to the start of the matched text, if any */
 
1873
        if (match_idx > 0)
 
1874
        {
 
1875
            /* copy the part from the last match to this match */
 
1876
            memcpy(dstp.getptr(), last_str, match_idx);
 
1877
 
 
1878
            /* advance the output pointer */
 
1879
            dstp.set(dstp.getptr() + match_idx);
 
1880
        }
 
1881
 
 
1882
        /*
 
1883
         *   Scan the replacement string again, and this time actually
 
1884
         *   build the result.  
 
1885
         */
 
1886
        for (p.set((char *)rpl + VMB_LEN), rem = vmb_get_len(rpl) ;
 
1887
             rem != 0 ; p.inc(&rem))
 
1888
        {
 
1889
            /* check for '%' sequences */
 
1890
            if (p.getch() == '%')
 
1891
            {
 
1892
                /* skip the '%' */
 
1893
                p.inc(&rem);
 
1894
                
 
1895
                /* if there's anything left, see what we have */
 
1896
                if (rem != 0)
 
1897
                {
 
1898
                    switch(p.getch())
 
1899
                    {
 
1900
                    case '1':
 
1901
                    case '2':
 
1902
                    case '3':
 
1903
                    case '4':
 
1904
                    case '5':
 
1905
                    case '6':
 
1906
                    case '7':
 
1907
                    case '8':
 
1908
                    case '9':
 
1909
                        /* get the group number */
 
1910
                        groupno = value_of_digit(p.getch()) - 1;
 
1911
                        
 
1912
                        /* if this group is valid, add its length */
 
1913
                        if (groupno < group_cnt)
 
1914
                        {
 
1915
                            /* get the register */
 
1916
                            reg = G_bif_tads_globals->rex_searcher
 
1917
                                  ->get_group_reg(groupno);
 
1918
                            
 
1919
                            /* if it's been set, add its text */
 
1920
                            if (reg->start_ofs != -1 && reg->end_ofs != -1)
 
1921
                            {
 
1922
                                size_t glen;
 
1923
 
 
1924
                                /* get the group length */
 
1925
                                glen = reg->end_ofs - reg->start_ofs;
 
1926
 
 
1927
                                /* copy the data */
 
1928
                                memcpy(dstp.getptr(),
 
1929
                                       str + VMB_LEN + reg->start_ofs, glen);
 
1930
 
 
1931
                                /* advance past it */
 
1932
                                dstp.set(dstp.getptr() + glen);
 
1933
                            }
 
1934
                        }
 
1935
                        break;
 
1936
 
 
1937
                    case '*':
 
1938
                        /* add the entire matched string */
 
1939
                        memcpy(dstp.getptr(), last_str + match_idx,
 
1940
                               match_len);
 
1941
                        dstp.set(dstp.getptr() + match_len);
 
1942
                        break;
 
1943
                        
 
1944
                    case '%':
 
1945
                        /* add a single '%' */
 
1946
                        dstp.setch('%');
 
1947
                        break;
 
1948
                        
 
1949
                    default:
 
1950
                        /* add the entire sequence unchanged */
 
1951
                        dstp.setch('%');
 
1952
                        dstp.setch(p.getch());
 
1953
                        break;
 
1954
                    }
 
1955
                }
 
1956
            }
 
1957
            else
 
1958
            {
 
1959
                /* copy this character literally */
 
1960
                dstp.setch(p.getch());
 
1961
            }
 
1962
        }
 
1963
 
 
1964
        /* advance past this matched string for the next search */
 
1965
        start_idx += match_idx + match_len;
 
1966
 
 
1967
        /* skip to the next character if it was a zero-length match */
 
1968
        if (match_len == 0)
 
1969
        {
 
1970
            /* copy the character we're skipping to the output */
 
1971
            p.set((char *)str + VMB_LEN + start_idx);
 
1972
            dstp.setch(p.getch());
 
1973
 
 
1974
            /* move on to the next character */
 
1975
            start_idx += 1;
 
1976
        }
 
1977
 
 
1978
        /* if we're only performing a single replacement, stop now */
 
1979
        if (!(flags & VMBIFTADS_REPLACE_ALL))
 
1980
            break;
 
1981
    }
 
1982
 
 
1983
    /* add the part after the end of the matched text */
 
1984
    if ((size_t)start_idx < vmb_get_len(str))
 
1985
        memcpy(dstp.getptr(), str + VMB_LEN + start_idx,
 
1986
               vmb_get_len(str) - start_idx);
 
1987
 
 
1988
    /* return the string */
 
1989
    retval_obj(vmg_ ret_obj);
 
1990
 
 
1991
done:
 
1992
    /* discard the garbage collection protection references */
 
1993
    G_stk->discard(3);
 
1994
 
 
1995
    /* if we created the pattern string, delete it */
 
1996
    if (cpat != 0 && cpat_is_ours)
 
1997
        CRegexParser::free_pattern(cpat);
 
1998
}
 
1999
 
 
2000
/* ------------------------------------------------------------------------ */
 
2001
/*
 
2002
 *   savepoint - establish an undo savepoint
 
2003
 */
 
2004
void CVmBifTADS::savepoint(VMG_ uint argc)
 
2005
{
 
2006
    /* check arguments */
 
2007
    check_argc(vmg_ argc, 0);
 
2008
 
 
2009
    /* establish the savepoint */
 
2010
    G_undo->create_savept(vmg0_);
 
2011
}
 
2012
 
 
2013
/*
 
2014
 *   undo - undo changes to most recent savepoint
 
2015
 */
 
2016
void CVmBifTADS::undo(VMG_ uint argc)
 
2017
{
 
2018
    /* check arguments */
 
2019
    check_argc(vmg_ argc, 0);
 
2020
 
 
2021
    /* if no undo is available, return nil to indicate that we can't undo */
 
2022
    if (G_undo->get_savept_cnt() == 0)
 
2023
    {
 
2024
        /* we can't undo */
 
2025
        retval_nil(vmg0_);
 
2026
    }
 
2027
    else
 
2028
    {
 
2029
        /* undo to the savepoint */
 
2030
        G_undo->undo_to_savept(vmg0_);
 
2031
 
 
2032
        /* tell the caller that we succeeded */
 
2033
        retval_true(vmg0_);
 
2034
    }
 
2035
}
 
2036
 
 
2037
/* ------------------------------------------------------------------------ */
 
2038
/*
 
2039
 *   save 
 
2040
 */
 
2041
void CVmBifTADS::save(VMG_ uint argc)
 
2042
{
 
2043
    char fname[OSFNMAX];
 
2044
    CVmFile *file;
 
2045
    osfildef *fp;
 
2046
    
 
2047
    /* check arguments */
 
2048
    check_argc(vmg_ argc, 1);
 
2049
 
 
2050
    /* get the filename as a null-terminated string */
 
2051
    pop_str_val_fname(vmg_ fname, sizeof(fname));
 
2052
 
 
2053
    /* open the file */
 
2054
    fp = osfoprwtb(fname, OSFTT3SAV);
 
2055
    if (fp == 0)
 
2056
        err_throw(VMERR_CREATE_FILE);
 
2057
 
 
2058
    /* set up the file writer */
 
2059
    file = new CVmFile();
 
2060
    file->set_file(fp, 0);
 
2061
 
 
2062
    err_try
 
2063
    {
 
2064
        /* save the state */
 
2065
        CVmSaveFile::save(vmg_ file);
 
2066
    }
 
2067
    err_finally
 
2068
    {
 
2069
        /* close the file */
 
2070
        delete file;
 
2071
    }
 
2072
    err_end;
 
2073
}
 
2074
 
 
2075
/*
 
2076
 *   restore
 
2077
 */
 
2078
void CVmBifTADS::restore(VMG_ uint argc)
 
2079
{
 
2080
    char fname[OSFNMAX];
 
2081
    CVmFile *file;
 
2082
    osfildef *fp;
 
2083
    int err;
 
2084
 
 
2085
    /* check arguments */
 
2086
    check_argc(vmg_ argc, 1);
 
2087
 
 
2088
    /* get the filename as a null-terminated string */
 
2089
    pop_str_val_fname(vmg_ fname, sizeof(fname));
 
2090
 
 
2091
    /* open the file */
 
2092
    fp = osfoprb(fname, OSFTT3SAV);
 
2093
    if (fp == 0)
 
2094
        err_throw(VMERR_FILE_NOT_FOUND);
 
2095
 
 
2096
    /* set up the file reader */
 
2097
    file = new CVmFile();
 
2098
    file->set_file(fp, 0);
 
2099
 
 
2100
    err_try
 
2101
    {
 
2102
        /* restore the state */
 
2103
        err = CVmSaveFile::restore(vmg_ file);
 
2104
    }
 
2105
    err_finally
 
2106
    {
 
2107
        /* close the file */
 
2108
        delete file;
 
2109
    }
 
2110
    err_end;
 
2111
 
 
2112
    /* if an error occurred, throw an exception */
 
2113
    if (err != 0)
 
2114
        err_throw(err);
 
2115
}
 
2116
 
 
2117
/*
 
2118
 *   restart
 
2119
 */
 
2120
void CVmBifTADS::restart(VMG_ uint argc)
 
2121
{
 
2122
    /* check arguments */
 
2123
    check_argc(vmg_ argc, 0);
 
2124
 
 
2125
    /* reset the VM to the image file's initial state */
 
2126
    CVmSaveFile::reset(vmg0_);
 
2127
}
 
2128
 
 
2129
 
 
2130
/* ------------------------------------------------------------------------ */
 
2131
/*
 
2132
 *   Get the maximum value from a set of argument 
 
2133
 */
 
2134
void CVmBifTADS::get_max(VMG_ uint argc)
 
2135
{
 
2136
    uint i;
 
2137
    vm_val_t cur_max;
 
2138
    
 
2139
    /* make sure we have at least one argument */
 
2140
    if (argc < 1)
 
2141
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
 
2142
 
 
2143
    /* start with the first argument as the presumptive maximum */
 
2144
    cur_max = *G_stk->get(0);
 
2145
 
 
2146
    /* compare each argument in turn */
 
2147
    for (i = 1 ; i < argc ; ++i)
 
2148
    {
 
2149
        /* 
 
2150
         *   compare this value to the maximum so far; if this value is
 
2151
         *   greater, it becomes the new maximum so far 
 
2152
         */
 
2153
        if (G_stk->get(i)->compare_to(vmg_ &cur_max) > 0)
 
2154
            cur_max = *G_stk->get(i);
 
2155
    }
 
2156
 
 
2157
    /* discard the arguments */
 
2158
    G_stk->discard(argc);
 
2159
 
 
2160
    /* return the maximum value */
 
2161
    retval(vmg_ &cur_max);
 
2162
}
 
2163
 
 
2164
/*
 
2165
 *   Get the minimum value from a set of argument 
 
2166
 */
 
2167
void CVmBifTADS::get_min(VMG_ uint argc)
 
2168
{
 
2169
    uint i;
 
2170
    vm_val_t cur_min;
 
2171
 
 
2172
    /* make sure we have at least one argument */
 
2173
    if (argc < 1)
 
2174
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
 
2175
 
 
2176
    /* start with the first argument as the presumptive minimum */
 
2177
    cur_min = *G_stk->get(0);
 
2178
 
 
2179
    /* compare each argument in turn */
 
2180
    for (i = 1 ; i < argc ; ++i)
 
2181
    {
 
2182
        /* 
 
2183
         *   compare this value to the minimum so far; if this value is
 
2184
         *   less, it becomes the new minimum so far 
 
2185
         */
 
2186
        if (G_stk->get(i)->compare_to(vmg_ &cur_min) < 0)
 
2187
            cur_min = *G_stk->get(i);
 
2188
    }
 
2189
 
 
2190
    /* discard the arguments */
 
2191
    G_stk->discard(argc);
 
2192
 
 
2193
    /* return the minimum value */
 
2194
    retval(vmg_ &cur_min);
 
2195
}
 
2196
 
 
2197
/* ------------------------------------------------------------------------ */
 
2198
/*
 
2199
 *   makeString - construct a string by repeating a character; by
 
2200
 *   converting a unicode code point to a string; or by converting a list
 
2201
 *   of unicode code points to a string 
 
2202
 */
 
2203
void CVmBifTADS::make_string(VMG_ uint argc)
 
2204
{
 
2205
    vm_val_t val;
 
2206
    long rpt;
 
2207
    vm_obj_id_t new_str_obj;
 
2208
    CVmObjString *new_str;
 
2209
    size_t new_str_len;
 
2210
    char *new_strp;
 
2211
    const char *lstp = 0;
 
2212
    const char *strp = 0;
 
2213
    size_t len;
 
2214
    size_t i;
 
2215
    utf8_ptr dst;
 
2216
    
 
2217
    /* check arguments */
 
2218
    check_argc_range(vmg_ argc, 1, 2);
 
2219
 
 
2220
    /* get the base value */
 
2221
    G_stk->pop(&val);
 
2222
 
 
2223
    /* if there's a repeat count, get it */
 
2224
    rpt = (argc >= 2 ? pop_long_val(vmg0_) : 1);
 
2225
 
 
2226
    /* if the repeat count is less than or equal to zero, make it 1 */
 
2227
    if (rpt < 1)
 
2228
        rpt = 1;
 
2229
 
 
2230
    /* leave the original value on the stack to protect it from GC */
 
2231
    G_stk->push(&val);
 
2232
 
 
2233
    /* 
 
2234
     *   see what we have, and calculate how much space we'll need for the
 
2235
     *   result string 
 
2236
     */
 
2237
    switch(val.typ)
 
2238
    {
 
2239
    case VM_LIST:
 
2240
        /* it's a list of integers giving unicode character values */
 
2241
        lstp = G_const_pool->get_ptr(val.val.ofs);
 
2242
 
 
2243
    do_list:
 
2244
        /* get the list count */
 
2245
        len = vmb_get_len(lstp);
 
2246
 
 
2247
        /* 
 
2248
         *   Run through the list and get the size of each character, so
 
2249
         *   we can determine how long the string will have to be. 
 
2250
         */
 
2251
        for (new_str_len = 0, i = 1 ; i <= len ; ++i)
 
2252
        {
 
2253
            vm_val_t ele_val;
 
2254
            
 
2255
            /* get this element */
 
2256
            CVmObjList::index_list(vmg_ &ele_val, lstp, i);
 
2257
 
 
2258
            /* if it's not an integer, it's an error */
 
2259
            if (ele_val.typ != VM_INT)
 
2260
                err_throw(VMERR_INT_VAL_REQD);
 
2261
 
 
2262
            /* add this character's byte size to the string size */
 
2263
            new_str_len +=
 
2264
                utf8_ptr::s_wchar_size((wchar_t)ele_val.val.intval);
 
2265
        }
 
2266
        break;
 
2267
 
 
2268
    case VM_SSTRING:
 
2269
        /* get the string pointer */
 
2270
        strp = G_const_pool->get_ptr(val.val.ofs);
 
2271
        
 
2272
    do_string:
 
2273
        /* 
 
2274
         *   it's a string - the output length is the same as the input
 
2275
         *   length 
 
2276
         */
 
2277
        new_str_len = vmb_get_len(strp);
 
2278
        break;
 
2279
 
 
2280
    case VM_INT:
 
2281
        /* 
 
2282
         *   it's an integer giving a unicode character value - we just
 
2283
         *   need enough space to store this particular character 
 
2284
         */
 
2285
        new_str_len = utf8_ptr::s_wchar_size((wchar_t)val.val.intval);
 
2286
        break;
 
2287
 
 
2288
    case VM_OBJ:
 
2289
        /* check to see if it's a string */
 
2290
        if ((strp = val.get_as_string(vmg0_)) != 0)
 
2291
            goto do_string;
 
2292
 
 
2293
        /* check to see if it's a list */
 
2294
        if ((lstp = val.get_as_list(vmg0_)) != 0)
 
2295
            goto do_list;
 
2296
 
 
2297
        /* it's invalid */
 
2298
        err_throw(VMERR_BAD_TYPE_BIF);
 
2299
        break;
 
2300
 
 
2301
    default:
 
2302
        /* other types are invalid */
 
2303
        err_throw(VMERR_BAD_TYPE_BIF);
 
2304
        break;
 
2305
    }
 
2306
 
 
2307
    /* 
 
2308
     *   if the length times the repeat count would be over the maximum
 
2309
     *   16-bit string length, it's an error 
 
2310
     */
 
2311
    if (new_str_len * rpt > 0xffffL - VMB_LEN)
 
2312
        err_throw(VMERR_BAD_VAL_BIF);
 
2313
 
 
2314
    /* multiply the length by the repeat count */
 
2315
    new_str_len *= rpt;
 
2316
    
 
2317
    /* allocate the string and gets its buffer */
 
2318
    new_str_obj = CVmObjString::create(vmg_ FALSE, new_str_len);
 
2319
    new_str = (CVmObjString *)vm_objp(vmg_ new_str_obj);
 
2320
    new_strp = new_str->cons_get_buf();
 
2321
    
 
2322
    /* set up the destination pointer */
 
2323
    dst.set(new_strp);
 
2324
 
 
2325
    /* run through the number of iterations requested */
 
2326
    for ( ; rpt != 0 ; --rpt)
 
2327
    {
 
2328
        /* build one iteration of the string, according to the type */
 
2329
        if (lstp != 0)
 
2330
        {
 
2331
            /* run through the list */
 
2332
            for (i = 1 ; i <= len ; ++i)
 
2333
            {
 
2334
                vm_val_t ele_val;
 
2335
                
 
2336
                /* get this element */
 
2337
                CVmObjList::index_list(vmg_ &ele_val, lstp, i);
 
2338
 
 
2339
                /* add this character to the string */
 
2340
                dst.setch((wchar_t)ele_val.val.intval);
 
2341
            }
 
2342
        }
 
2343
        else if (strp != 0)
 
2344
        {
 
2345
            /* copy the string's contents into the output string */
 
2346
            memcpy(dst.getptr(), strp + VMB_LEN, vmb_get_len(strp));
 
2347
 
 
2348
            /* advance past the bytes we copied */
 
2349
            dst.set(dst.getptr() + vmb_get_len(strp));
 
2350
        }
 
2351
        else
 
2352
        {
 
2353
            /* set this int value */
 
2354
            dst.setch((wchar_t)val.val.intval);
 
2355
        }
 
2356
    }
 
2357
 
 
2358
    /* return the new string */
 
2359
    retval_obj(vmg_ new_str_obj);
 
2360
 
 
2361
    /* discard the GC protection */
 
2362
    G_stk->discard();
 
2363
}
 
2364
 
 
2365
/* ------------------------------------------------------------------------ */
 
2366
/*
 
2367
 *   getFuncParams 
 
2368
 */
 
2369
void CVmBifTADS::get_func_params(VMG_ uint argc)
 
2370
{
 
2371
    vm_val_t val;
 
2372
    vm_val_t func;
 
2373
    CVmFuncPtr hdr;
 
2374
    vm_obj_id_t lst_obj;
 
2375
    CVmObjList *lst;
 
2376
 
 
2377
    /* check arguments */
 
2378
    check_argc(vmg_ argc, 1);
 
2379
 
 
2380
    /* the argument can be an anonymous function object or function pointer */
 
2381
    if (G_stk->get(0)->typ == VM_OBJ
 
2382
        && G_predef->obj_call_prop != VM_INVALID_PROP)
 
2383
    {
 
2384
        uint argc = 0;
 
2385
        vm_obj_id_t srcobj;
 
2386
        
 
2387
        /* it's an anonymous function - get the object */
 
2388
        G_interpreter->pop_obj(vmg_ &func);
 
2389
 
 
2390
        /* retrieve its ObjectCallProp value, and make sure it's a function */
 
2391
        if (!vm_objp(vmg_ func.val.obj)->get_prop(
 
2392
            vmg_ G_predef->obj_call_prop, &func, func.val.obj, &srcobj, &argc)
 
2393
            || func.typ != VM_FUNCPTR)
 
2394
            err_throw(VMERR_FUNCPTR_VAL_REQD);
 
2395
    }
 
2396
    else
 
2397
    {
 
2398
        /* it's a simple function pointer - retrieve it */
 
2399
        G_interpreter->pop_funcptr(vmg_ &func);
 
2400
    }
 
2401
 
 
2402
    /* set up a pointer to the function header */
 
2403
    hdr.set((const uchar *)G_code_pool->get_ptr(func.val.ofs));
 
2404
 
 
2405
    /* 
 
2406
     *   Allocate our return list.  We need three elements: [minArgs,
 
2407
     *   optionalArgs, isVarargs].  
 
2408
     */
 
2409
    lst_obj = CVmObjList::create(vmg_ FALSE, 3);
 
2410
 
 
2411
    /* get the list object, properly cast */
 
2412
    lst = (CVmObjList *)vm_objp(vmg_ lst_obj);
 
2413
 
 
2414
    /* set the minimum argument count */
 
2415
    val.set_int(hdr.get_min_argc());
 
2416
    lst->cons_set_element(0, &val);
 
2417
 
 
2418
    /* 
 
2419
     *   set the optional argument count (which is always zero for a
 
2420
     *   function, since there is no way to specify named optional arguments
 
2421
     *   for a function) 
 
2422
     */
 
2423
    val.set_int(0);
 
2424
    lst->cons_set_element(1, &val);
 
2425
 
 
2426
    /* set the varargs flag */
 
2427
    val.set_logical(hdr.is_varargs());
 
2428
    lst->cons_set_element(2, &val);
 
2429
 
 
2430
    /* return the list */
 
2431
    retval_obj(vmg_ lst_obj);
 
2432
 
 
2433
    /* 
 
2434
     *   re-touch the currently executing method's code page to make sure
 
2435
     *   it's the most recently used item in the cache, to avoid swapping it
 
2436
     *   out 
 
2437
     */
 
2438
    G_interpreter->touch_entry_ptr_page(vmg0_);
 
2439
}
 
2440