212 lines
7.3 KiB
C#
212 lines
7.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using TLang.BytecodeCompiler;
|
|
using TLang.Common;
|
|
namespace TLang.VM
|
|
{
|
|
public class ChunkExecuter
|
|
{
|
|
public string ClassName {get;set;}="";
|
|
public ChunkExecuter(TVMFile file,Chunk chunk,TLangEnvironment env)
|
|
{
|
|
File =file;
|
|
Chunk = chunk;
|
|
Environment = env;
|
|
}
|
|
public TLangEnvironment Environment {get;set;}
|
|
public TVMFile File {get;}
|
|
|
|
public Chunk Chunk {get;}
|
|
public int IP {get;set;}=0;
|
|
public Stack<TObject> Stack {get;}=new Stack<TObject>();
|
|
|
|
|
|
public TObject Execute(params TObject[] args)
|
|
{
|
|
var env=Environment.GetSubEnvironment();
|
|
for(int i = 0;i< args.Length;i++)
|
|
{
|
|
if(i<Chunk.Arguments.Count)
|
|
{
|
|
env.SetObject(Chunk.Arguments[i],args[i]);
|
|
}
|
|
}
|
|
while(IP < Chunk.Code.Length)
|
|
{
|
|
var instruction = Chunk.Code[IP++];
|
|
switch(instruction)
|
|
{
|
|
case Instruction.ADD:
|
|
Stack.Push(Add(Stack.Pop(),Stack.Pop()));
|
|
break;
|
|
case Instruction.SUB:
|
|
Stack.Push(Sub(Stack.Pop(),Stack.Pop()));
|
|
break;
|
|
case Instruction.TIMES:
|
|
Stack.Push(Times(Stack.Pop(),Stack.Pop()));
|
|
break;
|
|
case Instruction.DIVIDE:
|
|
Stack.Push(Divide(Stack.Pop(),Stack.Pop()));
|
|
break;
|
|
case Instruction.MOD:
|
|
Stack.Push(Mod(Stack.Pop(),Stack.Pop()));
|
|
break;
|
|
case Instruction.RET:
|
|
return Stack.Pop();
|
|
|
|
case Instruction.SCOPE_BEGIN:
|
|
env = Environment.GetSubEnvironment();
|
|
break;
|
|
case Instruction.SCOPE_END:
|
|
env = Environment.GetParentEnvironment();
|
|
break;
|
|
case Instruction.PUSH_DOUBLE:
|
|
Stack.Push(new TNumber(ReadDouble()));
|
|
break;
|
|
case Instruction.PUSH_CLOSURE:
|
|
int closureId = ReadInt();
|
|
if(closureId >= File.Chunks.Count) throw new IndexOutOfRangeException($"No chunk with id {closureId}");
|
|
Stack.Push(new TClosure(File,File.Chunks[closureId],env));
|
|
break;
|
|
case Instruction.PUSH_STRING:
|
|
Stack.Push(new TString(ReadString()));
|
|
break;
|
|
case Instruction.PUSH_VARIABLE_VALUE:
|
|
{
|
|
var res=EnsurePop<TString>();
|
|
Stack.Push(env.GetObject(res.Value));
|
|
}
|
|
break;
|
|
case Instruction.POP_VARIABLE_VALUE:
|
|
{
|
|
var res=EnsurePop<TString>();
|
|
env.SetObject(res.Value,Stack.Pop());
|
|
}
|
|
break;
|
|
case Instruction.CALL_FUNC:
|
|
{
|
|
var argC = (int)EnsurePop<TNumber>().Value;
|
|
TObject[] argV = new TObject[argC];
|
|
for(int i = argC;i>0;i--)
|
|
{
|
|
argV[i] = Stack.Pop();
|
|
}
|
|
var name = EnsurePop<TString>();
|
|
var callable=env.GetObject(name.Value) as TCallable;
|
|
if(callable != null)
|
|
{
|
|
Stack.Push(callable.Execute(argV));
|
|
}else{
|
|
Stack.Push(new TUndefined());
|
|
}
|
|
}
|
|
break;
|
|
case Instruction.CALL_METHOD:
|
|
{
|
|
var argC = (int)EnsurePop<TNumber>().Value;
|
|
TObject[] argV = new TObject[argC];
|
|
for(int i = argC;i>0;i--)
|
|
{
|
|
argV[i] = Stack.Pop();
|
|
}
|
|
var name = EnsurePop<TString>();
|
|
var symbol = Stack.Pop();
|
|
|
|
Stack.Push(ExecuteMethod(symbol,name.Value,argV));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(Stack.Count > 0)
|
|
return Stack.Pop();
|
|
return new TNull();
|
|
}
|
|
|
|
private TObject ExecuteMethod(TObject symbol, string name, params TObject[] argV)
|
|
{
|
|
|
|
}
|
|
|
|
private T EnsurePop<T>() where T : TObject
|
|
{
|
|
var item = Stack.Pop() as T;
|
|
if(item == null) throw new Exception("Pop failed");
|
|
return item;
|
|
}
|
|
|
|
private double ReadDouble()
|
|
{
|
|
if(IP + 8 < Chunk.Code.Length)
|
|
{
|
|
byte[] data =new byte[8];
|
|
Array.Copy(Chunk.Code,IP,data,0,8);
|
|
IP += 8;
|
|
if(BitConverter.IsLittleEndian) Array.Reverse(data);
|
|
return BitConverter.ToDouble(data,0);
|
|
}
|
|
throw new Exception("Malformed binary");
|
|
}
|
|
private int ReadInt()
|
|
{
|
|
if(IP + 4 < Chunk.Code.Length)
|
|
{
|
|
byte[] data =new byte[4];
|
|
Array.Copy(Chunk.Code,IP,data,0,4);
|
|
IP += 4;
|
|
if(BitConverter.IsLittleEndian) Array.Reverse(data);
|
|
return BitConverter.ToInt32(data,0);
|
|
}
|
|
throw new Exception("Malformed binary");
|
|
}
|
|
private string ReadString()
|
|
{
|
|
int strLen = ReadInt();
|
|
if(IP + strLen < Chunk.Code.Length)
|
|
{
|
|
var str=System.Text.Encoding.UTF8.GetString(Chunk.Code,IP,strLen);
|
|
IP += strLen;
|
|
return str;
|
|
}
|
|
throw new Exception("Malformed binary");
|
|
}
|
|
private TObject Mod(TObject object1, TObject object2)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private TObject Divide(TObject object1, TObject object2)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private TObject Times(TObject object1, TObject object2)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private TObject Sub(TObject object2, TObject object1)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private TObject Add(TObject object2, TObject object1)
|
|
{
|
|
var left = object2 as TNumber;
|
|
var right = object1 as TNumber;
|
|
var lStr = object2 as TString;
|
|
var rStr = object1 as TString;
|
|
if(left != null && right != null)
|
|
{
|
|
return new TNumber(left.Value + right.Value);
|
|
}
|
|
if(lStr != null && rStr != null)
|
|
{
|
|
return new TString(lStr.Value + rStr.Value);
|
|
}
|
|
return new TNumber(0);
|
|
}
|
|
}
|
|
|
|
|
|
}
|