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