munificent / magpie

The Magpie programming language.

Clone this repository (size: 954.7 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/munificent/magpie/
commit 36: 46d09f708b11
parent 35: d78a20a8a165
branch: default
Random intrinsic.
Robert Nystrom / munificent
9 months ago
    #   Introduced
1
e3e027f39344
using System;
2
e3e027f39344
using System.Collections.Generic;
3
e3e027f39344
using System.IO;
4
e3e027f39344
using System.Linq;
5
e3e027f39344
using System.Text;
6
e3e027f39344
7
e3e027f39344
namespace Magpie.Interpreter
8
e3e027f39344
{
9
e3e027f39344
    public class Machine
10
e3e027f39344
    {
11
e3e027f39344
        public event EventHandler<PrintEventArgs> Printed;
12
e3e027f39344
13
7ed6a1b915b7
        /// <summary>
14
d78a20a8a165
        /// Gets and sets the maximum stack depth. If set to a non-zero value, the interpreter
15
d78a20a8a165
        /// will track it during calls and immediately stop if the max is exceeded.
16
7ed6a1b915b7
        /// </summary>
17
d78a20a8a165
        public int MaxStackDepth { get; set; }
18
7ed6a1b915b7
19
83087846de08
        public Machine(IForeignRuntimeInterface foreignInterface)
20
83087846de08
        {
21
83087846de08
            mForeignInterface = foreignInterface;
22
83087846de08
        }
23
83087846de08
24
e3e027f39344
        public void Interpret(Stream stream)
25
e3e027f39344
        {
26
b18275779207
            BytecodeFile bytecode = new BytecodeFile(stream);
27
aa67edd44b43
28
aa67edd44b43
            Interpret(bytecode, String.Empty);
29
e3e027f39344
        }
30
e3e027f39344
31
b18275779207
        public void Interpret(BytecodeFile file, string argument)
32
e3e027f39344
        {
33
e3e027f39344
            mFile = file;
34
e3e027f39344
35
e3e027f39344
            // find "main"
36
aa67edd44b43
            Push(mFile.OffsetToMain);
37
7ed6a1b915b7
            Call(0, false);
38
e3e027f39344
39
e3e027f39344
            Interpret();
40
e3e027f39344
        }
41
e3e027f39344
42
b18275779207
        public void Interpret(BytecodeFile file)
43
e3e027f39344
        {
44
e3e027f39344
            Interpret(file, String.Empty);
45
e3e027f39344
        }
46
e3e027f39344
47
e3e027f39344
        public void Interpret()
48
e3e027f39344
        {
49
e3e027f39344
            bool running = true;
50
e3e027f39344
51
e3e027f39344
            while (running)
52
e3e027f39344
            {
53
e3e027f39344
                OpCode theOp = ReadOpCode();
54
e3e027f39344
                //Console.WriteLine(theOp.ToString());
55
e3e027f39344
56
e3e027f39344
                switch (theOp)
57
e3e027f39344
                {
58
e3e027f39344
                    case OpCode.PushNull:       Push((Structure)null); break;
59
e3e027f39344
                    case OpCode.PushBool:       Push(ReadByte() != 0); break;
60
e3e027f39344
                    case OpCode.PushInt:        Push(ReadInt()); break;
61
aa67edd44b43
                    case OpCode.PushString:     Push(mFile.ReadString(ReadInt())); break;
62
e3e027f39344
63
e3e027f39344
                    case OpCode.PushLocals:     Push(mCurrentFrame); break;
64
e3e027f39344
65
e3e027f39344
                    case OpCode.Alloc:
66
e3e027f39344
                        {
67
e3e027f39344
                            int slots = ReadInt();
68
e3e027f39344
                            Structure structure = new Structure(slots);
69
e3e027f39344
70
e3e027f39344
                            // initialize it
71
e3e027f39344
                            // note: slots are on stack in reverse order
72
e3e027f39344
                            // because they have been pushed in forward order.
73
e3e027f39344
                            // this ensures that arguments are evaluated left to right.
74
e3e027f39344
                            // for example: calling Foo (1, 2, 3) will create the arg
75
e3e027f39344
                            // tuple by evaluating 1, 2, 3 in order. this leaves the
76
e3e027f39344
                            // stack (from top down) looking like 3 2 1.
77
e3e027f39344
                            for (int i = slots - 1; i >= 0; i--)
78
e3e027f39344
                            {
79
e3e027f39344
                                structure[i] = Pop();
80
e3e027f39344
                            }
81
e3e027f39344
82
e3e027f39344
                            Push(structure);
83
e3e027f39344
                        }
84
e3e027f39344
                        break;
85
e3e027f39344
86
e3e027f39344
                    case OpCode.Load:
87
e3e027f39344
                        {
88
e3e027f39344
                            byte index = ReadByte();
89
e3e027f39344
                            Structure struc = PopStructure();
90
e3e027f39344
                            Value op = struc[index];
91
e3e027f39344
92
e3e027f39344
                            Push(op);
93
e3e027f39344
                        }
94
e3e027f39344
                        break;
95
e3e027f39344
96
e3e027f39344
                    case OpCode.Store:
97
e3e027f39344
                        {
98
e3e027f39344
                            byte index = ReadByte();
99
e3e027f39344
                            Structure struc = PopStructure();
100
e3e027f39344
                            struc[index] = Pop();
101
e3e027f39344
                        }
102
e3e027f39344
                        break;
103
e3e027f39344
104
f597738399d6
                    case OpCode.LoadArray:
105
f597738399d6
                        {
106
f597738399d6
                            Structure struc = PopStructure();
107
e28bf4a224c7
108
e28bf4a224c7
                            int index = struc[0].Int;
109
e28bf4a224c7
                            Structure array = struc[1].Struct;
110
f597738399d6
111
f597738399d6
                            //### bob: should bounds-check
112
f597738399d6
113
f597738399d6
                            // add one to skip the first slot which holds the array size
114
e28bf4a224c7
                            Push(array[index + 1]);
115
f597738399d6
                        }
116
f597738399d6
                        break;
117
f597738399d6
118
f597738399d6
                    case OpCode.StoreArray:
119
f597738399d6
                        {
120
f597738399d6
                            Structure struc = PopStructure();
121
e28bf4a224c7
                            int index = struc[0].Int;
122
e28bf4a224c7
                            Structure array = struc[1].Struct;
123
e28bf4a224c7
                            Value value = struc[2];
124
f597738399d6
125
f597738399d6
                            //### bob: should bounds-check
126
f597738399d6
127
f597738399d6
                            // add one to skip the first slot which holds the array size
128
e28bf4a224c7
                            array[index + 1] = value;
129
f597738399d6
                        }
130
f597738399d6
                        break;
131
f597738399d6
132
f597738399d6
                    case OpCode.SizeArray:
133
f597738399d6
                        {
134
f597738399d6
                            Structure struc = PopStructure();
135
f597738399d6
136
f597738399d6
                            // array size is the first element
137
f597738399d6
                            Push(struc[0]);
138
f597738399d6
                        }
139
f597738399d6
                        break;
140
f597738399d6
141
7ed6a1b915b7
                    case OpCode.Call0: Call(0, false); break;
142
7ed6a1b915b7
                    case OpCode.Call1: Call(1, false); break;
143
7ed6a1b915b7
                    case OpCode.CallN: Call(2, false); break;
144
7ed6a1b915b7
145
7ed6a1b915b7
                    case OpCode.TailCall0: Call(0, true); break;
146
7ed6a1b915b7
                    case OpCode.TailCall1: Call(1, true); break;
147
7ed6a1b915b7
                    case OpCode.TailCallN: Call(2, true); break;
148
e3e027f39344
149
4538302c85b1
                    case OpCode.ForeignCall0: ForeignCall(0); break;
150
4538302c85b1
                    case OpCode.ForeignCall1: ForeignCall(1); break;
151
4538302c85b1
                    case OpCode.ForeignCallN: ForeignCall(2); break;
152
83087846de08
153
e3e027f39344
                    case OpCode.Return:
154
e3e027f39344
                        {
155
aa67edd44b43
                            mInstruction = mCurrentFrame[mCurrentFrame.Count - 1].Int;
156
aa67edd44b43
                            mCurrentFrame = mCurrentFrame[mCurrentFrame.Count - 2].Struct;
157
e3e027f39344
158
e3e027f39344
                            // stop completely if we've returned from main
159
aa67edd44b43
                            if (mCurrentFrame == null) running = false;
160
e3e027f39344
                        }
161
e3e027f39344
                        break;
162
e3e027f39344
                        
163
aa67edd44b43
                    case OpCode.Jump:               mInstruction = ReadInt(); break;
164
e3e027f39344
                    case OpCode.JumpIfFalse:        int offset = ReadInt();
165
aa67edd44b43
                                                    if (!PopBool()) mInstruction = offset;
166
e3e027f39344
                                                    break;
167
e3e027f39344
168
e3e027f39344
                    case OpCode.BoolToString:       Push(PopBool() ? "true" : "false"); break;
169
e3e027f39344
                    case OpCode.IntToString:        Push(PopInt().ToString()); break;
170
e3e027f39344
171
e3e027f39344
                    case OpCode.EqualBool:
172
e3e027f39344
                        {
173
e3e027f39344
                            Structure tuple = PopStructure();
174
e3e027f39344
                            Push(tuple[0].Bool == tuple[1].Bool);
175
e3e027f39344
                        }
176
e3e027f39344
                        break;
177
e3e027f39344
178
e3e027f39344
                    case OpCode.EqualInt:
179
e3e027f39344
                        {
180
e3e027f39344
                            Structure tuple = PopStructure();
181
e3e027f39344
                            Push(tuple[0].Int == tuple[1].Int);
182
e3e027f39344
                        }
183
e3e027f39344
                        break;
184
e3e027f39344
185
e3e027f39344
                    case OpCode.EqualString:
186
e3e027f39344
                        {
187
e3e027f39344
                            Structure tuple = PopStructure();
188
e3e027f39344
                            Push(tuple[0].String == tuple[1].String);
189
e3e027f39344
                        }
190
e3e027f39344
                        break;
191
e3e027f39344
192
e3e027f39344
                    case OpCode.LessInt:
193
e3e027f39344
                        {
194
e3e027f39344
                            Structure tuple = PopStructure();
195
e3e027f39344
                            Push(tuple[0].Int < tuple[1].Int);
196
e3e027f39344
                        }
197
e3e027f39344
                        break;
198
e3e027f39344
199
e3e027f39344
                    case OpCode.GreaterInt:
200
e3e027f39344
                        {
201
e3e027f39344
                            Structure tuple = PopStructure();
202
e3e027f39344
                            Push(tuple[0].Int > tuple[1].Int);
203
e3e027f39344
                        }
204
e3e027f39344
                        break;
205
e3e027f39344
206
e3e027f39344
                    case OpCode.NegateBool:         Push(!PopBool()); break;
207
e3e027f39344
                    case OpCode.NegateInt:          Push(-PopInt()); break;
208
e3e027f39344
209
e3e027f39344
                    case OpCode.AndBool:
210
e3e027f39344
                        {
211
e3e027f39344
                            Structure tuple = PopStructure();
212
e3e027f39344
                            Push(tuple[0].Bool && tuple[1].Bool);
213
e3e027f39344
                        }
214
e3e027f39344
                        break;
215
e3e027f39344
216
e3e027f39344
                    case OpCode.OrBool:
217
e3e027f39344
                        {
218
e3e027f39344
                            Structure tuple = PopStructure();
219
e3e027f39344
                            Push(tuple[0].Bool || tuple[1].Bool);
220
e3e027f39344
                        }
221
e3e027f39344
                        break;
222
e3e027f39344
223
e3e027f39344
                    case OpCode.AddInt:
224
e3e027f39344
                        {
225
e3e027f39344
                            Structure tuple = PopStructure();
226
e3e027f39344
                            Push(tuple[0].Int + tuple[1].Int);
227
e3e027f39344
                        }
228
e3e027f39344
                        break;
229
e3e027f39344
230
e3e027f39344
                    case OpCode.SubInt:
231
e3e027f39344
                        {
232
e3e027f39344
                            Structure tuple = PopStructure();
233
e3e027f39344
                            Push(tuple[0].Int - tuple[1].Int);
234
e3e027f39344
                        }
235
e3e027f39344
                        break;
236
e3e027f39344
237
e3e027f39344
                    case OpCode.MultInt:
238
e3e027f39344
                        {
239
e3e027f39344
                            Structure tuple = PopStructure();
240
e3e027f39344
                            Push(tuple[0].Int * tuple[1].Int);
241
e3e027f39344
                        }
242
e3e027f39344
                        break;
243
e3e027f39344
244
e3e027f39344
                    case OpCode.DivInt:
245
e3e027f39344
                        {
246
e3e027f39344
                            Structure tuple = PopStructure();
247
e3e027f39344
                            Push(tuple[0].Int / tuple[1].Int);
248
e3e027f39344
                        }
249
e3e027f39344
                        break;
250
e3e027f39344
251
46d09f708b11
                    case OpCode.Random:
252
46d09f708b11
                        {
253
46d09f708b11
                            // only create if needed
254
46d09f708b11
                            if (mRandom == null)
255
46d09f708b11
                            {
256
46d09f708b11
                                mRandom = new Random();
257
46d09f708b11
                            }
258
e3e027f39344
259
46d09f708b11
                            Push(mRandom.Next(PopInt()));
260
e3e027f39344
                        }
261
e3e027f39344
                        break;
262
e3e027f39344
263
e3e027f39344
                    case OpCode.AddString:
264
e3e027f39344
                        {
265
e3e027f39344
                            Structure tuple = PopStructure();
266
e3e027f39344
                            Push(tuple[0].String + tuple[1].String);
267
e3e027f39344
                        }
268
e3e027f39344
                        break;
269
e3e027f39344
270
e3e027f39344
                    case OpCode.Print:
271
e3e027f39344
                        string text = PopString();
272
e3e027f39344
                        if (Printed != null) Printed(this, new PrintEventArgs(text));
273
e3e027f39344
                        break;
274
e3e027f39344
275
e3e027f39344
                    case OpCode.StringSize:         Push(PopString().Length); break;
276
e3e027f39344
277
e3e027f39344
                    case OpCode.Substring:
278
e3e027f39344
                        {
279
e3e027f39344
                            Structure tuple = PopStructure();
280
e3e027f39344
                            Push(tuple[0].String.Substring(tuple[1].Int, tuple[2].Int));
281
e3e027f39344
                        }
282
e3e027f39344
                        break;
283
e3e027f39344
284
e3e027f39344
                    default: throw new Exception("Unknown opcode.");
285
e3e027f39344
                }
286
e3e027f39344
            }
287
e3e027f39344
        }
288
e3e027f39344
289
e3e027f39344
        private OpCode ReadOpCode()
290
e3e027f39344
        {
291
aa67edd44b43
            return (OpCode)mFile.Bytes[mInstruction++];
292
e3e027f39344
        }
293
e3e027f39344
294
e3e027f39344
        private byte ReadByte()
295
e3e027f39344
        {
296
aa67edd44b43
            return mFile.Bytes[mInstruction++];
297
e3e027f39344
        }
298
e3e027f39344
299
e3e027f39344
        private char ReadChar()
300
e3e027f39344
        {
301
aa67edd44b43
            int c = ((int)mFile.Bytes[mInstruction++]) |
302
aa67edd44b43
                    ((int)mFile.Bytes[mInstruction++] << 8);
303
e3e027f39344
304
e3e027f39344
            return (char)c;
305
e3e027f39344
        }
306
e3e027f39344
307
e3e027f39344
        private int ReadInt()
308
e3e027f39344
        {
309
aa67edd44b43
            return ((int)mFile.Bytes[mInstruction++]) |
310
aa67edd44b43
                   ((int)mFile.Bytes[mInstruction++] << 8) |
311
aa67edd44b43
                   ((int)mFile.Bytes[mInstruction++] << 16) |
312
aa67edd44b43
                   ((int)mFile.Bytes[mInstruction++] << 24);
313
e3e027f39344
        }
314
e3e027f39344
315
e3e027f39344
        private void Push(Value value) { mOperands.Push(value); }
316
e3e027f39344
        private void Push(bool value) { Push(new Value(value)); }
317
e3e027f39344
        private void Push(char value) { Push(new Value(value)); }
318
e3e027f39344
        private void Push(int value) { Push(new Value(value)); }
319
e3e027f39344
        private void Push(string value) { Push(new Value(value)); }
320
e3e027f39344
        private void Push(Structure value) { Push(new Value(value)); }
321
e3e027f39344
322
e3e027f39344
        private Value Pop() { return mOperands.Pop(); }
323
e3e027f39344
        private bool PopBool() { return mOperands.Pop().Bool; }
324
e3e027f39344
        private int PopInt() { return mOperands.Pop().Int; }
325
e3e027f39344
        private string PopString() { return mOperands.Pop().String; }
326
e3e027f39344
        private Structure PopStructure() { return mOperands.Pop().Struct; }
327
e3e027f39344
328
aa67edd44b43
        private Structure MakeCallFrame(int numLocals, Structure parentFrame, int instruction)
329
e3e027f39344
        {
330
aa67edd44b43
            var frame = new Structure(numLocals + 2);
331
e3e027f39344
            frame[numLocals] = new Value(parentFrame);
332
aa67edd44b43
            frame[numLocals + 1] = new Value(instruction);
333
e3e027f39344
334
e3e027f39344
            return frame;
335
e3e027f39344
        }
336
e3e027f39344
337
7ed6a1b915b7
        private void Call(int paramType, bool isTailCall)
338
aa67edd44b43
        {
339
aa67edd44b43
            // jump to the function
340
aa67edd44b43
            int previousInstruction = mInstruction;
341
aa67edd44b43
342
aa67edd44b43
            mInstruction = PopInt();
343
aa67edd44b43
            int numLocals = ReadInt();
344
aa67edd44b43
345
7ed6a1b915b7
            // if it's a tail call, discard the parent frame
346
7ed6a1b915b7
            //### bob: not tested. compiler doesn't emit tail calls yet
347
7ed6a1b915b7
            var parent = mCurrentFrame;
348
7ed6a1b915b7
            if (isTailCall)
349
7ed6a1b915b7
            {
350
7ed6a1b915b7
                parent = mCurrentFrame[mCurrentFrame.Count - 2].Struct;
351
7ed6a1b915b7
                previousInstruction = mCurrentFrame[mCurrentFrame.Count - 1].Int;
352
7ed6a1b915b7
            }
353
7ed6a1b915b7
354
7ed6a1b915b7
            mCurrentFrame = MakeCallFrame(numLocals, parent, previousInstruction);
355
aa67edd44b43
356
3754d24a3bf3
            // pop and store the argument
357
3754d24a3bf3
            if (paramType != 0) mCurrentFrame[0] = Pop();
358
d78a20a8a165
359
d78a20a8a165
            // track stack depth
360
d78a20a8a165
            if (MaxStackDepth != 0)
361
d78a20a8a165
            {
362
d78a20a8a165
                int stackDepth = GetStackDepth(mCurrentFrame);
363
d78a20a8a165
364
d78a20a8a165
                if (stackDepth > MaxStackDepth) throw new MaxStackDepthExceededException(MaxStackDepth);
365
d78a20a8a165
            }
366
aa67edd44b43
        }
367
aa67edd44b43
368
4538302c85b1
        private void ForeignCall(int paramType) // (int id, Value[] args)
369
83087846de08
        {
370
4538302c85b1
            int id = ReadInt();
371
4538302c85b1
372
4538302c85b1
            Value[] args;
373
4538302c85b1
            switch (paramType)
374
4538302c85b1
            {
375
4538302c85b1
                case 0: args = new Value[0]; break;
376
4538302c85b1
                case 1: args = new Value[] { Pop() }; break;
377
4538302c85b1
                case 2: args = PopStructure().Fields.ToArray(); break;
378
4538302c85b1
                default: throw new ArgumentException("Unknown parameter type.");
379
4538302c85b1
            }
380
4538302c85b1
381
83087846de08
            Value result = mForeignInterface.ForeignCall(id, args);
382
83087846de08
            if (result != null) Push(result);
383
83087846de08
        }
384
83087846de08
385
d78a20a8a165
        private int GetStackDepth(Structure callFrame)
386
d78a20a8a165
        {
387
d78a20a8a165
            if (callFrame == null) return 0;
388
d78a20a8a165
            return 1 + GetStackDepth(callFrame[callFrame.Count - 2].Struct);
389
d78a20a8a165
        }
390
d78a20a8a165
391
b18275779207
        private BytecodeFile mFile;
392
e3e027f39344
393
aa67edd44b43
        //### bob: could also just store this in the current frame
394
aa67edd44b43
        private int mInstruction; // position in bytecode file
395
e3e027f39344
396
e3e027f39344
        private readonly Stack<Value> mOperands = new Stack<Value>();
397
e3e027f39344
398
e3e027f39344
        // call frame for the current function
399
e3e027f39344
        // contains:
400
e3e027f39344
        // 0 ... n: local variables
401
e3e027f39344
        // n + 1  : reference to parent call frame
402
e3e027f39344
        // n + 2  : instruction pointer for parent frame
403
e3e027f39344
        private Structure mCurrentFrame;
404
83087846de08
405
46d09f708b11
        private Random mRandom;
406
46d09f708b11
407
83087846de08
        private readonly IForeignRuntimeInterface mForeignInterface;
408
e3e027f39344
    }
409
e3e027f39344
}