void DoGoto(SourceLine line) { if (!line.OperandHasToken) { Assembler.Log.LogEntry(line, line.Instruction, "Destination not specified for \".goto\" directive."); return; } var gotoExp = line.OperandExpression; if (gotoExp.Equals(line.LabelName)) { Assembler.Log.LogEntry(line, line.Instruction, "Destination cannot be the same line as \".goto\" directive."); return; } var iterCopy = new RandomAccessIterator <SourceLine>(Assembler.LineIterator); iterCopy.Reset(); SourceLine currLine; while ((currLine = iterCopy.Skip(l => !l.LabelName.Equals(gotoExp))) != null) { if (currLine.InstructionName.Contains("=") || currLine.InstructionName.Equals(".equ") || currLine.Instruction.Equals(".let")) { Assembler.Log.LogEntry(line, line.Instruction, $"\"{gotoExp}\" is not a valid destination."); return; } if (iterCopy.Index >= Assembler.LineIterator.Index) { Assembler.LineIterator.FastForward(iterCopy.Index); } else { if (iterCopy.Index == 0) { Assembler.LineIterator.Reset(); } else { Assembler.LineIterator.Rewind(iterCopy.Index); } } return; } Assembler.Log.LogEntry(line, line.Instruction, $"Could not find destination \"{gotoExp}\"."); }
/// <summary> /// Parses the source string into a tokenized <see cref="SourceLine"/> collection. /// </summary> /// <param name="fileName">The source file's path/name.</param> /// <param name="source">The source string.</param> /// <returns>A collection of <see cref="SourceLine"/>s whose components are /// properly tokenized for further evaluation and assembly.</returns> /// <exception cref="ExpressionException"/> public static IEnumerable <SourceLine> Parse(string fileName, string source) { var iterator = new RandomAccessIterator <char>(source.ToCharArray()); Token rootParent, currentParent; Token token = null; Reset(); Token currentOpen = null; int currentLine = 1, lineNumber = currentLine; // lineIndex is the iterator index at the start of each line for purposes of calculating token // positions. sourceLindeIndex is the iterator index at the start of each new line // of source. Usually lineIndex and sourceLindeIndex are the same, but for those source lines // whose source code span multiple lines, they will be different. int lineIndex = -1, opens = 0, sourceLineIndex = lineIndex; var lines = new List <SourceLine>(); char previousChar = iterator.Current; while (iterator.GetNext() != EOF) { if (iterator.Current != NewLine && iterator.Current != ':' && iterator.Current != ';') { try { token = ParseToken(previousChar, token, iterator); if (token != null) { previousChar = iterator.Current; token.Parent = currentParent; token.Position = iterator.Index - lineIndex - token.Name.Length + 1; if (token.OperatorType == OperatorType.Open || token.OperatorType == OperatorType.Closed || token.OperatorType == OperatorType.Separator) { if (token.OperatorType == OperatorType.Open) { opens++; currentParent.AddChild(token); currentOpen = currentParent = token; AddBlankSeparator(); } else if (token.OperatorType == OperatorType.Closed) { if (currentOpen == null) { throw new ExpressionException(token, $"Missing opening for closure \"{token.Name}\""); } // check if matching ( to ) if (!Groups[currentOpen.Name].Equals(token.Name)) { throw new ExpressionException(token, $"Mismatch between \"{currentOpen.Name}\" in column {currentOpen.Position} and \"{token.Name}\""); } // go up the ladder currentOpen = currentParent = token.Parent = currentOpen.Parent; while (currentOpen != null && currentOpen.OperatorType != OperatorType.Open) { currentOpen = currentOpen.Parent; } opens--; } else { currentParent = currentParent.Parent; currentParent.AddChild(token); currentParent = token; } } else if (token.Type == TokenType.Instruction) { while (currentParent.Parent != rootParent) { currentParent = currentParent.Parent; } currentParent.AddChild(token); AddBlankSeparator(); AddBlankSeparator(); } else { currentParent.AddChild(token); } } } catch (ExpressionException ex) { Assembler.Log.LogEntry(fileName, lineNumber, ex.Position, ex.Message); } if (iterator.PeekNext() == NewLine) { iterator.MoveNext(); } } if (iterator.Current == ';') { _ = iterator.Skip(c => c != NewLine && (c != ':' || Assembler.Options.IgnoreColons) && c != EOF); } if (iterator.Current == NewLine || iterator.Current == ':' || iterator.Current == EOF) { previousChar = iterator.Current; /* A new source line is when: * 1. A line termination character (New Line, colon, EOF) is encountered * 2. And either there are no more characters left or the most recent token created * 3. Is not a binary operator nor it is a comma separator. */ var newLine = iterator.Current == EOF || (opens == 0 && (token == null || (token.OperatorType != OperatorType.Binary && token.OperatorType != OperatorType.Open && !token.Name.Equals(",") ) ) ); if (iterator.Current == NewLine) { currentLine++; } if (newLine) { var newSourceLine = new SourceLine(fileName, lineNumber, GetSourceLineSource(), rootParent.Children[0]); lines.Add(newSourceLine); if (Assembler.Options.WarnLeft && newSourceLine.Label != null && newSourceLine.Label.Position != 1) { Assembler.Log.LogEntry(newSourceLine, newSourceLine.Label, "Label is not at the beginning of the line.", false); } Reset(); lineNumber = currentLine; } else { token = null; } lineIndex = iterator.Index; if (newLine) { sourceLineIndex = iterator.Index; } } } if (currentOpen != null && currentOpen.OperatorType == OperatorType.Open) { Assembler.Log.LogEntry(fileName, 1, currentOpen.LastChild.Position, $"End of source reached without finding closing \"{Groups[currentOpen.Name]}\"."); } if (token != null) { lines.Add(new SourceLine(fileName, lineNumber, GetSourceLineSource(), rootParent.Children[0])); } return(lines); void AddBlankSeparator() { var sepToken = new Token() { Type = TokenType.Operator, OperatorType = OperatorType.Separator, Name = string.Empty, Position = token == null ? 1 : token.Position, Children = new List <Token>() }; currentParent.AddChild(sepToken); currentParent = sepToken; } string GetSourceLineSource() { if (iterator.Index > sourceLineIndex + 1) { return(source.Substring(sourceLineIndex + 1, iterator.Index - sourceLineIndex - 1)); } return(string.Empty); } void Reset() { currentParent = rootParent = new Token(); currentParent.Children = new List <Token>(); AddBlankSeparator(); AddBlankSeparator(); token = null; } }