private static LineOfCode ParseStackInstruction(LineOfCode loc, string[] fragments) { if (fragments.Length < 2) { loc.Error = "Stack instructions must include a segment"; } else if (fragments.Length < 3) { loc.Error = "Stack instructions must include a value"; } else { if (!Enum.TryParse(fragments[1], true, out Segment s)) { loc.Error = $"Segment '{fragments[1]}' not recognised"; } else { loc.Segment = s; if (SetValue(loc, fragments[2])) { if (loc.Instruction == InstructionType.Pop && loc.Segment == Segment.Constant) { loc.Error = "pop cannot be performed on a constant"; } else if (loc.Segment == Segment.Pointer && loc.Value > 1) { loc.Error = "pointer value can only be 0 or 1"; } } } } return(loc); }
private LineOfCode ParseFunctionInstruction(LineOfCode loc, string[] fragments) { if (loc.Instruction == InstructionType.Function || loc.Instruction == InstructionType.Call) { if (fragments.Length < 2) { loc.Error = "Function must have a name"; } else { loc.FunctionName = fragments[1]; if (fragments.Length < 3) { loc.Error = loc.Instruction == InstructionType.Function ? "Function must specify the number of local variables" : "Call must specify the number of arguments"; } else { SetValue(loc, fragments[2]); } } } if (loc.Instruction == InstructionType.Return && fragments.Length > 1) { loc.Error = "Return cannot have a name"; } if (loc.Instruction == InstructionType.Function) { functionName = loc.FunctionName; } if (loc.Instruction == InstructionType.Return) { loc.FunctionName = functionName; } return(loc); }
public LineOfCode Parse(string line) { lineNumber++; line = StripComments(line); if (IsCommentOrWhitespace(line)) { return(null); } string[] fragments = line.Split(' '); LineOfCode loc = ParseInstruction(fragments, line); switch (loc.Category) { case InstructionCategory.Branching: return(ParseBranchingInstruction(loc, fragments)); case InstructionCategory.Stack: return(ParseStackInstruction(loc, fragments)); case InstructionCategory.Function: return(ParseFunctionInstruction(loc, fragments)); case InstructionCategory.Arithmetic: case InstructionCategory.Logical: return(ParseArithmeticOrLogicalInstruction(loc, fragments)); default: return(ParseUnrecognisedInstruction(loc, fragments)); } }
private static LineOfCode ParseArithmeticOrLogicalInstruction(LineOfCode loc, string[] fragments) { if (fragments.Length > 1) { loc.Error = "Arithmetic instructions cannot include any additional arguments"; } return(loc); }
private static bool SetValue(LineOfCode loc, string s) { if (!int.TryParse(s, out int v)) { loc.Error = $"Value '{s}' is not a valid integer"; return(false); } loc.Value = v; return(true); }
private string Merge(string template, LineOfCode loc) { return(template .Replace("{vmcode}", loc.VmCode) .Replace("{functionname}", loc.FunctionName) .Replace("{label}", loc.Label) .Replace("{linenumber}", loc.LineNumber.ToString()) .Replace("{value}", loc.Value.ToString()) .Replace("{segment}", GetRamForSegment(loc)) .Replace("{filename}", loc.FileName)); }
private LineOfCode ParseInstruction(string[] fragments, string line) { LineOfCode loc = new LineOfCode { VmCode = line, LineNumber = lineNumber, FileName = fileName }; if (Enum.TryParse(fragments[0].Replace("-", ""), true, out InstructionType c)) { loc.Instruction = c; } return(loc); }
private static LineOfCode ParseBranchingInstruction(LineOfCode loc, string[] fragments) { if (fragments.Length < 2) { loc.Error = "Branching instructions must have a label"; } else { loc.Label = fragments[1]; } return(loc); }
private static string GetPopTemplate(LineOfCode loc) { switch (loc.Segment) { case Segment.Pointer: return(loc.Value == 0 ? popToPointer0 : popToPointer1); case Segment.Static: return(popToStatic); case Segment.Temp: return(popToTemp); default: return(popToSegment); } }
private static string GetPushTemplate(LineOfCode loc) { switch (loc.Segment) { case Segment.Constant: return(pushConstant); case Segment.Pointer: return(loc.Value == 0 ? pushFromPointer0 : pushFromPointer1); case Segment.Static: return(pushFromStatic); case Segment.Temp: return(pushFromTemp); default: return(pushFromSegment); } }
private string GetRamForSegment(LineOfCode loc) { switch (loc.Segment) { case Segment.Argument: return("ARG"); case Segment.Local: return("LCL"); case Segment.This: return("THIS"); case Segment.That: return("THAT"); case Segment.Static: return($"{loc.FileName}"); default: return(null); } }
private static LineOfCode ParseUnrecognisedInstruction(LineOfCode loc, string[] fragments) { loc.Error = $"Command '{fragments[0]}' not recognised"; return(loc); }
public string Translate(LineOfCode loc) { switch (loc.Instruction) { case InstructionType.Push: string push = GetPushTemplate(loc); return(Merge(push, loc)); case InstructionType.Pop: string pop = GetPopTemplate(loc); return(Merge(pop, loc)); case InstructionType.Add: return(add); case InstructionType.Sub: return(sub); case InstructionType.And: return(and); case InstructionType.Or: return(or); case InstructionType.Neg: return(neg); case InstructionType.Not: return(not); case InstructionType.Eq: return(Merge(eq, loc)); case InstructionType.Lt: return(Merge(lt, loc)); case InstructionType.Gt: return(Merge(gt, loc)); case InstructionType.Label: return(Merge(label, loc)); case InstructionType.Goto: return(Merge(goTo, loc)); case InstructionType.IfGoto: return(Merge(ifGoto, loc)); case InstructionType.Function: if (loc.Value == 0) { return(Merge(functionNoArgs, loc)); } if (loc.Value == 1) { return(Merge(function1Arg, loc)); } return(Merge(functionMultipleArgs, loc)); case InstructionType.Return: return(Merge(functionReturn, loc)); case InstructionType.Call: return(Merge(call, loc)); default: return(null); } }