tlang-runtime-compiler/TLang.VM/ChunkExecuter.cs

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);
}
}
}