File: nanoparrot.c

package info (click to toggle)
parrot 6.6.0-1
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 25,164 kB
  • ctags: 16,050
  • sloc: ansic: 110,715; perl: 94,382; yacc: 1,911; lex: 1,529; lisp: 1,163; cpp: 782; python: 646; ruby: 335; sh: 140; makefile: 129; cs: 49; asm: 30
file content (347 lines) | stat: -rw-r--r-- 7,314 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
/*
 * Copyright (C) 2001-2008, Parrot Foundation.

=head1 NAME

examples/c/nanoparrot.c

=head1 DESCRIPTION

demonstrates how the interpreter interprets bytecode
its vastly simplified but the very basics are the same

 - compile with:
   -DTRACE ...    turn on opcode tracing (FUNC_CORE, SWITCH_CORE only)
   -DFUNC_CORE    run function base opcodes
   -DF            same
   -DSWITCH_CORE  run switched opcode core
   -DS            same
                  else run CGOTO core

The CGOTO run core works only for compilers like gcc that allow
labels as values.

=head1 SYNOPSIS

   cc -o nanoparrot -Wall nanoparrot.c -O3 && time ./nanoparrot mops

=head2 Functions

=over 4

=cut

*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef int INTVAL;
typedef int opcode_t;
typedef double FLOATVAL;
typedef void PMC;
typedef void STRING;

#define NUM_REGISTERS 32

struct Reg {
    INTVAL int_reg;
    FLOATVAL num_reg;
    STRING *string_reg;
    PMC *pmc_reg;
};

#define REG_INT(interp, x) (interp)->bp[(x)].int_reg

#if defined(PREDEREF_CORE)
#  define IREG(x)   (_reg_base + pc[(x)])
#  define ICONST(x) *(INTVAL*)pc[(x)]
#  define SCONST(x) *(STRING**)pc[(x)]
#else
#  define IREG(x)   REG_INT(interp, pc[(x)])
#  define ICONST(x) pc[(x)]
#  define SCONST(x) interp->code->const_table[pc[(x)]]
#endif

struct pf {
    opcode_t *byte_code;
    char **const_table;
};

typedef struct Interp {
    struct Reg *bp;
    struct pf *code;
    opcode_t *(**op_func)(opcode_t *, struct Interp*);
    const char **op_info;
    int flags;
} Interp;

/*
 * list of all opcodes
 */

#define OPCODES OP(end),      OP(print_sc), OP(print_i),  \
                OP(set_i_ic), OP(if_i_ic),  OP(sub_i_i_i), \
                OP(MAX)

/*
 * some macros to get 3 different kinds of run loops
 * you might skip this ugliness and continue
 * at dispatch loop ~90 lines below
 *
 * or for the curious: look at the preprocessor output
 */

#define OP(x) OP_##x
typedef enum { OPCODES } opcodes;
#undef OP

#ifdef F
#  define FUNC_CORE
#endif
#ifdef S
#  define SWITCH_CORE
#endif

#if defined(FUNC_CORE)
#  ifdef TRACE
#    define ENDRUN \
static void \
run(Interp *interp, opcode_t *pc) \
{ \
    while (pc) { \
        printf("PC %2d %s\n", pc - interp->code->byte_code, \
                interp->op_info[*pc]); \
        pc = interp->op_func[*pc](pc, interp); \
    } \
}
#  else
#    define ENDRUN \
static void \
run(Interp *interp, opcode_t *pc) \
{ \
    while (pc) { \
        pc = interp->op_func[*pc](pc, interp); \
    } \
}
#  endif

