diff --git a/dwarf-cs/dwarf-cs.sln b/dwarf-cs/dwarf-cs.sln new file mode 100644 index 0000000..58c730c --- /dev/null +++ b/dwarf-cs/dwarf-cs.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dwarf.core", "dwarf.core\dwarf.core.csproj", "{FD95899A-79B4-4424-A531-92F0935AC39C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tests", "tests\tests.csproj", "{E48C4A1A-A404-466F-9F66-5D9A9C80896B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FD95899A-79B4-4424-A531-92F0935AC39C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FD95899A-79B4-4424-A531-92F0935AC39C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FD95899A-79B4-4424-A531-92F0935AC39C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FD95899A-79B4-4424-A531-92F0935AC39C}.Release|Any CPU.Build.0 = Release|Any CPU + {E48C4A1A-A404-466F-9F66-5D9A9C80896B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E48C4A1A-A404-466F-9F66-5D9A9C80896B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E48C4A1A-A404-466F-9F66-5D9A9C80896B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E48C4A1A-A404-466F-9F66-5D9A9C80896B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/dwarf-cs/dwarf.core/BytecodeBuilder.cs b/dwarf-cs/dwarf.core/BytecodeBuilder.cs new file mode 100644 index 0000000..48d819e --- /dev/null +++ b/dwarf-cs/dwarf.core/BytecodeBuilder.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; + +namespace dwarf.core +{ + public class BytecodeBuilder + { + private List bytecode = new List(); + + private Dictionary labels = new Dictionary(); + private Dictionary> fixups = new Dictionary>(); + + private int LabelOffset(Label label) + { + if (labels.ContainsKey(label)) + return labels[label] - (bytecode.Count + 4); + + if (!fixups.ContainsKey(label)) + fixups.Add(label, new List()); + + fixups[label].Add(bytecode.Count); + return bytecode.Count + 4; + } + + private void DoFixups(List offsets, int address) + { + foreach (var offset in offsets) + { + var pc = BitConverter.ToInt32(bytecode.GetRange(offset, 4).ToArray(), 0); + var b = BitConverter.GetBytes(address - pc); + + for (int i = 0; i < 4; i++) + { + bytecode[offset + i] = b[i]; + } + } + } + + public List Bytecode + { + get { return bytecode; } + } + + public BytecodeBuilder halt() + { + bytecode.Add(0x00); + return this; + } + + public BytecodeBuilder iadd() + { + bytecode.Add(0x01); + return this; + } + + public BytecodeBuilder isub() + { + bytecode.Add(0x02); + return this; + } + + public BytecodeBuilder imul() + { + bytecode.Add(0x03); + return this; + } + + public BytecodeBuilder idiv() + { + bytecode.Add(0x04); + return this; + } + + public BytecodeBuilder dup() + { + bytecode.Add(0x05); + return this; + } + + public BytecodeBuilder ipushc(long c) + { + bytecode.Add(0x06); + bytecode.AddRange(BitConverter.GetBytes(c)); + return this; + } + + public BytecodeBuilder ipushreg(short reg) + { + bytecode.Add(0x07); + bytecode.AddRange(BitConverter.GetBytes(reg)); + return this; + } + + public BytecodeBuilder ipopreg(short reg) + { + bytecode.Add(0x08); + bytecode.AddRange(BitConverter.GetBytes(reg)); + return this; + } + + public BytecodeBuilder jmp(Label label) + { + bytecode.Add(0x09); + bytecode.AddRange(BitConverter.GetBytes(LabelOffset(label))); + return this; + } + + public BytecodeBuilder jz(Label label) + { + bytecode.Add(0x0A); + bytecode.AddRange(BitConverter.GetBytes(LabelOffset(label))); + return this; + } + + public BytecodeBuilder jnz(Label label) + { + bytecode.Add(0x0B); + bytecode.AddRange(BitConverter.GetBytes(LabelOffset(label))); + return this; + } + + public BytecodeBuilder icmp() + { + bytecode.Add(0x0C); + return this; + } + + public BytecodeBuilder eq() + { + bytecode.Add(0x0D); + return this; + } + + public BytecodeBuilder print() + { + bytecode.Add(0x0E); + return this; + } + + public BytecodeBuilder label(Label label) + { + if (fixups.ContainsKey(label)) + { + DoFixups(fixups[label], bytecode.Count); + fixups.Remove(label); + } + + labels.Add(label, bytecode.Count); + + return this; + } + } +} diff --git a/dwarf-cs/dwarf.core/Label.cs b/dwarf-cs/dwarf.core/Label.cs new file mode 100644 index 0000000..6c4aac0 --- /dev/null +++ b/dwarf-cs/dwarf.core/Label.cs @@ -0,0 +1,6 @@ +namespace dwarf.core +{ + public class Label + { + } +} diff --git a/dwarf-cs/dwarf.core/Properties/AssemblyInfo.cs b/dwarf-cs/dwarf.core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..07695e0 --- /dev/null +++ b/dwarf-cs/dwarf.core/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("dwarf.core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("dwarf.core")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("71dc51e8-79d2-4c7f-8de8-c2ff5c0fb6fd")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dwarf-cs/dwarf.core/Vm.cs b/dwarf-cs/dwarf.core/Vm.cs new file mode 100644 index 0000000..feb760d --- /dev/null +++ b/dwarf-cs/dwarf.core/Vm.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace dwarf.core +{ + public class Vm + { + private byte[] bytecode; + private readonly StringWriter printer; + + private Stack stack = new Stack(); + private bool halted; + private int pc; + + public Vm(IEnumerable bytecode, StringWriter printer) + { + this.bytecode = bytecode.ToArray(); + this.printer = printer; + } + + public void Run() + { + pc = 0; + halted = false; + + while (!halted) + { + var instruction = bytecode[pc++]; + + switch (instruction) + { + case 0x00: + halted = true; + break; + + case 0x01: + stack.Push(stack.Pop() + stack.Pop()); + break; + + case 0x02: + { + var b = stack.Pop(); + var a = stack.Pop(); + + stack.Push(a - b); + break; + } + + case 0x03: + stack.Push(stack.Pop() * stack.Pop()); + break; + + case 0x05: + stack.Push(stack.Peek()); + break; + + case 0x06: + stack.Push(BitConverter.ToInt64(bytecode, pc)); + pc += sizeof(long); + break; + + case 0x09: + { + var off = BitConverter.ToInt32(bytecode, pc); + pc += sizeof (int); + + pc = pc + off; + break; + } + + case 0x0A: + { + var off = BitConverter.ToInt32(bytecode, pc); + pc += sizeof (int); + + if (stack.Pop() == 0) + pc = pc + off; + break; + } + + case 0x0B: + { + var off = BitConverter.ToInt32(bytecode, pc); + pc += sizeof(int); + + if (stack.Pop() != 0) + pc = pc + off; + break; + } + + case 0x0D: + { + var a = stack.Pop(); + var b = stack.Pop(); + + stack.Push(a == b ? 1 : 0); + + break; + } + + case 0x0E: + printer.WriteLine(stack.Pop()); + break; + } + } + } + } +} diff --git a/dwarf-cs/dwarf.core/dwarf.core.csproj b/dwarf-cs/dwarf.core/dwarf.core.csproj new file mode 100644 index 0000000..3e327cd --- /dev/null +++ b/dwarf-cs/dwarf.core/dwarf.core.csproj @@ -0,0 +1,58 @@ + + + + + Debug + AnyCPU + {FD95899A-79B4-4424-A531-92F0935AC39C} + Library + Properties + dwarf.core + dwarf.core + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dwarf-cs/dwarf.core/lang/Lexer.cs b/dwarf-cs/dwarf.core/lang/Lexer.cs new file mode 100644 index 0000000..1fbfd74 --- /dev/null +++ b/dwarf-cs/dwarf.core/lang/Lexer.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace dwarf.core.lang +{ + interface ITokenRule + { + bool Check(char c, int pos); + Token CreateToken(string src); + } + + internal class IdentifierRule : ITokenRule + { + public bool Check(char c, int pos) + { + return pos == 0 ? Char.IsLetter(c) : Char.IsLetterOrDigit(c); + } + + public Token CreateToken(string src) + { + return new IdentifierToken(src); + } + } + + internal class KeywordRule : ITokenRule + { + private string keyword; + + public KeywordRule(string keyword) + { + this.keyword = keyword; + } + + public bool Check(char c, int pos) + { + return pos < keyword.Length && keyword[pos] == c; + } + + public Token CreateToken(string src) + { + return new KeywordToken(src); + } + } + + internal class NumberRule : ITokenRule + { + public bool Check(char c, int pos) + { + return Char.IsNumber(c); + } + + public Token CreateToken(string src) + { + return new ConstantToken(long.Parse(src)); + } + } + + internal class SignedNumberRule : ITokenRule + { + public bool Check(char c, int pos) + { + if (pos == 0) + return c == '-' || c == '+'; + else + return Char.IsNumber(c); + } + + public Token CreateToken(string src) + { + long c; + + // FIXME: Это по сути затычка для ситуации, когда src не является + // правильной константой, но check возвращает true потому, + // что проверяет только один символ. По сути это ошибка + // дизайна. + + if (Int64.TryParse(src, out c)) + { + return new ConstantToken(c); + } + else + { + return null; + } + } + } + + internal class WhitespaceRule : ITokenRule + { + public bool Check(char c, int pos) + { + return Char.IsWhiteSpace(c); + } + + public Token CreateToken(string src) + { + return new WhitespaceToken(); + } + } + + internal class Matcher + { + private ITokenRule rule; + private string matched; + + public Token Token { get; private set; } + public bool Stop { get; private set; } + + public int MatchedLen + { + get { return matched.Length; } + } + + public Matcher(ITokenRule rule) + { + this.rule = rule; + } + + public void Reset() + { + Stop = false; + matched = String.Empty; + Token = null; + } + + public void Update(char c) + { + if (Stop) + return; + + if (rule.Check(c, matched.Length)) + { + matched += c; + } + else + { + Stop = true; + } + + if (Stop && matched.Length > 0) + { + Token = rule.CreateToken(matched); + } + } + } + + public class Lexer + { + private static readonly ITokenRule[] Rules = + { + new WhitespaceRule(), + + new NumberRule(), + new SignedNumberRule(), + + new KeywordRule("if"), + new KeywordRule("then"), + new KeywordRule("else"), + + new KeywordRule("+"), + new KeywordRule("-"), + new KeywordRule("*"), + new KeywordRule("/"), + + new KeywordRule("while"), + + new KeywordRule("{"), + new KeywordRule("}"), + + new KeywordRule("("), + new KeywordRule(")"), + + new KeywordRule("=="), + + new KeywordRule(";"), + + new KeywordRule(":="), + + new IdentifierRule(), + }; + + public IEnumerable Tokenize(string source) + { + var matchers = Rules.Select(rule => new Matcher(rule)).ToArray(); + + var state = 0; + var pos = 0; + var tok = ""; + + // HACK + source += (char) 0xffff; + + while (pos < source.Length) + { + if (state == 0) + { + foreach (var matcher in matchers) + { + matcher.Reset(); + } + + tok = ""; + state = 1; + } + + foreach (var matcher in matchers) + { + matcher.Update(source[pos]); + } + + Token token = null; + + if (matchers.All(m => m.Stop)) + { + int len = 0; + + foreach (var matcher in matchers) + { + if (matcher.Token != null && matcher.MatchedLen > len) + { + token = matcher.Token; + len = matcher.MatchedLen; + } + } + + if (token == null && source[pos] != 0xffff) + { + tok += source[pos]; + throw new Exception(String.Format("Unknown token: {0}", tok)); + } + } + + if (token != null) + { + state = 0; + yield return token; + } + else + { + tok += source[pos]; + pos ++; + } + } + } + } +} diff --git a/dwarf-cs/dwarf.core/lang/Tokens.cs b/dwarf-cs/dwarf.core/lang/Tokens.cs new file mode 100644 index 0000000..882794d --- /dev/null +++ b/dwarf-cs/dwarf.core/lang/Tokens.cs @@ -0,0 +1,59 @@ +namespace dwarf.core.lang +{ + public class Token + { + } + + public class KeywordToken : Token + { + private string keyword; + + public KeywordToken(string keyword) + { + this.keyword = keyword; + } + + public override string ToString() + { + return keyword; + } + } + + public class IdentifierToken : Token + { + private string name; + + public IdentifierToken(string name) + { + this.name = name; + } + + public override string ToString() + { + return string.Format("[{0}]", name); + } + } + + public class ConstantToken : Token + { + private long value; + + public ConstantToken(long value) + { + this.value = value; + } + + public override string ToString() + { + return string.Format("<{0}>", value); + } + } + + public class WhitespaceToken : Token + { + public override string ToString() + { + return " "; + } + } +} diff --git a/dwarf-cs/tests/BytecodeBuilderTests.cs b/dwarf-cs/tests/BytecodeBuilderTests.cs new file mode 100644 index 0000000..854768b --- /dev/null +++ b/dwarf-cs/tests/BytecodeBuilderTests.cs @@ -0,0 +1,186 @@ +using dwarf.core; +using NUnit.Framework; + +namespace tests +{ + [TestFixture] + public class BytecodeBuilderTests + { + [Test] + public void HaltTest() + { + var builder = new BytecodeBuilder(); + + builder + .halt(); + + var expexted = new byte[] + { + 0x00 // HALT + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void OperationsTest() + { + var builder = new BytecodeBuilder(); + + builder + .iadd() + .isub() + .imul() + .idiv(); + + var expexted = new byte[] + { + 0x01, // IADD + 0x02, // ISUB + 0x03, // IMUL + 0x04 // IDIV + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void ConstTest() + { + var builder = new BytecodeBuilder(); + + builder + .ipushc(0x0102030405060708); + + var expexted = new byte[] + { + 0x06, + 0x08, 0x07, 0x06, 0x05, + 0x04, 0x03, 0x02, 0x01 // IPUSHC 0x0102030405060708 + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void JmpTest() + { + var builder = new BytecodeBuilder(); + + var loop = new Label(); + var next = new Label(); + + builder + .label(loop) + .jmp(loop) + .jmp(next) + .label(next); + + var expexted = new byte[] + { + 0x09, 0xFB, 0xFF, 0xFF, 0xFF, // JMP -5 + 0x09, 0x00, 0x00, 0x00, 0x00 // JMP 0 + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void JzTest() + { + var builder = new BytecodeBuilder(); + + var loop = new Label(); + var next = new Label(); + + builder + .label(loop) + .jz(loop) + .jz(next) + .label(next); + + var expexted = new byte[] + { + 0x0A, 0xFB, 0xFF, 0xFF, 0xFF, // JZ -5 + 0x0A, 0x00, 0x00, 0x00, 0x00 // JZ 0 + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void JnzTest() + { + var builder = new BytecodeBuilder(); + + var loop = new Label(); + var next = new Label(); + + builder + .label(loop) + .jnz(loop) + .jnz(next) + .label(next); + + var expexted = new byte[] + { + 0x0B, 0xFB, 0xFF, 0xFF, 0xFF, // JNZ -5 + 0x0B, 0x00, 0x00, 0x00, 0x00 // JNZ 0 + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void PushPopRegTest() + { + var builder = new BytecodeBuilder(); + + builder + .ipushreg(0x01) + .ipopreg(0x02); + + var expexted = new byte[] + { + 0x07, 0x01, 0x00, // IPUSHREG 1 + 0x08, 0x02, 0x00 // IPOPREG 2 + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void IcmpEqTest() + { + var builder = new BytecodeBuilder(); + + builder + .icmp() + .eq(); + + var expexted = new byte[] + { + 0x0C, // ICMP + 0x0D // EQ + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + + [Test] + public void PrintTest() + { + var builder = new BytecodeBuilder(); + + builder + .print(); + + var expexted = new byte[] + { + 0x0E // PRINT + }; + + Assert.AreEqual(expexted, builder.Bytecode); + } + } +} diff --git a/dwarf-cs/tests/LexerTests.cs b/dwarf-cs/tests/LexerTests.cs new file mode 100644 index 0000000..0f92235 --- /dev/null +++ b/dwarf-cs/tests/LexerTests.cs @@ -0,0 +1,85 @@ +using System; +using dwarf.core.lang; +using NUnit.Framework; + +namespace tests +{ + [TestFixture] + public class LexerTests : TestBase + { + [Test] + public void ExpressionsTest() + { + var source = Multiline( + "a + b", + "a - b", + "a / b", + "a * b"); + + var lexer = new Lexer(); + var actual = String.Join("", lexer.Tokenize(source)); + + const string expected = "[a] + [b] " + + "[a] - [b] " + + "[a] / [b] " + + "[a] * [b] "; + + Assert.AreEqual(expected, actual); + } + + [Test] + public void IfThenElseTest() + { + var source = Multiline( + "if (a == b) then {", + " a := a - b;", + "} else {", + " a := 0;", + "}"); + + var lexer = new Lexer(); + var actual = String.Join("", lexer.Tokenize(source)); + + const string expected = "if ([a] == [b]) then { " + + "[a] := [a] - [b]; " + + "} else { " + + "[a] := <0>; " + + "} "; + + Assert.AreEqual(expected, actual); + } + + [Test] + public void AmbiguousIdentifiersTest() + { + var source = Multiline( + "if iff"); + + var lexer = new Lexer(); + var actual = String.Join("", lexer.Tokenize(source)); + + const string expected = "if [iff] "; + + Assert.AreEqual(expected, actual); + } + + [Test] + public void ConstTest() + { + var source = Multiline( + "a := 10", + "a := -12", + "a := +13" + ); + + var lexer = new Lexer(); + var actual = String.Join("", lexer.Tokenize(source)); + + const string expected = "[a] := <10> " + + "[a] := <-12> " + + "[a] := <13> "; + + Assert.AreEqual(expected, actual); + } + } +} diff --git a/dwarf-cs/tests/Properties/AssemblyInfo.cs b/dwarf-cs/tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ca6dc4b --- /dev/null +++ b/dwarf-cs/tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("tests")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("281b1730-55eb-4d41-ba05-00b4cd105478")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/dwarf-cs/tests/TestBase.cs b/dwarf-cs/tests/TestBase.cs new file mode 100644 index 0000000..2165edb --- /dev/null +++ b/dwarf-cs/tests/TestBase.cs @@ -0,0 +1,19 @@ +using System.Text; + +namespace tests +{ + public class TestBase + { + protected static string Multiline(params string[] lines) + { + var builder = new StringBuilder(); + + foreach (var line in lines) + { + builder.AppendLine(line); + } + + return builder.ToString(); + } + } +} diff --git a/dwarf-cs/tests/VmTests.cs b/dwarf-cs/tests/VmTests.cs new file mode 100644 index 0000000..d12168a --- /dev/null +++ b/dwarf-cs/tests/VmTests.cs @@ -0,0 +1,182 @@ +using System.IO; +using dwarf.core; +using NUnit.Framework; + +namespace tests +{ + [TestFixture] + public class VmTests:TestBase + { + [Test] + public void Test1() + { + var builder = new BytecodeBuilder(); + + builder + .ipushc(1) + .ipushc(2) + .iadd() + .print() + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("3"), printer.ToString()); + } + + [Test] + public void Test2() + { + var builder = new BytecodeBuilder(); + + builder + .ipushc(1) + .dup() + .isub() + .print() + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("0"), printer.ToString()); + } + + [Test] + public void Test3() + { + var builder = new BytecodeBuilder(); + + builder + .ipushc(3) + .ipushc(5) + .imul() + .print() + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("15"), printer.ToString()); + } + + [Test] + public void Test4() + { + var builder = new BytecodeBuilder(); + + var l1 = new Label(); + + builder + .ipushc(1) + .jz(l1) + .ipushc(10) + .print() + + .label(l1) + .ipushc(20) + .print() + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("10", "20"), printer.ToString()); + } + + [Test] + public void Test5() + { + var builder = new BytecodeBuilder(); + + var l1 = new Label(); + var l2 = new Label(); + var l3 = new Label(); + + builder + .jmp(l2) + + .label(l1) + .ipushc(10) + .print() + .jmp(l3) + + .label(l2) + .jmp(l1) + + .label(l3) + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("10"), printer.ToString()); + } + + [Test] + public void Test6() + { + var builder = new BytecodeBuilder(); + + builder + .ipushc(10) + .ipushc(10) + .eq() + .print() + + .ipushc(10) + .ipushc(11) + .eq() + .print() + + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("1", "0"), printer.ToString()); + } + + [Test] + public void Test7() + { + var builder = new BytecodeBuilder(); + + var loop = new Label(); + + builder + .ipushc(10) + + .label(loop) + .dup() + .print() + + .ipushc(1) + .isub() + .dup() + .jnz(loop) + + .halt(); + + var printer = new StringWriter(); + var vm = new Vm(builder.Bytecode, printer); + + vm.Run(); + + Assert.AreEqual(Multiline("10", "9", "8", "7", "6", "5", "4", "3", "2", "1"), printer.ToString()); + } + } +} diff --git a/dwarf-cs/tests/packages.config b/dwarf-cs/tests/packages.config new file mode 100644 index 0000000..5a3253f --- /dev/null +++ b/dwarf-cs/tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dwarf-cs/tests/tests.csproj b/dwarf-cs/tests/tests.csproj new file mode 100644 index 0000000..cc49c30 --- /dev/null +++ b/dwarf-cs/tests/tests.csproj @@ -0,0 +1,68 @@ + + + + + Debug + AnyCPU + {E48C4A1A-A404-466F-9F66-5D9A9C80896B} + Library + Properties + tests + tests + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + {FD95899A-79B4-4424-A531-92F0935AC39C} + dwarf.core + + + + + \ No newline at end of file