diff --git a/ByteCodeTest/.vscode/launch.json b/ByteCodeTest/.vscode/launch.json new file mode 100644 index 0000000..3f4f16f --- /dev/null +++ b/ByteCodeTest/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net7.0/ByteCodeTest.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/ByteCodeTest/.vscode/tasks.json b/ByteCodeTest/.vscode/tasks.json new file mode 100644 index 0000000..b58c8df --- /dev/null +++ b/ByteCodeTest/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/ByteCodeTest.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/ByteCodeTest.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/ByteCodeTest.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/ByteCodeTest/ByteCodeTest.csproj b/ByteCodeTest/ByteCodeTest.csproj new file mode 100644 index 0000000..78c6818 --- /dev/null +++ b/ByteCodeTest/ByteCodeTest.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net7.0 + enable + enable + + + diff --git a/ByteCodeTest/Program.cs b/ByteCodeTest/Program.cs new file mode 100644 index 0000000..70f4443 --- /dev/null +++ b/ByteCodeTest/Program.cs @@ -0,0 +1,10 @@ +using TLang.BytecodeCompiler; +using TLang.Common; +using TLang.Parser; + + +var res=Parse.ParseFromFiles("app.tlang"); +using(var f = File.Create("app.tvm")) +{ + ByteCodeGenerator.GenerateToStream(res,f,new TLangVersion(1,0,0,0),new TLangVersion(1,0,0,0)); +} \ No newline at end of file diff --git a/ByteCodeTest/app.tlang b/ByteCodeTest/app.tlang new file mode 100644 index 0000000..e73eeae --- /dev/null +++ b/ByteCodeTest/app.tlang @@ -0,0 +1 @@ +37+5; \ No newline at end of file diff --git a/ByteCodeTest/app.tvm b/ByteCodeTest/app.tvm new file mode 100644 index 0000000..3817887 Binary files /dev/null and b/ByteCodeTest/app.tvm differ diff --git a/TLang.BytecodeCompiler/Class1.cs b/TLang.BytecodeCompiler/Class1.cs new file mode 100644 index 0000000..745d2e3 --- /dev/null +++ b/TLang.BytecodeCompiler/Class1.cs @@ -0,0 +1,648 @@ +using System; +using System.Collections.Generic; +using System.IO; +using TLang.Parser; +using TLang.Common; + +namespace TLang.BytecodeCompiler +{ + + public class ByteCodeGenerator + { + private int I = 0; + public int NextNumber() + { + return I++; + } + public ByteCodeGenerator() + { + Chunks.Add(RootChunk); + } + + public List Classes {get;set;}=new List(); + public List Functions {get;set;}=new List(); + public BytecodeChunk RootChunk = new BytecodeChunk(); + public List Chunks {get;set;}=new List(); + + public static void GenerateToStream(Node node,Stream strm,TLangVersion vmVersion,TLangVersion version,params TLangDependency[] dependencies) + { + ByteCodeGenerator gen=new ByteCodeGenerator(); + gen._generateFile(node,strm,vmVersion,version,dependencies); + } + + private void _generateFile(Node node,Stream strm,TLangVersion vmVersion,TLangVersion version,TLangDependency[] dependencies) + { + byte[] data = new byte[]{(byte)'T',(byte)'V',(byte)'M',vmVersion.Major,vmVersion.Minor,vmVersion.Patch,vmVersion.Build,version.Major,version.Minor,version.Patch,version.Build}; + strm.Write(data,0,data.Length); + WriteBigEndianInt(strm,dependencies.Length); + foreach(var item in dependencies) + { + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(item.Name); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + byte[] dependV = new byte[]{item.Version.Major,item.Version.Minor,item.Version.Patch,item.Version.Build}; + strm.Write(dependV,0,dependV.Length); + } + + GenerateCode(node,RootChunk,0,0,-1,-1,true); + WriteBigEndianInt(strm,Functions.Count); + foreach(var item in Functions) + { + int closure = Chunks.Count; + BytecodeChunk _chunk = new BytecodeChunk(); + _chunk.Arguments = item.ClosureNode.Arguments; + Chunks.Add(_chunk); + GenerateCode(item.ClosureNode.Node,_chunk,0,0,-1,-1,false); + + + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(item.FunctionName); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + WriteBigEndianInt(strm,closure); + } + + WriteBigEndianInt(strm,Classes.Count); + foreach(var item in Classes) + { + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(item.Name); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + + bytes = System.Text.Encoding.UTF8.GetBytes(item.InheritsFrom); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + WriteBigEndianInt(strm,item.Entries.Count); + + foreach(var classEntry in item.Entries) + { + var cValue = classEntry.InitialValue as ClosureNode; + if(cValue != null) + { + byte c = 0b00000100; + if(classEntry.Abstract) { c |= 0b00001000;} + if(classEntry.Modifier == "prot") {c |= 0b00000001;} + if(classEntry.Modifier == "pub") {c |= 0b00000010;} + strm.WriteByte(c); + bytes = System.Text.Encoding.UTF8.GetBytes(classEntry.Name); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + int closure = Chunks.Count; + BytecodeChunk _chunk = new BytecodeChunk(); + _chunk.Arguments = cValue.Arguments; + GenerateCode(cValue.Node,_chunk,0,0,-1,-1,false); + Chunks.Add(_chunk); + WriteBigEndianInt(strm,closure); + } + else + { + byte c = 0b00000100; + + if(classEntry.Modifier == "prot") {c |= 0b00000001;} + if(classEntry.Modifier == "pub") {c |= 0b00000010;} + strm.WriteByte(c); + bytes = System.Text.Encoding.UTF8.GetBytes(classEntry.Name); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + int closure = Chunks.Count; + BytecodeChunk _chunk = new BytecodeChunk(); + + GenerateCode(new ReturnNode(classEntry.InitialValue),_chunk,0,0,-1,-1,false); + Chunks.Add(_chunk); + WriteBigEndianInt(strm,closure); + } + } + } + + WriteBigEndianInt(strm,Chunks.Count); + foreach(var item in Chunks) + { + item.SetLabelable(); + WriteBigEndianInt(strm,item.Arguments.Count); + foreach(var arg in item.Arguments) + { + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(arg); + WriteBigEndianInt(strm,bytes.Length); + strm.Write(bytes,0,bytes.Length); + } + WriteBigEndianInt(strm,item.CurrentByteLength); + foreach(var instruction in item.Instructions) + { + instruction.WriteData(strm); + } + } + } + + private void WriteBigEndianInt(Stream strm, int count) + { + byte[] data = BitConverter.GetBytes(count); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + } + + public void GenerateCode(Node node,BytecodeChunk chunk,int scopeIndex,int scopeBreakIndex,int _labelBeginningLoop,int _labelEndLoop,bool isRoot=false) + { + var funcDec = node as FunctionDeclaritionNode; + var chrNode = node as CharNode; + var strNode = node as StringNode; + var dblNode = node as ConstNumberNode; + var clsDec = node as ClassNode; + var addNode = node as AddNode; + var subNode = node as SubtractNode; + var timesNode = node as MultiplyNode; + var divNode = node as DivideNode; + var modNode = node as ModuloNode; + var powNode = node as PowNode; + var lshiftNode = node as LeftShiftNode; + var rshiftNode = node as RightShiftNode; + var bOrNode = node as BOrNode; + var bAndNode = node as BAndNode; + var xOrNode = node as XOrNode; + var negNode = node as NegativeNode; + var bNotNode = node as BitwiseNotNode; + + var nEqNode = node as NotEqualsNode; + var eqNode = node as EqualsNode; + var lOrNode = node as LOrNode; + var lAndNode = node as LAndNode; + var ltNode = node as LessThanNode; + var lteNode = node as LessThanEqualsNode; + var gtNode = node as GreaterThanNode; + var gteNode = node as GreaterThanEqualsNode; + var notNode = node as NotNode; + + var scNode = node as ScopeNode; + var retNode = node as ReturnNode; + var closureNode = node as ClosureNode; + var whileNode = node as WhileLoop; + var eachNode = node as EachLoopNode; + var forNode = node as ForLoopNode; + var ifNode = node as IfNode; + var functionCallNode = node as FunctionCallNode; + var methodCallNode = node as MethodCallNode; + var getVariableNode = node as GetVariableNode; + var getMemberNode = node as GetMemberNode; + var getArrayValue = node as GetArrayNode; + var setVariableNode = node as SetVariableNode; + + + if(scNode != null) + { + if(scNode.IsSwitchScope) + { + int caseNumber = NextNumber(); + int labelNumEnd = NextNumber(); + int caseNo2=0; + + chunk.Add(new SimpleInstruction(Instruction.SCOPE_BEGIN)); + bool hasDefault =false; + bool beforeCase=true; + foreach(var item in scNode.Nodes) + { + var c = item as CaseNode; + var d = item as DefaultNode; + if(c != null) + { + beforeCase = false; + EqualsNode equals=new EqualsNode(scNode.SwitchCondition,c.Variable); + GenerateCode(equals,chunk,scopeIndex+1,0,_labelBeginningLoop,labelNumEnd,false); + JumpConditional unconditional = new JumpConditional($"_case{caseNumber}_{caseNo2}"); + chunk.Add(unconditional); + + caseNo2++; + } + if(d != null) + { + hasDefault =false; + beforeCase = false; + JumpUnconditional unconditional = new JumpUnconditional($"_default{caseNumber}"); + chunk.Add(unconditional); + } + if(beforeCase) + { + GenerateCode(item,chunk,scopeIndex+1,0,_labelBeginningLoop,_labelEndLoop,isRoot); + } + } + if(!hasDefault) + { + JumpUnconditional unconditional = new JumpUnconditional($"_label{labelNumEnd}"); + chunk.Add(unconditional); + } + + beforeCase=true; + caseNo2=0; + foreach(var item in scNode.Nodes) + { + var c = item as CaseNode; + var d = item as DefaultNode; + if(c != null) + { + beforeCase = false; + LabelInstruction instruction = new LabelInstruction($"_case{caseNumber}_{caseNo2}"); + chunk.Add(instruction); + caseNo2++; + } + + if(d != null) + { + beforeCase = false; + LabelInstruction instruction = new LabelInstruction($"_default{caseNumber}"); + chunk.Add(instruction); + } + + if(!beforeCase && c == null && d == null) + { + GenerateCode(item,chunk,scopeIndex+1,scopeBreakIndex,_labelBeginningLoop,labelNumEnd,scNode.RootScope); + } + } + LabelInstruction endLabel = new LabelInstruction($"_label{labelNumEnd}"); + chunk.Add(endLabel); + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + else + { + if(!scNode.RootScope) + chunk.Add(new SimpleInstruction(Instruction.SCOPE_BEGIN)); + foreach(var item in scNode.Nodes) + { + GenerateCode(item,chunk,scopeIndex+1,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,scNode.RootScope); + } + if(!scNode.RootScope) + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + + } + if(ifNode != null) + { + int labelNumNo = NextNumber(); + int labelNumEnd = NextNumber(); + GenerateCode(new NotNode(ifNode.Condition),chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + JumpConditional jc=new JumpConditional($"_label{labelNumNo}"); + chunk.Add(jc); + GenerateCode(ifNode.Yes,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + JumpUnconditional ju = new JumpUnconditional($"_label{labelNumEnd}"); + chunk.Add(ju); + LabelInstruction labelNo = new LabelInstruction($"_label{labelNumNo}"); + chunk.Add(labelNo); + GenerateCode(ifNode.No,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + LabelInstruction labelEnd = new LabelInstruction($"_label{labelNumEnd}"); + chunk.Add(labelEnd); + } + if(bNotNode != null) + { + GenerateCode(bNotNode.Node,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.BNOT)); + } + if(negNode != null) + { + GenerateCode(negNode.Node,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.NEG)); + } + if(notNode != null) + { + GenerateCode(notNode.Node,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LNOT)); + } + if(eachNode != null) + { + int ittrVarNum = NextNumber(); + int labelNum = NextNumber(); + int labelNumEnd = NextNumber(); + chunk.Add(new SimpleInstruction(Instruction.SCOPE_BEGIN)); + + GetVariableNode variableNode=new GetVariableNode($"__compGen{ittrVarNum}"); + var member= new GetMemberNode(eachNode.Itterator,"ittr"); + SetVariableNode variableNode1=new SetVariableNode(variableNode,member); + GenerateCode(variableNode1,chunk,scopeIndex+1,0,_labelBeginningLoop,_labelEndLoop,false); + LabelInstruction labelNode=new LabelInstruction($"_label{labelNum}"); + chunk.Add(labelNode); + + JumpConditional conditional = new JumpConditional($"_label{labelNumEnd}"); + chunk.Add(conditional); + + SetVariableNode variableNode2=new SetVariableNode(eachNode.Name,new NotNode(new MethodCallNode(variableNode,"movenext"))); + GenerateCode(variableNode2,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + GenerateCode(eachNode.Body,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + + JumpUnconditional juC=new JumpUnconditional($"_label{labelNum}"); + chunk.Add(juC); + LabelInstruction endLabel = new LabelInstruction($"_label{labelNumEnd}"); + chunk.Add(endLabel); + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + if(forNode != null) + { + int labelNum = NextNumber(); + int labelNumEnd = NextNumber(); + + chunk.Add(new SimpleInstruction(Instruction.SCOPE_BEGIN)); + GenerateCode(forNode.Init,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + LabelInstruction labelNode=new LabelInstruction($"_label{labelNum}"); + chunk.Add(labelNode); + + GenerateCode(new NotNode(forNode.Condition),chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + JumpConditional conditional = new JumpConditional($"_label{labelNumEnd}"); + chunk.Add(conditional); + + GenerateCode(forNode.Body,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + GenerateCode(forNode.Increment,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + JumpUnconditional juC=new JumpUnconditional($"_label{labelNum}"); + chunk.Add(juC); + LabelInstruction endLabel = new LabelInstruction($"_label{labelNumEnd}"); + chunk.Add(endLabel); + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + if(whileNode != null) + { + int labelNum = NextNumber(); + int labelNumEnd = NextNumber(); + + chunk.Add(new SimpleInstruction(Instruction.SCOPE_BEGIN)); + LabelInstruction labelNode=new LabelInstruction($"_label{labelNum}"); + chunk.Add(labelNode); + if(whileNode.IsDo) + { + GenerateCode(whileNode.Body,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + GenerateCode(new NotNode(whileNode.Condition),chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + JumpConditional conditional = new JumpConditional($"_label{labelNum}"); + chunk.Add(conditional); + } + else + { + GenerateCode(new NotNode(whileNode.Condition),chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + JumpConditional conditional = new JumpConditional($"_label{labelNumEnd}"); + chunk.Add(conditional); + + GenerateCode(whileNode.Body,chunk,scopeIndex+1,0,labelNum,labelNumEnd,false); + JumpUnconditional juC=new JumpUnconditional($"_label{labelNum}"); + chunk.Add(juC); + } + + LabelInstruction endLabel = new LabelInstruction($"_label{labelNumEnd}"); + chunk.Add(endLabel); + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + if(node is ContinueNode && _labelBeginningLoop >= 0) + { + for(int i = scopeBreakIndex;i>0;i--) + { + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + JumpUnconditional conditional=new JumpUnconditional($"_label{_labelBeginningLoop}"); + chunk.Add(conditional); + } + if(node is BreakNode && _labelEndLoop >= 0) + { + for(int i = scopeBreakIndex;i>0;i--) + { + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + JumpUnconditional conditional=new JumpUnconditional($"_label{_labelEndLoop}"); + chunk.Add(conditional); + } + if(closureNode != null) + { + BytecodeChunk _chunk = new BytecodeChunk(); + _chunk.Arguments = closureNode.Arguments; + GenerateCode(closureNode.Node,_chunk,0,0,-1,-1,false); + int i = Chunks.Count; + Chunks.Add(_chunk); + chunk.Add(new PushClosureNode(i)); + } + if(node is NullNode) + { + chunk.Add(new SimpleInstruction(Instruction.PUSH_NULL)); + } + if(node is UndefinedNode) + { + chunk.Add(new SimpleInstruction(Instruction.PUSH_UNDEFINED)); + } + if(node is TrueNode) + { + chunk.Add(new SimpleInstruction(Instruction.PUSH_TRUE)); + } + if(node is FalseNode) + { + chunk.Add(new SimpleInstruction(Instruction.PUSH_FALSE)); + } + if(chrNode != null) + { + chunk.Add(new PushCharNode(chrNode.Text)); + } + if(strNode != null) + { + chunk.Add(new PushStringInstruction(strNode.Text)); + } + if(dblNode != null) + { + chunk.Add(new PushDoubleInstruction(dblNode.Value)); + } + + if(funcDec != null) + { + if(isRoot) + { + Functions.Add(funcDec); + } + else + { + SetVariableNode variableNode=new SetVariableNode(new GetVariableNode(funcDec.FunctionName),funcDec.ClosureNode) {LineInfo = funcDec.LineInfo}; + GenerateCode(variableNode,chunk,scopeIndex,scopeBreakIndex,-1,-1,false); + } + } + if(clsDec != null) + { + Classes.Add(clsDec); + } + if(addNode != null) + { + GenerateCode(addNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(addNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.ADD)); + } + if(subNode != null) + { + GenerateCode(subNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(subNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.SUB)); + } + if(timesNode != null) + { + GenerateCode(timesNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(timesNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.TIMES)); + } + if(divNode != null) + { + GenerateCode(divNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(divNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.DIVIDE)); + } + if(modNode != null) + { + GenerateCode(modNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(modNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.MOD)); + } + if(powNode != null) + { + GenerateCode(powNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(powNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.POW)); + } + if(lshiftNode != null) + { + GenerateCode(lshiftNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(lshiftNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LSHIFT)); + } + if(rshiftNode != null) + { + GenerateCode(rshiftNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(rshiftNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.RSHIFT)); + } + if(bOrNode != null) + { + GenerateCode(bOrNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(bOrNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.BOR)); + } + if(bAndNode != null) + { + GenerateCode(bAndNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(bAndNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.BAND)); + } + if(xOrNode != null) + { + GenerateCode(xOrNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(xOrNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.XOR)); + } + if(nEqNode != null) + { + GenerateCode(nEqNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(nEqNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.NEQ)); + } + if(eqNode != null) + { + GenerateCode(eqNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(eqNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.EQ)); + } + if(lOrNode != null) + { + GenerateCode(lOrNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(lOrNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LOR)); + } + if(lAndNode != null) + { + GenerateCode(lAndNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(lAndNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LAND)); + } + if(ltNode != null) + { + GenerateCode(ltNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(ltNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LT)); + } + if(lteNode != null) + { + GenerateCode(lteNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(lteNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.LTE)); + } + if(gtNode != null) + { + GenerateCode(gtNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(gtNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.GT)); + } + if(gteNode != null) + { + GenerateCode(gteNode.Left,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(gteNode.Right,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.GTE)); + } + if(retNode != null) + { + GenerateCode(retNode.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + for(int i = scopeIndex;i>0;i--) + { + chunk.Add(new SimpleInstruction(Instruction.SCOPE_END)); + } + chunk.Add(new SimpleInstruction(Instruction.RET)); + } + if(methodCallNode != null) + { + + GenerateCode(methodCallNode.Symbol,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new PushStringInstruction(methodCallNode.Name)); + foreach(var arg in methodCallNode.Arguments) + { + GenerateCode(arg,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + } + chunk.Add(new PushDoubleInstruction(methodCallNode.Arguments.Count)); + chunk.Add(new SimpleInstruction(Instruction.CALL_METHOD)); + } + if(functionCallNode != null) + { + chunk.Add(new PushStringInstruction(functionCallNode.Name)); + foreach(var arg in functionCallNode.Arguments) + { + GenerateCode(arg,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + } + chunk.Add(new PushDoubleInstruction(functionCallNode.Arguments.Count)); + chunk.Add(new SimpleInstruction(Instruction.CALL_FUNC)); + } + if(getVariableNode != null) + { + chunk.Add(new PushStringInstruction(getVariableNode.Name)); + chunk.Add(new SimpleInstruction(Instruction.PUSH_VARIABLE_VALUE)); + } + if(getMemberNode != null) + { + GenerateCode(getMemberNode.Symbol,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new PushStringInstruction(getMemberNode.Name)); + chunk.Add(new SimpleInstruction(Instruction.PUSH_FIELD_VALUE)); + } + if(getArrayValue != null) + { + GenerateCode(getArrayValue.Symbol,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(getArrayValue.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.PUSH_ARRAY_VALUE)); + } + + if(setVariableNode != null) + { + var gVN = setVariableNode.SetTo as GetVariableNode; + var gMN = setVariableNode.SetTo as GetMemberNode; + var gAN = setVariableNode.SetTo as GetArrayNode; + if(gVN != null) + { + chunk.Add(new PushStringInstruction(gVN.Name)); + GenerateCode(setVariableNode.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.POP_VARIABLE_VALUE)); + } + if(gMN != null) + { + GenerateCode(gMN.Symbol,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new PushStringInstruction(gMN.Name)); + GenerateCode(setVariableNode.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.POP_FIELD_VALUE)); + } + if(gAN != null) + { + GenerateCode(gAN.Symbol,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(gAN.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + GenerateCode(setVariableNode.Expression,chunk,scopeIndex,scopeBreakIndex,_labelBeginningLoop,_labelEndLoop,isRoot); + chunk.Add(new SimpleInstruction(Instruction.POP_ARRAY_VALUE)); + } + } + } + + } +} diff --git a/TLang.BytecodeCompiler/TLang.BytecodeCompiler.csproj b/TLang.BytecodeCompiler/TLang.BytecodeCompiler.csproj new file mode 100644 index 0000000..3f1cc94 --- /dev/null +++ b/TLang.BytecodeCompiler/TLang.BytecodeCompiler.csproj @@ -0,0 +1,12 @@ + + + + + + + + + netstandard2.0 + + + diff --git a/TLang.Common/BytecodeChunk.cs b/TLang.Common/BytecodeChunk.cs new file mode 100644 index 0000000..5d0285c --- /dev/null +++ b/TLang.Common/BytecodeChunk.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using TLang.Common; + +namespace TLang.Common +{ + public class BytecodeChunk + { + public int CurrentByteLength {get;set;}=0; + public void Add(Instruction instruction) + { + var i = instruction as LabelInstruction; + if(i != null) Offsets.Add(i.Key,CurrentByteLength); + else { + CurrentByteLength += instruction.CalculateLength(); + Instructions.Add(instruction); + } + } + public void SetLabelable() + { + foreach(var item in Instructions) + { + var item2 = item as ILabelable; + if(item2 != null) + { + if(Offsets.ContainsKey(item2.Key)) item2.Value = Offsets[item2.Key]; + } + } + } + public Dictionary Offsets {get;set;}=new Dictionary(); + public List Arguments {get;set;}=new List(); + public List Instructions {get;set;}=new List(); + } +} diff --git a/TLang.Common/ILabelable.cs b/TLang.Common/ILabelable.cs new file mode 100644 index 0000000..e36496e --- /dev/null +++ b/TLang.Common/ILabelable.cs @@ -0,0 +1,8 @@ +namespace TLang.Common +{ + public interface ILabelable + { + string Key {get;set;} + int Value {get;set;} + } +} diff --git a/TLang.Common/Instruction.cs b/TLang.Common/Instruction.cs new file mode 100644 index 0000000..081cdba --- /dev/null +++ b/TLang.Common/Instruction.cs @@ -0,0 +1,71 @@ +using System.IO; + +namespace TLang.Common +{ + public abstract class Instruction + { + public abstract int CalculateLength(); + + public abstract void WriteData(Stream strm); + + + public const byte ADD = 0x00; + public const byte SUB = 0x01; + public const byte TIMES = 0x02; + public const byte DIVIDE = 0x03; + public const byte MOD = 0x04; + + public const byte POW = 0x05; + + public const byte LSHIFT = 0x06; + public const byte RSHIFT = 0x07; + public const byte BOR = 0x08; + public const byte BAND = 0x09; + public const byte XOR = 0x0A; + + public const byte NEG = 0x0B; + public const byte BNOT = 0x0C; + + public const byte NEQ = 0x10; + public const byte EQ = 0x11; + + public const byte LOR = 0x12; + + public const byte LAND = 0x13; + + public const byte LNOT = 0x14; + + public const byte LT = 0x15; + public const byte LTE = 0x16; + public const byte GT = 0x17; + public const byte GTE = 0x18; + + + public const byte SCOPE_BEGIN = 0xEA; + public const byte SCOPE_END = 0xEB; + public const byte CALL_FUNC = 0xEC; + public const byte CALL_METHOD = 0xED; + public const byte JMP = 0xEE; + public const byte JMPC = 0xEF; + + public const byte POP_ARRAY_VALUE = 0xF0; + public const byte PUSH_ARRAY_VALUE = 0xF1; + + public const byte POP_FIELD_VALUE = 0xF2; + public const byte PUSH_FIELD_VALUE = 0xF3; + public const byte POP_VARIABLE_VALUE = 0xF4; + public const byte PUSH_VARIABLE_VALUE = 0xF5; + public const byte PUSH_CHAR = 0xF6; + public const byte PUSH_FALSE = 0xF7; + public const byte PUSH_TRUE = 0xF8; + public const byte PUSH_UNDEFINED = 0xF9; + public const byte PUSH_NULL = 0xFA; + public const byte PUSH_CLOSURE = 0xFB; + public const byte PUSH_STRING = 0xFC; + public const byte PUSH_DOUBLE = 0xFD; + public const byte NOP = 0xFE; + public const byte RET = 0xFF; + + + } +} \ No newline at end of file diff --git a/TLang.Common/JumpConditional.cs b/TLang.Common/JumpConditional.cs new file mode 100644 index 0000000..267375a --- /dev/null +++ b/TLang.Common/JumpConditional.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; + +namespace TLang.Common +{ + public class JumpConditional : Instruction, ILabelable + { + public string Key {get;set;}=""; + public int Value {get;set;} =0; + public JumpConditional(string key) + { + Key = key; + } + + public override int CalculateLength() + { + return 5; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(JMPC); + var data=BitConverter.GetBytes(Value); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + } + } +} diff --git a/TLang.Common/JumpUnconditional.cs b/TLang.Common/JumpUnconditional.cs new file mode 100644 index 0000000..d976794 --- /dev/null +++ b/TLang.Common/JumpUnconditional.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; + +namespace TLang.Common +{ + public class JumpUnconditional : Instruction, ILabelable + { + public string Key {get;set;} + public int Value {get;set;} =0; + public JumpUnconditional(string key) + { + Key = key; + } + + public override int CalculateLength() + { + return 5; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(JMP); + var data=BitConverter.GetBytes(Value); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + } + } +} diff --git a/TLang.Common/LabelInstruction.cs b/TLang.Common/LabelInstruction.cs new file mode 100644 index 0000000..5e233a0 --- /dev/null +++ b/TLang.Common/LabelInstruction.cs @@ -0,0 +1,24 @@ +using System.IO; + +namespace TLang.Common +{ + public class LabelInstruction : Instruction + { + public string Key {get;set;} + + public LabelInstruction(string key) + { + Key = key; + } + + public override int CalculateLength() + { + return 0; + } + + public override void WriteData(Stream strm) + { + + } + } +} diff --git a/TLang.Common/PushCharNode.cs b/TLang.Common/PushCharNode.cs new file mode 100644 index 0000000..e935eb9 --- /dev/null +++ b/TLang.Common/PushCharNode.cs @@ -0,0 +1,26 @@ +using System.IO; +using TLang.Common; + +namespace TLang.BytecodeCompiler +{ + public class PushCharNode : Instruction + { + public char Text {get;set;} + + public PushCharNode(char text) + { + Text = text; + } + + public override int CalculateLength() + { + return 2; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(PUSH_CHAR); + strm.WriteByte((byte)Text); + } + } +} \ No newline at end of file diff --git a/TLang.Common/PushClosureNode.cs b/TLang.Common/PushClosureNode.cs new file mode 100644 index 0000000..13c08d6 --- /dev/null +++ b/TLang.Common/PushClosureNode.cs @@ -0,0 +1,28 @@ +using System; +using System.IO; +using TLang.Common; + +namespace TLang.Common +{ + public class PushClosureNode : Instruction + { + public int Value {get;set;} + public PushClosureNode(int i) + { + Value = i; + } + + public override int CalculateLength() + { + return 5; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(PUSH_CLOSURE); + var data=BitConverter.GetBytes(Value); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + } + } +} \ No newline at end of file diff --git a/TLang.Common/PushDouble.cs b/TLang.Common/PushDouble.cs new file mode 100644 index 0000000..b85ef18 --- /dev/null +++ b/TLang.Common/PushDouble.cs @@ -0,0 +1,29 @@ +using System; +using System.IO; + +using TLang.Common; + +namespace TLang.BytecodeCompiler +{ + public class PushDoubleInstruction : Instruction + { + public double Value {get;set;} + public PushDoubleInstruction(double value) + { + Value = value; + } + + public override int CalculateLength() + { + return 9; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(PUSH_DOUBLE); + var data=BitConverter.GetBytes(Value); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + } + } +} diff --git a/TLang.Common/PushStringInstruction.cs b/TLang.Common/PushStringInstruction.cs new file mode 100644 index 0000000..f838303 --- /dev/null +++ b/TLang.Common/PushStringInstruction.cs @@ -0,0 +1,30 @@ +using System; +using System.IO; +using TLang.Common; + +namespace TLang.BytecodeCompiler +{ + public class PushStringInstruction : Instruction + { + public string Value {get;set;} + public PushStringInstruction(string value) + { + Value = value; + } + + public override int CalculateLength() + { + return System.Text.Encoding.UTF8.GetByteCount(Value) + 5; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(PUSH_STRING); + byte[] bytes = System.Text.Encoding.UTF8.GetBytes(Value); + var data=BitConverter.GetBytes(bytes.Length); + if(BitConverter.IsLittleEndian) Array.Reverse(data); + strm.Write(data,0,data.Length); + strm.Write(bytes,0,bytes.Length); + } + } +} diff --git a/TLang.Common/SimpleInstruction.cs b/TLang.Common/SimpleInstruction.cs new file mode 100644 index 0000000..82d5330 --- /dev/null +++ b/TLang.Common/SimpleInstruction.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; +using TLang.Common; + +namespace TLang.BytecodeCompiler +{ + public class SimpleInstruction : Instruction + { + public byte Value {get;set;} + public SimpleInstruction(byte value) + { + Value = value; + } + + public override int CalculateLength() + { + return 1; + } + + public override void WriteData(Stream strm) + { + strm.WriteByte(Value); + } + } +} diff --git a/TLang.Common/TLang.Common.csproj b/TLang.Common/TLang.Common.csproj new file mode 100644 index 0000000..9f5c4f4 --- /dev/null +++ b/TLang.Common/TLang.Common.csproj @@ -0,0 +1,7 @@ + + + + netstandard2.0 + + + diff --git a/TLang.Common/TLangDependency.cs b/TLang.Common/TLangDependency.cs new file mode 100644 index 0000000..719baab --- /dev/null +++ b/TLang.Common/TLangDependency.cs @@ -0,0 +1,17 @@ +namespace TLang.Common +{ + public class TLangDependency + { + public TLangDependency(string name,TLangVersion version) + { + Name = name; + Version = version; + } + public TLangDependency(string name,byte major,byte minor,byte patch,byte build) : this(name,new TLangVersion(major,minor,patch,build)) + { + + } + public string Name {get;set;} + public TLangVersion Version {get;set;} + } +} \ No newline at end of file diff --git a/TLang.Common/TLangVersion.cs b/TLang.Common/TLangVersion.cs new file mode 100644 index 0000000..7897f97 --- /dev/null +++ b/TLang.Common/TLangVersion.cs @@ -0,0 +1,54 @@ +using System; + +namespace TLang.Common +{ + public class TLangVersion + { + public static TLangVersion Version => new TLangVersion(1,0,0,0); + + public int IntegerVersion => Major << 24 | Minor << 16 | Patch << 8 | Build; + + public static TLangVersion FromBytes(byte[] data,int offset=0) + { + TLangVersion version=new TLangVersion(); + + if(offset < data.Length) version.Major = data[offset]; + if(offset + 1 < data.Length) version.Minor = data[offset + 1]; + if(offset + 2 < data.Length) version.Minor = data[offset + 2]; + if(offset + 3 < data.Length) version.Minor = data[offset + 3]; + return version; + } + public TLangVersion() : this(1,0,0,0) + { + + } + public TLangVersion(byte major,byte minor,byte patch,byte build) + { + Major =major; + Minor = minor; + Patch = patch; + Build = build; + } + public TLangVersion(byte major,byte minor) : this(major,minor,0,0) + { + + } + public static explicit operator Version(TLangVersion version) + { + return new Version(version.Major,version.Minor,version.Patch,version.Build); + } + public static explicit operator TLangVersion(Version version) + { + return new TLangVersion((byte)version.Major,(byte)version.Minor,(byte)version.Build,(byte)version.Revision); + } + public byte Major {get;set;} + public byte Minor {get;set;} + + public byte Patch {get;set;} + public byte Build {get;set;} + public override string ToString() + { + return $"{Major}.{Minor}.{Patch}.{Build}"; + } + } +} \ No newline at end of file diff --git a/TLang.Lexer/LexContext.cs b/TLang.Lexer/LexContext.cs index 7f5141d..d545f63 100644 --- a/TLang.Lexer/LexContext.cs +++ b/TLang.Lexer/LexContext.cs @@ -31,11 +31,7 @@ namespace TLang.Lexer return FromOffset(Offset); } - public LexToken Next(int i = 0) - { - if(Offset + i < Tokens.Count) return Tokens[Offset + i]; - return new LexToken(); - } + public void Add(int i=1) { @@ -78,7 +74,7 @@ namespace TLang.Lexer foreach(var item in tokenText) { - if(Tokens[Offset].IsDocumentation || Tokens[Offset].IsChar || Tokens[Offset].IsString || Tokens[Offset].Text == item) + if(!Tokens[Offset].IsDocumentation && !Tokens[Offset].IsChar && !Tokens[Offset].IsString && Tokens[Offset].Text == item) { token = Tokens[Offset]; if(consumeIfTrue) Offset++; @@ -90,15 +86,15 @@ namespace TLang.Lexer return false; } - public LexToken NextEntry + public LexToken NextEntry() { - get { + if(Offset < Tokens.Count) { return Tokens[Offset++]; } return LexToken.Empty; - } + } public LexToken PeekEntry { diff --git a/TLang.Parser/AddNode.cs b/TLang.Parser/AddNode.cs index 816f0f3..225653b 100644 --- a/TLang.Parser/AddNode.cs +++ b/TLang.Parser/AddNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class AddNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public AddNode(Node left, Node right) { diff --git a/TLang.Parser/BAndNode.cs b/TLang.Parser/BAndNode.cs index 8338b3f..9dcd21b 100644 --- a/TLang.Parser/BAndNode.cs +++ b/TLang.Parser/BAndNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class BAndNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public BAndNode(Node left, Node right) { diff --git a/TLang.Parser/BOrNode.cs b/TLang.Parser/BOrNode.cs index 5f571c9..18a4e37 100644 --- a/TLang.Parser/BOrNode.cs +++ b/TLang.Parser/BOrNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class BOrNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public BOrNode(Node left, Node right) { diff --git a/TLang.Parser/CallNode.cs b/TLang.Parser/CallNode.cs index 5533819..060b9fa 100644 --- a/TLang.Parser/CallNode.cs +++ b/TLang.Parser/CallNode.cs @@ -4,6 +4,6 @@ namespace TLang.Parser { public class CallNode : SymbolNode { - public List Arguments {get;set;} + public List Arguments {get;set;}=new List(); } } \ No newline at end of file diff --git a/TLang.Parser/CaseNode.cs b/TLang.Parser/CaseNode.cs index 3f017a8..a97cf45 100644 --- a/TLang.Parser/CaseNode.cs +++ b/TLang.Parser/CaseNode.cs @@ -5,12 +5,11 @@ namespace TLang.Parser public class CaseNode : Node { public Node Variable {get;set;} - public Node Body {get;set;} - - public CaseNode(Node expr, Node node) + + public CaseNode(Node expr) { Variable = expr; - Body = node; + } diff --git a/TLang.Parser/CharNode.cs b/TLang.Parser/CharNode.cs index d1adb69..74232a7 100644 --- a/TLang.Parser/CharNode.cs +++ b/TLang.Parser/CharNode.cs @@ -4,11 +4,11 @@ namespace TLang.Parser { public class CharNode : Node { - public string Text {get;set;} + public char Text {get;set;} public CharNode(string text) { - Text = text; + Text = text[0]; } diff --git a/TLang.Parser/Class1.cs b/TLang.Parser/Class1.cs index d38bf92..3870adb 100644 --- a/TLang.Parser/Class1.cs +++ b/TLang.Parser/Class1.cs @@ -51,20 +51,30 @@ namespace TLang.Parser private Node ParseNode(bool isRoot=false,bool inCase=false) { - ScopeNode node = new ScopeNode(); - node.LineInfo = Context.CurrentLineInfo; - - - while(Context.Offset < Context.Tokens.Count && (isRoot || !Context.NextEntries(true,"}").Success)) - { - if(Context.NextEntries(true,"{").Success) + + if(Context.NextEntries(true,"{").Success || isRoot) { - node.Add(ParseNode()); + ScopeNode node = new ScopeNode(); + node.LineInfo = Context.CurrentLineInfo; + node.RootScope = isRoot; + + while(Context.Offset < Context.Tokens.Count && !Context.Tokens[Context.Offset].IsTokenWith("}")) + { + + + node.Add(ParseNode()); + + + if(Context.Offset < Context.Tokens.Count && Context.Tokens[Context.Offset].IsTokenWith(";")) Context.Offset++; + } + if(!isRoot) + Context.Offset++; + return node; } - node.Add(ParseAssigable()); - } + var res= ParseAssigable(); + if(Context.MoreTokens && Context.NextEntries(true,";").Success) _=0; - return node; +return res; } private Node ParseAssigable() @@ -233,7 +243,7 @@ namespace TLang.Parser private Node ParseRo() { - Node expr = ParseRo(); + Node expr = ParseShift(); while(Context.NextEntryIsAnyOf(true,out var token,"<","<=",">",">=")) { @@ -332,7 +342,7 @@ namespace TLang.Parser private Node ParseValue() { var doc = Context.PopDocumentation(); - var token = Context.NextEntry; + var token = Context.NextEntry(); if(token.IsString) return new StringNode(token.Text){LineInfo = token.Position}; if(token.IsChar) return new CharNode(token.Text){LineInfo = token.Position}; if(token.Text == "(") @@ -345,25 +355,25 @@ namespace TLang.Parser if(token.Text == "class") { ClassNode classInstance = new ClassNode(); - classInstance.Name = Context.NextEntry.Text; + classInstance.Name = Context.NextEntry().Text; classInstance.Documentation = doc; if(Context.NextEntries(true,":").Success) { - classInstance.InheritsFrom = Context.NextEntry.Text; + classInstance.InheritsFrom = Context.NextEntry().Text; } Context.NextEntries(true,"{"); while(Context.MoreTokens && !Context.NextEntries(true,"}").Success) { var doc2 = Context.PopDocumentation(); - var token2 = Context.NextEntry; + var token2 = Context.NextEntry(); var modifier = token2.Text; if(Context.NextEntryIsAnyOf(true,out var token3,"func","abst")) { - var name = Context.NextEntry.Text; + var name = Context.NextEntry().Text; if(Context.NextEntries(true,"(").Success) { var pos = Context.PeekEntry.Position; @@ -374,16 +384,8 @@ namespace TLang.Parser if(Context.Offset < Context.Tokens.Count) { - var n0 = ParseNode(); - var n= n0 as GetVariableNode; - if(n != null) - { - args.Add(n.Name); - } - else - { - throw new CompilerError("Argument must be a GetVariableNode",n0); - } + args.Add(Context.NextEntry().Text); + } } ClassEntryNode nod=new ClassEntryNode(){LineInfo = token2.Position}; @@ -399,7 +401,7 @@ namespace TLang.Parser { ClassEntryNode nod = new ClassEntryNode(){LineInfo = token2.Position}; nod.Documentation = doc2; - nod.Name = Context.NextEntry.Text; + nod.Name = Context.NextEntry().Text; if(!Context.NextEntries(true,"=").Success) throw new CompilerError("Invalid member",classInstance); nod.Modifier = modifier; @@ -426,14 +428,14 @@ namespace TLang.Parser { var expr = ParseNode(); if(!Context.NextEntries(true,":").Success) throw new CompilerError("Missing :",expr); - return new CaseNode(expr,ParseNode(true,true)){LineInfo = token.Position}; + return new CaseNode(expr){LineInfo = token.Position}; } if(token.Text == "default") { var cur = Context.CurrentLineInfo; var r=Context.NextEntries(true,":"); if(!r.Success) throw new CompilerError("Missing :",new Node(){LineInfo = cur}); - return new DefaultNode(ParseNode(true,true)){LineInfo = token.Position}; + return new DefaultNode(){LineInfo = token.Position}; } if(token.Text == "-") { @@ -451,7 +453,7 @@ namespace TLang.Parser { if(Context.NextEntries(true,".").Success) { - var entry=Context.NextEntry; + var entry=Context.NextEntry(); if(!entry.IsChar && !entry.IsDocumentation && !entry.IsEmpty && !entry.IsString && double.TryParse($"{number}.{entry.Text}",out var number2)) { return new ConstNumberNode(number2); @@ -475,16 +477,9 @@ namespace TLang.Parser if(Context.Offset < Context.Tokens.Count) { - var n0 = ParseNode(); - var n= n0 as GetVariableNode; - if(n != null) - { - args.Add(n.Name); - } - else - { - throw new CompilerError("Argument must be a GetVariableNode",n0); - } + + args.Add(Context.NextEntry().Text); + } } return new ClosureNode(args,ParseNode()); @@ -597,7 +592,7 @@ namespace TLang.Parser } while(Context.MoreTokens && Context.NextEntries(true,".").Success) { - var token2 = Context.NextEntry; + var token2 = Context.NextEntry(); if(Context.MoreTokens && Context.NextEntries(true,"(").Success) { @@ -641,29 +636,23 @@ namespace TLang.Parser SymbolNode ret = new NullNode(); if(token.Text == "func") { - var res = new GetVariableNode(Context.NextEntry.Text); + var funcName= Context.NextEntry().Text; if(Context.MoreTokens && Context.NextEntries(true,"(").Success) { List args=new List(); - while(!Context.NextEntries(true,")").Success) + while(Context.MoreTokens && !Context.NextEntries(true,")").Success) { if(Context.NextEntries(true,",").Success) continue; if(Context.Offset < Context.Tokens.Count) { - var n0 = ParseNode(); - var n= n0 as GetVariableNode; - if(n != null) - { - args.Add(n.Name); - } - else - { - throw new CompilerError("Argument must be a GetVariableNode",n0); - } + + args.Add(Context.NextEntry().Text); + + } } - return new SetVariableNode( res,new ClosureNode(args,ParseNode())); + return new FunctionDeclaritionNode( funcName,new ClosureNode(args,ParseNode())){LineInfo=token.Position}; } } if(token.Text == "null") @@ -695,7 +684,7 @@ namespace TLang.Parser } while(Context.MoreTokens && Context.NextEntries(true,".").Success) { - var token2 = Context.NextEntry; + var token2 = Context.NextEntry(); if(Context.MoreTokens && Context.NextEntries(true,"(").Success) { diff --git a/TLang.Parser/DefaultNode.cs b/TLang.Parser/DefaultNode.cs index fe451bd..1ef4e63 100644 --- a/TLang.Parser/DefaultNode.cs +++ b/TLang.Parser/DefaultNode.cs @@ -4,11 +4,10 @@ namespace TLang.Parser { public class DefaultNode : Node { - public Node Node {get;set;} - - public DefaultNode(Node node) + + public DefaultNode() { - Node = node; + } diff --git a/TLang.Parser/DivideNode.cs b/TLang.Parser/DivideNode.cs index 9cda4e2..466f478 100644 --- a/TLang.Parser/DivideNode.cs +++ b/TLang.Parser/DivideNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class DivideNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public DivideNode(Node left, Node right) { diff --git a/TLang.Parser/EqualsNode.cs b/TLang.Parser/EqualsNode.cs index 7b095d7..61f6c12 100644 --- a/TLang.Parser/EqualsNode.cs +++ b/TLang.Parser/EqualsNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class EqualsNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public EqualsNode(Node left, Node right) { diff --git a/TLang.Parser/FalseNode.cs b/TLang.Parser/FalseNode.cs new file mode 100644 index 0000000..7b681de --- /dev/null +++ b/TLang.Parser/FalseNode.cs @@ -0,0 +1,6 @@ +namespace TLang.Parser +{ + public class FalseNode : SymbolNode + { + } +} \ No newline at end of file diff --git a/TLang.Parser/FunctionDeclaritionNode.cs b/TLang.Parser/FunctionDeclaritionNode.cs new file mode 100644 index 0000000..f81afba --- /dev/null +++ b/TLang.Parser/FunctionDeclaritionNode.cs @@ -0,0 +1,18 @@ +using TLang.Lexer; + +namespace TLang.Parser +{ + public class FunctionDeclaritionNode : Node + { + public string FunctionName {get;set;} + public ClosureNode ClosureNode {get;set;} + + public FunctionDeclaritionNode(string funcName, ClosureNode closureNode) + { + FunctionName = funcName; + ClosureNode = closureNode; + } + + + } +} \ No newline at end of file diff --git a/TLang.Parser/GetMemberNode.cs b/TLang.Parser/GetMemberNode.cs index 92f4547..d83801c 100644 --- a/TLang.Parser/GetMemberNode.cs +++ b/TLang.Parser/GetMemberNode.cs @@ -2,11 +2,11 @@ using TLang.Lexer; namespace TLang.Parser { - internal class GetMemberNode : SymbolNode + public class GetMemberNode : SymbolNode { - public SymbolNode Symbol {get;set;} + public Node Symbol {get;set;} - public GetMemberNode(SymbolNode symbol, string name) + public GetMemberNode(Node symbol, string name) { Symbol = symbol; Name = name; diff --git a/TLang.Parser/GreaterThanEqualsNode.cs b/TLang.Parser/GreaterThanEqualsNode.cs index b578cdd..8a1051a 100644 --- a/TLang.Parser/GreaterThanEqualsNode.cs +++ b/TLang.Parser/GreaterThanEqualsNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class GreaterThanEqualsNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public GreaterThanEqualsNode(Node left, Node right) { diff --git a/TLang.Parser/GreaterThanNode.cs b/TLang.Parser/GreaterThanNode.cs index 10ff18a..d4db651 100644 --- a/TLang.Parser/GreaterThanNode.cs +++ b/TLang.Parser/GreaterThanNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class GreaterThanNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public GreaterThanNode(Node left, Node right) { diff --git a/TLang.Parser/IfNode.cs b/TLang.Parser/IfNode.cs index 8aa1100..3dc1aee 100644 --- a/TLang.Parser/IfNode.cs +++ b/TLang.Parser/IfNode.cs @@ -1,6 +1,6 @@ namespace TLang.Parser { - internal class IfNode : Node + public class IfNode : Node { public Node Condition {get;set;} public Node Yes {get;set;} diff --git a/TLang.Parser/LAndNode.cs b/TLang.Parser/LAndNode.cs index 21cc1e8..953dc58 100644 --- a/TLang.Parser/LAndNode.cs +++ b/TLang.Parser/LAndNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class LAndNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public LAndNode(Node left, Node right) { diff --git a/TLang.Parser/LOrNode.cs b/TLang.Parser/LOrNode.cs index cc8a708..f23e710 100644 --- a/TLang.Parser/LOrNode.cs +++ b/TLang.Parser/LOrNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class LOrNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public LOrNode(Node left, Node right) { diff --git a/TLang.Parser/LeftShiftNode.cs b/TLang.Parser/LeftShiftNode.cs index 034f544..ade7a7b 100644 --- a/TLang.Parser/LeftShiftNode.cs +++ b/TLang.Parser/LeftShiftNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class LeftShiftNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public LeftShiftNode(Node left, Node right) { diff --git a/TLang.Parser/LessThanEqualsNode.cs b/TLang.Parser/LessThanEqualsNode.cs index 31f5c6c..ff66163 100644 --- a/TLang.Parser/LessThanEqualsNode.cs +++ b/TLang.Parser/LessThanEqualsNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class LessThanEqualsNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public LessThanEqualsNode(Node left, Node right) { diff --git a/TLang.Parser/LessThanNode.cs b/TLang.Parser/LessThanNode.cs index 505b919..9eb0a29 100644 --- a/TLang.Parser/LessThanNode.cs +++ b/TLang.Parser/LessThanNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class LessThanNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public LessThanNode(Node left, Node right) { diff --git a/TLang.Parser/ModuloNode.cs b/TLang.Parser/ModuloNode.cs index faca061..918f61b 100644 --- a/TLang.Parser/ModuloNode.cs +++ b/TLang.Parser/ModuloNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class ModuloNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public ModuloNode(Node left, Node right) { diff --git a/TLang.Parser/MultiplyNode.cs b/TLang.Parser/MultiplyNode.cs index 54a5cc9..1f6aa19 100644 --- a/TLang.Parser/MultiplyNode.cs +++ b/TLang.Parser/MultiplyNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class MultiplyNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public MultiplyNode(Node left, Node right) { diff --git a/TLang.Parser/NotEqualsNode.cs b/TLang.Parser/NotEqualsNode.cs index 71901b9..718afc4 100644 --- a/TLang.Parser/NotEqualsNode.cs +++ b/TLang.Parser/NotEqualsNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class NotEqualsNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public NotEqualsNode(Node left, Node right) { diff --git a/TLang.Parser/NullNode.cs b/TLang.Parser/NullNode.cs new file mode 100644 index 0000000..4f61216 --- /dev/null +++ b/TLang.Parser/NullNode.cs @@ -0,0 +1,6 @@ +namespace TLang.Parser +{ + public class NullNode : SymbolNode + { + } +} \ No newline at end of file diff --git a/TLang.Parser/PowNode.cs b/TLang.Parser/PowNode.cs index a6b7568..55b245c 100644 --- a/TLang.Parser/PowNode.cs +++ b/TLang.Parser/PowNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class PowNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public PowNode(Node left, Node right) { diff --git a/TLang.Parser/RightShiftNode.cs b/TLang.Parser/RightShiftNode.cs index 8420fba..f7ecc47 100644 --- a/TLang.Parser/RightShiftNode.cs +++ b/TLang.Parser/RightShiftNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class RightShiftNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public RightShiftNode(Node left, Node right) { diff --git a/TLang.Parser/ScopeNode.cs b/TLang.Parser/ScopeNode.cs index e719f9c..d33cfa4 100644 --- a/TLang.Parser/ScopeNode.cs +++ b/TLang.Parser/ScopeNode.cs @@ -5,6 +5,7 @@ namespace TLang.Parser { public class ScopeNode : Node { + public bool RootScope {get;set;}=false; public bool IsSwitchScope {get;set;} public Node SwitchCondition {get;set;}=new Node(); public List Nodes {get;set;}=new List(); diff --git a/TLang.Parser/SubtractNode.cs b/TLang.Parser/SubtractNode.cs index 70b61e9..7a16a50 100644 --- a/TLang.Parser/SubtractNode.cs +++ b/TLang.Parser/SubtractNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class SubtractNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public SubtractNode(Node left, Node right) { diff --git a/TLang.Parser/TrueNode.cs b/TLang.Parser/TrueNode.cs new file mode 100644 index 0000000..6db78a7 --- /dev/null +++ b/TLang.Parser/TrueNode.cs @@ -0,0 +1,6 @@ +namespace TLang.Parser +{ + public class TrueNode : SymbolNode + { + } +} \ No newline at end of file diff --git a/TLang.Parser/UndefinedNode.cs b/TLang.Parser/UndefinedNode.cs new file mode 100644 index 0000000..b397585 --- /dev/null +++ b/TLang.Parser/UndefinedNode.cs @@ -0,0 +1,6 @@ +namespace TLang.Parser +{ + public class UndefinedNode : SymbolNode + { + } +} \ No newline at end of file diff --git a/TLang.Parser/XOrNode.cs b/TLang.Parser/XOrNode.cs index c36f24f..f05a230 100644 --- a/TLang.Parser/XOrNode.cs +++ b/TLang.Parser/XOrNode.cs @@ -2,8 +2,8 @@ namespace TLang.Parser { public class XOrNode : Node { - private Node Left {get;set;} - private Node Right {get;set;} + public Node Left {get;set;} + public Node Right {get;set;} public XOrNode(Node left, Node right) { diff --git a/TLang.VM/Chunk.cs b/TLang.VM/Chunk.cs new file mode 100644 index 0000000..2814c5d --- /dev/null +++ b/TLang.VM/Chunk.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace TLang.VM +{ + public class Chunk + { + public List Arguments {get;set;}=new List(); + + public byte[] Code {get;set;} + } +} \ No newline at end of file diff --git a/TLang.VM/ChunkExecuter.cs b/TLang.VM/ChunkExecuter.cs new file mode 100644 index 0000000..b86d7fd --- /dev/null +++ b/TLang.VM/ChunkExecuter.cs @@ -0,0 +1,211 @@ +using System; +using System.Collections.Generic; +using TLang.BytecodeCompiler; +using TLang.Common; +namespace TLang.VM +{ + public class ChunkExecuter + { + public string ClassName {get;set;}=""; + public ChunkExecuter(TVMFile file,Chunk chunk,TLangEnvironment env) + { + File =file; + Chunk = chunk; + Environment = env; + } + public TLangEnvironment Environment {get;set;} + public TVMFile File {get;} + + public Chunk Chunk {get;} + public int IP {get;set;}=0; + public Stack Stack {get;}=new Stack(); + + + public TObject Execute(params TObject[] args) + { + var env=Environment.GetSubEnvironment(); + for(int i = 0;i< args.Length;i++) + { + if(i= File.Chunks.Count) throw new IndexOutOfRangeException($"No chunk with id {closureId}"); + Stack.Push(new TClosure(File,File.Chunks[closureId],env)); + break; + case Instruction.PUSH_STRING: + Stack.Push(new TString(ReadString())); + break; + case Instruction.PUSH_VARIABLE_VALUE: + { + var res=EnsurePop(); + Stack.Push(env.GetObject(res.Value)); + } + break; + case Instruction.POP_VARIABLE_VALUE: + { + var res=EnsurePop(); + env.SetObject(res.Value,Stack.Pop()); + } + break; + case Instruction.CALL_FUNC: + { + var argC = (int)EnsurePop().Value; + TObject[] argV = new TObject[argC]; + for(int i = argC;i>0;i--) + { + argV[i] = Stack.Pop(); + } + var name = EnsurePop(); + var callable=env.GetObject(name.Value) as TCallable; + if(callable != null) + { + Stack.Push(callable.Execute(argV)); + }else{ + Stack.Push(new TUndefined()); + } + } + break; + case Instruction.CALL_METHOD: + { + var argC = (int)EnsurePop().Value; + TObject[] argV = new TObject[argC]; + for(int i = argC;i>0;i--) + { + argV[i] = Stack.Pop(); + } + var name = EnsurePop(); + var symbol = Stack.Pop(); + + Stack.Push(ExecuteMethod(symbol,name.Value,argV)); + } + break; + } + } + if(Stack.Count > 0) + return Stack.Pop(); + return new TNull(); + } + + private TObject ExecuteMethod(TObject symbol, string name, params TObject[] argV) + { + + } + + private T EnsurePop() where T : TObject + { + var item = Stack.Pop() as T; + if(item == null) throw new Exception("Pop failed"); + return item; + } + + private double ReadDouble() + { + if(IP + 8 < Chunk.Code.Length) + { + byte[] data =new byte[8]; + Array.Copy(Chunk.Code,IP,data,0,8); + IP += 8; + if(BitConverter.IsLittleEndian) Array.Reverse(data); + return BitConverter.ToDouble(data,0); + } + throw new Exception("Malformed binary"); + } + private int ReadInt() + { + if(IP + 4 < Chunk.Code.Length) + { + byte[] data =new byte[4]; + Array.Copy(Chunk.Code,IP,data,0,4); + IP += 4; + if(BitConverter.IsLittleEndian) Array.Reverse(data); + return BitConverter.ToInt32(data,0); + } + throw new Exception("Malformed binary"); + } + private string ReadString() + { + int strLen = ReadInt(); + if(IP + strLen < Chunk.Code.Length) + { + var str=System.Text.Encoding.UTF8.GetString(Chunk.Code,IP,strLen); + IP += strLen; + return str; + } + throw new Exception("Malformed binary"); + } + private TObject Mod(TObject object1, TObject object2) + { + throw new NotImplementedException(); + } + + private TObject Divide(TObject object1, TObject object2) + { + throw new NotImplementedException(); + } + + private TObject Times(TObject object1, TObject object2) + { + throw new NotImplementedException(); + } + + private TObject Sub(TObject object2, TObject object1) + { + throw new NotImplementedException(); + } + + private TObject Add(TObject object2, TObject object1) + { + var left = object2 as TNumber; + var right = object1 as TNumber; + var lStr = object2 as TString; + var rStr = object1 as TString; + if(left != null && right != null) + { + return new TNumber(left.Value + right.Value); + } + if(lStr != null && rStr != null) + { + return new TString(lStr.Value + rStr.Value); + } + return new TNumber(0); + } + } + + +} diff --git a/TLang.VM/Class.cs b/TLang.VM/Class.cs new file mode 100644 index 0000000..2e266e0 --- /dev/null +++ b/TLang.VM/Class.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace TLang.VM +{ + public class Class + { + public string Name {get;set;}=""; + public string InheritsFrom {get;set;}=""; + + public List ClassEntries {get;set;}=new List(); + } +} \ No newline at end of file diff --git a/TLang.VM/ClassEntry.cs b/TLang.VM/ClassEntry.cs new file mode 100644 index 0000000..b6a28bd --- /dev/null +++ b/TLang.VM/ClassEntry.cs @@ -0,0 +1,18 @@ +namespace TLang.VM +{ + public class ClassEntry + { + public byte Modifier {get;set;}=0; + public bool Abstract => (Modifier & 0b00001000) > 0; + + public bool Method => (Modifier & 0b00000100) > 0; + + public bool Private => (Modifier & 0b00000011) == 0; + public bool Protected => (Modifier & 0b00000011) == 1; + public bool Public => (Modifier & 0b00000011) == 2; + + public string Name {get;set;} + + public int ChunkId {get;set;} + } +} \ No newline at end of file diff --git a/TLang.VM/ClassEnvironment.cs b/TLang.VM/ClassEnvironment.cs new file mode 100644 index 0000000..fba324d --- /dev/null +++ b/TLang.VM/ClassEnvironment.cs @@ -0,0 +1,49 @@ +namespace TLang.VM +{ + internal class ClassEnvironment : TLangEnvironment + { + private TLangEnvironment oGenv; + private TClassInstance classInstance; + + public ClassEnvironment(TLangEnvironment oGenv, TClassInstance classInstance) + { + this.oGenv = oGenv; + this.classInstance = classInstance; + } + + public override TObject GetObject(string key) + { + throw new System.NotImplementedException(); + } + + public override TLangEnvironment GetParentEnvironment() + { + throw new System.NotImplementedException(); + } + + public override RootEnvironment GetRootEnvironment() + { + throw new System.NotImplementedException(); + } + + public override TLangEnvironment GetSubEnvironment() + { + throw new System.NotImplementedException(); + } + + public override bool HasObject(string key) + { + throw new System.NotImplementedException(); + } + + public override bool HasObjectRecurse(string key) + { + throw new System.NotImplementedException(); + } + + public override void SetObject(string key, TObject value) + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TLang.VM/ClassMethod.cs b/TLang.VM/ClassMethod.cs new file mode 100644 index 0000000..f757777 --- /dev/null +++ b/TLang.VM/ClassMethod.cs @@ -0,0 +1,15 @@ +namespace TLang.VM +{ + public class ClassMethod + { + public bool Public {get;set;} + + public bool Private {get;set;} + + public bool Protected {get;set;} + + public Chunk Chunk {get;set;} + + public TVMFile File {get;set;} + } +} \ No newline at end of file diff --git a/TLang.VM/DefaultDependencyPool.cs b/TLang.VM/DefaultDependencyPool.cs new file mode 100644 index 0000000..45d7132 --- /dev/null +++ b/TLang.VM/DefaultDependencyPool.cs @@ -0,0 +1,50 @@ +using System.Collections; +using System.Collections.Generic; +using System.IO; +using TLang.Common; + +namespace TLang.VM +{ + public class DefaultDependencyPool : IDependencyPool + { + List<(TLangDependency Dependency,TVMFile File)> items = new List<(TLangDependency Dependency, TVMFile File)>(); + public bool AddDependency(TLangDependency dependency) + { + string fileName = $"{dependency.Name}-{dependency.Version}.tvm"; + if(File.Exists(fileName)) + { + using(var f = File.OpenRead(fileName)) + { + TVMFile file = new TVMFile(f); + items.Add((dependency,file)); + return file.LoadDependencies(this); + } + } + return false; + } + private static TLangDependencyResponse DefaultDependencyCheck(TLangDependency dep) + { + string fileName = $"{dep.Name}-{dep.Version}.tvm"; + if(File.Exists(fileName)) + { + return new TLangDependencyResponse(){Stream = File.OpenRead(fileName)}; + } + return new TLangDependencyResponse(){Found = false}; + } + + public bool DependencyExists(TLangDependency dependency) + { + throw new System.NotImplementedException(); + } + + public IEnumerator<(TLangDependency Dependency, TVMFile File)> GetEnumerator() + { + throw new System.NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new System.NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TLang.VM/Function.cs b/TLang.VM/Function.cs new file mode 100644 index 0000000..14ef599 --- /dev/null +++ b/TLang.VM/Function.cs @@ -0,0 +1,8 @@ +namespace TLang.VM +{ + public class Function + { + public string Name {get;set;} + public int ChunkId {get;set;} + } +} \ No newline at end of file diff --git a/TLang.VM/IDependencyPool.cs b/TLang.VM/IDependencyPool.cs new file mode 100644 index 0000000..8496ff6 --- /dev/null +++ b/TLang.VM/IDependencyPool.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using TLang.Common; + +namespace TLang.VM +{ + public interface IDependencyPool : IEnumerable<(TLangDependency Dependency,TVMFile File)> + { + + + bool DependencyExists(TLangDependency dependency); + + bool AddDependency(TLangDependency dependency,RootEnvironment env); + + + + } +} \ No newline at end of file diff --git a/TLang.VM/LoadedClassData.cs b/TLang.VM/LoadedClassData.cs new file mode 100644 index 0000000..700ee77 --- /dev/null +++ b/TLang.VM/LoadedClassData.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; + +namespace TLang.VM +{ + public class LoadedClassData + { + public LoadedClassData(string inheritsFrom,TVMFile file,List entries) + { + InheritsFrom = inheritsFrom; + File = file; + Entries = entries; + } + public string InheritsFrom {get;set;} + + public TVMFile File {get;set;} + + public List Entries {get;set;} + + internal Class ToClass(string name) + { + Class cla=new Class(); + cla.ClassEntries = Entries; + cla.InheritsFrom = InheritsFrom; + cla.Name = name; + return cla; + } + } +} \ No newline at end of file diff --git a/TLang.VM/RootEnvironment.cs b/TLang.VM/RootEnvironment.cs new file mode 100644 index 0000000..046b3fe --- /dev/null +++ b/TLang.VM/RootEnvironment.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; + +namespace TLang.VM +{ + public class RootEnvironment : TLangEnvironment + { + public RootEnvironment() + { + + } + public Dictionary AvailableClasses {get;set;}=new Dictionary(); + + Dictionary items = new Dictionary(); + public override TObject GetObject(string key) + { + if(HasObject(key)) + { + return items[key]; + } + return new TUndefined(); + } + + public override bool HasObject(string key) + { + return items.ContainsKey(key); + } + + public override TLangEnvironment GetParentEnvironment() + { + return this; + } + + public override RootEnvironment GetRootEnvironment() + { + return this; + } + + public override void SetObject(string key, TObject value) + { + items[key] = value; + } + public override bool HasObjectRecurse(string key) + { + return HasObject(key); + } + public override TLangEnvironment GetSubEnvironment() + { + return new SubEnvironment(this); + } + } +} \ No newline at end of file diff --git a/TLang.VM/SubEnvironment.cs b/TLang.VM/SubEnvironment.cs new file mode 100644 index 0000000..731d661 --- /dev/null +++ b/TLang.VM/SubEnvironment.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; + +namespace TLang.VM +{ + public class SubEnvironment : TLangEnvironment + { + public SubEnvironment(TLangEnvironment env) + { + this.pEnv=env; + } + TLangEnvironment pEnv; + + Dictionary items = new Dictionary(); + public override TObject GetObject(string key) + { + if(pEnv.HasObjectRecurse(key)) + { + return pEnv.GetObject(key); + } + if(HasObject(key)) + { + return items[key]; + } + return new TUndefined(); + } + + public override bool HasObject(string key) + { + return items.ContainsKey(key); + } + public override bool HasObjectRecurse(string key) + { + return HasObject(key) && pEnv.HasObjectRecurse(key); + } + + + public override TLangEnvironment GetParentEnvironment() + { + return pEnv; + } + + public override RootEnvironment GetRootEnvironment() + { + return pEnv.GetRootEnvironment(); + } + + public override void SetObject(string key, TObject value) + { + if(pEnv.HasObjectRecurse(key)) + { + pEnv.SetObject(key,value); + } + else + { + items[key] = value; + } + + } + + public override TLangEnvironment GetSubEnvironment() + { + return new SubEnvironment(this); + } + } +} \ No newline at end of file diff --git a/TLang.VM/TCallable.cs b/TLang.VM/TCallable.cs new file mode 100644 index 0000000..75f3892 --- /dev/null +++ b/TLang.VM/TCallable.cs @@ -0,0 +1,7 @@ +namespace TLang.VM +{ + public abstract class TCallable : TObject + { + public abstract TObject Execute(params TObject[] args); + } +} \ No newline at end of file diff --git a/TLang.VM/TClassInstance.cs b/TLang.VM/TClassInstance.cs new file mode 100644 index 0000000..8a26bb6 --- /dev/null +++ b/TLang.VM/TClassInstance.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; + +namespace TLang.VM +{ + public class TClassInstance : TObject + { + TVMFile file; + Class classEntry; + TLangEnvironment oGenv; + public TClassInstance(TVMFile file,Class classEntry,TLangEnvironment env) + { + this.file = file; + this.classEntry = classEntry; + this.oGenv = env; + } + public List InheritenceTree {get;}=new List(); + public Dictionary Methods {get;} = new Dictionary(); + + + + public void Init(params TObject[] args) + { + Environment = new ClassEnvironment(oGenv,this); + + foreach(var item in classEntry.ClassEntries) + { + + } + var cE = classEntry; + var aC =oGenv.GetRootEnvironment().AvailableClasses; + //we need to check inheritence + while(cE.InheritsFrom != "object") + { + InheritenceTree.Add(cE.InheritsFrom); + if(aC.ContainsKey(cE.InheritsFrom)) + { + var aC2 = aC[cE.InheritsFrom]; + cE = aC2.ToClass(cE.InheritsFrom); + foreach(var item in cE.ClassEntries) + { + if(!Methods.ContainsKey(item.Name) && item.Method) + { + if(item.Abstract) throw new Exception("Method is abstract"); + ClassMethod meth=new ClassMethod(); + meth.File = aC2.File; + meth.Chunk = aC2.File.Chunks[item.ChunkId]; + meth.Private = item.Private; + meth.Protected = item.Protected; + meth.Public = item.Public; + Methods.Add(item.Name,meth); + } + } + } + } + } + public TLangEnvironment Environment {get; private set;} + public void SetField(bool inside,string className,string key,TObject obj) + { + + } + public TObject GetField(string className,string key) + { + + return new TUndefined(); + } + public TObject CallMethod(string className,string key,params TObject[] args) + { + if(Methods.ContainsKey(key)) + { + var method=Methods[key]; + if(method.Private && className != classEntry.Name) return new TUndefined(); + + } + + return new TUndefined(); + } + + + } +} \ No newline at end of file diff --git a/TLang.VM/TClosure.cs b/TLang.VM/TClosure.cs new file mode 100644 index 0000000..3b65328 --- /dev/null +++ b/TLang.VM/TClosure.cs @@ -0,0 +1,22 @@ +namespace TLang.VM +{ + public class TClosure : TCallable + { + private TVMFile file; + private Chunk chunk; + private TLangEnvironment env; + + public TClosure(TVMFile file, Chunk chunk, TLangEnvironment env) + { + this.file = file; + this.chunk = chunk; + this.env = env; + } + + public override TObject Execute(params TObject[] args) + { + ChunkExecuter executer = new ChunkExecuter(file,chunk,env); + return executer.Execute(args); + } + } +} \ No newline at end of file diff --git a/TLang.VM/TLang.VM.csproj b/TLang.VM/TLang.VM.csproj new file mode 100644 index 0000000..a1c6194 --- /dev/null +++ b/TLang.VM/TLang.VM.csproj @@ -0,0 +1,11 @@ + + + + + + + + netstandard2.0 + + + diff --git a/TLang.VM/TLangDependencyResponse.cs b/TLang.VM/TLangDependencyResponse.cs new file mode 100644 index 0000000..50b5e4b --- /dev/null +++ b/TLang.VM/TLangDependencyResponse.cs @@ -0,0 +1,15 @@ +using System.IO; + +namespace TLang.VM +{ + public class TLangDependencyResponse + { + public bool IgnoreVersion {get;set;}=false; + + public Stream Stream {get;set;}=Stream.Null; + + public bool DisposeStream {get;set;}=true; + + public bool Found {get;set;}=false; + } +} \ No newline at end of file diff --git a/TLang.VM/TLangEnvironment.cs b/TLang.VM/TLangEnvironment.cs new file mode 100644 index 0000000..b163743 --- /dev/null +++ b/TLang.VM/TLangEnvironment.cs @@ -0,0 +1,21 @@ +using System; + +namespace TLang.VM +{ + public abstract class TLangEnvironment + { + public abstract TLangEnvironment GetSubEnvironment(); + + public abstract TObject GetObject(string key); + + public abstract void SetObject(string key,TObject value); + + public abstract bool HasObject(string key); + + public abstract bool HasObjectRecurse(string key); + + public abstract RootEnvironment GetRootEnvironment(); + + public abstract TLangEnvironment GetParentEnvironment(); + } +} \ No newline at end of file diff --git a/TLang.VM/TNull.cs b/TLang.VM/TNull.cs new file mode 100644 index 0000000..fffed42 --- /dev/null +++ b/TLang.VM/TNull.cs @@ -0,0 +1,6 @@ +namespace TLang.VM +{ + public class TNull : TObject + { + } +} \ No newline at end of file diff --git a/TLang.VM/TNumber.cs b/TLang.VM/TNumber.cs new file mode 100644 index 0000000..144ab55 --- /dev/null +++ b/TLang.VM/TNumber.cs @@ -0,0 +1,12 @@ +namespace TLang.VM +{ + public class TNumber : TObject + { + public double Value {get;set;} + + public TNumber(double value) + { + this.Value = value; + } + } +} \ No newline at end of file diff --git a/TLang.VM/TObject.cs b/TLang.VM/TObject.cs new file mode 100644 index 0000000..7819649 --- /dev/null +++ b/TLang.VM/TObject.cs @@ -0,0 +1,6 @@ +namespace TLang.VM +{ + public class TObject + { + } +} \ No newline at end of file diff --git a/TLang.VM/TString.cs b/TLang.VM/TString.cs new file mode 100644 index 0000000..c4bc272 --- /dev/null +++ b/TLang.VM/TString.cs @@ -0,0 +1,12 @@ +namespace TLang.VM +{ + public class TString : TObject + { + public string Value {get;set;} + + public TString(string value) + { + this.Value = value; + } + } +} \ No newline at end of file diff --git a/TLang.VM/TUndefined.cs b/TLang.VM/TUndefined.cs new file mode 100644 index 0000000..fecdb48 --- /dev/null +++ b/TLang.VM/TUndefined.cs @@ -0,0 +1,6 @@ +namespace TLang.VM +{ + public class TUndefined : TObject + { + } +} \ No newline at end of file diff --git a/TLang.VM/TVMFile.cs b/TLang.VM/TVMFile.cs new file mode 100644 index 0000000..849dc84 --- /dev/null +++ b/TLang.VM/TVMFile.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.IO; +using TLang.Common; + +namespace TLang.VM +{ + public class TVMFile + { + public List Classes {get;set;}=new List(); + public List Functions {get;set;}=new List(); + public List Dependencies {get;set;}=new List(); + + public List Chunks {get;set;}=new List(); + public TLangVersion Version {get;set;} + public RootEnvironment Environment {get;private set;} + public TVMFile(Stream file,RootEnvironment env) + { + Environment = env; + byte[] data=new byte[11]; + int read=file.Read(data,0,data.Length); + CheckData(data,read); + int deps=ReadBigEndianIntenger(file); + for(int i = 0;i 0) + read0=file.Read(code,offset,read0); + + offset+=read0; + } while(read0 > 0); + chunk.Code = code; + Chunks.Add(chunk); + } + } + private void EnsureRead(Stream file,byte[] data) + { + int i = file.Read(data,0,data.Length); + if(i != data.Length) throw new IndexOutOfRangeException("Array must be filled"); + + } + private int ReadBigEndianIntenger(Stream file) + { + byte[] bytes=new byte[4]; + EnsureRead(file,bytes); + if(BitConverter.IsLittleEndian) Array.Reverse(bytes); + return BitConverter.ToInt32(bytes,0); + } + + private void CheckData(byte[] data, int read) + { + if(read < 11 || data[0] != 'T' || data[1] != 'V' || data[2] != 'M') throw new BadImageFormatException("TVM file invalid"); + + if(TLangVersion.FromBytes(data,3).IntegerVersion > TLangVersion.Version.IntegerVersion) throw new BadImageFormatException("TVM Version is too old"); + + Version = TLangVersion.FromBytes(data,7); + + } + + public bool LoadDependencies(IDependencyPool dependencyPool) + { + foreach(var dep in Dependencies) + { + if(!dependencyPool.DependencyExists(dep)) + { + if(!dependencyPool.AddDependency(dep,Environment)) return false; + } + } + foreach(var func in Functions) + { + Environment.SetObject(func.Name,new TClosure(this,Chunks[func.ChunkId],Environment)); + } + foreach(var class0 in Classes) + { + Environment.AvailableClasses.Add(class0.Name,new LoadedClassData(class0.InheritsFrom,this,class0.ClassEntries)); + } + return true; + + } + + + } +} \ No newline at end of file diff --git a/VMTest/.vscode/launch.json b/VMTest/.vscode/launch.json new file mode 100644 index 0000000..0b14c26 --- /dev/null +++ b/VMTest/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net7.0/VMTest.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/VMTest/.vscode/tasks.json b/VMTest/.vscode/tasks.json new file mode 100644 index 0000000..4625b9e --- /dev/null +++ b/VMTest/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/VMTest.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/VMTest.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/VMTest.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/VMTest/Program.cs b/VMTest/Program.cs new file mode 100644 index 0000000..e7b795e --- /dev/null +++ b/VMTest/Program.cs @@ -0,0 +1,10 @@ +using TLang.VM; +using var f = File.OpenRead("app.tvm"); +TVMFile file = new TVMFile(f); + +ChunkExecuter executer=new ChunkExecuter(file,file.Chunks[0],new RootEnvironment()); +var res=executer.Execute() as TNumber; +if(res != null) +{ + Console.WriteLine($"Number: {res.Value}"); +} \ No newline at end of file diff --git a/VMTest/VMTest.csproj b/VMTest/VMTest.csproj new file mode 100644 index 0000000..a07b8b7 --- /dev/null +++ b/VMTest/VMTest.csproj @@ -0,0 +1,14 @@ + + + + + + + + Exe + net7.0 + enable + enable + + + diff --git a/VMTest/app.tvm b/VMTest/app.tvm new file mode 100644 index 0000000..3817887 Binary files /dev/null and b/VMTest/app.tvm differ