#  define DISPATCH
#  define ENDDISPATCH
#  define CASE(function) \
static opcode_t * \
function(opcode_t *pc, Interp *interp) \
{ \

#  define NEXT return pc; }
#  define DONE            return 0; }

#else   /* !FUNC_CORE */

#  define ENDRUN  }

#  if defined(SWITCH_CORE)

/*

=item C<static void run(Interp *interp, opcode_t *pc)>

Execute a single opcode.

=cut

*/

static void
run(Interp *interp, opcode_t *pc)
{
#    ifdef TRACE
#      define DISPATCH  \
    for (;;) { \
        printf("PC %2d %s\n", pc - interp->code->byte_code, \
                interp->op_info[*pc]); \
        switch (*pc) {
#    else
#      define DISPATCH \
    for (;;) { \
        switch (*pc) {
#    endif

#    define CASE(x)         case OP_##x:
#    define NEXT            continue;
#    define DONE            return;
#    define ENDDISPATCH     default : printf("illegal instruction"); \
                                  exit(EXIT_FAILURE);                \
                        }}
#  else  /* CGOTO */

static void
run(Interp *interp, opcode_t *pc)
{
#    define OP(x)          &&lOP_##x
    static  void *labels[] = { OPCODES };
#    undef OP
#    define CASE(x)         lOP_##x:
#    define NEXT            goto *labels[*pc];
#    define DISPATCH        NEXT
#    define ENDDISPATCH
#    define DONE            return;

#  endif        /* SWITCH or CGOTO */
#endif  /* !FUNC_CORE */

/*
 * dispatch loop / opcode (function) bodies i.e. the .ops files
 */

    DISPATCH
        CASE(end)
            DONE
        CASE(print_sc)
            printf("%s", SCONST(1));
            pc += 2;
            NEXT
        CASE(print_i)
            printf("%d", IREG(1));
            pc += 2;
            NEXT
        CASE(set_i_ic)
            IREG(1) = ICONST(2);
            pc += 3;
            NEXT
        CASE(if_i_ic)
            if (IREG(1))
                pc += ICONST(2);
            else
                pc += 3;
            NEXT
        CASE(sub_i_i_i)
            IREG(1) = IREG(2) - IREG(3);
            pc += 4;
            NEXT
        CASE(MAX)
            printf("illegal opcode\n");
            exit(EXIT_FAILURE);
            NEXT
    ENDDISPATCH
ENDRUN

#ifdef FUNC_CORE
#  define DEF_OP(op) \
    interp->op_func[OP_##op] = (op); \
    interp->op_info[OP_##op] = #op
#else
#  define DEF_OP(op) \
    interp->op_info[OP_##op] = #op
#endif

/*

=item C<static void init(Interp *interp, opcode_t *prog)>

=cut

*/

static void
init(Interp *interp, opcode_t *prog)
{
    /*
     * create 1 register frame
     */
    interp->bp = calloc(NUM_REGISTERS, sizeof (struct Reg));
    /*
     * and some space for opcodes
     */
    interp->op_func = malloc(OP_MAX * sizeof (void*));
    interp->op_info = malloc(OP_MAX * sizeof (char*));
    /*
     * define opcode function and opcode info
     */
    DEF_OP(end);
    DEF_OP(print_sc);
    DEF_OP(print_i);
    DEF_OP(set_i_ic);
    DEF_OP(if_i_ic);
    DEF_OP(sub_i_i_i);
    DEF_OP(MAX);

    /*
     * the "packfile"
     */
    interp->code = malloc(sizeof (struct pf));
    interp->code->byte_code = prog;

    /*
     * create a simplified constant table
     */
#define N_CONSTS 4
    interp->code->const_table = malloc(N_CONSTS * sizeof (char*));
    interp->code->const_table[0] = "\n";
    interp->code->const_table[1] = "done\n";
    interp->code->const_table[2] = "error\n";
    interp->code->const_table[3] = "usage: ./nanoparrot mops\n";
}

/*

=item C<int main(int argc, char *argv[])>

Initialize a minimal Parrotesque interpreter and run some hard-coded bytecode.

=cut

*/

int
main(int argc, char *argv[])
{
    opcode_t *prog;

    /*
     * the mops main loop
     */
    opcode_t mops[] =
        { OP_set_i_ic, 4, 100000000,    /* set I4, n */
          OP_print_i, 4,        /* print I4 */
          OP_print_sc, 0,       /* print "\n" */
          OP_set_i_ic, 5, 1,    /* set I5, 1 */
          OP_sub_i_i_i, 4, 4, 5,        /* L1: sub I4, I4, I5 */
          OP_if_i_ic, 4, -4,    /* if I4, L1 */
          OP_print_sc, 1,       /* print "done\n" */
          OP_end                /* end */
        };
    opcode_t usage[] =
        {
          OP_set_i_ic, 0, 2,    /* set I0, 2 */
          OP_if_i_ic, 0, 6,     /* if I0, L1 */
          OP_print_sc, 2,       /* print "error\n" */
          OP_end,               /* end */
          OP_print_sc, 3,       /* L1: print "usage...\n" */
          OP_end                /* end */
        };
    Interp *interp = malloc(sizeof (Interp));

    prog = usage;
    if (argc > 1) {
        if (strcmp(argv[1], "mops") == 0)
            prog = mops;
    }
    init(interp, prog);
    run(interp, prog);
    return 0;
}

/*

=back

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
 */