/// <summary> /// Translates a Z80AsmParserErrorInfo instance into an error /// </summary> /// <param name="sourceItem"> /// Source file information, to allow the error to track the filename the error occurred in /// </param> /// <param name="error">Error information</param> private void ReportError(SourceFileItem sourceItem, Z80AsmParserErrorInfo error) { var errInfo = new AssemblerErrorInfo(sourceItem, error); Output.Errors.Add(errInfo); ReportScopeError(errInfo.ErrorCode); }
/// <summary> /// This method compiles the passed Z80 Assembly code into Z80 /// binary code. /// </summary> /// <param name="sourceItem"></param> /// <param name="sourceText">Source code text</param> /// <param name="options"> /// Compilation options. If null is passed, the compiler uses the /// default options /// </param> /// <exception cref="ArgumentNullException"></exception> /// <returns> /// Output of the compilation /// </returns> private AssemblerOutput DoCompile(SourceFileItem sourceItem, string sourceText, AssemblerOptions options = null) { // --- Init the compilation process if (sourceText == null) { throw new ArgumentNullException(nameof(sourceText)); } _options = options ?? new AssemblerOptions(); ConditionSymbols = new HashSet <string>(_options.PredefinedSymbols); CurrentModule = Output = new AssemblerOutput(sourceItem); CompareBins = new List <BinaryComparisonInfo>(); // --- Do the compilation phases if (!ExecuteParse(0, sourceItem, sourceText, out var lines) || !EmitCode(lines) || !FixupSymbols() || !CompareBinaries()) { // --- Compilation failed, remove segments Output.Segments.Clear(); } PreprocessedLines = lines; // --- Create symbol map Output.CreateSymbolMap(); return(Output); }
public AssemblerErrorInfo(SourceFileItem sourceItem, string errorCode, SourceLineBase line, params object[] parameters) { ErrorCode = errorCode; Line = line?.SourceLine ?? 0; Column = line?.FirstColumn ?? 0; Message = Errors.GetMessage(ErrorCode, parameters); Filename = sourceItem?.Filename; }
/// <summary> /// Retrieves task-related comments from the parsed lines. /// </summary> /// <param name="sourceItem">The file that has been parsed</param> /// <param name="lines">Parsed lines</param> private void StoreTasks(SourceFileItem sourceItem, IEnumerable <SourceLineBase> lines) { foreach (var line in lines) { if (line.DefinesTask) { _output.Tasks.Add(new AssemblerTaskInfo(line.TaskDescription, sourceItem.Filename, line.SourceLine)); } } }
public AssemblerErrorInfo(SourceFileItem sourceItem, Z80AsmParserErrorInfo syntaxErrorInfo) { var token = syntaxErrorInfo.Token.Trim(); ErrorCode = token.Length == 0 ? Errors.Z0101 : Errors.Z0100; Line = syntaxErrorInfo.SourceLine; Column = syntaxErrorInfo.Position; Message = Errors.GetMessage(ErrorCode, token); Filename = sourceItem.Filename; }
/// <summary> /// Adds the specified item to the "Includes" list /// </summary> /// <param name="childItem">Included source file item</param> /// <returns> /// True, if including the child item is OK; /// False, if the inclusion would create a circular reference, /// or the child is already is in the list /// </returns> public bool Include(SourceFileItem childItem) { var current = this; while (current != null) { if (string.Compare(current.Filename, childItem.Filename, StringComparison.OrdinalIgnoreCase) == 0) { return(false); } current = current.Parent; } if (ContainsInIncludeList(childItem)) { return(false); } Includes.Add(childItem); childItem.Parent = this; return(true); }
/// <summary> /// This method compiles the passed Z80 Assembly code into Z80 /// binary code. /// </summary> /// <param name="sourceItem"></param> /// <param name="sourceText">Source code text</param> /// <param name="options"> /// Compilation options. If null is passed, the compiler uses the /// default options /// </param> /// <exception cref="ArgumentNullException"></exception> /// <returns> /// Output of the compilation /// </returns> private AssemblerOutput DoCompile(SourceFileItem sourceItem, string sourceText, AssemblerOptions options = null) { // --- Init the compilation process if (sourceText == null) { throw new ArgumentNullException(nameof(sourceText)); } _options = options ?? new AssemblerOptions(); ConditionSymbols = new HashSet <string>(_options.PredefinedSymbols); _output = new AssemblerOutput(sourceItem); // --- Do the compilation phases if (!ExecuteParse(0, sourceItem, sourceText, out var lines) || !EmitCode(lines) || !FixupSymbols()) { // --- Compilation failed, remove segments _output.Segments.Clear(); } PreprocessedLines = lines; return(_output); }
/// <summary> /// Translates a Z80AsmParserErrorInfo instance into an error /// </summary> /// <param name="sourceItem"> /// Source file information, to allow the error to track the filename the error ocurred in /// </param> /// <param name="error">Error information</param> private void ReportError(SourceFileItem sourceItem, Z80AsmParserErrorInfo error) { _output.Errors.Add(new AssemblerErrorInfo(sourceItem, error)); }
/// <summary> /// Loads and parses the file according the the #include directive /// </summary> /// <param name="fileIndex">Include file index</param> /// <param name="incDirective">Directive with the file</param> /// <param name="sourceItem">Source file item</param> /// <param name="parsedLines">Collection of source code lines</param> private bool ApplyIncludeDirective(int fileIndex, IncludeDirective incDirective, SourceFileItem sourceItem, out List <SourceLineBase> parsedLines) { parsedLines = new List <SourceLineBase>(); // --- Check the #include directive var filename = incDirective.Filename.Trim(); if (filename.StartsWith("<") && filename.EndsWith(">")) { // TODO: System include file filename = filename.Substring(1, filename.Length - 2); } // --- Now, we have the file name, calculate the path if (sourceItem.Filename != NOFILE_ITEM) { // --- The file name is taken into account as relative var dirname = Path.GetDirectoryName(sourceItem.Filename) ?? string.Empty; filename = Path.Combine(dirname, filename); } // --- Check for file existence if (!File.Exists(filename)) { ReportError(Errors.Z0300, incDirective, filename); return(false); } var fi = new FileInfo(filename); var fullName = fi.FullName; // --- Check for repetition var childItem = new SourceFileItem(fullName); if (sourceItem.ContainsInIncludeList(childItem)) { ReportError(Errors.Z0302, incDirective, filename); return(false); } // --- Check for circular reference if (!sourceItem.Include(childItem)) { ReportError(Errors.Z0303, incDirective, filename); return(false); } // --- Now, add the included item to the output _output.SourceFileList.Add(childItem); // --- Read the include file string sourceText; try { sourceText = File.ReadAllText(filename); } catch (Exception ex) { ReportError(Errors.Z0301, incDirective, filename, ex.Message); return(false); } // --- Parse the file return(ExecuteParse(fileIndex, childItem, sourceText, out parsedLines)); }
/// <summary> /// Parses the source code passed to the compiler /// </summary> /// <param name="fileIndex">Source file index</param> /// <param name="sourceItem">Source file item</param> /// <param name="sourceText">Source text to parse</param> /// <param name="parsedLines"></param> /// <returns>True, if parsing was successful</returns> private bool ExecuteParse(int fileIndex, SourceFileItem sourceItem, string sourceText, out List <SourceLineBase> parsedLines) { // --- No lines has been parsed yet parsedLines = new List <SourceLineBase>(); // --- Parse all source codelines var inputStream = new AntlrInputStream(sourceText); var lexer = new Z80AsmLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new Z80AsmParser(tokenStream); var context = parser.compileUnit(); var visitor = new Z80AsmVisitor(); visitor.Visit(context); var visitedLines = visitor.Compilation; // --- Store any tasks defined by the user StoreTasks(sourceItem, visitedLines.Lines); // --- Collect syntax errors foreach (var error in parser.SyntaxErrors) { ReportError(sourceItem, error); } // --- Exit if there are any errors if (_output.ErrorCount != 0) { return(false); } // --- Now, process directives and the .model pragma var currentLineIndex = 0; var ifdefStack = new Stack <bool?>(); var processOps = true; parsedLines = new List <SourceLineBase>(); // --- Traverse through parsed lines var includeIndex = fileIndex; while (currentLineIndex < visitedLines.Lines.Count) { var line = visitedLines.Lines[currentLineIndex]; if (line is ModelPragma modelPragma) { ProcessModelPragma(modelPragma); } else if (line is IncludeDirective incDirective) { includeIndex++; // --- Parse the included file if (ApplyIncludeDirective(includeIndex, incDirective, sourceItem, out var includedLines)) { // --- Add the parse result of the include file to the result parsedLines.AddRange(includedLines); } } else if (line is Directive preProc) { ApplyDirective(preProc, ifdefStack, ref processOps); } else if (processOps) { line.FileIndex = fileIndex; parsedLines.Add(line); } currentLineIndex++; } // --- Check if all #if and #ifdef has a closing #endif tag if (ifdefStack.Count > 0 && visitedLines.Lines.Count > 0) { ReportError(Errors.Z0062, visitedLines.Lines.Last()); } return(_output.ErrorCount == 0); }
/// <summary> /// Parses the source code passed to the compiler /// </summary> /// <param name="fileIndex">Source file index</param> /// <param name="sourceItem">Source file item</param> /// <param name="sourceText">Source text to parse</param> /// <param name="parsedLines"></param> /// <returns>True, if parsing was successful</returns> private bool ExecuteParse(int fileIndex, SourceFileItem sourceItem, string sourceText, out List <SourceLineBase> parsedLines) { // --- No lines has been parsed yet parsedLines = new List <SourceLineBase>(); // --- Parse all source code lines var inputStream = new AntlrInputStream(sourceText); var lexer = new Z80AsmLexer(inputStream); var tokenStream = new CommonTokenStream(lexer); var parser = new Z80AsmParser(tokenStream); var context = parser.compileUnit(); var visitor = new Z80AsmVisitor(inputStream); visitor.Visit(context); var visitedLines = visitor.Compilation; // --- Store any tasks defined by the user StoreTasks(sourceItem, visitedLines.Lines); // --- Collect syntax errors foreach (var error in parser.SyntaxErrors) { ReportError(sourceItem, error); } // --- Exit if there are any errors if (Output.ErrorCount != 0) { return(false); } // --- Now, process directives and the .model pragma var currentLineIndex = 0; var ifdefStack = new Stack <bool?>(); var processOps = true; parsedLines = new List <SourceLineBase>(); var anyProcessed = false; // --- Traverse through parsed lines while (currentLineIndex < visitedLines.Lines.Count) { var line = visitedLines.Lines[currentLineIndex]; switch (line) { case ZxBasicPragma _: { if (anyProcessed) { ReportError(Errors.Z0450, line); break; } _options.UseCaseSensitiveSymbols = true; _options.ProcExplicitLocalsOnly = true; _options.FlexibleDefPragmas = true; CurrentModule = Output = new AssemblerOutput(sourceItem, true); Output.SourceType = "zxbasic"; anyProcessed = true; break; } case ModelPragma modelPragma: ProcessModelPragma(modelPragma); anyProcessed = true; break; case IncludeDirective incDirective: { // --- Parse the included file if (ApplyIncludeDirective(incDirective, sourceItem, out var includedLines)) { // --- Add the parse result of the include file to the result parsedLines.AddRange(includedLines); anyProcessed = true; } break; } case LineDirective lineDirective: // TODO: Process a #line directive break; case Directive preProc: ApplyDirective(preProc, ifdefStack, ref processOps); anyProcessed = true; break; default: { if (processOps) { line.FileIndex = fileIndex; line.MacroSourceText = sourceText.Substring(line.FirstPosition, line.LastPosition - line.FirstPosition + 1); parsedLines.Add(line); anyProcessed = true; } break; } } currentLineIndex++; } // --- Check if all #if and #ifdef has a closing #endif tag if (ifdefStack.Count > 0 && visitedLines.Lines.Count > 0) { ReportError(Errors.Z0062, visitedLines.Lines.Last()); } return(Output.ErrorCount == 0); }
/// <summary> /// Checks if this item already contains the specified child item in /// its "Includes" list /// </summary> /// <param name="childItem">Child item to check</param> /// <returns> /// True, if this item contains the child item; /// otherwise, false /// </returns> public bool ContainsInIncludeList(SourceFileItem childItem) => Includes.Any(c => string.Compare(c.Filename, childItem.Filename, StringComparison.OrdinalIgnoreCase) == 0);