diff --git a/AssemblerVVM/Program.cs b/AssemblerVVM/Program.cs new file mode 100644 index 0000000..7e55685 --- /dev/null +++ b/AssemblerVVM/Program.cs @@ -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 ConstID_VALUE = new Dictionary(); + Dictionary ConstKEY_ID = new Dictionary(); + Dictionary CodeMARK_POS = new Dictionary(); + + List FuncPos = new List(); //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 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 PositionAnalyse(string[] input, ref int[] posD, ref List posC, ref Dictionary 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 src = new List(); + 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 src, int[] pos, BinaryWriter bw, ref Dictionary id_v, ref Dictionary 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 code, pair pos, BinaryWriter bw, Dictionary dictStr, Dictionary 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 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 onebyte = new List{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 ninebytes = new List { opcode.DLOAD, opcode.ILOAD }; + + static List threebytes = new List { opcode.LOADDVAR, opcode.LOADIVAR, opcode.LOADSVAR, opcode.STOREDVAR, + opcode.STOREIVAR, opcode.STORESVAR, opcode.SLOAD, opcode.CALL}; + + static List fivebytes = new List {opcode.LOADCTXDVAR, opcode.LOADCTXIVAR, opcode.LOADCTXSVAR, opcode.STORECTXDVAR, + opcode.STORECTXIVAR, opcode.STORECTXSVAR}; + + static List jumps = new List {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 + }; +} diff --git a/AssemblerVVM/readme.txt b/AssemblerVVM/readme.txt new file mode 100644 index 0000000..5415a0e --- /dev/null +++ b/AssemblerVVM/readme.txt @@ -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 \ No newline at end of file diff --git a/CompilerVVM/CodeGen.cs b/CompilerVVM/CodeGen.cs new file mode 100644 index 0000000..dea005a --- /dev/null +++ b/CompilerVVM/CodeGen.cs @@ -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 ConstData; + Dictionary Methods; + public string[] asm { get; set; } + + + public CodeGen(Dictionary ConstData, Dictionary Methods) + { + this.ConstData = ConstData; + this.Methods = Methods; + asm = null; + + foreach (KeyValuePair p in Methods) + { + Optimization(p.Value); + } + + + FillAsmCode(); + } + + private void FillAsmCode() + { + List Code = new List(); + if (ConstData.Count > 0) + { + Code.Add(".data"); + foreach (KeyValuePair pair in ConstData) + { + Code.Add(string.Format("{0} \"{1}\"", pair.Value, pair.Key)); + } + Code.Add(".endd"); + } + + foreach (KeyValuePair 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 OptimizationSub = new List(){"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(" ", ""); + } + } + } + } +} diff --git a/CompilerVVM/CustomExceptions.cs b/CompilerVVM/CustomExceptions.cs new file mode 100644 index 0000000..9a4fff8 --- /dev/null +++ b/CompilerVVM/CustomExceptions.cs @@ -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) + { + ; + } + } +} diff --git a/CompilerVVM/Parser.cs b/CompilerVVM/Parser.cs new file mode 100644 index 0000000..614d62d --- /dev/null +++ b/CompilerVVM/Parser.cs @@ -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 Tokens; + int idvar; + Dictionary Constants; + Dictionary Methods; + int jumpid; + + public Parser(IList tokens, Dictionary TextConst, Dictionary Methods) + { + Tokens = tokens; + pointer = 0; + jumpid = 0; + Constants = TextConst; + this.Methods = Methods; + ParseMethod(); + } + + private void ParseMethod() + { + // := + + while (pointer != Tokens.Count) + { + // := method (*) + // := int | double | string + // := | 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 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 param = new List(); + 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 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 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 code_types = new List() { "int", "double", "string" }; + } +} diff --git a/CompilerVVM/ProcedureScanner.cs b/CompilerVVM/ProcedureScanner.cs new file mode 100644 index 0000000..bd16810 --- /dev/null +++ b/CompilerVVM/ProcedureScanner.cs @@ -0,0 +1,53 @@ +п»їusing System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace CompilerVVM +{ + class ProcedureScanner + { + public Dictionary Methods { get; set; } + int pointer; + + public ProcedureScanner(IList Tokens) + { + // TODO: Complete member initialization + pointer = 0; + ScanForMethods(Tokens); + } + + private void ScanForMethods(IList Tokens) + { + Methods = new Dictionary(); + 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); + } + } + } + } +} diff --git a/CompilerVVM/Program.cs b/CompilerVVM/Program.cs new file mode 100644 index 0000000..b955f6f --- /dev/null +++ b/CompilerVVM/Program.cs @@ -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 TextConst = new Dictionary(); + 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); + } + + } + } +} diff --git a/CompilerVVM/TokenScanner.cs b/CompilerVVM/TokenScanner.cs new file mode 100644 index 0000000..841e3f1 --- /dev/null +++ b/CompilerVVM/TokenScanner.cs @@ -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 tokens; + private IList list; + public IList Tokens { get { return tokens; } } + private void Scan(System.IO.TextReader input, Dictionary 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 TextConstant) + { + tokens = new List(); + this.Scan(input, TextConstant); + } + } + + enum OP + { + Semicolon, + OpenBlock, + CloseBlock, + OpenParam, + CloseParam, + Assigment, + Equal, + NotEqual, + Less, + Greater, + LessEqual, + GreaterEqual, + Add, + Sub, + Mul, + Div, + Mod + } +} diff --git a/CompilerVVM/grammar.txt b/CompilerVVM/grammar.txt new file mode 100644 index 0000000..ca91cc7 --- /dev/null +++ b/CompilerVVM/grammar.txt @@ -0,0 +1,43 @@ +п»ї := + + + := method (* ) + + := int | double | string + := | void + + := * + := | + := 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 + + := + + := . + := " ? " + + := + + := ; + | = ; + | ; + | = ; + | if + | if else + | while + | do until ; + + := + := < | > | == | <= | >= + + := + | + | + | + | + | + | + + := + | - | * | / + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/VaninVM/CodeHeader.h b/VaninVM/CodeHeader.h new file mode 100644 index 0000000..9ee8f89 --- /dev/null +++ b/VaninVM/CodeHeader.h @@ -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 \ No newline at end of file diff --git a/VaninVM/Context.c b/VaninVM/Context.c new file mode 100644 index 0000000..64e5644 --- /dev/null +++ b/VaninVM/Context.c @@ -0,0 +1,54 @@ +#include "Context.h" +#include +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; +} \ No newline at end of file diff --git a/VaninVM/Context.h b/VaninVM/Context.h new file mode 100644 index 0000000..936ca24 --- /dev/null +++ b/VaninVM/Context.h @@ -0,0 +1,34 @@ +#ifndef CONTEXT_H +#define CONTEXT_H + +#include "CodeHeader.h" +#include + +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 \ No newline at end of file diff --git a/VaninVM/IOcode.c b/VaninVM/IOcode.c new file mode 100644 index 0000000..f1f00c4 --- /dev/null +++ b/VaninVM/IOcode.c @@ -0,0 +1,77 @@ +#include "IOcode.h" +#include "CodeHeader.h" +#include + +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; icode=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; iConst_Header.count_const) + break; + + if (buffer[i] == 0) + (*(index))[j++]=&buffer[i+1]; + } + return 0; +} diff --git a/VaninVM/IOcode.h b/VaninVM/IOcode.h new file mode 100644 index 0000000..c7beee9 --- /dev/null +++ b/VaninVM/IOcode.h @@ -0,0 +1,10 @@ +#ifndef IOCODE_H +#define IOCODE_H + +#include "CodeHeader.h" +#include +#include + +int read_bytecode(FILE*, func***); +int read_constant(FILE*, int*, char***); +#endif \ No newline at end of file diff --git a/VaninVM/LocalVars.c b/VaninVM/LocalVars.c new file mode 100644 index 0000000..7023762 --- /dev/null +++ b/VaninVM/LocalVars.c @@ -0,0 +1,50 @@ +#include +#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)); + } +} diff --git a/VaninVM/LocalVars.h b/VaninVM/LocalVars.h new file mode 100644 index 0000000..7853c3c --- /dev/null +++ b/VaninVM/LocalVars.h @@ -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 \ No newline at end of file diff --git a/VaninVM/Main.c b/VaninVM/Main.c new file mode 100644 index 0000000..502679e --- /dev/null +++ b/VaninVM/Main.c @@ -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; +} \ No newline at end of file diff --git a/VaninVM/OpCode.h b/VaninVM/OpCode.h new file mode 100644 index 0000000..c13bbc4 --- /dev/null +++ b/VaninVM/OpCode.h @@ -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 \ No newline at end of file diff --git a/VaninVM/ReturnStack.c b/VaninVM/ReturnStack.c new file mode 100644 index 0000000..85b9522 --- /dev/null +++ b/VaninVM/ReturnStack.c @@ -0,0 +1,20 @@ +#include +#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; +} \ No newline at end of file diff --git a/VaninVM/ReturnStack.h b/VaninVM/ReturnStack.h new file mode 100644 index 0000000..74fdab7 --- /dev/null +++ b/VaninVM/ReturnStack.h @@ -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 \ No newline at end of file diff --git a/VaninVM/TOS.c b/VaninVM/TOS.c new file mode 100644 index 0000000..af5da47 --- /dev/null +++ b/VaninVM/TOS.c @@ -0,0 +1,41 @@ +#include +#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; +} \ No newline at end of file diff --git a/VaninVM/TOS.h b/VaninVM/TOS.h new file mode 100644 index 0000000..072a0ba --- /dev/null +++ b/VaninVM/TOS.h @@ -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 \ No newline at end of file diff --git a/VaninVM/opc.txt b/VaninVM/opc.txt new file mode 100644 index 0000000..2bc9db8 --- /dev/null +++ b/VaninVM/opc.txt @@ -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) \ No newline at end of file diff --git a/readme.md b/readme.md deleted file mode 100644 index dc4f8be..0000000 --- a/readme.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/trustgraph.go b/trustgraph.go deleted file mode 100644 index 079d842..0000000 --- a/trustgraph.go +++ /dev/null @@ -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 -} diff --git a/trustgraph_test.go b/trustgraph_test.go deleted file mode 100644 index 0f66b06..0000000 --- a/trustgraph_test.go +++ /dev/null @@ -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") - } -}