Compare commits
No commits in common. "c309d6158a2135d2f5378db203a3f76e14301586" and "b1eceec1d242d76154cf3275a2295dae55bafbb3" have entirely different histories.
c309d6158a
...
b1eceec1d2
27 changed files with 2832 additions and 263 deletions
504
AssemblerVVM/Program.cs
Normal file
504
AssemblerVVM/Program.cs
Normal file
|
@ -0,0 +1,504 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace AssemblerVVM
|
||||||
|
{
|
||||||
|
class pair
|
||||||
|
{
|
||||||
|
public int start;
|
||||||
|
public int end;
|
||||||
|
public pair(int one, int two)
|
||||||
|
{
|
||||||
|
start = one;
|
||||||
|
end = two;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class str_header
|
||||||
|
{
|
||||||
|
public byte[] signature
|
||||||
|
{ get; set; }
|
||||||
|
public int version
|
||||||
|
{ get; set; }
|
||||||
|
public int const_count
|
||||||
|
{ get; set; }
|
||||||
|
public int size_const
|
||||||
|
{ get; set; }
|
||||||
|
|
||||||
|
public str_header()
|
||||||
|
{
|
||||||
|
signature = new byte[2];
|
||||||
|
signature[0] = 0xBA;
|
||||||
|
signature[1] = 0xBA;
|
||||||
|
version = 0100;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
using (MemoryStream m = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(m))
|
||||||
|
{
|
||||||
|
writer.Write(signature);
|
||||||
|
writer.Write(version);
|
||||||
|
writer.Write(const_count);
|
||||||
|
writer.Write(size_const);
|
||||||
|
}
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class funcH_common
|
||||||
|
{
|
||||||
|
public ushort start_id
|
||||||
|
{ get; set; }
|
||||||
|
public int count_of_funcs
|
||||||
|
{ get; set; }
|
||||||
|
|
||||||
|
public funcH_common(ushort id, int count)
|
||||||
|
{
|
||||||
|
start_id = id;
|
||||||
|
count_of_funcs = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
using (MemoryStream m = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(m))
|
||||||
|
{
|
||||||
|
writer.Write(start_id);
|
||||||
|
writer.Write(count_of_funcs);
|
||||||
|
|
||||||
|
}
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class funcH_signature
|
||||||
|
{
|
||||||
|
public int size_func
|
||||||
|
{ get; set; }
|
||||||
|
public int size_bytecode
|
||||||
|
{ get; set; }
|
||||||
|
public int size_signature
|
||||||
|
{ get; set; }
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
using (MemoryStream m = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(m))
|
||||||
|
{
|
||||||
|
writer.Write(size_func);
|
||||||
|
writer.Write(size_bytecode);
|
||||||
|
writer.Write(size_signature);
|
||||||
|
}
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class funcH_bytecode
|
||||||
|
{
|
||||||
|
public ushort id
|
||||||
|
{ get; set; }
|
||||||
|
public int count_locals
|
||||||
|
{ get; set; }
|
||||||
|
public int count_args
|
||||||
|
{ get; set; }
|
||||||
|
|
||||||
|
public byte[] Serialize()
|
||||||
|
{
|
||||||
|
using (MemoryStream m = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(m))
|
||||||
|
{
|
||||||
|
writer.Write(id);
|
||||||
|
writer.Write(count_locals);
|
||||||
|
writer.Write(count_args);
|
||||||
|
}
|
||||||
|
return m.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public funcH_bytecode()
|
||||||
|
{
|
||||||
|
count_locals = 0;
|
||||||
|
count_args = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static int Main(string[] args)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (args.Length < 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No input files");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Dictionary<int, string> ConstID_VALUE = new Dictionary<int, string>();
|
||||||
|
Dictionary<string, int> ConstKEY_ID = new Dictionary<string, int>();
|
||||||
|
Dictionary<string, int> CodeMARK_POS = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
List<pair> FuncPos = new List<pair>(); //This array defines first and last line of every procedure
|
||||||
|
|
||||||
|
string outname = args[0].Replace(".vasm","")+".vvm";
|
||||||
|
if (args.Length > 1)
|
||||||
|
outname = args[1];
|
||||||
|
|
||||||
|
int[] pos_d = new int[2] { 0, 0 }; //This array defines first and last line of data segment
|
||||||
|
string[] source = System.IO.File.ReadAllLines(args[0]); //Array of source code
|
||||||
|
|
||||||
|
List<string> src = PositionAnalyse(source, ref pos_d, ref FuncPos, ref CodeMARK_POS);
|
||||||
|
|
||||||
|
using (var bw = new BinaryWriter(File.Open(outname, FileMode.OpenOrCreate))) //Writing into a file with bytecode
|
||||||
|
{
|
||||||
|
HeaderAnalyse(src, pos_d, bw, ref ConstID_VALUE, ref ConstKEY_ID);
|
||||||
|
funcH_common FuncCommonH = new funcH_common(CRC16_alg("main"), FuncPos.Count); //We define there that start procedure calls "main"
|
||||||
|
bw.Write(FuncCommonH.Serialize()); //We also define there number of procedures in our code
|
||||||
|
for (int i = 0; i < FuncPos.Count; i++)
|
||||||
|
{
|
||||||
|
FuncAnalyse(src, FuncPos[i], bw, ConstKEY_ID, CodeMARK_POS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<string> PositionAnalyse(string[] input, ref int[] posD, ref List<pair> posC, ref Dictionary<string, int> marks)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function fills arrays that define positions of functions and data in source text
|
||||||
|
* Return value - is an array of source text without comments, empty strings etc
|
||||||
|
* ---
|
||||||
|
* input - is an array of source text
|
||||||
|
* posD - is an array of start and end position of data segment
|
||||||
|
* posC - is an array of start and end position of every procedure
|
||||||
|
* marks - is an array of labes
|
||||||
|
*/
|
||||||
|
List<string> src = new List<string>();
|
||||||
|
bool func_flag = false;
|
||||||
|
int pos1 = 0, pos2 = 0, numline = 0;
|
||||||
|
foreach (string s in input)
|
||||||
|
if (s != "") //Skip empty strings
|
||||||
|
{
|
||||||
|
if (func_flag == true && Regex.IsMatch(s, @"\w+:")) //Labels cannot be outside of procedure
|
||||||
|
{
|
||||||
|
marks.Add(s.Trim(' ', '\t', ':'), numline);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
src.Add(s.Trim(' ', '\t'));
|
||||||
|
|
||||||
|
if (s.Contains(".data")) //Checking data segment
|
||||||
|
posD[0] = src.Count - 1;
|
||||||
|
if (s.Contains(".endd"))
|
||||||
|
posD[1] = src.Count - 1;
|
||||||
|
|
||||||
|
if (s.Contains(".proc")) //Checking procedure segment
|
||||||
|
{
|
||||||
|
numline = 0;
|
||||||
|
pos1 = src.Count - 1;
|
||||||
|
func_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.Contains(".endp"))
|
||||||
|
{
|
||||||
|
pos2 = src.Count - 1;
|
||||||
|
if (func_flag == true)
|
||||||
|
{
|
||||||
|
func_flag = false;
|
||||||
|
posC.Add(new pair(pos1, pos2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numline++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void HeaderAnalyse(List<string> src, int[] pos, BinaryWriter bw, ref Dictionary<int, string> id_v, ref Dictionary<string, int> k_id)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function creates bytecode header. Header contains signature, version, text constants and their size
|
||||||
|
* ---
|
||||||
|
* src - clear source text
|
||||||
|
* pos - position of data segment in source text
|
||||||
|
* bw - writer to a file
|
||||||
|
* id_v, k_id - dictionaries for text constatns
|
||||||
|
*/
|
||||||
|
str_header ConstH = new str_header(); //Object that stores all text consntants.
|
||||||
|
string pattern = "\".*\""; //Pattern to take text constants (to delete)
|
||||||
|
string pattern_adv = "\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\""; //Advanced patternn
|
||||||
|
int j = 1;
|
||||||
|
for (int i = pos[0] + 1; i < pos[1]; i++) //pos[0] = .data ; pos[0]+1 = first text const
|
||||||
|
{
|
||||||
|
int position = src[i].IndexOf(" ");
|
||||||
|
string key = src[i].Substring(0, position);
|
||||||
|
string value = Regex.Match(src[i], pattern_adv).ToString();
|
||||||
|
value = value.Substring(1, value.Length-2).Replace(@"\n", "\n").Replace(@"\r", "\r").Replace("\\\"","\"") + "\0";
|
||||||
|
id_v.Add(j, value); k_id.Add(key, j++); //All contstants have their numeric equivalent, so we store both.
|
||||||
|
ConstH.const_count++; ConstH.size_const += (value.Length); //Defining total size of constants
|
||||||
|
}
|
||||||
|
bw.Write(ConstH.Serialize());
|
||||||
|
for (int i = 1; i < j; i++)
|
||||||
|
{
|
||||||
|
bw.Write(Encoding.ASCII.GetBytes(id_v[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FuncAnalyse(List<string> code, pair pos, BinaryWriter bw, Dictionary<string, int> dictStr, Dictionary<string, int> dictJmp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function writes an actual procedure in bytecode.
|
||||||
|
* It will decode text-name of instructio into bytecode aswell as agruments for instruction
|
||||||
|
* ---
|
||||||
|
* code - clear source code
|
||||||
|
* pos - position of procedures
|
||||||
|
* dicStr - dictionary for text constants
|
||||||
|
* dictJmp - dictionary for every jump
|
||||||
|
*/
|
||||||
|
string name = "";
|
||||||
|
MemoryStream str = new MemoryStream();
|
||||||
|
funcH_signature sign = new funcH_signature();
|
||||||
|
funcH_bytecode bc = new funcH_bytecode();
|
||||||
|
|
||||||
|
string[] current_str = code[pos.start].Split(' '); //Spliting string in case of arguments for instruction
|
||||||
|
switch (current_str.Length)
|
||||||
|
{
|
||||||
|
case 4: //2 arg instruction
|
||||||
|
bc.count_args = System.Convert.ToInt32(current_str[3]);
|
||||||
|
bc.count_locals = System.Convert.ToInt32(current_str[2]);
|
||||||
|
name = current_str[1];
|
||||||
|
break;
|
||||||
|
case 3: //1 arg intruction
|
||||||
|
bc.count_locals = System.Convert.ToInt32(current_str[2]);
|
||||||
|
name = current_str[1];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2: //No arg
|
||||||
|
name = current_str[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bc.id = CRC16_alg(name); //Hash encode for function name
|
||||||
|
name += "\0";
|
||||||
|
sign.size_signature = name.Length;
|
||||||
|
using (BinaryWriter writer = new BinaryWriter(str))
|
||||||
|
{
|
||||||
|
int j = 1;
|
||||||
|
for (int i = pos.start + 1; i < pos.end; i++)
|
||||||
|
{
|
||||||
|
current_str = code[i].Split(' ');
|
||||||
|
opcode current_opc = (opcode)Enum.Parse(typeof(opcode), current_str[0].ToUpper());
|
||||||
|
writer.Write((byte)current_opc);
|
||||||
|
|
||||||
|
if (current_opc == opcode.DLOAD)
|
||||||
|
writer.Write(Convert.ToDouble(current_str[1]));
|
||||||
|
else if (current_opc == opcode.ILOAD)
|
||||||
|
writer.Write(Convert.ToInt64(current_str[1]));
|
||||||
|
else if (current_opc == opcode.SLOAD)
|
||||||
|
writer.Write((ushort)dictStr[current_str[1]]);
|
||||||
|
else if (current_opc == opcode.CALL)
|
||||||
|
writer.Write(CRC16_alg(current_str[1]));
|
||||||
|
else if (threebytes.Contains(current_opc))
|
||||||
|
writer.Write(ushort.Parse(current_str[1]));
|
||||||
|
else if (fivebytes.Contains(current_opc))
|
||||||
|
{
|
||||||
|
writer.Write(CRC16_alg(current_str[1]));
|
||||||
|
writer.Write(ushort.Parse(current_str[2]));
|
||||||
|
}
|
||||||
|
else if (jumps.Contains(current_opc)) //Pain in the arse
|
||||||
|
writer.Write(FindOffset(code, pos, j, ((ushort)dictJmp[current_str[1]]-j)));
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bcode = str.ToArray();
|
||||||
|
sign.size_bytecode = bcode.Length;
|
||||||
|
sign.size_func = 22 + sign.size_bytecode + sign.size_signature; //Magic number 22 - size of meta-info for
|
||||||
|
|
||||||
|
bw.Write(sign.Serialize());
|
||||||
|
bw.Write(Encoding.ASCII.GetBytes(name));
|
||||||
|
bw.Write(bc.Serialize());
|
||||||
|
bw.Write(bcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static short FindOffset(List<string> code, pair pos, int curr_pos, int off)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This function calculating offset of bytes to jump a label.
|
||||||
|
*/
|
||||||
|
short result = 0;
|
||||||
|
if (off > 0) //Jumping forward
|
||||||
|
{
|
||||||
|
for (int i = curr_pos + 1; i < curr_pos + off; i++)
|
||||||
|
{
|
||||||
|
result += OpCodeSize((opcode)Enum.Parse(typeof(opcode), code[pos.start+i].Split(' ')[0].ToUpper()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //Jumping backward
|
||||||
|
{
|
||||||
|
for (int i = curr_pos; i >= curr_pos + off; i--)
|
||||||
|
{
|
||||||
|
result -= OpCodeSize((opcode)Enum.Parse(typeof(opcode), code[pos.start+i].Split(' ')[0].ToUpper()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static short OpCodeSize(opcode opc)
|
||||||
|
{
|
||||||
|
short result = 0;
|
||||||
|
if (jumps.Contains(opc) || threebytes.Contains(opc))
|
||||||
|
result += 3;
|
||||||
|
else if (fivebytes.Contains(opc))
|
||||||
|
result += 5;
|
||||||
|
else if (ninebytes.Contains(opc))
|
||||||
|
result += 9;
|
||||||
|
else result++;
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ushort CRC16_alg(string msg)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* HashFunction on Cyclic redundacy check algorythm
|
||||||
|
*/
|
||||||
|
byte[] text = Encoding.ASCII.GetBytes(msg);
|
||||||
|
const ushort polinom = 0xa001;
|
||||||
|
ushort code = 0xffff;
|
||||||
|
|
||||||
|
for (int i = 0, size = text.Length; i < size; ++i)
|
||||||
|
{
|
||||||
|
code ^= (ushort)(text[i] << 8);
|
||||||
|
|
||||||
|
for (uint j = 0; j < 8; ++j)
|
||||||
|
{
|
||||||
|
code >>= 1;
|
||||||
|
if ((code & 0x01) != 0) code ^= polinom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*static List<opcode> onebyte = new List<opcode>{opcode.INVALID, opcode.DLOAD0, opcode.ILOAD0, opcode.SLOAD0, opcode.DLOAD1, opcode.ILOAD1, opcode.DLOADM1,
|
||||||
|
opcode.ILOADM1, opcode.DADD, opcode.IADD, opcode.DSUB, opcode.ISUB, opcode.DMUL, opcode.IMUL, opcode.DDIV, opcode.IDIV, opcode.IMOD, opcode.DNEG,
|
||||||
|
opcode.INEG, opcode.IAOR, opcode.IAAND, opcode.IAXOR, opcode.IPRINT, opcode.DPRINT, opcode.SPRINT, opcode.I2D, opcode.D2I, opcode.S2I, opcode.SWAP,
|
||||||
|
opcode.POP, opcode.LOADDVAR0, opcode.LOADDVAR1, opcode.LOADDVAR2, opcode.LOADDVAR3, opcode.LOADIVAR0, opcode.LOADIVAR1, opcode.LOADIVAR2, opcode.LOADIVAR3,
|
||||||
|
opcode.LOADSVAR0, opcode.LOADSVAR1, opcode.LOADSVAR2, opcode.LOADSVAR3, opcode.STOREDVAR0, opcode.STOREDVAR1, opcode.STOREDVAR2, opcode.STOREDVAR3,
|
||||||
|
opcode.STOREIVAR0, opcode.STOREIVAR1, opcode.STOREIVAR2, opcode.STOREIVAR3, opcode.STORESVAR0, opcode.STORESVAR1, opcode.STORESVAR2, opcode.STORESVAR3,
|
||||||
|
opcode.ICMP, opcode.DCMP, opcode.DUMP, opcode.STOP, opcode.RETURN, opcode.BREAK};*/
|
||||||
|
|
||||||
|
static List<opcode> ninebytes = new List<opcode> { opcode.DLOAD, opcode.ILOAD };
|
||||||
|
|
||||||
|
static List<opcode> threebytes = new List<opcode> { opcode.LOADDVAR, opcode.LOADIVAR, opcode.LOADSVAR, opcode.STOREDVAR,
|
||||||
|
opcode.STOREIVAR, opcode.STORESVAR, opcode.SLOAD, opcode.CALL};
|
||||||
|
|
||||||
|
static List<opcode> fivebytes = new List<opcode> {opcode.LOADCTXDVAR, opcode.LOADCTXIVAR, opcode.LOADCTXSVAR, opcode.STORECTXDVAR,
|
||||||
|
opcode.STORECTXIVAR, opcode.STORECTXSVAR};
|
||||||
|
|
||||||
|
static List<opcode> jumps = new List<opcode> {opcode.JA, opcode.IFICMPE, opcode.IFICMPG, opcode.IFICMPGE, opcode.IFICMPL,
|
||||||
|
opcode.IFICMPLE, opcode.IFICMPNE};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum opcode
|
||||||
|
{
|
||||||
|
INVALID,
|
||||||
|
DLOAD,
|
||||||
|
ILOAD,
|
||||||
|
SLOAD,
|
||||||
|
DLOAD0,
|
||||||
|
ILOAD0,
|
||||||
|
SLOAD0,
|
||||||
|
DLOAD1,
|
||||||
|
ILOAD1,
|
||||||
|
DLOADM1,
|
||||||
|
ILOADM1,
|
||||||
|
DADD,
|
||||||
|
IADD,
|
||||||
|
DSUB,
|
||||||
|
ISUB,
|
||||||
|
DMUL,
|
||||||
|
IMUL,
|
||||||
|
DDIV,
|
||||||
|
IDIV,
|
||||||
|
IMOD,
|
||||||
|
DNEG,
|
||||||
|
INEG,
|
||||||
|
IAOR,
|
||||||
|
IAAND,
|
||||||
|
IAXOR,
|
||||||
|
IPRINT,
|
||||||
|
DPRINT,
|
||||||
|
SPRINT,
|
||||||
|
I2D,
|
||||||
|
D2I,
|
||||||
|
S2I,
|
||||||
|
SWAP,
|
||||||
|
POP,
|
||||||
|
LOADDVAR0,
|
||||||
|
LOADDVAR1,
|
||||||
|
LOADDVAR2,
|
||||||
|
LOADDVAR3,
|
||||||
|
LOADIVAR0,
|
||||||
|
LOADIVAR1,
|
||||||
|
LOADIVAR2,
|
||||||
|
LOADIVAR3,
|
||||||
|
LOADSVAR0,
|
||||||
|
LOADSVAR1,
|
||||||
|
LOADSVAR2,
|
||||||
|
LOADSVAR3,
|
||||||
|
STOREDVAR0,
|
||||||
|
STOREDVAR1,
|
||||||
|
STOREDVAR2,
|
||||||
|
STOREDVAR3,
|
||||||
|
STOREIVAR0,
|
||||||
|
STOREIVAR1,
|
||||||
|
STOREIVAR2,
|
||||||
|
STOREIVAR3,
|
||||||
|
STORESVAR0,
|
||||||
|
STORESVAR1,
|
||||||
|
STORESVAR2,
|
||||||
|
STORESVAR3,
|
||||||
|
LOADDVAR,
|
||||||
|
LOADIVAR,
|
||||||
|
LOADSVAR,
|
||||||
|
STOREDVAR,
|
||||||
|
STOREIVAR,
|
||||||
|
STORESVAR,
|
||||||
|
LOADCTXDVAR,
|
||||||
|
LOADCTXIVAR,
|
||||||
|
LOADCTXSVAR,
|
||||||
|
STORECTXDVAR,
|
||||||
|
STORECTXIVAR,
|
||||||
|
STORECTXSVAR,
|
||||||
|
DCMP,
|
||||||
|
ICMP,
|
||||||
|
JA,
|
||||||
|
IFICMPNE,
|
||||||
|
IFICMPE,
|
||||||
|
IFICMPG,
|
||||||
|
IFICMPGE,
|
||||||
|
IFICMPL,
|
||||||
|
IFICMPLE,
|
||||||
|
DUMP,
|
||||||
|
STOP,
|
||||||
|
CALL,
|
||||||
|
RETURN,
|
||||||
|
BREAK
|
||||||
|
};
|
||||||
|
}
|
61
AssemblerVVM/readme.txt
Normal file
61
AssemblerVVM/readme.txt
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
VVM Assembler.
|
||||||
|
Описание.
|
||||||
|
|
||||||
|
Параметром утилите подается текстовый файл с ассемблерными инструкциями для VVM.
|
||||||
|
|
||||||
|
|
||||||
|
Код разивается на блоки:
|
||||||
|
-Один константный блок
|
||||||
|
-Переменное количество процедурных блоков с кодом.
|
||||||
|
|
||||||
|
Блоки могут идти в любом порядке, но они не должны быть вложенными или пересекающимися.
|
||||||
|
Каждая инструкция или информация о блоке должна начинаться с новой строки.
|
||||||
|
Константный блок начинается с препроцессорной инструкции .data и заканчивается .endd
|
||||||
|
Внутри константного строки располагаются так:
|
||||||
|
signature "string"
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
.data
|
||||||
|
str1 "Hello World!"
|
||||||
|
str2 "Second String!"
|
||||||
|
.endd
|
||||||
|
|
||||||
|
Процедурные блоки начинаются с препроцессорной инструкции
|
||||||
|
.proc name [num_of_locals] [num_of_args]
|
||||||
|
и заканчиваются на .endp
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
.proc main
|
||||||
|
DLOAD1
|
||||||
|
DPRINT
|
||||||
|
STOP
|
||||||
|
.endp
|
||||||
|
|
||||||
|
Стартовая процедура носит имя main .
|
||||||
|
|
||||||
|
Реализована поддержка меток. Метка должна находится на отдельной строке и выглядит так
|
||||||
|
name:
|
||||||
|
|
||||||
|
Пример:
|
||||||
|
|
||||||
|
.data
|
||||||
|
src2 "Done"
|
||||||
|
.endd
|
||||||
|
|
||||||
|
.proc main
|
||||||
|
ILOAD0
|
||||||
|
ILOAD -3
|
||||||
|
again:
|
||||||
|
CALL inc
|
||||||
|
IFICMPNE again
|
||||||
|
SLOAD src2
|
||||||
|
SPRINT
|
||||||
|
STOP
|
||||||
|
.endp
|
||||||
|
|
||||||
|
.proc inc
|
||||||
|
ILOAD1
|
||||||
|
IADD
|
||||||
|
RETURN
|
||||||
|
.endp
|
68
CompilerVVM/CodeGen.cs
Normal file
68
CompilerVVM/CodeGen.cs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
class CodeGen
|
||||||
|
{
|
||||||
|
Dictionary<string, string> ConstData;
|
||||||
|
Dictionary<string, MethodAtom> Methods;
|
||||||
|
public string[] asm { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public CodeGen(Dictionary<string, string> ConstData, Dictionary<string, MethodAtom> Methods)
|
||||||
|
{
|
||||||
|
this.ConstData = ConstData;
|
||||||
|
this.Methods = Methods;
|
||||||
|
asm = null;
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, MethodAtom> p in Methods)
|
||||||
|
{
|
||||||
|
Optimization(p.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FillAsmCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FillAsmCode()
|
||||||
|
{
|
||||||
|
List<string> Code = new List<string>();
|
||||||
|
if (ConstData.Count > 0)
|
||||||
|
{
|
||||||
|
Code.Add(".data");
|
||||||
|
foreach (KeyValuePair<string, string> pair in ConstData)
|
||||||
|
{
|
||||||
|
Code.Add(string.Format("{0} \"{1}\"", pair.Value, pair.Key));
|
||||||
|
}
|
||||||
|
Code.Add(".endd");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (KeyValuePair<string, MethodAtom> entry in Methods)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < entry.Value.Code.Count; i++)
|
||||||
|
Code.Add(entry.Value.Code[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
asm = Code.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Optimization(MethodAtom Method)
|
||||||
|
{
|
||||||
|
List<string> OptimizationSub = new List<string>(){"LOADDVAR", "LOADIVAR", "LOADSVAR", "STOREDVAR", "STOREIVAR",
|
||||||
|
"STORESVAR"};
|
||||||
|
|
||||||
|
for (int i = 0; i < Method.Code.Count; i++ )
|
||||||
|
{
|
||||||
|
string[] parts = Method.Code[i].Split(' ');
|
||||||
|
if (OptimizationSub.Contains(parts[0]) && int.Parse(parts[1]) < 4)
|
||||||
|
{
|
||||||
|
Method.Code[i] = Method.Code[i].Replace(" ", "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
CompilerVVM/CustomExceptions.cs
Normal file
35
CompilerVVM/CustomExceptions.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
public class ScannerException : Exception
|
||||||
|
{
|
||||||
|
public ScannerException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProcedureException : Exception
|
||||||
|
{
|
||||||
|
public ProcedureException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ParserException : Exception
|
||||||
|
{
|
||||||
|
public ParserException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
558
CompilerVVM/Parser.cs
Normal file
558
CompilerVVM/Parser.cs
Normal file
|
@ -0,0 +1,558 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
public class Parser
|
||||||
|
{
|
||||||
|
int pointer;
|
||||||
|
IList<object> Tokens;
|
||||||
|
int idvar;
|
||||||
|
Dictionary<string, string> Constants;
|
||||||
|
Dictionary<string, MethodAtom> Methods;
|
||||||
|
int jumpid;
|
||||||
|
|
||||||
|
public Parser(IList<object> tokens, Dictionary<string, string> TextConst, Dictionary<string, MethodAtom> Methods)
|
||||||
|
{
|
||||||
|
Tokens = tokens;
|
||||||
|
pointer = 0;
|
||||||
|
jumpid = 0;
|
||||||
|
Constants = TextConst;
|
||||||
|
this.Methods = Methods;
|
||||||
|
ParseMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseMethod()
|
||||||
|
{
|
||||||
|
//<program> := <method>+
|
||||||
|
while (pointer != Tokens.Count)
|
||||||
|
{
|
||||||
|
//<method> := method <type_method> <ident>(<param>*) <block>
|
||||||
|
//<type> := int | double | string
|
||||||
|
//<type_method> := <type> | void
|
||||||
|
MethodAtom method = null;
|
||||||
|
if (!Tokens[pointer++].Equals("method"))
|
||||||
|
throw new ParserException("No method detected");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
method = Methods[Tokens[++pointer].ToString()];
|
||||||
|
idvar = method.NumOfParams;
|
||||||
|
method.Code.Add(String.Format(".proc {0}", method.Name, method.NumOfParams));
|
||||||
|
|
||||||
|
pointer += 3 + (2 * method.NumOfParams);
|
||||||
|
if (Tokens[pointer].Equals(OP.OpenBlock))
|
||||||
|
{
|
||||||
|
pointer++;
|
||||||
|
ParseBlock(method);
|
||||||
|
}
|
||||||
|
else throw new ParserException("No code block in method: " + method.Name);
|
||||||
|
|
||||||
|
if (method.Name == "main")
|
||||||
|
method.Code.Add("STOP");
|
||||||
|
else if (method.Type == "void")
|
||||||
|
method.Code.Add("RETURN");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (method.Code[method.Code.Count - 1] != "RETURN")
|
||||||
|
throw new ParserException("No return sequence in method:" + method.Name);
|
||||||
|
}
|
||||||
|
method.Code.Add(".endp");
|
||||||
|
|
||||||
|
method.NumOfLocals = method.Variables.Count;
|
||||||
|
method.Code[0] = String.Format(".proc {0} {1} {2}", method.Name, method.NumOfLocals, method.NumOfParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseBlock(MethodAtom method)
|
||||||
|
{
|
||||||
|
while (!Tokens[pointer].Equals(OP.CloseBlock))
|
||||||
|
{
|
||||||
|
if (Tokens[pointer].Equals(OP.Semicolon))
|
||||||
|
{
|
||||||
|
pointer++;
|
||||||
|
}
|
||||||
|
else if (code_types.Contains(Tokens[pointer].ToString()))
|
||||||
|
DeclareVar(method);
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Assigment))
|
||||||
|
{
|
||||||
|
List<Variable> NeededVars = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
||||||
|
if (NeededVars.Count == 0)
|
||||||
|
throw new ParserException(string.Format("Variable {0} is not defined", Tokens[pointer].ToString()));
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pointer += 2;
|
||||||
|
ParseExpression(method, NeededVars[NeededVars.Count - 1].Type);
|
||||||
|
switch (NeededVars[NeededVars.Count - 1].Type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add(string.Format("STOREIVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add(string.Format("STOREDVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
method.Code.Add(string.Format("STORESVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].ToString() == "return")
|
||||||
|
{
|
||||||
|
ParseSingleExpr(method, pointer + 1, method.Type);
|
||||||
|
method.Code.Add("RETURN");
|
||||||
|
pointer += 3;
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].ToString() == "if")
|
||||||
|
{
|
||||||
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam))
|
||||||
|
throw new ParserException("Wrong if statement");
|
||||||
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
||||||
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
||||||
|
if (var1 != var2)
|
||||||
|
throw new ParserException("Incompatible variable types in if statement");
|
||||||
|
int jump = jumpid++;
|
||||||
|
ParseSingleExpr(method, pointer + 4, var2);
|
||||||
|
ParseSingleExpr(method, pointer + 2, var1);
|
||||||
|
|
||||||
|
switch ((OP)Tokens[pointer + 3])
|
||||||
|
{
|
||||||
|
case OP.Equal:
|
||||||
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.NotEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.Greater:
|
||||||
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.GreaterEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.Less:
|
||||||
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.LessEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
if (!Tokens[pointer + 6].Equals(OP.OpenBlock))
|
||||||
|
throw new ParserException("No { code } in if statement");
|
||||||
|
pointer += 7;
|
||||||
|
ParseBlock(method);
|
||||||
|
if (Tokens[pointer].ToString() != "else")
|
||||||
|
{
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int jump2 = jumpid++;
|
||||||
|
method.Code.Add(string.Format("JA jump_{0}", jump2));
|
||||||
|
if (!Tokens[pointer + 1].Equals(OP.OpenBlock))
|
||||||
|
throw new ParserException("No { code } in else statement");
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
pointer += 2;
|
||||||
|
ParseBlock(method);
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].ToString() == "while")
|
||||||
|
{
|
||||||
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam))
|
||||||
|
throw new ParserException("Wrong while statement");
|
||||||
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
||||||
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
||||||
|
if (var1 != var2)
|
||||||
|
throw new ParserException("Incompatible variable types in while statement");
|
||||||
|
int jump = jumpid++;
|
||||||
|
int jump2 = jumpid++;
|
||||||
|
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
||||||
|
ParseSingleExpr(method, pointer + 4, var2);
|
||||||
|
ParseSingleExpr(method, pointer + 2, var1);
|
||||||
|
|
||||||
|
switch ((OP)Tokens[pointer + 3])
|
||||||
|
{
|
||||||
|
case OP.Equal:
|
||||||
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
case OP.NotEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
case OP.Greater:
|
||||||
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
case OP.GreaterEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
case OP.Less:
|
||||||
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
case OP.LessEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump2));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
if (!Tokens[pointer + 6].Equals(OP.OpenBlock))
|
||||||
|
throw new ParserException("No { code } in while statement");
|
||||||
|
pointer += 7;
|
||||||
|
ParseBlock(method);
|
||||||
|
method.Code.Add(string.Format("JA jump_{0}", jump));
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].ToString() == "do")
|
||||||
|
{
|
||||||
|
int jump = jumpid++;
|
||||||
|
int jump2 = jumpid++;
|
||||||
|
method.Code.Add(string.Format("JA jump_{0}", jump2));
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump));
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add(string.Format("jump_{0}:", jump2));
|
||||||
|
pointer += 2;
|
||||||
|
ParseBlock(method);
|
||||||
|
|
||||||
|
if (!Tokens[pointer + 1].Equals(OP.OpenParam) || !Tokens[pointer + 5].Equals(OP.CloseParam) ||
|
||||||
|
Tokens[pointer].ToString() != "until")
|
||||||
|
throw new ParserException("Wrong until statement");
|
||||||
|
string var1 = GetTypeOfVar(method, pointer + 2);
|
||||||
|
string var2 = GetTypeOfVar(method, pointer + 4);
|
||||||
|
if (var1 != var2)
|
||||||
|
throw new Exception("Incompatible variable types in until statement");
|
||||||
|
|
||||||
|
ParseSingleExpr(method, pointer + 4, var2);
|
||||||
|
ParseSingleExpr(method, pointer + 2, var1);
|
||||||
|
|
||||||
|
switch ((OP)Tokens[pointer + 3])
|
||||||
|
{
|
||||||
|
case OP.Equal:
|
||||||
|
method.Code.Add(string.Format("IFICMPNE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.NotEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.Greater:
|
||||||
|
method.Code.Add(string.Format("IFICMPLE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.GreaterEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPL jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.Less:
|
||||||
|
method.Code.Add(string.Format("IFICMPGE jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
case OP.LessEqual:
|
||||||
|
method.Code.Add(string.Format("IFICMPG jump_{0}", jump));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
method.Code.Add("POP");
|
||||||
|
method.Code.Add("POP");
|
||||||
|
pointer += 7;
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].ToString() == "print")
|
||||||
|
{
|
||||||
|
pointer += 2;
|
||||||
|
string type = GetTypeOfVar(method, pointer);
|
||||||
|
ParseSingleExpr(method, pointer, type);
|
||||||
|
pointer += 3;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("IPRINT");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DPRINT");
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
method.Code.Add("SPRINT");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer+1].Equals(OP.OpenParam))
|
||||||
|
{
|
||||||
|
ParseCall(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pointer++;
|
||||||
|
}
|
||||||
|
private void ParseExpression(MethodAtom method, string type)
|
||||||
|
{
|
||||||
|
if (Tokens[pointer + 1].Equals(OP.Semicolon))
|
||||||
|
{
|
||||||
|
ParseSingleExpr(method, pointer, type);
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.OpenParam))
|
||||||
|
{
|
||||||
|
ParseCall(method, type);
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer].Equals(OP.Sub))
|
||||||
|
{
|
||||||
|
ParseSingleExpr(method, pointer + 1, type);
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("INEG");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DNEG");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Add))
|
||||||
|
{
|
||||||
|
int prevpointer = pointer;
|
||||||
|
pointer += 2;
|
||||||
|
|
||||||
|
ParseExpression(method, type);
|
||||||
|
ParseSingleExpr(method, prevpointer, type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("IADD");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DADD");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Sub))
|
||||||
|
{
|
||||||
|
int prevpointer = pointer;
|
||||||
|
pointer += 2;
|
||||||
|
|
||||||
|
ParseExpression(method, type);
|
||||||
|
ParseSingleExpr(method, prevpointer, type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("ISUB");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DSUB");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Mul))
|
||||||
|
{
|
||||||
|
int prevpointer = pointer;
|
||||||
|
pointer += 2;
|
||||||
|
|
||||||
|
ParseExpression(method, type);
|
||||||
|
ParseSingleExpr(method, prevpointer, type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("IMUL");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DMUL");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Div))
|
||||||
|
{
|
||||||
|
int prevpointer = pointer;
|
||||||
|
pointer += 2;
|
||||||
|
|
||||||
|
ParseExpression(method, type);
|
||||||
|
ParseSingleExpr(method, prevpointer, type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("IDIV");
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add("DDIV");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Tokens[pointer + 1].Equals(OP.Mod))
|
||||||
|
{
|
||||||
|
int prevpointer = pointer;
|
||||||
|
pointer += 2;
|
||||||
|
|
||||||
|
ParseExpression(method, type);
|
||||||
|
ParseSingleExpr(method, prevpointer, type);
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add("IMOD");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ParserException("Incompatible types");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseCall(MethodAtom method, string type = null)
|
||||||
|
{
|
||||||
|
if (Methods.ContainsKey(Tokens[pointer].ToString()))
|
||||||
|
{
|
||||||
|
MethodAtom CallMethod = Methods[Tokens[pointer].ToString()];
|
||||||
|
if (type != null && type != CallMethod.Type)
|
||||||
|
throw new ParserException("Incompatible types when call method" + CallMethod.Name);
|
||||||
|
|
||||||
|
pointer += 2;
|
||||||
|
List<object> param = new List<object>();
|
||||||
|
while (!Tokens[pointer++].Equals(OP.CloseParam))
|
||||||
|
{
|
||||||
|
param.Add(Tokens[pointer]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.Count != CallMethod.NumOfParams)
|
||||||
|
throw new ParserException("Wrong params when call method" + CallMethod.Name);
|
||||||
|
|
||||||
|
for (int i = 0; i < param.Count; i++)
|
||||||
|
{
|
||||||
|
ParseSingleExpr(method, pointer - 2 - i, CallMethod.Variables[i].Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.Code.Add(string.Format("CALL {0}", CallMethod.Name));
|
||||||
|
|
||||||
|
}
|
||||||
|
else throw new ParserException("Undefined method to call");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseSingleExpr(MethodAtom method, int pointer, string type)
|
||||||
|
{
|
||||||
|
if (Tokens[pointer] is StringBuilder)
|
||||||
|
{
|
||||||
|
if (type == "string")
|
||||||
|
method.Code.Add(string.Format("SLOAD {0}", Constants[Tokens[pointer++].ToString()]));
|
||||||
|
else
|
||||||
|
throw new ParserException("Incompatible type");
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer] is Number)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
if (!Tokens[pointer].ToString().Contains("."))
|
||||||
|
{
|
||||||
|
method.Code.Add(string.Format("ILOAD {0}", Tokens[pointer++].ToString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else throw new ParserException("Incompatible type");
|
||||||
|
|
||||||
|
case "double":
|
||||||
|
method.Code.Add(string.Format("DLOAD {0}", Tokens[pointer++].ToString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Variable> NeededVars = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
||||||
|
if (NeededVars.Count != 0)
|
||||||
|
{
|
||||||
|
if (NeededVars[NeededVars.Count - 1].Type != type)
|
||||||
|
throw new ParserException("Incompatible type");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add(string.Format("LOADIVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
case "double":
|
||||||
|
method.Code.Add(string.Format("LOADDVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
method.Code.Add(string.Format("LOADSVAR {0}", NeededVars[NeededVars.Count - 1].ID));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw new ParserException("Can't parse sequence");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeclareVar(MethodAtom method)
|
||||||
|
{
|
||||||
|
method.Variables.Add(new Variable(idvar++, Tokens[pointer].ToString(), Tokens[pointer + 1].ToString()));
|
||||||
|
if (Tokens[pointer + 2].Equals(OP.Semicolon))
|
||||||
|
{
|
||||||
|
pointer += 3; return;
|
||||||
|
}
|
||||||
|
else if (Tokens[pointer + 2].Equals(OP.Assigment))
|
||||||
|
{
|
||||||
|
pointer += 3;
|
||||||
|
ParseExpression(method, method.Variables[idvar - 1].Type);
|
||||||
|
switch (method.Variables[idvar - 1].Type)
|
||||||
|
{
|
||||||
|
case "int":
|
||||||
|
method.Code.Add(string.Format("STOREIVAR {0}", idvar - 1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "double":
|
||||||
|
method.Code.Add(string.Format("STOREDVAR {0}", idvar - 1));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "string":
|
||||||
|
method.Code.Add(string.Format("STORESVAR {0}", idvar - 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Tokens[pointer].Equals(OP.Semicolon))
|
||||||
|
{
|
||||||
|
pointer++; return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new ParserException("Wrong variable defenition");
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTypeOfVar(MethodAtom method, int pointer)
|
||||||
|
{
|
||||||
|
string result = null;
|
||||||
|
if (Tokens[pointer] is StringBuilder)
|
||||||
|
result = "string";
|
||||||
|
else if (Tokens[pointer] is Number)
|
||||||
|
{
|
||||||
|
if (Tokens[pointer].ToString().Contains("."))
|
||||||
|
result = "double";
|
||||||
|
else result = "int";
|
||||||
|
}
|
||||||
|
else if (Methods.ContainsKey(Tokens[pointer].ToString()))
|
||||||
|
{
|
||||||
|
result = Methods[Tokens[pointer].ToString()].Type;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Variable> isVariable = method.Variables.FindAll(x => x.Name == Tokens[pointer].ToString());
|
||||||
|
if (isVariable.Count == 0)
|
||||||
|
throw new ParserException("No defined variable with name "+Tokens[pointer].ToString());
|
||||||
|
else result = isVariable[isVariable.Count - 1].Type;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<string> code_types = new List<string>() { "int", "double", "string" };
|
||||||
|
}
|
||||||
|
}
|
53
CompilerVVM/ProcedureScanner.cs
Normal file
53
CompilerVVM/ProcedureScanner.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
class ProcedureScanner
|
||||||
|
{
|
||||||
|
public Dictionary<string, MethodAtom> Methods { get; set; }
|
||||||
|
int pointer;
|
||||||
|
|
||||||
|
public ProcedureScanner(IList<object> Tokens)
|
||||||
|
{
|
||||||
|
// TODO: Complete member initialization
|
||||||
|
pointer = 0;
|
||||||
|
ScanForMethods(Tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ScanForMethods(IList<object> Tokens)
|
||||||
|
{
|
||||||
|
Methods = new Dictionary<string, MethodAtom>();
|
||||||
|
while (pointer != Tokens.Count)
|
||||||
|
{
|
||||||
|
MethodAtom method = null;
|
||||||
|
if (Tokens[pointer++].Equals("method"))
|
||||||
|
{
|
||||||
|
if (!Tokens[pointer].Equals("void") && !Tokens[pointer].Equals("int") &&
|
||||||
|
!Tokens[pointer].Equals("double") && !Tokens[pointer].Equals("string"))
|
||||||
|
{
|
||||||
|
throw new ProcedureException("Wrong method defenition");
|
||||||
|
}
|
||||||
|
int idvar = 0;
|
||||||
|
method = new MethodAtom(Tokens[++pointer].ToString());
|
||||||
|
method.Type = Tokens[pointer++ - 1].ToString();
|
||||||
|
if (!Tokens[pointer++].Equals(OP.OpenParam))
|
||||||
|
throw new ProcedureException("Wrong method defenition of method: "+method.Name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (!Tokens[pointer].Equals(OP.CloseParam))
|
||||||
|
{
|
||||||
|
Variable a = new Variable(idvar++, Tokens[pointer].ToString(), Tokens[pointer + 1].ToString());
|
||||||
|
method.Variables.Add(a);
|
||||||
|
method.NumOfParams++;
|
||||||
|
pointer += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Methods.Add(method.Name, method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
CompilerVVM/Program.cs
Normal file
53
CompilerVVM/Program.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length != 1)
|
||||||
|
{
|
||||||
|
Console.WriteLine("No input file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* This program translate source code into assembly instructions
|
||||||
|
* This can be done in several steps:
|
||||||
|
* 1. Compiler finds tokens (individual parts such as text, spec. symbols, numbers etc);
|
||||||
|
* 2. Compiler goes through tokens and defines functions
|
||||||
|
* 3. Compiler parse every function into assembler instruction
|
||||||
|
*/
|
||||||
|
TokenScanner scanner = null;
|
||||||
|
ProcedureScanner procscanner = null;
|
||||||
|
Parser parser = null;
|
||||||
|
CodeGen code = null;
|
||||||
|
Dictionary<string, string> TextConst = new Dictionary<string, string>();
|
||||||
|
using (System.IO.TextReader input = System.IO.File.OpenText(args[0]))
|
||||||
|
{
|
||||||
|
scanner = new TokenScanner(input, TextConst);
|
||||||
|
procscanner = new ProcedureScanner(scanner.Tokens);
|
||||||
|
parser = new Parser(scanner.Tokens, TextConst, procscanner.Methods);
|
||||||
|
|
||||||
|
code = new CodeGen(TextConst, procscanner.Methods);
|
||||||
|
//ByteCode.GenerateByteCode(code.asm, args[0] + ".vvm");
|
||||||
|
System.IO.File.WriteAllLines(args[0] + ".vasm", code.asm);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(e.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
293
CompilerVVM/TokenScanner.cs
Normal file
293
CompilerVVM/TokenScanner.cs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.IO;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace CompilerVVM
|
||||||
|
{
|
||||||
|
class TokenScanner
|
||||||
|
{
|
||||||
|
int idconst = 1;
|
||||||
|
private IList<object> tokens;
|
||||||
|
private IList<object> list;
|
||||||
|
public IList<object> Tokens { get { return tokens; } }
|
||||||
|
private void Scan(System.IO.TextReader input, Dictionary<string, string> dict)
|
||||||
|
{
|
||||||
|
while (input.Peek() != -1)
|
||||||
|
{
|
||||||
|
char ch = (char)input.Peek();
|
||||||
|
|
||||||
|
if (char.IsWhiteSpace(ch))
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (char.IsLetter(ch) || ch == '_')
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
while (char.IsLetter(ch) || ch == '_' || char.IsNumber(ch))
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char)input.Peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokens.Add(accum.ToString());
|
||||||
|
}
|
||||||
|
else if (ch == '"')
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
throw new ScannerException("Unterminated string");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ch = (char)input.Peek()) != '"')
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
throw new ScannerException("Unterminated string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Read();
|
||||||
|
dict.Add(accum.ToString(), string.Format("text_const_{0}", idconst++));
|
||||||
|
this.tokens.Add(accum);
|
||||||
|
}
|
||||||
|
else if (char.IsDigit(ch))
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
while (char.IsDigit(ch) || ch == '.')
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char)input.Peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokens.Add(new Number(accum.ToString()));
|
||||||
|
}
|
||||||
|
else if (ch == '(')
|
||||||
|
{
|
||||||
|
this.tokens.Add(OP.OpenParam);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
throw new ScannerException("Unterminated parameter section");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ch = (char)input.Peek()) != ')')
|
||||||
|
{
|
||||||
|
if (char.IsLetter(ch) || ch == '_')
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
while (char.IsLetter(ch) || ch == '_')
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char)input.Peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokens.Add(accum.ToString());
|
||||||
|
}
|
||||||
|
else if (char.IsDigit(ch))
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
while (char.IsDigit(ch) || ch == '.')
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ch = (char)input.Peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokens.Add(new Number(accum.ToString()));
|
||||||
|
}
|
||||||
|
else if (ch == '"')
|
||||||
|
{
|
||||||
|
StringBuilder accum = new StringBuilder();
|
||||||
|
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
throw new ScannerException("Unterminated string");
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ch = (char)input.Peek()) != '"')
|
||||||
|
{
|
||||||
|
accum.Append(ch);
|
||||||
|
input.Read();
|
||||||
|
|
||||||
|
if (input.Peek() == -1)
|
||||||
|
{
|
||||||
|
throw new ScannerException("Unterminated string");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input.Read();
|
||||||
|
dict.Add(accum.ToString(), string.Format("text_const_{0}", idconst++));
|
||||||
|
this.tokens.Add(accum);
|
||||||
|
}
|
||||||
|
else if (char.IsWhiteSpace(ch) || ch == ',')
|
||||||
|
input.Read();
|
||||||
|
else switch (ch)
|
||||||
|
{
|
||||||
|
case '=':
|
||||||
|
input.Read();
|
||||||
|
if ((char)input.Peek() == '=')
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Equal);
|
||||||
|
}
|
||||||
|
else this.tokens.Add(OP.Assigment);
|
||||||
|
break;
|
||||||
|
case '<':
|
||||||
|
input.Read();
|
||||||
|
if ((char)input.Peek() == '=')
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.LessEqual);
|
||||||
|
}
|
||||||
|
else this.tokens.Add(OP.Less);
|
||||||
|
break;
|
||||||
|
case '>':
|
||||||
|
input.Read();
|
||||||
|
if ((char)input.Peek() == '=')
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.GreaterEqual);
|
||||||
|
}
|
||||||
|
else this.tokens.Add(OP.Greater);
|
||||||
|
break;
|
||||||
|
case '!':
|
||||||
|
input.Read();
|
||||||
|
if ((char)input.Peek() == '=')
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.NotEqual);
|
||||||
|
}
|
||||||
|
else throw new Exception("!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.tokens.Add(OP.CloseParam);
|
||||||
|
input.Read();
|
||||||
|
}
|
||||||
|
else switch (ch)
|
||||||
|
{
|
||||||
|
case ';':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Semicolon);
|
||||||
|
break;
|
||||||
|
case '{':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.OpenBlock);
|
||||||
|
break;
|
||||||
|
case '}':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.CloseBlock);
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Add);
|
||||||
|
break;
|
||||||
|
case '-':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Sub);
|
||||||
|
break;
|
||||||
|
case '*':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Mul);
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Div);
|
||||||
|
break;
|
||||||
|
case '%':
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Mod);
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
input.Read();
|
||||||
|
if ((char)input.Peek() == '=')
|
||||||
|
{
|
||||||
|
input.Read();
|
||||||
|
this.tokens.Add(OP.Equal);
|
||||||
|
}
|
||||||
|
else this.tokens.Add(OP.Assigment);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ScannerException("Scanner encountered unrecognized character '" + ch + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TokenScanner(TextReader input, Dictionary<string, string> TextConstant)
|
||||||
|
{
|
||||||
|
tokens = new List<object>();
|
||||||
|
this.Scan(input, TextConstant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OP
|
||||||
|
{
|
||||||
|
Semicolon,
|
||||||
|
OpenBlock,
|
||||||
|
CloseBlock,
|
||||||
|
OpenParam,
|
||||||
|
CloseParam,
|
||||||
|
Assigment,
|
||||||
|
Equal,
|
||||||
|
NotEqual,
|
||||||
|
Less,
|
||||||
|
Greater,
|
||||||
|
LessEqual,
|
||||||
|
GreaterEqual,
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod
|
||||||
|
}
|
||||||
|
}
|
43
CompilerVVM/grammar.txt
Normal file
43
CompilerVVM/grammar.txt
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
<program> := <method>+
|
||||||
|
|
||||||
|
<method> := method <type_method> <ident>(<param>* ) <block>
|
||||||
|
|
||||||
|
<type> := int | double | string
|
||||||
|
<type_method> := <type> | void
|
||||||
|
|
||||||
|
<ident> := <char> <ident_rest>*
|
||||||
|
<ident_rest> := <char> | <digit>
|
||||||
|
<digit> := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
|
||||||
|
|
||||||
|
<int> := <digit>+
|
||||||
|
<double> := <int>.<int>
|
||||||
|
<string> := " <char>? "
|
||||||
|
|
||||||
|
<param> := <type> <ident>
|
||||||
|
|
||||||
|
<block> := <call>;
|
||||||
|
| <ident> = <expr>;
|
||||||
|
| <type> <ident>;
|
||||||
|
| <type> <ident> = <expr>;
|
||||||
|
| if <condit> <block>
|
||||||
|
| if <condit> <block> else <block>
|
||||||
|
| while <condit> <block>
|
||||||
|
| do <block> until <condit>;
|
||||||
|
|
||||||
|
<condit> := <int> <cond_op> <int>
|
||||||
|
<cond_op> := < | > | == | <= | >=
|
||||||
|
|
||||||
|
<expr> := <int>
|
||||||
|
| <double>
|
||||||
|
| <string>
|
||||||
|
| <int> <arith_op> <int>
|
||||||
|
| <double> <arith_op> <double>
|
||||||
|
| <ident>
|
||||||
|
| <call>
|
||||||
|
|
||||||
|
<arith_op> := + | - | * | /
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
0
README.md
Normal file
0
README.md
Normal file
39
VaninVM/CodeHeader.h
Normal file
39
VaninVM/CodeHeader.h
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#ifndef CODE_HEADER
|
||||||
|
#define CODE_HEADER
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
char signature[2];
|
||||||
|
int version;
|
||||||
|
int count_const;
|
||||||
|
int size_const;
|
||||||
|
}Const_Header;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned short id_start;
|
||||||
|
int count_code;
|
||||||
|
}ByteCodeH_Common;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int size_func;
|
||||||
|
int size_bytecode;
|
||||||
|
int size_signature;
|
||||||
|
}ByteCodeH_Signat;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
unsigned short id;
|
||||||
|
int count_locals;
|
||||||
|
int count_args;
|
||||||
|
}ByteCodeH_Primary;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int count_locals;
|
||||||
|
int count_args;
|
||||||
|
char* code;
|
||||||
|
} func;
|
||||||
|
#endif
|
||||||
|
#pragma pop
|
54
VaninVM/Context.c
Normal file
54
VaninVM/Context.c
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#include "Context.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
context* create_context(int function, func** hash, char** code)
|
||||||
|
{
|
||||||
|
context* cont;
|
||||||
|
cont = (context*)malloc(sizeof(context));
|
||||||
|
|
||||||
|
cont->id = function;
|
||||||
|
cont->locals=(l_var*)malloc(sizeof(l_var)*hash[function]->count_locals);
|
||||||
|
*(code)=hash[function]->code;
|
||||||
|
return cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_context(context* cont)
|
||||||
|
{
|
||||||
|
free(cont->locals);
|
||||||
|
free(cont);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_context(context* cont)
|
||||||
|
{
|
||||||
|
c_node* node;
|
||||||
|
node = (c_node*)malloc(sizeof(c_node));
|
||||||
|
node->obj=cont;
|
||||||
|
node->prev = node_last;
|
||||||
|
node_last = node;
|
||||||
|
}
|
||||||
|
context* pop_context()
|
||||||
|
{
|
||||||
|
context* poped = node_last->obj;
|
||||||
|
c_node* last = node_last;
|
||||||
|
node_last = node_last->prev;
|
||||||
|
|
||||||
|
free(last);
|
||||||
|
return poped;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find Context in Context Stack.
|
||||||
|
context* find_context(int id)
|
||||||
|
{
|
||||||
|
context* result = NULL;
|
||||||
|
c_node* current = node_last;
|
||||||
|
while (current != NULL || current->prev != NULL)
|
||||||
|
{
|
||||||
|
if (current->obj->id == id)
|
||||||
|
{
|
||||||
|
result = current->obj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
current = current->prev;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
34
VaninVM/Context.h
Normal file
34
VaninVM/Context.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef CONTEXT_H
|
||||||
|
#define CONTEXT_H
|
||||||
|
|
||||||
|
#include "CodeHeader.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
double d_data;
|
||||||
|
long long i_data;
|
||||||
|
char* s_data;
|
||||||
|
} l_var;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int id;
|
||||||
|
l_var* locals;
|
||||||
|
} context;
|
||||||
|
|
||||||
|
typedef struct NODE
|
||||||
|
{
|
||||||
|
context* obj;
|
||||||
|
struct NODE* prev;
|
||||||
|
} c_node;
|
||||||
|
|
||||||
|
c_node* node_last;
|
||||||
|
|
||||||
|
context* create_context(int function, func** hash,char** code);
|
||||||
|
void push_context(context*);
|
||||||
|
context* pop_context();
|
||||||
|
void remove_context(context*);
|
||||||
|
context* find_context(int id);
|
||||||
|
|
||||||
|
#endif
|
77
VaninVM/IOcode.c
Normal file
77
VaninVM/IOcode.c
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#include "IOcode.h"
|
||||||
|
#include "CodeHeader.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int version = 100;
|
||||||
|
|
||||||
|
int read_bytecode(FILE* stream, func*** hash)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char* name;
|
||||||
|
char* code;
|
||||||
|
func* function;
|
||||||
|
|
||||||
|
*(hash) = (func**)malloc(sizeof(func*)*65536);
|
||||||
|
fread_s(&ByteCodeH_Common, sizeof(ByteCodeH_Common), sizeof(ByteCodeH_Common), 1, stream);
|
||||||
|
|
||||||
|
for (i=0; i<ByteCodeH_Common.count_code; i++)
|
||||||
|
{
|
||||||
|
function = (func*)malloc(sizeof(func));
|
||||||
|
|
||||||
|
fread(&ByteCodeH_Signat, sizeof(ByteCodeH_Signat), 1, stream);
|
||||||
|
name = (char*)malloc(ByteCodeH_Signat.size_signature);
|
||||||
|
fread(name, ByteCodeH_Signat.size_signature, 1, stream);
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
fread(&ByteCodeH_Primary, sizeof(ByteCodeH_Primary), 1, stream);
|
||||||
|
code = (char*)malloc(ByteCodeH_Signat.size_bytecode);
|
||||||
|
fread(code, ByteCodeH_Signat.size_bytecode, 1, stream);
|
||||||
|
|
||||||
|
function->code=code;
|
||||||
|
function->count_args=ByteCodeH_Primary.count_args;
|
||||||
|
function->count_locals=ByteCodeH_Primary.count_locals;
|
||||||
|
(*(hash))[ByteCodeH_Primary.id]=function;
|
||||||
|
}
|
||||||
|
return ByteCodeH_Common.id_start;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_constant(FILE* stream, int* count, char*** index)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
char* buffer;
|
||||||
|
|
||||||
|
fread_s(&Const_Header, sizeof(Const_Header), sizeof(Const_Header), 1, stream); //Reading first part of header
|
||||||
|
if (Const_Header.signature[0]==Const_Header.signature[1]== 0xBA)
|
||||||
|
{
|
||||||
|
printf("%s", "Wrong file-format");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Const_Header.version != version)
|
||||||
|
{
|
||||||
|
printf("%s", "Unsupported bytecode format");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(count) = Const_Header.count_const;
|
||||||
|
|
||||||
|
buffer = (char*)malloc(Const_Header.size_const+1);
|
||||||
|
buffer[0]=0;
|
||||||
|
*(index) = (char**)malloc(sizeof(char**)*Const_Header.count_const+1);
|
||||||
|
|
||||||
|
|
||||||
|
fread_s(buffer+1, Const_Header.size_const, sizeof(char), Const_Header.size_const, stream); //Reading constant values
|
||||||
|
|
||||||
|
j=0;
|
||||||
|
(*(index))[j++]=buffer;
|
||||||
|
for (i = 0; i<Const_Header.size_const+1; i++)
|
||||||
|
{
|
||||||
|
if (j>Const_Header.count_const)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (buffer[i] == 0)
|
||||||
|
(*(index))[j++]=&buffer[i+1];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
10
VaninVM/IOcode.h
Normal file
10
VaninVM/IOcode.h
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef IOCODE_H
|
||||||
|
#define IOCODE_H
|
||||||
|
|
||||||
|
#include "CodeHeader.h"
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int read_bytecode(FILE*, func***);
|
||||||
|
int read_constant(FILE*, int*, char***);
|
||||||
|
#endif
|
50
VaninVM/LocalVars.c
Normal file
50
VaninVM/LocalVars.c
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include "LocalVars.h"
|
||||||
|
#include "TOS.h"
|
||||||
|
|
||||||
|
long long getlocal_int(context* cont, int id)
|
||||||
|
{
|
||||||
|
long long result;
|
||||||
|
result = (cont->locals)[id].i_data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getlocal_double(context* cont, int id)
|
||||||
|
{
|
||||||
|
double result;
|
||||||
|
result = (cont->locals)[id].d_data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* getlocal_string(context* cont, int id)
|
||||||
|
{
|
||||||
|
char* result;
|
||||||
|
result = (cont->locals)[id].s_data;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putlocal_int(long long* num, context* cont, int id)
|
||||||
|
{
|
||||||
|
memmove((cont->locals)+id, num, sizeof (long long));
|
||||||
|
}
|
||||||
|
|
||||||
|
void putlocal_double(double* num, context* cont, int id)
|
||||||
|
{
|
||||||
|
memmove((cont->locals)+id, num, sizeof(double));
|
||||||
|
}
|
||||||
|
|
||||||
|
void putlocal_string(char* str, context* cont, int id)
|
||||||
|
{
|
||||||
|
memmove((cont->locals)+id, str, sizeof(int*));
|
||||||
|
}
|
||||||
|
|
||||||
|
void args_to_local(context* cont, func** hash)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
long long tmp;
|
||||||
|
for (i=0; i<(hash[cont->id]->count_args); i++)
|
||||||
|
{
|
||||||
|
tmp = pop_int();
|
||||||
|
memmove(&((cont->locals)[i]), &tmp, sizeof(long long));
|
||||||
|
}
|
||||||
|
}
|
14
VaninVM/LocalVars.h
Normal file
14
VaninVM/LocalVars.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef LOCALVARS_H
|
||||||
|
#define LOCALVARS_H
|
||||||
|
#include "Context.h"
|
||||||
|
|
||||||
|
long long getlocal_int(context*, int);
|
||||||
|
double getlocal_double(context*, int);
|
||||||
|
char* getlocal_string(context*, int);
|
||||||
|
|
||||||
|
void putlocal_int(long long*, context*, int);
|
||||||
|
void putlocal_double(double*, context*, int);
|
||||||
|
void putlocal_string(char*, context*, int);
|
||||||
|
|
||||||
|
void args_to_local(context*, func**);
|
||||||
|
#endif
|
623
VaninVM/Main.c
Normal file
623
VaninVM/Main.c
Normal file
|
@ -0,0 +1,623 @@
|
||||||
|
#include "OpCode.h"
|
||||||
|
|
||||||
|
#include "IOcode.h"
|
||||||
|
#include "CodeHeader.h"
|
||||||
|
|
||||||
|
#include "TOS.h"
|
||||||
|
#include "Context.h"
|
||||||
|
#include "ReturnStack.h"
|
||||||
|
#include "LocalVars.h"
|
||||||
|
|
||||||
|
|
||||||
|
long long cmp_int(long long* a, long long* b);
|
||||||
|
double cmp_double(double* a, double* b);
|
||||||
|
int run_interpreter(char* filename);
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
int return_code;
|
||||||
|
if (argc<2)
|
||||||
|
{
|
||||||
|
printf("%s", "File is not specified");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return_code = run_interpreter(argv[1]);
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_interpreter(char* filename)
|
||||||
|
{
|
||||||
|
//ByteCode Variables
|
||||||
|
FILE* input;
|
||||||
|
|
||||||
|
//ExecutionProcess Variables
|
||||||
|
double d1, d2;
|
||||||
|
long long i1, i2;
|
||||||
|
unsigned short s1;
|
||||||
|
short s2;
|
||||||
|
char *code;
|
||||||
|
int ip, startfunc;
|
||||||
|
|
||||||
|
//ConstSection Variables
|
||||||
|
int const_count;
|
||||||
|
char** const_index;
|
||||||
|
|
||||||
|
//CodeSection Variables
|
||||||
|
func** phash_table;
|
||||||
|
context* current_context;
|
||||||
|
|
||||||
|
//Running Variable
|
||||||
|
int exec_status = 1;
|
||||||
|
|
||||||
|
fopen_s(&input, filename, "rb");
|
||||||
|
//const_pull
|
||||||
|
if (read_constant(input, &const_count, &const_index) != 0)
|
||||||
|
return 1;
|
||||||
|
//code_pull
|
||||||
|
startfunc = read_bytecode(input, &phash_table);
|
||||||
|
fclose(input);
|
||||||
|
|
||||||
|
initTOS(3000);
|
||||||
|
|
||||||
|
initRStack(1000);
|
||||||
|
ip = 0;
|
||||||
|
|
||||||
|
current_context = create_context(startfunc, phash_table, &code);
|
||||||
|
|
||||||
|
node_last = NULL;
|
||||||
|
|
||||||
|
while (exec_status)
|
||||||
|
{
|
||||||
|
switch (code[ip++])
|
||||||
|
{
|
||||||
|
case INVALID:
|
||||||
|
//DO(INVALID, "Invalid instruction.", 1)
|
||||||
|
break;
|
||||||
|
case DLOAD:
|
||||||
|
//DO(DLOAD, "Load double on TOS, inlined into insn stream.", 9)
|
||||||
|
d1 = *((double*)(code+ip));
|
||||||
|
push_double(d1);
|
||||||
|
ip+=8; break;
|
||||||
|
case ILOAD:
|
||||||
|
//DO(ILOAD, "Load int on TOS, inlined into insn stream.", 9)
|
||||||
|
i1 = *((long long*)(code+ip));
|
||||||
|
push_int(i1);
|
||||||
|
ip+=8; break;
|
||||||
|
case SLOAD:
|
||||||
|
s1 = *((short*)(code+ip));
|
||||||
|
push_int((long long)(const_index[s1]));
|
||||||
|
ip+=2; break;
|
||||||
|
case DLOAD0:
|
||||||
|
// DO(DLOAD0, "Load double 0 on TOS.", 1)
|
||||||
|
push_double(0);
|
||||||
|
break;
|
||||||
|
case ILOAD0:
|
||||||
|
//DO(ILOAD0, "Load int 0 on TOS.", 1)
|
||||||
|
push_int(0);
|
||||||
|
break;
|
||||||
|
case SLOAD0:
|
||||||
|
//DO(SLOAD0, "Load empty string on TOS.", 1)
|
||||||
|
push_int((long long)(const_index[0]));
|
||||||
|
break;
|
||||||
|
case DLOAD1:
|
||||||
|
//DO(DLOAD1, "Load double 1 on TOS.", 1)
|
||||||
|
push_double(1);
|
||||||
|
break;
|
||||||
|
case ILOAD1:
|
||||||
|
//DO(ILOAD1, "Load int 1 on TOS.", 1)
|
||||||
|
push_int(1);
|
||||||
|
break;
|
||||||
|
case DLOADM1:
|
||||||
|
//DO(DLOADM1, "Load double -1 on TOS.", 1)
|
||||||
|
push_double(-1);
|
||||||
|
break;
|
||||||
|
case ILOADM1:
|
||||||
|
//DO(ILOADM1, "Load int -1 on TOS.", 1)
|
||||||
|
push_int(-1);
|
||||||
|
break;
|
||||||
|
case DADD:
|
||||||
|
//DO(DADD, "Add 2 doubles on TOS, push value back.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
d2 = pop_double();
|
||||||
|
d1 += d2;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case IADD:
|
||||||
|
//DO(IADD, "Add 2 ints on TOS, push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 += i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case DSUB:
|
||||||
|
//DO(DSUB, "Subtract 2 doubles on TOS (lower from upper), push value back.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
d2 = pop_double();
|
||||||
|
d1 -= d2;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case ISUB:
|
||||||
|
//DO(ISUB, "Subtract 2 ints on TOS (lower from upper), push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 -= i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case DMUL:
|
||||||
|
//DO(DMUL, "Multiply 2 doubles on TOS, push value back.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
d2 = pop_double();
|
||||||
|
d1 *= d2;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case IMUL:
|
||||||
|
//DO(IMUL, "Multiply 2 ints on TOS, push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 *= i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case DDIV:
|
||||||
|
//DO(DDIV, "Divide 2 doubles on TOS (upper to lower), push value back.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
d2 = pop_double();
|
||||||
|
d1 = d1/d2;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case IDIV:
|
||||||
|
//DO(IDIV, "Divide 2 ints on TOS (upper to lower), push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 = i1/i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case IMOD:
|
||||||
|
//DO(IMOD, "Modulo operation on 2 ints on TOS (upper to lower), push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 = i1 % i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case DNEG:
|
||||||
|
//DO(DNEG, "Negate double on TOS.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
d1 = -d1;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case INEG:
|
||||||
|
//DO(INEG, "Negate int on TOS.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i1 = - i1;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case IAOR:
|
||||||
|
//DO(IAOR, "Arithmetic OR of 2 ints on TOS, push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 = i1 | i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case IAAND:
|
||||||
|
//DO(IAAND, "Arithmetic AND of 2 ints on TOS, push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 = i1 & i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case IAXOR:
|
||||||
|
//DO(IAXOR, "Arithmetic XOR of 2 ints on TOS, push value back.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
i1 = i1 ^ i2;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case IPRINT:
|
||||||
|
//DO(IPRINT, "Pop and print integer TOS.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
printf("%llu", i1);
|
||||||
|
break;
|
||||||
|
case DPRINT:
|
||||||
|
//DO(DPRINT, "Pop and print double TOS.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
printf("%f", d1);
|
||||||
|
break;
|
||||||
|
case SPRINT:
|
||||||
|
//DO(SPRINT, "Pop and print string TOS.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
printf("%s", (char*)i1);
|
||||||
|
break;
|
||||||
|
case I2D:
|
||||||
|
//DO(I2D, "Convert int on TOS to double.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
d1 = (double)i1;
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case D2I:
|
||||||
|
//DO(D2I, "Convert double on TOS to int.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
i1 = (int)d1;
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case S2I:
|
||||||
|
//DO(S2I, "Convert string on TOS to int.", 1)
|
||||||
|
break;
|
||||||
|
case SWAP:
|
||||||
|
//DO(SWAP, "Swap 2 topmost values.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
i2 = pop_int();
|
||||||
|
push_int(i1);
|
||||||
|
push_int(i2);
|
||||||
|
break;
|
||||||
|
case POP:
|
||||||
|
//DO(POP, "Remove topmost value.", 1)
|
||||||
|
TOS--;
|
||||||
|
break;
|
||||||
|
case LOADDVAR0:
|
||||||
|
//DO(LOADDVAR0, "Load double from variable 0, push on TOS.", 1)
|
||||||
|
d1 = getlocal_double(current_context, 0);
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case LOADDVAR1:
|
||||||
|
//DO(LOADDVAR1, "Load double from variable 1, push on TOS.", 1)
|
||||||
|
d1 = getlocal_double(current_context, 1);
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case LOADDVAR2:
|
||||||
|
//DO(LOADDVAR2, "Load double from variable 2, push on TOS.", 1)
|
||||||
|
d1 = getlocal_double(current_context, 2);
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case LOADDVAR3:
|
||||||
|
//DO(LOADDVAR3, "Load double from variable 3, push on TOS.", 1)
|
||||||
|
d1 = getlocal_double(current_context, 3);
|
||||||
|
push_double(d1);
|
||||||
|
break;
|
||||||
|
case LOADIVAR0:
|
||||||
|
//DO(LOADIVAR0, "Load int from variable 0, push on TOS.", 1)
|
||||||
|
i1 = getlocal_int(current_context, 0);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADIVAR1:
|
||||||
|
//DO(LOADIVAR1, "Load int from variable 1, push on TOS.", 1)
|
||||||
|
i1 = getlocal_int(current_context, 1);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADIVAR2:
|
||||||
|
//DO(LOADIVAR2, "Load int from variable 2, push on TOS.", 1)
|
||||||
|
i1 = getlocal_int(current_context, 2);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADIVAR3:
|
||||||
|
//DO(LOADIVAR3, "Load int from variable 3, push on TOS.", 1)
|
||||||
|
i1 = getlocal_int(current_context, 3);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADSVAR0:
|
||||||
|
//DO(LOADSVAR0, "Load string from variable 0, push on TOS.", 1)
|
||||||
|
i1 = (long long)getlocal_string(current_context, 0);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADSVAR1:
|
||||||
|
//DO(LOADSVAR1, "Load string from variable 1, push on TOS.", 1)
|
||||||
|
i1 = (long long)getlocal_string(current_context, 1);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADSVAR2:
|
||||||
|
//DO(LOADSVAR2, "Load string from variable 2, push on TOS.", 1)
|
||||||
|
i1 = (long long)getlocal_string(current_context, 2);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case LOADSVAR3:
|
||||||
|
//DO(LOADSVAR3, "Load string from variable 3, push on TOS.", 1)
|
||||||
|
i1 = (long long)getlocal_string(current_context, 3);
|
||||||
|
push_int(i1);
|
||||||
|
break;
|
||||||
|
case STOREDVAR0:
|
||||||
|
//DO(STOREDVAR0, "Pop TOS and store to double variable 0.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
putlocal_double(&d1, current_context, 0);
|
||||||
|
break;
|
||||||
|
case STOREDVAR1:
|
||||||
|
//DO(STOREDVAR1, "Pop TOS and store to double variable 1.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
putlocal_double(&d1, current_context, 1);
|
||||||
|
break;
|
||||||
|
case STOREDVAR2:
|
||||||
|
//DO(STOREDVAR2, "Pop TOS and store to double variable 2.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
putlocal_double(&d1, current_context, 2);
|
||||||
|
break;
|
||||||
|
case STOREDVAR3:
|
||||||
|
//DO(STOREDVAR3, "Pop TOS and store to double variable 3.", 1)
|
||||||
|
d1 = pop_double();
|
||||||
|
putlocal_double(&d1, current_context, 3);
|
||||||
|
break;
|
||||||
|
case STOREIVAR0:
|
||||||
|
//DO(STOREIVAR0, "Pop TOS and store to int variable 0.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_int(&i1, current_context, 0);
|
||||||
|
break;
|
||||||
|
case STOREIVAR1:
|
||||||
|
//DO(STOREIVAR1, "Pop TOS and store to int variable 1.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_int(&i1, current_context, 1);
|
||||||
|
break;
|
||||||
|
case STOREIVAR2:
|
||||||
|
//DO(STOREIVAR2, "Pop TOS and store to int variable 2.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_int(&i1, current_context, 2);
|
||||||
|
break;
|
||||||
|
case STOREIVAR3:
|
||||||
|
//DO(STOREIVAR3, "Pop TOS and store to int variable 3.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_int(&i1, current_context, 3);
|
||||||
|
break;
|
||||||
|
case STORESVAR0:
|
||||||
|
//DO(STORESVAR0, "Pop TOS and store to string variable 0.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_string((char*)&i1, current_context, 0);
|
||||||
|
break;
|
||||||
|
case STORESVAR1:
|
||||||
|
//DO(STORESVAR1, "Pop TOS and store to string variable 1.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_string((char*)&i1, current_context, 1);
|
||||||
|
break;
|
||||||
|
case STORESVAR2:
|
||||||
|
//DO(STORESVAR2, "Pop TOS and store to string variable 2.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_string((char*)&i1, current_context, 2);
|
||||||
|
break;
|
||||||
|
case STORESVAR3:
|
||||||
|
//DO(STORESVAR3, "Pop TOS and store to string variable 3.", 1)
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_string((char*)&i1, current_context, 3);
|
||||||
|
break;
|
||||||
|
case LOADDVAR:
|
||||||
|
//DO(LOADDVAR, "Load double from variable, whose 2-byte is id inlined to insn stream, push on TOS.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
d1 = getlocal_double(current_context, s1);
|
||||||
|
push_double(d1);
|
||||||
|
ip+=2; break;
|
||||||
|
case LOADIVAR:
|
||||||
|
//DO(LOADIVAR, "Load int from variable, whose 2-byte id is inlined to insn stream, push on TOS.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
i1 = getlocal_int(current_context, s1);
|
||||||
|
push_int(i1);
|
||||||
|
ip+=2; break;
|
||||||
|
case LOADSVAR:
|
||||||
|
//DO(LOADSVAR, "Load string from variable, whose 2-byte id is inlined to insn stream, push on TOS.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
i1 = (long long)getlocal_string(current_context, s1);
|
||||||
|
push_int(i1);
|
||||||
|
ip+=2; break;
|
||||||
|
case STOREDVAR:
|
||||||
|
//DO(STOREDVAR, "Pop TOS and store to double variable, whose 2-byte id is inlined to insn stream.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
d1 = pop_double();
|
||||||
|
putlocal_double(&d1, current_context, s1);
|
||||||
|
ip+=2; break;
|
||||||
|
case STOREIVAR:
|
||||||
|
//DO(STOREIVAR, "Pop TOS and store to int variable, whose 2-byte id is inlined to insn stream.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_int(&i1, current_context, s1);
|
||||||
|
ip+=2; break;
|
||||||
|
case STORESVAR:
|
||||||
|
//DO(STORESVAR, "Pop TOS and store to string variable, whose 2-byte id is inlined to insn stream.", 3)
|
||||||
|
s1 = *((short*)(code+ip));
|
||||||
|
i1 = pop_int();
|
||||||
|
putlocal_string((char*)&i1, current_context, s1);
|
||||||
|
ip+=2; break;
|
||||||
|
case LOADCTXDVAR:
|
||||||
|
//DO(LOADCTXDVAR, "Load double from variable, whose 2-byte context and 2-byte id inlined to insn stream, push on TOS.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
d1 = getlocal_double(find_context(s1), s2);
|
||||||
|
push_double(d1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case LOADCTXIVAR:
|
||||||
|
//DO(LOADCTXIVAR, "Load int from variable, whose 2-byte context and 2-byte id is inlined to insn stream, push on TOS.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
i1 = getlocal_int(find_context(s1), s2);
|
||||||
|
push_int(i1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case LOADCTXSVAR:
|
||||||
|
//DO(LOADCTXSVAR, "Load string from variable, whose 2-byte context and 2-byte id is inlined to insn stream, push on TOS.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
i1 = (long long)getlocal_string(find_context(s1), s2);
|
||||||
|
push_double(i1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case STORECTXDVAR:
|
||||||
|
//DO(STORECTXDVAR, "Pop TOS and store to double variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
d1 = pop_double();
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
putlocal_double(&d1, find_context(s1), s2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case STORECTXIVAR:
|
||||||
|
//DO(STORECTXIVAR, "Pop TOS and store to int variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
i1 = pop_int();
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
putlocal_int(&i1, find_context(s1), s2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case STORECTXSVAR:
|
||||||
|
//DO(STORECTXSVAR, "Pop TOS and store to string variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
i1 = pop_int();
|
||||||
|
if (find_context(s1) != NULL)
|
||||||
|
{
|
||||||
|
putlocal_string((char*)&d1, find_context(s1), s2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%s", "Context Not Found");
|
||||||
|
exec_status = 0;
|
||||||
|
}
|
||||||
|
ip+=2; break;
|
||||||
|
case DCMP:
|
||||||
|
//DO(DCMP, "Compare 2 topmost doubles, pushing libc-style comparator value cmp(upper, lower) as integer.", 1)
|
||||||
|
d1 = TOS[tp-1];
|
||||||
|
d2 = TOS[tp-2];
|
||||||
|
push_double(cmp_double(&d1, &d2));
|
||||||
|
break;
|
||||||
|
case ICMP:
|
||||||
|
//DO(ICMP, "Compare 2 topmost ints, pushing libc-style comparator value cmp(upper, lower) as integer.", 1)
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
push_int(cmp_int(&i1, &i2));
|
||||||
|
break;
|
||||||
|
case JA:
|
||||||
|
//DO(JA, "Jump always, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip += (2+s2); break;
|
||||||
|
case IFICMPNE:
|
||||||
|
// DO(IFICMPNE, "Compare two topmost integers and jump if upper != lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 != i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case IFICMPE:
|
||||||
|
//DO(IFICMPE, "Compare two topmost integers and jump if upper == lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 == i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case IFICMPG:
|
||||||
|
//DO(IFICMPG, "Compare two topmost integers and jump if upper > lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 > i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case IFICMPGE:
|
||||||
|
//DO(IFICMPGE, "Compare two topmost integers and jump if upper >= lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 >= i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case IFICMPL:
|
||||||
|
//DO(IFICMPL, "Compare two topmost integers and jump if upper < lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 < i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case IFICMPLE:
|
||||||
|
//DO(IFICMPLE, "Compare two topmost integers and jump if upper <= lower, next two bytes - signed offset of jump destination.", 3)
|
||||||
|
s2 = *((short*)(code+ip));
|
||||||
|
ip+=2;
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
i2 = get_int(tp-2);
|
||||||
|
if (i1 <= i2)
|
||||||
|
ip += s2;
|
||||||
|
break;
|
||||||
|
case DUMP:
|
||||||
|
//DO(DUMP, "Dump value on TOS, without removing it.", 1)
|
||||||
|
i1 = get_int(tp-1);
|
||||||
|
d1 = TOS[tp-1];
|
||||||
|
printf("TOS: Double:%e Integer:%llu Hex:%#08llx", d1, i1, i1);
|
||||||
|
break;
|
||||||
|
case STOP:
|
||||||
|
//DO(STOP, "Stop execution.", 1)
|
||||||
|
exec_status = 0;
|
||||||
|
break;
|
||||||
|
case CALL:
|
||||||
|
//DO(CALL, "Call function, next two bytes - unsigned function id.", 3)
|
||||||
|
s1 = *((unsigned short*)(code+ip));
|
||||||
|
ip+=2; push_ret(ip);
|
||||||
|
push_context(current_context);
|
||||||
|
current_context = create_context(s1, phash_table, &code);
|
||||||
|
args_to_local(current_context, phash_table);
|
||||||
|
ip = 0; break;
|
||||||
|
case RETURN:
|
||||||
|
//DO(RETURN, "Return to call location", 1)
|
||||||
|
remove_context(current_context);
|
||||||
|
current_context = pop_context();
|
||||||
|
code = phash_table[current_context->id]->code;
|
||||||
|
ip = pop_ret(); break;
|
||||||
|
case BREAK:
|
||||||
|
//DO(BREAK, "Breakpoint for the debugger.", 1)
|
||||||
|
getchar();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remove_context(current_context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double cmp_double(long long* a, long long* b)
|
||||||
|
{
|
||||||
|
if (*a == *b) return 0;
|
||||||
|
else if (*a > *b) return 1;
|
||||||
|
else return -1;
|
||||||
|
}
|
||||||
|
long long cmp_int(long long* a, long long* b)
|
||||||
|
{
|
||||||
|
if (*a == *b) return 0;
|
||||||
|
else if (*a > *b) return 1;
|
||||||
|
else return -1;
|
||||||
|
}
|
89
VaninVM/OpCode.h
Normal file
89
VaninVM/OpCode.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#ifndef OPCODE_H
|
||||||
|
#define OPCODE_H
|
||||||
|
enum opcode
|
||||||
|
{
|
||||||
|
INVALID,
|
||||||
|
DLOAD,
|
||||||
|
ILOAD,
|
||||||
|
SLOAD,
|
||||||
|
DLOAD0,
|
||||||
|
ILOAD0,
|
||||||
|
SLOAD0,
|
||||||
|
DLOAD1,
|
||||||
|
ILOAD1,
|
||||||
|
DLOADM1,
|
||||||
|
ILOADM1,
|
||||||
|
DADD,
|
||||||
|
IADD,
|
||||||
|
DSUB,
|
||||||
|
ISUB,
|
||||||
|
DMUL,
|
||||||
|
IMUL,
|
||||||
|
DDIV,
|
||||||
|
IDIV,
|
||||||
|
IMOD,
|
||||||
|
DNEG,
|
||||||
|
INEG,
|
||||||
|
IAOR,
|
||||||
|
IAAND,
|
||||||
|
IAXOR,
|
||||||
|
IPRINT,
|
||||||
|
DPRINT,
|
||||||
|
SPRINT,
|
||||||
|
I2D,
|
||||||
|
D2I,
|
||||||
|
S2I,
|
||||||
|
SWAP,
|
||||||
|
POP,
|
||||||
|
LOADDVAR0,
|
||||||
|
LOADDVAR1,
|
||||||
|
LOADDVAR2,
|
||||||
|
LOADDVAR3,
|
||||||
|
LOADIVAR0,
|
||||||
|
LOADIVAR1,
|
||||||
|
LOADIVAR2,
|
||||||
|
LOADIVAR3,
|
||||||
|
LOADSVAR0,
|
||||||
|
LOADSVAR1,
|
||||||
|
LOADSVAR2,
|
||||||
|
LOADSVAR3,
|
||||||
|
STOREDVAR0,
|
||||||
|
STOREDVAR1,
|
||||||
|
STOREDVAR2,
|
||||||
|
STOREDVAR3,
|
||||||
|
STOREIVAR0,
|
||||||
|
STOREIVAR1,
|
||||||
|
STOREIVAR2,
|
||||||
|
STOREIVAR3,
|
||||||
|
STORESVAR0,
|
||||||
|
STORESVAR1,
|
||||||
|
STORESVAR2,
|
||||||
|
STORESVAR3,
|
||||||
|
LOADDVAR,
|
||||||
|
LOADIVAR,
|
||||||
|
LOADSVAR,
|
||||||
|
STOREDVAR,
|
||||||
|
STOREIVAR,
|
||||||
|
STORESVAR,
|
||||||
|
LOADCTXDVAR,
|
||||||
|
LOADCTXIVAR,
|
||||||
|
LOADCTXSVAR,
|
||||||
|
STORECTXDVAR,
|
||||||
|
STORECTXIVAR,
|
||||||
|
STORECTXSVAR,
|
||||||
|
DCMP,
|
||||||
|
ICMP,
|
||||||
|
JA,
|
||||||
|
IFICMPNE,
|
||||||
|
IFICMPE,
|
||||||
|
IFICMPG,
|
||||||
|
IFICMPGE,
|
||||||
|
IFICMPL,
|
||||||
|
IFICMPLE,
|
||||||
|
DUMP,
|
||||||
|
STOP,
|
||||||
|
CALL,
|
||||||
|
RETURN,
|
||||||
|
BREAK
|
||||||
|
};
|
||||||
|
#endif
|
20
VaninVM/ReturnStack.c
Normal file
20
VaninVM/ReturnStack.c
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "ReturnStack.h"
|
||||||
|
|
||||||
|
void push_ret(int num)
|
||||||
|
{
|
||||||
|
RStack[rp++] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pop_ret()
|
||||||
|
{
|
||||||
|
int num = RStack[--rp];
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
int initRStack(int size)
|
||||||
|
{
|
||||||
|
RStack = (int*)calloc(1, size);
|
||||||
|
rp = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
9
VaninVM/ReturnStack.h
Normal file
9
VaninVM/ReturnStack.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef RETURNSTACK_H
|
||||||
|
#define RETURNSTACK_H
|
||||||
|
|
||||||
|
int* RStack;
|
||||||
|
int rp;
|
||||||
|
int initRStack(int);
|
||||||
|
void push_ret(int);
|
||||||
|
int pop_ret();
|
||||||
|
#endif
|
41
VaninVM/TOS.c
Normal file
41
VaninVM/TOS.c
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include <malloc.h>
|
||||||
|
#include "TOS.h"
|
||||||
|
|
||||||
|
void push_int(long long num)
|
||||||
|
{
|
||||||
|
tos_num number;
|
||||||
|
number.num_i = num;
|
||||||
|
TOS[tp++] = number.num_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long pop_int()
|
||||||
|
{
|
||||||
|
tos_num number;
|
||||||
|
number.num_d = TOS[--tp];
|
||||||
|
return number.num_i;
|
||||||
|
}
|
||||||
|
|
||||||
|
long long get_int(int id)
|
||||||
|
{
|
||||||
|
tos_num number;
|
||||||
|
number.num_d = TOS[id];
|
||||||
|
return number.num_i;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_double(double num)
|
||||||
|
{
|
||||||
|
TOS[tp++] = num;
|
||||||
|
}
|
||||||
|
|
||||||
|
double pop_double()
|
||||||
|
{
|
||||||
|
double num = TOS[--tp];
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
int initTOS(int count)
|
||||||
|
{
|
||||||
|
TOS = (double*)calloc(count, sizeof(double*));
|
||||||
|
tp = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
19
VaninVM/TOS.h
Normal file
19
VaninVM/TOS.h
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#ifndef TOS_H
|
||||||
|
#define TOS_H
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
double num_d;
|
||||||
|
long long num_i;
|
||||||
|
} tos_num;
|
||||||
|
|
||||||
|
double* TOS;
|
||||||
|
int tp;
|
||||||
|
int initTOS(int);
|
||||||
|
void push_int(long long);
|
||||||
|
void push_double(double);
|
||||||
|
long long pop_int();
|
||||||
|
long long get_int(int);
|
||||||
|
double pop_double();
|
||||||
|
|
||||||
|
#endif
|
85
VaninVM/opc.txt
Normal file
85
VaninVM/opc.txt
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
#define FOR_BYTECODES(DO) \
|
||||||
|
?-DO(INVALID, "Invalid instruction.", 1) \
|
||||||
|
+DO(DLOAD, "Load double on TOS, inlined into insn stream.", 9) \
|
||||||
|
+DO(ILOAD, "Load int on TOS, inlined into insn stream.", 9) \
|
||||||
|
+DO(SLOAD, "Load string reference on TOS, next two bytes - constant id.", 3) \
|
||||||
|
+DO(DLOAD0, "Load double 0 on TOS.", 1) \
|
||||||
|
+DO(ILOAD0, "Load int 0 on TOS.", 1) \
|
||||||
|
+DO(SLOAD0, "Load empty string on TOS.", 1) \
|
||||||
|
+DO(DLOAD1, "Load double 1 on TOS.", 1) \
|
||||||
|
+DO(ILOAD1, "Load int 1 on TOS.", 1) \
|
||||||
|
+DO(DLOADM1, "Load double -1 on TOS.", 1) \
|
||||||
|
+DO(ILOADM1, "Load int -1 on TOS.", 1) \
|
||||||
|
+DO(DADD, "Add 2 doubles on TOS, push value back.", 1) \
|
||||||
|
+DO(IADD, "Add 2 ints on TOS, push value back.", 1) \
|
||||||
|
+DO(DSUB, "Subtract 2 doubles on TOS (lower from upper), push value back.", 1) \
|
||||||
|
+DO(ISUB, "Subtract 2 ints on TOS (lower from upper), push value back.", 1) \
|
||||||
|
+DO(DMUL, "Multiply 2 doubles on TOS, push value back.", 1) \
|
||||||
|
+DO(IMUL, "Multiply 2 ints on TOS, push value back.", 1) \
|
||||||
|
+DO(DDIV, "Divide 2 doubles on TOS (upper to lower), push value back.", 1) \
|
||||||
|
+DO(IDIV, "Divide 2 ints on TOS (upper to lower), push value back.", 1) \
|
||||||
|
+DO(IMOD, "Modulo operation on 2 ints on TOS (upper to lower), push value back.", 1) \
|
||||||
|
+DO(DNEG, "Negate double on TOS.", 1) \
|
||||||
|
+DO(INEG, "Negate int on TOS.", 1) \
|
||||||
|
+DO(IAOR, "Arithmetic OR of 2 ints on TOS, push value back.", 1) \
|
||||||
|
+DO(IAAND, "Arithmetic AND of 2 ints on TOS, push value back.", 1) \
|
||||||
|
+DO(IAXOR, "Arithmetic XOR of 2 ints on TOS, push value back.", 1) \
|
||||||
|
+DO(IPRINT, "Pop and print integer TOS.", 1) \
|
||||||
|
+DO(DPRINT, "Pop and print double TOS.", 1) \
|
||||||
|
+DO(SPRINT, "Pop and print string TOS.", 1) \
|
||||||
|
+DO(I2D, "Convert int on TOS to double.", 1) \
|
||||||
|
+DO(D2I, "Convert double on TOS to int.", 1) \
|
||||||
|
+DO(S2I, "Convert string on TOS to int.", 1) \
|
||||||
|
+DO(SWAP, "Swap 2 topmost values.", 1) \
|
||||||
|
+DO(POP, "Remove topmost value.", 1) \
|
||||||
|
+DO(LOADDVAR0, "Load double from variable 0, push on TOS.", 1) \
|
||||||
|
+DO(LOADDVAR1, "Load double from variable 1, push on TOS.", 1) \
|
||||||
|
+DO(LOADDVAR2, "Load double from variable 2, push on TOS.", 1) \
|
||||||
|
+DO(LOADDVAR3, "Load double from variable 3, push on TOS.", 1) \
|
||||||
|
+DO(LOADIVAR0, "Load int from variable 0, push on TOS.", 1) \
|
||||||
|
+DO(LOADIVAR1, "Load int from variable 1, push on TOS.", 1) \
|
||||||
|
+DO(LOADIVAR2, "Load int from variable 2, push on TOS.", 1) \
|
||||||
|
+DO(LOADIVAR3, "Load int from variable 3, push on TOS.", 1) \
|
||||||
|
+DO(LOADSVAR0, "Load string from variable 0, push on TOS.", 1) \
|
||||||
|
+DO(LOADSVAR1, "Load string from variable 1, push on TOS.", 1) \
|
||||||
|
+DO(LOADSVAR2, "Load string from variable 2, push on TOS.", 1) \
|
||||||
|
+DO(LOADSVAR3, "Load string from variable 3, push on TOS.", 1) \
|
||||||
|
+DO(STOREDVAR0, "Pop TOS and store to double variable 0.", 1) \
|
||||||
|
+DO(STOREDVAR1, "Pop TOS and store to double variable 1.", 1) \
|
||||||
|
+DO(STOREDVAR2, "Pop TOS and store to double variable 2.", 1) \
|
||||||
|
+DO(STOREDVAR3, "Pop TOS and store to double variable 3.", 1) \
|
||||||
|
+DO(STOREIVAR0, "Pop TOS and store to int variable 0.", 1) \
|
||||||
|
+DO(STOREIVAR1, "Pop TOS and store to int variable 1.", 1) \
|
||||||
|
+DO(STOREIVAR2, "Pop TOS and store to int variable 2.", 1) \
|
||||||
|
+DO(STOREIVAR3, "Pop TOS and store to int variable 3.", 1) \
|
||||||
|
+DO(STORESVAR0, "Pop TOS and store to string variable 0.", 1) \
|
||||||
|
+DO(STORESVAR1, "Pop TOS and store to string variable 1.", 1) \
|
||||||
|
+DO(STORESVAR2, "Pop TOS and store to string variable 2.", 1) \
|
||||||
|
+DO(STORESVAR3, "Pop TOS and store to string variable 3.", 1) \
|
||||||
|
+DO(LOADDVAR, "Load double from variable, whose 2-byte is id inlined to insn stream, push on TOS.", 3) \
|
||||||
|
+DO(LOADIVAR, "Load int from variable, whose 2-byte id is inlined to insn stream, push on TOS.", 3) \
|
||||||
|
+DO(LOADSVAR, "Load string from variable, whose 2-byte id is inlined to insn stream, push on TOS.", 3) \
|
||||||
|
+DO(STOREDVAR, "Pop TOS and store to double variable, whose 2-byte id is inlined to insn stream.", 3) \
|
||||||
|
+DO(STOREIVAR, "Pop TOS and store to int variable, whose 2-byte id is inlined to insn stream.", 3) \
|
||||||
|
+DO(STORESVAR, "Pop TOS and store to string variable, whose 2-byte id is inlined to insn stream.", 3) \
|
||||||
|
+DO(LOADCTXDVAR, "Load double from variable, whose 2-byte context and 2-byte id inlined to insn stream, push on TOS.", 5) \
|
||||||
|
+DO(LOADCTXIVAR, "Load int from variable, whose 2-byte context and 2-byte id is inlined to insn stream, push on TOS.", 5) \
|
||||||
|
+DO(LOADCTXSVAR, "Load string from variable, whose 2-byte context and 2-byte id is inlined to insn stream, push on TOS.", 5) \
|
||||||
|
+DO(STORECTXDVAR, "Pop TOS and store to double variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5) \
|
||||||
|
+DO(STORECTXIVAR, "Pop TOS and store to int variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5) \
|
||||||
|
+DO(STORECTXSVAR, "Pop TOS and store to string variable, whose 2-byte context and 2-byte id is inlined to insn stream.", 5) \
|
||||||
|
?+DO(DCMP, "Compare 2 topmost doubles, pushing libc-style comparator value cmp(upper, lower) as integer.", 1) \
|
||||||
|
?+DO(ICMP, "Compare 2 topmost ints, pushing libc-style comparator value cmp(upper, lower) as integer.", 1) \
|
||||||
|
+DO(JA, "Jump always, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPNE, "Compare two topmost integers and jump if upper != lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPE, "Compare two topmost integers and jump if upper == lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPG, "Compare two topmost integers and jump if upper > lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPGE, "Compare two topmost integers and jump if upper >= lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPL, "Compare two topmost integers and jump if upper < lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(IFICMPLE, "Compare two topmost integers and jump if upper <= lower, next two bytes - signed offset of jump destination.", 3) \
|
||||||
|
+DO(DUMP, "Dump value on TOS, without removing it.", 1) \
|
||||||
|
+DO(STOP, "Stop execution.", 1) \
|
||||||
|
+DO(CALL, "Call function, next two bytes - unsigned function id.", 3) \
|
||||||
|
?DO(CALLNATIVE, "Call native function, next two bytes - id of the native function.", 3) \
|
||||||
|
+DO(RETURN, "Return to call location", 1) \
|
||||||
|
?+DO(BREAK, "Breakpoint for the debugger.", 1)
|
|
@ -1,8 +0,0 @@
|
||||||
## TrustGraph
|
|
||||||
|
|
||||||
An implementation of the basic EigenTrust algorithm (http://nlp.stanford.edu/pubs/eigentrust.pdf).
|
|
||||||
|
|
||||||
The algorithm is meant to find the trustworthiness of peers in a distributed system. A (potentially sparse) matrix is populated with values representing how much peers trust each other. A map is also populated with how much trust is extended by default to a sub-set of peers. From that starting point, the algorithm converges on the global trustworthiness of each peer.
|
|
||||||
|
|
||||||
### To-Do
|
|
||||||
This is a first pass. It does not yet implement distributed EigenTrust. There is also some room to improve error handling.
|
|
142
trustgraph.go
142
trustgraph.go
|
@ -1,142 +0,0 @@
|
||||||
// Package trustGraph is based on EigenTrust
|
|
||||||
// http://nlp.stanford.edu/pubs/eigentrust.pdf
|
|
||||||
package trustGraph
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Group represents a group of peers. Peers need to be given unique, int IDs.
|
|
||||||
// Certainty represents the threshold of RMS change at which the algorithm will
|
|
||||||
// escape. Max is the maximum number of loos the algorithm will perform before
|
|
||||||
// escaping (regardless of certainty). These default to 0.001 and 200
|
|
||||||
// respectivly and generally don't need to be changed.
|
|
||||||
type Group struct {
|
|
||||||
trustGrid map[int]map[int]float32
|
|
||||||
initialTrust map[int]float32
|
|
||||||
Certainty float32
|
|
||||||
Max int
|
|
||||||
Alpha float32
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewGroup is the constructor for Group.
|
|
||||||
func NewGroup() Group {
|
|
||||||
return Group{
|
|
||||||
trustGrid: map[int]map[int]float32{},
|
|
||||||
initialTrust: map[int]float32{},
|
|
||||||
Certainty: 0.001,
|
|
||||||
Max: 200,
|
|
||||||
Alpha: 0.95,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add will add or override a trust relationship. The first arg is the peer who
|
|
||||||
// is extending trust, the second arg is the peer being trusted (by the peer
|
|
||||||
// in the first arg). The 3rd arg is the amount of trust, which must be
|
|
||||||
func (g Group) Add(truster, trusted int, amount float32) (err error) {
|
|
||||||
err = float32InRange(amount)
|
|
||||||
if err == nil {
|
|
||||||
a, ok := g.trustGrid[truster]
|
|
||||||
if !ok {
|
|
||||||
a = map[int]float32{}
|
|
||||||
g.trustGrid[truster] = a
|
|
||||||
}
|
|
||||||
a[trusted] = amount
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitialTrust sets the vaulues used to seed the calculation as well as the
|
|
||||||
// corrective factor used by Alpha.
|
|
||||||
func (g Group) InitialTrust(trusted int, amount float32) (err error) {
|
|
||||||
err = float32InRange(amount)
|
|
||||||
if err == nil {
|
|
||||||
g.initialTrust[trusted] = amount
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// float32InRange is a helper to check that a value is 0.0 <= x <= 1.0
|
|
||||||
func float32InRange(x float32) error {
|
|
||||||
if x < 0 {
|
|
||||||
return errors.New("Trust amount cannot be less than 0")
|
|
||||||
}
|
|
||||||
if x > 1 {
|
|
||||||
return errors.New("Trust amount cannot be greater than 1")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute will approximate the trustworthyness of each peer from the
|
|
||||||
// information known of how much peers trust eachother.
|
|
||||||
// It wil loop, upto g.Max times or until the average difference between
|
|
||||||
// iterations is less than g.Certainty.
|
|
||||||
func (g Group) Compute() map[int]float32 {
|
|
||||||
if len(g.initialTrust) == 0 {
|
|
||||||
return map[int]float32{}
|
|
||||||
}
|
|
||||||
t0 := g.initialTrust //trust map for previous iteration
|
|
||||||
|
|
||||||
for i := 0; i < g.Max; i++ {
|
|
||||||
t1 := *g.computeIteration(&t0) // trust map for current iteration
|
|
||||||
d := avgD(&t0, &t1)
|
|
||||||
t0 = t1
|
|
||||||
if d < g.Certainty {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return t0
|
|
||||||
}
|
|
||||||
|
|
||||||
// computeIteration is broken out of Compute to aid comprehension. It is the
|
|
||||||
// inner loop of Compute. It loops over every value in t (the current trust map)
|
|
||||||
// and looks up how much trust that peer extends to every other peer. The
|
|
||||||
// product of the direct trust and indirect trust
|
|
||||||
func (g Group) computeIteration(t0 *map[int]float32) *map[int]float32 {
|
|
||||||
|
|
||||||
t1 := map[int]float32{}
|
|
||||||
for truster, directTrust := range *t0 {
|
|
||||||
for trusted, indirectTrust := range g.trustGrid[truster] {
|
|
||||||
if trusted != truster {
|
|
||||||
t1[trusted] += directTrust * indirectTrust
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize the trust values
|
|
||||||
// in the EigenTrust paper, this was not done every step, but I prefer to
|
|
||||||
// Not doing it means the diff (d) needs to be normalized in
|
|
||||||
// proportion to the values (because they increase with every iteration)
|
|
||||||
highestTrust := float32(0)
|
|
||||||
for _, v := range t1 {
|
|
||||||
if v > highestTrust {
|
|
||||||
highestTrust = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Todo handle highestTrust == 0
|
|
||||||
for i, v := range t1 {
|
|
||||||
t1[i] = (v/highestTrust)*g.Alpha + (1-g.Alpha)*g.initialTrust[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return &t1
|
|
||||||
}
|
|
||||||
|
|
||||||
// abs is helper to take abs of float32
|
|
||||||
func abs(x float32) float32 {
|
|
||||||
if x < 0 {
|
|
||||||
return -x
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// avgD is helper to compare 2 maps of float32s and return the average
|
|
||||||
// difference between them
|
|
||||||
func avgD(t0, t1 *map[int]float32) float32 {
|
|
||||||
d := float32(0)
|
|
||||||
for i, v := range *t1 {
|
|
||||||
d += abs(v - (*t0)[i])
|
|
||||||
}
|
|
||||||
d = d / float32(len(*t0))
|
|
||||||
return d
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
package trustGraph
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBasic(t *testing.T) {
|
|
||||||
g := NewGroup()
|
|
||||||
g.Add(1, 2, 1)
|
|
||||||
g.Add(1, 3, .5)
|
|
||||||
g.Add(2, 1, 1)
|
|
||||||
g.Add(2, 3, .5)
|
|
||||||
g.Add(3, 1, 1)
|
|
||||||
g.Add(3, 2, 1)
|
|
||||||
|
|
||||||
g.InitialTrust(1, 1)
|
|
||||||
|
|
||||||
out := g.Compute()
|
|
||||||
|
|
||||||
if out[1] < 0.975 {
|
|
||||||
t.Error("Trust in node 1 should be closer to 1.00")
|
|
||||||
}
|
|
||||||
|
|
||||||
if out[2] < 0.93 {
|
|
||||||
t.Error("Trust in node 2 should be closer to 1.00")
|
|
||||||
}
|
|
||||||
if out[3] < 0.4 || out[3] > 0.6 {
|
|
||||||
t.Error("Trust in node 3 should be closer to 0.50")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRand(t *testing.T) {
|
|
||||||
peers := 200
|
|
||||||
rand.Seed(time.Now().UTC().UnixNano())
|
|
||||||
g := NewGroup()
|
|
||||||
|
|
||||||
//randomly set actual trust values for peers
|
|
||||||
actualTrust := make([]float32, peers)
|
|
||||||
for i := 0; i < peers; i++ {
|
|
||||||
actualTrust[i] = rand.Float32()
|
|
||||||
}
|
|
||||||
|
|
||||||
// peer0 is set to and granted 100% trust
|
|
||||||
actualTrust[0] = 1
|
|
||||||
g.InitialTrust(0, 1)
|
|
||||||
|
|
||||||
// set 30% of trust values to +/- 10% of actual trust
|
|
||||||
for i := 0; i < peers; i++ {
|
|
||||||
for j := 0; j < peers; j++ {
|
|
||||||
if rand.Float32() > .7 {
|
|
||||||
g.Add(i, j, randNorm(actualTrust[j]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// compute trust
|
|
||||||
out := g.Compute()
|
|
||||||
|
|
||||||
// find RMS error
|
|
||||||
e := float32(0)
|
|
||||||
for i := 0; i < peers; i++ {
|
|
||||||
x := actualTrust[i] - out[i]
|
|
||||||
e += x * x
|
|
||||||
}
|
|
||||||
e = float32(math.Sqrt(float64(e / float32(peers))))
|
|
||||||
|
|
||||||
if e > .2 {
|
|
||||||
t.Error("RMS Error should be less than 20% for a 30% full trust grid of 200 nodes")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// randNorm takes a float and returns a value within +/- 10%,
|
|
||||||
// without going over 1
|
|
||||||
func randNorm(x float32) float32 {
|
|
||||||
r := rand.Float32()*.2 + .9
|
|
||||||
x *= r
|
|
||||||
if x > 1 {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRangeError(t *testing.T) {
|
|
||||||
g := NewGroup()
|
|
||||||
|
|
||||||
err := g.Add(1, 2, 1.1)
|
|
||||||
if err.Error() != "Trust amount cannot be greater than 1" {
|
|
||||||
t.Error("Expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = g.Add(1, 2, -1)
|
|
||||||
if err.Error() != "Trust amount cannot be less than 0" {
|
|
||||||
t.Error("Expected error less than 0 error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = g.Add(1, 2, 1)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("Did not expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = g.Add(1, 2, 0)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("Did not expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = g.Add(1, 2, 0.5)
|
|
||||||
if err != nil {
|
|
||||||
t.Error("Did not expected error")
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue