using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.IO; using Cosmos.Assembler; namespace XSharp.Compiler { // This class performs the translation from X# source code into a target // assembly language. At current time the only supported assembler syntax is NASM. public class AsmGenerator { protected TokenPatterns mPatterns = new TokenPatterns(); /// Should we keep the user comments in the generated target assembly program ? public bool EmitUserComments = false; protected int mLineNo = 0; protected string mPathname = ""; /// Invoke this method when end of source code file is reached to make sure the last /// function or interrupt handler has well balanced opening/closing curly braces. private void AssertLastFunctionComplete() { if (!mPatterns.InFunctionBody) { return; } throw new Exception("The last function or interrupt handler from source code file is missing a curly brace."); } /// Parse the input X# source code file and generate the matching target assembly /// language. /// X# source code reader. /// The resulting target assembler content. The returned object contains /// a code and a data block. public Assembler Generate(TextReader aReader) { if (aReader == null) { throw new ArgumentNullException(nameof(aReader)); } mPatterns.EmitUserComments = EmitUserComments; mLineNo = 0; var xResult = new Assembler(); try { // Read one X# source code line at a time and process it. while (true) { mLineNo++; string xLine = aReader.ReadLine(); if (xLine == null) { break; } ProcessLine(xLine, mLineNo); } AssertLastFunctionComplete(); return xResult; } finally { Assembler.ClearCurrentInstance(); } } /// Parse the input X# source code file and generate the matching target assembly /// language. /// X# source code file. /// The resulting target assembler content. The returned object contains /// a code and a data block. public Assembler Generate(string aSrcPathname) { try { using (var xInput = new StreamReader(aSrcPathname)) { return Generate(xInput); } } catch (Exception E) { throw new Exception("Error while generating output for file " + Path.GetFileName(aSrcPathname), E); } } /// Parse the input X# source code file and generate two new files with target /// assembly language. The two generated files contain target assembler source and target /// assembler data respectively. /// X# source code file. public void GenerateToFiles(string aSrcPathname) { mPathname = Path.GetFileName(aSrcPathname); new Assembler(false); try { using (var xInput = new StreamReader(aSrcPathname)) { using (var xOutput = new StreamWriter(Path.ChangeExtension(aSrcPathname, ".asm"))) { xOutput.WriteLine("; Generated at {0}", DateTime.Now.ToString(new CultureInfo("en-US"))); Generate(xInput, xOutput); } } } finally { Assembler.ClearCurrentInstance(); } } /// Parse the input X# source code from the given reader and write both target /// assembler code and target assembler data in their respective writers. /// A reader to acquire X# source code from. /// A writer that will receive target assembler data. /// A writer that will receive target assembler code. public void Generate(TextReader aInput, TextWriter aOutput) { mPatterns.EmitUserComments = EmitUserComments; mLineNo = 0; // Read one X# source code line at a time and process it. while (true) { mLineNo++; string xLine = aInput.ReadLine(); if (xLine == null) { break; } ProcessLine(xLine, mLineNo); } Assembler.CurrentInstance.FlushText(aOutput); AssertLastFunctionComplete(); } /// Process a single X# source code line and translate it into the target /// assembler syntax. /// The processed X# source code line. /// Line number for debugging and diagnostic messages. /// The resulting target assembler content. The returned object contains /// a code and a data block. protected void ProcessLine(string aLine, int lineNumber) { aLine = aLine.Trim(); if (String.IsNullOrEmpty(aLine) || aLine == "//") { return; } // Currently we use a new assembler for every line. // If we dont it could create a really large in memory object. if (!mPatterns.GetCode(aLine, lineNumber)) { var xMsg = new StringBuilder(); if (mPathname != "") { xMsg.Append("File " + mPathname + ", "); } xMsg.Append("Line " + mLineNo + ", "); xMsg.Append("Parsing error: " + aLine); throw new Exception(xMsg.ToString()); } } } }