|
1 |
|
2 var OP_LOADK = 1; |
|
3 var OP_GETGLOBAL = 5; |
|
4 var OP_CALL = 28; |
|
5 |
|
6 function LValue(type, value) |
|
7 { |
|
8 this.type = type||"nil"; |
|
9 this.value = value||null; |
|
10 } |
|
11 |
|
12 LValue.prototype = { |
|
13 call: function (args) |
|
14 { |
|
15 if(typeof(this.value) == "function") |
|
16 { |
|
17 return this.value.apply(null, args.map(function (a) { return a.value; })); |
|
18 } |
|
19 }, |
|
20 index: function (key) |
|
21 { |
|
22 if(this.type == "table") |
|
23 { |
|
24 return this.value[key.value]; |
|
25 } |
|
26 }, |
|
27 setIndex: function (key, value) |
|
28 { |
|
29 if(this.type == "table") |
|
30 { |
|
31 this.value[key.value] = value; |
|
32 } |
|
33 } |
|
34 }; |
|
35 |
|
36 function LValueFromString(string) |
|
37 { |
|
38 return new LValue("string", string); |
|
39 } |
|
40 |
|
41 function LValueFromFunction(func) |
|
42 { |
|
43 return new LValue("function", func); |
|
44 } |
|
45 |
|
46 var default_environment = new LValue("table", {}); |
|
47 |
|
48 var print; |
|
49 if(typeof(document) == "object") |
|
50 print = function (a) { document.write(a+"<br/>") }; |
|
51 else |
|
52 { |
|
53 // Assume running under Nodejs |
|
54 print = require("sys").puts; |
|
55 } |
|
56 default_environment.setIndex(LValueFromString("print"), LValueFromFunction(print)); |
|
57 |
|
58 function LFunction() |
|
59 { |
|
60 this.constants = []; |
|
61 this.instructions = []; |
|
62 this.environment = default_environment; |
|
63 return this; |
|
64 } |
|
65 |
|
66 LFunction.prototype = { |
|
67 addInstruction: function (instruction) |
|
68 { |
|
69 this.instructions.push(instruction); |
|
70 }, |
|
71 addConstant: function (constant) |
|
72 { |
|
73 this.constants.push(constant); |
|
74 } |
|
75 }; |
|
76 |
|
77 function LVM() |
|
78 { |
|
79 this.callstack = []; |
|
80 this.stack = []; |
|
81 return this; |
|
82 } |
|
83 |
|
84 LVM.prototype = { |
|
85 run: function (lfFunction) |
|
86 { |
|
87 this.currentFrame = {f:lfFunction,pc:0}; |
|
88 this.callstack.push(this.currentFrame); |
|
89 var instruction; |
|
90 while(this.callstack.length>0) |
|
91 { |
|
92 instruction = this.currentFrame.f.instructions[this.currentFrame.pc++]; |
|
93 if(!instruction) |
|
94 break; |
|
95 switch(instruction[0]) |
|
96 { |
|
97 case OP_GETGLOBAL: |
|
98 var name = this.currentFrame.f.constants[instruction[2]]; |
|
99 this.stack[instruction[1]] = this.currentFrame.f.environment.index(name); |
|
100 break; |
|
101 case OP_LOADK: |
|
102 var value = this.currentFrame.f.constants[instruction[2]]; |
|
103 this.stack[instruction[1]] = value; |
|
104 break; |
|
105 case OP_CALL: |
|
106 var f = this.stack[instruction[1]]; |
|
107 f.call(this.stack.splice(instruction[1]+1, instruction[1]+(instruction[2]-1))); |
|
108 break; |
|
109 case OP_RETURN: |
|
110 this.callstack.pop(); |
|
111 break; |
|
112 default: |
|
113 } |
|
114 } |
|
115 } |
|
116 }; |
|
117 |
|
118 var testvm = new LVM(); |
|
119 var mycode = new LFunction(); |
|
120 mycode.addConstant(LValueFromString("print")); |
|
121 mycode.addConstant(LValueFromString("Hello world")); |
|
122 mycode.addInstruction([OP_GETGLOBAL, 0, 0]); |
|
123 mycode.addInstruction([OP_LOADK, 1, 1]); |
|
124 mycode.addInstruction([OP_CALL, 0, 2, 1]); |
|
125 testvm.run(mycode); |