Compare commits

...

No commits in common. "b1eceec1d242d76154cf3275a2295dae55bafbb3" and "c309d6158a2135d2f5378db203a3f76e14301586" have entirely different histories.

27 changed files with 263 additions and 2832 deletions

View file

@ -1,504 +0,0 @@
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
};
}

View file

@ -1,61 +0,0 @@
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

View file

@ -1,68 +0,0 @@
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(" ", "");
}
}
}
}
}

View file

@ -1,35 +0,0 @@
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)
{
;
}
}
}

View file

@ -1,558 +0,0 @@
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" };
}
}

View file

@ -1,53 +0,0 @@
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);
}
}
}
}
}

View file

@ -1,53 +0,0 @@
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);
}
}
}
}

View file

@ -1,293 +0,0 @@
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
}
}

View file

@ -1,43 +0,0 @@
<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> := + | - | * | /

View file

View file

@ -1,39 +0,0 @@
#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

View file

@ -1,54 +0,0 @@
#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;
}

View file

@ -1,34 +0,0 @@
#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

View file

@ -1,77 +0,0 @@
#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;
}

View file

@ -1,10 +0,0 @@
#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

View file

@ -1,50 +0,0 @@
#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));
}
}

View file

@ -1,14 +0,0 @@
#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

View file

@ -1,623 +0,0 @@
#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;
}

View file

@ -1,89 +0,0 @@
#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

View file

@ -1,20 +0,0 @@
#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;
}

View file

@ -1,9 +0,0 @@
#ifndef RETURNSTACK_H
#define RETURNSTACK_H
int* RStack;
int rp;
int initRStack(int);
void push_ret(int);
int pop_ret();
#endif

View file

@ -1,41 +0,0 @@
#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;
}

View file

@ -1,19 +0,0 @@
#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

View file

@ -1,85 +0,0 @@
#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)

8
readme.md Normal file
View file

@ -0,0 +1,8 @@
## 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 Normal file
View file

@ -0,0 +1,142 @@
// 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
}

113
trustgraph_test.go Normal file
View file

@ -0,0 +1,113 @@
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")
}
}