1
0
Files
otyaSMILEBASIC/SMILEBASIC/VM.d

427 lines
10 KiB
D
Raw Normal View History

module otya.smilebasic.vm;
import otya.smilebasic.type;
import otya.smilebasic.token;
import otya.smilebasic.error;
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;
int[wstring] globalTable;
ValueType getType(wstring name)
{
wchar s = name[name.length - 1];
switch(s)
{
case '$':
return ValueType.String;
case '#':
return ValueType.Double;
case '%':
return ValueType.Integer;
default:
return ValueType.Integer;//DEFINT時
}
}
this(Code[] code, int len, int[wstring] globalTable)
{
this.code = code;
this.stack = new Value[1024 * 1024];
2015-06-05 19:57:35 +09:00
this.global = new Value[len];
this.globalTable = globalTable;
foreach(wstring k, int v ; globalTable)
{
this.global[v] = Value(getType(k));
}
}
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];
}
Value testGetGlobalVariable(wstring name)
2015-06-05 22:29:37 +09:00
{
return global[globalTable[name]];
2015-06-05 22:29:37 +09:00
}
}
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:
2015-06-07 14:35:03 +09:00
//type mismatch
2015-06-07 19:11:18 +09:00
throw new TypeMismatch();
}
}
2015-06-07 19:33:51 +09:00
stdout.flush();
}
}
/*
* スタックに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)
{
Value v;
Value g = vm.global[var];
vm.pop(v);
if(v.type == ValueType.Integer && g.type == ValueType.Double)
{
vm.global[var] = Value(cast(double)v.integerValue);
return;
}
if(g.type == ValueType.Integer && v.type == ValueType.Double)
{
vm.global[var] = Value(cast(int)v.doubleValue);
return;
}
if(v.type == ValueType.Void)
{
vm.global[var] = v;
return;
}
if(v.type != g.type)
{
throw new TypeMismatch();
}
vm.global[var] = v;
2015-06-05 21:41:40 +09:00
}
}
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-07 19:11:18 +09:00
int ri = r.integerValue;
double rd = r.integerValue;
2015-06-07 19:33:51 +09:00
bool numf = r.type == ValueType.Double || r.type == ValueType.Integer;
2015-06-07 19:11:18 +09:00
if(r.type == ValueType.Double)
{
ri = cast(int)r.doubleValue;
rd = r.doubleValue;
}
switch(operator)
{
//単項演算子
case TokenType.Not:
2015-06-07 19:33:51 +09:00
if(numf)
vm.push(Value(~ri));
else
throw new TypeMismatch();
2015-06-07 19:11:18 +09:00
return;
case TokenType.LogicalNot:
2015-06-07 19:33:51 +09:00
if(numf)
vm.push(Value(!ri));
else
throw new TypeMismatch();
2015-06-07 19:11:18 +09:00
return;
default:
break;
}
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:
2015-06-07 14:35:03 +09:00
//type mismatch
throw new TypeMismatch();
2015-06-07 13:56:30 +09:00
}
}
if(r.type == ValueType.Integer || r.type == ValueType.Double)
{
switch(operator)
{
//数値 * 文字列だとエラー
case TokenType.Mul:
{
2015-06-07 19:33:51 +09:00
wstring delegate(wstring, wstring, int) mul;
2015-06-07 13:56:30 +09:00
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:
2015-06-07 14:35:03 +09:00
//type mismatch
throw new TypeMismatch();
2015-06-07 13:56:30 +09:00
}
}
}
int li = l.integerValue;
2015-06-05 21:50:40 +09:00
double ld = l.integerValue;
2015-06-07 19:11:18 +09:00
if(l.type == ValueType.Double)
{
li = cast(int)l.doubleValue;
ld = l.doubleValue;
}
//とりあえず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 19:11:18 +09:00
case TokenType.IntDiv:
//TODO:範囲外だとOverflow
vm.push(Value(cast(int)(ld / rd)));
return;
2015-06-07 13:56:30 +09:00
case TokenType.Mod:
ld %= rd;
break;
case TokenType.And:
2015-06-07 19:11:18 +09:00
vm.push(Value(li & ri));
return;
2015-06-07 13:56:30 +09:00
case TokenType.Or:
2015-06-07 19:11:18 +09:00
vm.push(Value(li | ri));
return;
case TokenType.LogicalAnd:
vm.push(Value(li && ri));
return;
case TokenType.LogicalOr:
vm.push(Value(li || ri));
return;
2015-06-07 13:56:30 +09:00
case TokenType.Xor:
2015-06-07 19:11:18 +09:00
vm.push(Value(li ^ ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.Equal:
2015-06-07 19:11:18 +09:00
vm.push(Value(li == ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.NotEqual:
2015-06-07 19:11:18 +09:00
vm.push(Value(li != ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.Less:
2015-06-07 19:11:18 +09:00
vm.push(Value(li < ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.LessEqual:
2015-06-07 19:11:18 +09:00
vm.push(Value(li <= ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.Greater:
2015-06-07 19:11:18 +09:00
vm.push(Value(li > ri));
return;
2015-06-07 13:28:19 +09:00
case TokenType.GreaterEqual:
2015-06-07 19:11:18 +09:00
vm.push(Value(li >= ri));
return;
case TokenType.LeftShift:
vm.push(Value(li << ri));
return;
case TokenType.RightShift:
vm.push(Value(li >> ri));
return;
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;
}
2015-06-07 19:11:18 +09:00
l.type = ValueType.Double;
l.doubleValue = 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
{
}