forked from CosmosOS/Cosmos
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AsmGenerator.cs
161 lines (147 loc) · 5.71 KB
/
AsmGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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();
/// <summary>Should we keep the user comments in the generated target assembly program ?</summary>
public bool EmitUserComments = false;
protected int mLineNo = 0;
protected string mPathname = "";
/// <summary>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.</summary>
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.");
}
/// <summary>Parse the input X# source code file and generate the matching target assembly
/// language.</summary>
/// <param name="aReader">X# source code reader.</param>
/// <returns>The resulting target assembler content. The returned object contains
/// a code and a data block.</returns>
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();
}
}
/// <summary>Parse the input X# source code file and generate the matching target assembly
/// language.</summary>
/// <param name="aSrcPathname">X# source code file.</param>
/// <returns>The resulting target assembler content. The returned object contains
/// a code and a data block.</returns>
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);
}
}
/// <summary>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.</summary>
/// <param name="aSrcPathname">X# source code file.</param>
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();
}
}
/// <summary>Parse the input X# source code from the given reader and write both target
/// assembler code and target assembler data in their respective writers.</summary>
/// <param name="aInput">A reader to acquire X# source code from.</param>
/// <param name="aOutputData">A writer that will receive target assembler data.</param>
/// <param name="aOutputCode">A writer that will receive target assembler code.</param>
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();
}
/// <summary>Process a single X# source code line and translate it into the target
/// assembler syntax.</summary>
/// <param name="aLine">The processed X# source code line.</param>
/// <param name="lineNumber">Line number for debugging and diagnostic messages.</param>
/// <returns>The resulting target assembler content. The returned object contains
/// a code and a data block.</returns>
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());
}
}
}
}