1
0
Files
otyaSMILEBASIC/SMILEBASIC/VM.d

341 lines
7.8 KiB
D
Raw Normal View History

module otya.smilebasic.vm;
import otya.smilebasic.type;
import otya.smilebasic.token;
import std.uni;
2015-06-04 23:03:37 +09:00
import std.utf;
import std.conv;
import std.stdio;
class VM
{
Code[] code;
int stacki;
2015-06-05 19:57:35 +09:00
int pc;
Value[] stack;
2015-06-05 19:57:35 +09:00
Value[] global;
2015-06-05 22:29:37 +09:00
int[wstring] debugTable;
this(Code[] code, int len, int[wstring] debugTable)
{
this.code = code;
this.stack = new Value[1024 * 1024];
2015-06-05 19:57:35 +09:00
this.global = new Value[len];
2015-06-05 22:29:37 +09:00
this.debugTable = debugTable;
}
void run()
{
2015-06-05 19:57:35 +09:00
for(pc = 0; pc < this.code.length; pc++)
{
2015-06-05 19:57:35 +09:00
code[pc].execute(this);
}
}
void push(ref Value value)
{
stack[stacki++] = value;
}
2015-06-07 13:56:30 +09:00
void push(Value value)
{
stack[stacki++] = value;
}
void pop(out Value value)
{
2015-06-07 13:28:19 +09:00
if(stacki <= 0)
{
writeln("Stack underflow");
readln();
}
value = stack[--stacki];
}
2015-06-05 22:29:37 +09:00
Value testGetGlobaVariable(wstring name)
{
return global[debugTable[name]];
}
}
enum CodeType
{
Push,
2015-06-05 19:57:35 +09:00
PushG,
Operate,
Return,
Goto,
Gosub,
Print,
2015-06-05 21:41:40 +09:00
PopG,
2015-06-06 14:51:45 +09:00
GotoS,
2015-06-06 17:11:02 +09:00
GotoFalse,
GotoTrue,
}
abstract class Code
{
CodeType type;
abstract void execute(VM vm);
}
class PrintCode : Code
{
int count;
this(int count)
{
this.type = CodeType.Print;
this.count = count;
}
override void execute(VM vm)
{
for(int i = 0;i < count; i++)
{
Value arg;
vm.pop(arg);
switch(arg.type)
{
case ValueType.Integer:
write(arg.integerValue);
break;
case ValueType.String:
2015-06-05 19:57:35 +09:00
write(arg.stringValue);
break;
default:
//type missmatch
break;
}
}
}
}
/*
* スタックにPush
*/
class Push : Code
{
Value imm;
this(Value imm)
{
this.type = CodeType.Push;
this.imm = imm;
}
override void execute(VM vm)
{
vm.push(imm);
}
}
2015-06-05 19:57:35 +09:00
class PushG : Code
{
int var;
this(int var)
{
this.type = CodeType.PushG;
this.var = var;
}
override void execute(VM vm)
{
vm.push(vm.global[var]);
}
}
2015-06-05 21:41:40 +09:00
class PopG : Code
{
int var;
this(int var)
{
this.type = CodeType.PopG;
this.var = var;
}
override void execute(VM vm)
{
vm.pop(vm.global[var]);
}
}
class Operate : Code
{
TokenType operator;
this(TokenType op)
{
this.operator = op;
}
override void execute(VM vm)
{
Value l;
Value r;
vm.pop(r);
2015-06-05 22:29:37 +09:00
vm.pop(l);
2015-06-07 13:56:30 +09:00
if(l.type == ValueType.String)
{
wstring ls = l.stringValue;
if(r.type == ValueType.String)
{
wstring rs = r.stringValue;
switch(operator)
{
case TokenType.Plus:
vm.push(Value(ls ~ rs));
return;
case TokenType.Equal:
vm.push(Value(ls == rs));
return;
case TokenType.NotEqual:
vm.push(Value(ls != rs));
return;
case TokenType.Less:
vm.push(Value(ls < rs));
return;
case TokenType.LessEqual:
vm.push(Value(ls <= rs));
return;
case TokenType.Greater:
vm.push(Value(ls > rs));
return;
case TokenType.GreaterEqual:
vm.push(Value(ls >= rs));
return;
default:
//type missmatch
stderr.writeln("Type missmatch");
return;
}
}
if(r.type == ValueType.Integer || r.type == ValueType.Double)
{
double rd = r.integerValue;
switch(operator)
{
//数値 * 文字列だとエラー
case TokenType.Mul:
{
wstring delegate(wstring x, wstring y, int num) mul;
mul = (x, y, z) => z > 0 ? x ~ mul(x , y, z - 1) : "";
vm.push(Value(mul(ls, ls, cast(int)rd)));
}
return;
//3.1から?文字列と数値を比較すると3を返す
//(数値 compare 文字列だとエラー)
case TokenType.Equal:
case TokenType.NotEqual:
case TokenType.Less:
case TokenType.LessEqual:
case TokenType.Greater:
case TokenType.GreaterEqual:
vm.push(Value(3));
return;
default:
//type missmatch
stderr.writeln("Type missmatch");
return;
}
}
}
int li = l.integerValue;
int ri = r.integerValue;
2015-06-05 21:50:40 +09:00
double ld = l.integerValue;
double rd = r.integerValue;
//とりあえずInteger
2015-06-05 21:50:40 +09:00
switch(operator)
{
case TokenType.Plus:
ld += rd;
break;
case TokenType.Minus:
ld -= rd;
break;
case TokenType.Mul:
ld *= rd;
break;
case TokenType.Div:
ld /= rd;
break;
2015-06-07 13:56:30 +09:00
case TokenType.Mod:
ld %= rd;
break;
case TokenType.And:
ld = li & ri;
break;
case TokenType.Or:
ld = li | ri;
break;
case TokenType.Xor:
ld = li ^ ri;
break;
2015-06-07 13:28:19 +09:00
case TokenType.Equal:
ld = ld == rd;
break;
case TokenType.NotEqual:
ld = ld != rd;
break;
case TokenType.Less:
ld = ld < rd;
break;
case TokenType.LessEqual:
ld = ld <= rd;
break;
case TokenType.Greater:
ld = ld > rd;
break;
case TokenType.GreaterEqual:
ld = ld >= rd;
break;
2015-06-05 21:50:40 +09:00
default:
2015-06-05 22:29:37 +09:00
writeln("NotImpl: ", operator);
2015-06-05 21:50:40 +09:00
break;
}
l.integerValue = cast(int)ld;
vm.push(l);
}
}
2015-06-06 14:51:45 +09:00
class GotoAddr : Code
{
2015-06-05 19:57:35 +09:00
int address;
this(int addr)
{
this.type = CodeType.Goto;
address = addr;
}
override void execute(VM vm)
{
vm.pc = address - 1;
}
}
2015-06-06 14:51:45 +09:00
class GotoS : Code
{
wstring label;
this(wstring label)
{
this.type = CodeType.GotoS;
this.label = label;
}
override void execute(VM vm)
{
stderr.writeln("can't execute");
}
}
2015-06-06 17:11:02 +09:00
class GotoTrue : Code
{
int address;
this(int addr)
{
this.type = CodeType.GotoTrue;
address = addr;
}
override void execute(VM vm)
{
Value cond;
vm.pop(cond);
if(cond.boolValue)
vm.pc = address - 1;
}
}
class GotoFalse : Code
{
int address;
this(int addr)
{
this.type = CodeType.GotoFalse;
address = addr;
}
override void execute(VM vm)
{
Value cond;
vm.pop(cond);
if(!cond.boolValue)
vm.pc = address - 1;
}
}
class Gosub : Code
{
}