private static void Main(string[] args) { if (args == null || args.Length < 1) { var msg = "Expected one argument. The path to a directory with *.vm files"; throw new ArgumentException(msg); } var root = args[0]; if (!Directory.Exists(root)) { var msg = $"directory {root} does not exist"; throw new FileNotFoundException(msg); } var paths = Directory.EnumerateFiles(root, "*.vm", SearchOption.AllDirectories); var dirName = Path.GetFileName(root); var outFile = Path.Combine(root, $"{dirName}.asm"); using (var ctx = new Context(File.CreateText(outFile))) { ctx.Writer.WriteLine("@256"); ctx.Writer.WriteLine("D=A"); ctx.Writer.WriteLine("@SP"); ctx.Writer.WriteLine("M=D"); var sysInit = Path.Combine(root, "Sys.vm"); if (File.Exists(sysInit)) { string EntryPoint = "Sys.Init".ToUpper(); ctx.CurrentFunction = new FunctionContext(EntryPoint, 0); ctx.InputFilePath = "Sys.vm"; AssemblyWriter.EmitCall(ctx, EntryPoint, 0); } foreach (var path in paths) { Console.WriteLine($"processing {path}"); var inputFile = Path.GetFileName(path); ctx.InputFilePath = inputFile; var callbacks = new ParserCallBacks { OnPop = (segment, index) => AssemblyWriter.EmitPop(ctx, segment, index), OnPush = (segment, index) => AssemblyWriter.EmitPush(ctx, segment, index), OnArithmeticCommand = command => AssemblyWriter.EmitOperator(ctx.Writer, command), OnGoTo = label => AssemblyWriter.EmitGoTo(ctx, label), OnIfGoTo = label => AssemblyWriter.EmitIfGoTo(ctx, label), OnLabelDeclaration = label => AssemblyWriter.EmitLabel(ctx, label), OnCall = (command, n) => AssemblyWriter.EmitCall(ctx, command.ToUpper(), n), OnFuncDef = (functionName, n) => { var name = functionName.ToUpper(); ctx.CurrentFunction = new FunctionContext(name, n); AssemblyWriter.EmitFunction(ctx, name, n); }, OnReturn = () => { AssemblyWriter.EmitReturn(ctx); }, }; Parser.Parse(File.ReadAllLines(path), callbacks); ctx.Writer.Flush(); } } }
public void TestParser2() { //const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\FunctionCalls\NestedCall"; //const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\ProgramFlow\BasicLoop"; const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\ProgramFlow\FibonacciSeries"; //const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\FunctionCalls\SimpleFunction"; //const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\FunctionCalls\FibonacciElement"; //const string root = @"C:\Users\Dean.Pavlovsky\Projects\nand2tetris\08\FunctionCalls\StaticsTest"; var paths = Directory.EnumerateFiles(root, "*.vm", SearchOption.AllDirectories); //var paths = new[] //{ // $@"{root}\MemoryAccess\BasicTest\BasicTest.vm", // $@"{root}\MemoryAccess\PointerTest\PointerTest.vm", // $@"{root}\MemoryAccess\StaticTest\StaticTest.vm", // $@"{root}\StackArithmetic\SimpleAdd\SimpleAdd.vm", // $@"{root}\StackArithmetic\StackTest\StackTest.vm" //}; var dirName = Path.GetFileName(root); var outFile = Path.Combine(root, $"{dirName}.asm"); using (var ctx = new Context(File.CreateText(outFile) /*new StringWriter(sb)*/)) { ctx.Writer.WriteLine("@256"); ctx.Writer.WriteLine("D=A"); ctx.Writer.WriteLine("@SP"); ctx.Writer.WriteLine("M=D"); var sysInit = Path.Combine(root, "Sys.vm"); if (File.Exists(sysInit)) { string EntryPoint = "Sys.Init".ToUpper(); ctx.CurrentFunction = new FunctionContext(EntryPoint, 0); ctx.InputFilePath = "Sys.vm"; AssemblyWriter.EmitCall(ctx, EntryPoint, 0); } foreach (var path in paths) { Console.WriteLine($"processing {path}"); var inputFile = Path.GetFileName(path); ctx.InputFilePath = inputFile; var callbacks = new ParserCallBacks { OnPop = (segment, index) => AssemblyWriter.EmitPop(ctx, segment, index), OnPush = (segment, index) => AssemblyWriter.EmitPush(ctx, segment, index), OnArithmeticCommand = command => AssemblyWriter.EmitOperator(ctx.Writer, command), OnGoTo = label => AssemblyWriter.EmitGoTo(ctx, label), OnIfGoTo = label => AssemblyWriter.EmitIfGoTo(ctx, label), OnLabelDeclaration = label => AssemblyWriter.EmitLabel(ctx, label), OnCall = (command, n) => AssemblyWriter.EmitCall(ctx, command.ToUpper(), n), //Console.WriteLine($">> call {command},{n}"), OnFuncDef = (functionName, n) => { var name = functionName.ToUpper(); ctx.CurrentFunction = new FunctionContext(name, n); AssemblyWriter.EmitFunction(ctx, name, n); }, OnReturn = () => { AssemblyWriter.EmitReturn(ctx); //ctx.CurrentFunction = null; }, }; Parser.Parse(File.ReadAllLines(path), callbacks); ctx.Writer.Flush(); } } }
public static void Parse(IEnumerable <string> lines, ParserCallBacks callbacks) { IEnumerable <string> filteredLines = from line in lines where !string.IsNullOrWhiteSpace(line) select line.Trim(); foreach (string item in filteredLines) { string[] elems = item.Split((string[])null, StringSplitOptions.RemoveEmptyEntries); if (elems.Length != 0) { string opcode = elems.First(); if (!opcode.StartsWith("//")) { switch (opcode) { case "add": callbacks.OnArithmeticCommand(Operator.Add); break; case "sub": callbacks.OnArithmeticCommand(Operator.Sub); break; case "neg": callbacks.OnArithmeticCommand(Operator.Neg); break; case "eq": callbacks.OnArithmeticCommand(Operator.Eq); break; case "gt": callbacks.OnArithmeticCommand(Operator.Gt); break; case "lt": callbacks.OnArithmeticCommand(Operator.Lt); break; case "and": callbacks.OnArithmeticCommand(Operator.And); break; case "or": callbacks.OnArithmeticCommand(Operator.Or); break; case "not": callbacks.OnArithmeticCommand(Operator.Not); break; case "push": if (elems.Length < 3) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnPush(ParseMemorySegment(elems[1]), short.Parse(elems[2])); break; case "pop": if (elems.Length < 3) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnPop(ParseMemorySegment(elems[1]), short.Parse(elems[2])); break; case "label": if (elems.Length < 2 || string.IsNullOrWhiteSpace(elems[1])) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnLabelDeclaration(elems[1].Trim()); break; case "goto": if (elems.Length < 2 || string.IsNullOrWhiteSpace(elems[1])) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnGoTo(elems[1].Trim()); break; case "if-goto": if (elems.Length < 2 || string.IsNullOrWhiteSpace(elems[1])) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnIfGoTo(elems[1].Trim()); break; case "function": if (elems.Length < 3 || string.IsNullOrWhiteSpace(elems[1]) || string.IsNullOrWhiteSpace(elems[2])) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnFuncDef(elems[1].Trim(), short.Parse(elems[2])); break; case "call": if (elems.Length < 3 || string.IsNullOrWhiteSpace(elems[1]) || string.IsNullOrWhiteSpace(elems[2])) { throw new ArgumentException($"invalid line {item}"); } callbacks.OnCall(elems[1].Trim(), short.Parse(elems[2])); break; case "return": callbacks.OnReturn(); break; default: throw new NotImplementedException($"Can't parse {item}"); } } } } }