Skip to content

Commit 3010e63

Browse files
committed
Changed Assembler interfacing to use AssemblyResult
1 parent 8773be0 commit 3010e63

File tree

15 files changed

+176
-119
lines changed

15 files changed

+176
-119
lines changed

src/Apps/Mipser.ViewModels/Services/Build/BuildService.cs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,23 @@ public async Task AssembleFilesAsync(BindableFile[] files)
9090
}
9191

9292
// Assemble the file
93-
var assembler = await AssembleFileAsync(file, null, saveFile);
93+
var result = await AssembleFileAsync(file, null, saveFile);
9494

9595
// Restore the original language
9696
CultureInfo.CurrentUICulture = restore;
9797

9898
// Check the assembler result
99-
if (assembler is null)
99+
if (result is null)
100100
continue; // TODO: Handle file loading errors
101101

102102
// Update the status and log
103-
var assemblerFailed = assembler?.Failed ?? false;
104-
failed = failed || (assembler?.Failed ?? false);
103+
var assemblerFailed = result?.Failed ?? false;
104+
failed = failed || (result?.Failed ?? false);
105105
Status = failed ? BuildStatus.Failing : BuildStatus.Assembling;
106-
if (assembler?.Logs is not null)
107-
logs.AddRange(assembler.Logs);
106+
if (result?.Logs is not null)
107+
logs.AddRange(result.Logs);
108108

109-
_messenger.Send(new FileAssembledMessage(file.Path, saveFile?.Path, assemblerFailed, assembler?.Logs));
109+
_messenger.Send(new FileAssembledMessage(file.Path, saveFile?.Path, assemblerFailed, result?.Logs));
110110
}
111111

112112
// Send a message with the build results.
@@ -117,7 +117,7 @@ public async Task AssembleFilesAsync(BindableFile[] files)
117117
await WaitAndClearStatus();
118118
}
119119

120-
private static async Task<Assembler?> AssembleFileAsync(BindableFile file, AssemblerConfig? config, BindableFile? saveLocation = null)
120+
private static async Task<AssemblyResult?> AssembleFileAsync(BindableFile file, RasmConfig? config, BindableFile? saveLocation = null)
121121
{
122122
config ??= new RasmConfig();
123123

@@ -126,21 +126,15 @@ public async Task AssembleFilesAsync(BindableFile[] files)
126126
if (stream is null)
127127
return null;
128128

129-
// Assemble the file
130-
var assembler = await Assembler.AssembleAsync(stream, file.Path, config);
131-
132-
// Return if no save file is provided
133-
if (saveLocation is null)
134-
return assembler;
135-
136-
// Open save file for
137-
stream = await saveLocation.GetWriteStreamAsync();
138-
if (stream is null)
139-
return null;
129+
// Open save location stream
130+
Stream? saveStream = null;
131+
if (saveLocation is not null)
132+
{
133+
saveStream = await saveLocation.GetWriteStreamAsync();
134+
}
140135

141-
// TODO: Support other module formats
142-
assembler.CompleteModule<RasmModule>(stream);
143-
return assembler;
136+
// Assemble the file
137+
return await Assembler.AssembleAsync<RasmModule, RasmConfig>(stream, file.Path, config, saveStream);
144138
}
145139

146140
private bool PreBuildChecks()

src/Apps/Mipser/Program.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,12 @@ private static async Task Run(string filePath)
5454

5555
var inFile = File.Open(filePath, FileMode.Open);
5656
var outFile = File.Open(resultFile, FileMode.Create);
57-
var assembler = await Assembler.AssembleAsync(inFile, filePath, new RasmConfig());
57+
var assembler = await Assembler.AssembleAsync<RasmModule, RasmConfig>(inFile, filePath, new RasmConfig(), outFile);
5858

5959
Console.WriteLine(!assembler.Failed
6060
? $"Assembled with {assembler.Logs.Count} messages."
6161
: $"Failed to assemble with {assembler.Logs.Count} messages.");
6262

63-
if (!assembler.Failed)
64-
{
65-
assembler.CompleteModule<RasmModule>(outFile);
66-
}
67-
6863
Console.WriteLine();
6964

7065
if (assembler.Logs.Count > 0)

src/Formats/ELF.Modules/ElfModule.cs

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace ELF.Modules;
1313
/// <summary>
1414
/// A fully assembled object module in ELF format
1515
/// </summary>
16-
public class ElfModule : IBuildModule<ElfModule>
16+
public class ElfModule : IBuildModule<ElfModule, ElfConfig>
1717
{
1818
/// <summary>
1919
/// Initializes a new instance of the <see cref="ElfModule"/> class.
@@ -27,28 +27,21 @@ public ElfModule(string name)
2727
public string? Name { get; }
2828

2929
/// <inheritdoc/>
30-
public unsafe static ElfModule? Create(ModuleConstructor constructor, AssemblerConfig config, Stream? stream = null)
30+
public unsafe static ElfModule? Create(ModuleConstructor constructor, ElfConfig config, Stream? stream = null)
3131
{
3232
stream ??= new MemoryStream();
3333

34-
// Verify
35-
if (config is not ElfConfig elfConfig)
36-
{
37-
ThrowHelper.ThrowArgumentException(nameof(config), $"{config} must be an {nameof(ElfConfig)}.");
38-
return null;
39-
}
40-
4134
// Ensure 32-bit class
4235
// TODO: Support creating 64-bit elf.
43-
if (elfConfig.ElfIdentity.Class is not Class.Bit32)
36+
if (config.ElfIdentity.Class is not Class.Bit32)
4437
{
45-
ThrowHelper.ThrowArgumentException(nameof(elfConfig.ElfIdentity.Class), $"Configuration class must be 32bit for MIPS.");
38+
ThrowHelper.ThrowArgumentException(nameof(config.ElfIdentity.Class), $"Configuration class must be 32bit for MIPS.");
4639
}
4740

4841
// Allocate space for header
4942
var header = new Header<uint>()
5043
{
51-
Identity = elfConfig.ElfIdentity,
44+
Identity = config.ElfIdentity,
5245
HeaderSize = (ushort)sizeof(Header<uint>),
5346
};
5447
header.TryWrite(stream);
@@ -67,7 +60,7 @@ public ElfModule(string name)
6760

6861
throw new NotImplementedException();
6962
}
70-
63+
7164
/// <inheritdoc/>
7265
public static ElfModule? Load(string name, Stream stream)
7366
{

src/Formats/ObjectFiles/ElfModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace ObjectFiles;
1313
/// <summary>
1414
/// An object module in ELF format.
1515
/// </summary>
16-
public class ElfModule : IBuildModule<ElfModule>, IExecutableModule
16+
public class ElfModule : IBuildModule<ElfModule, AssemblerConfig>, IExecutableModule
1717
{
1818
private readonly ElfFile _elfFile;
1919

src/Formats/RASM.Modules/RasmModule.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace RASM.Modules;
1919
/// <summary>
2020
/// A fully assembled object module in RASM format.
2121
/// </summary>
22-
public class RasmModule : IBuildModule<RasmModule>, IExecutableModule
22+
public class RasmModule : IBuildModule<RasmModule, RasmConfig>, IExecutableModule
2323
{
2424
private const int SECTION_COUNT = 6;
2525

@@ -50,16 +50,10 @@ public RasmModule(string? name, Header header, Stream source)
5050
public string? Name { get; }
5151

5252
/// <inheritdoc/>
53-
public static RasmModule? Create(ModuleConstructor constructor, AssemblerConfig config, Stream? stream = null)
53+
public static RasmModule? Create(ModuleConstructor constructor, RasmConfig config, Stream? stream = null)
5454
{
5555
stream ??= new MemoryStream();
5656

57-
if (config is not RasmConfig rasmConfig)
58-
{
59-
ThrowHelper.ThrowArgumentException(nameof(config), $"{config} must be a {nameof(RasmConfig)}.");
60-
return null;
61-
}
62-
6357
// TODO: Flags and entry point properly
6458
// TODO: Construct string list.
6559

@@ -75,7 +69,7 @@ public RasmModule(string? name, Header header, Stream source)
7569
0, 0, 0, 0
7670
};
7771

78-
var header = new Header(rasmConfig.MagicNumber, rasmConfig.VersionNumber, 0, 0, sizes);
72+
var header = new Header(config.MagicNumber, config.VersionNumber, 0, 0, sizes);
7973
header.TryWrite(stream);
8074

8175
// Append segments to stream

src/Formats/Raw.Modules/RawModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Raw.Modules;
1010
/// <summary>
1111
/// A raw wrapper format for a module that contains the raw binary data of the assembled program.
1212
/// </summary>
13-
public class RawModule : IBuildModule<RawModule>, IExecutableModule
13+
public class RawModule : IBuildModule<RawModule, AssemblerConfig>, IExecutableModule
1414
{
1515
private readonly Stream _source;
1616

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Avishai Dernis 2025
2+
3+
using MIPS.Assembler.Models;
4+
using MIPS.Assembler.Models.Modules.Interfaces;
5+
using System.IO;
6+
using System.Threading.Tasks;
7+
8+
namespace MIPS.Assembler;
9+
10+
public partial class Assembler
11+
{
12+
/// <summary>
13+
/// Assembles a string.
14+
/// </summary>
15+
public static async Task<AssemblyResult> AssembleAsync(string str, string? filename, AssemblerConfig config)
16+
{
17+
using var reader = new StringReader(str);
18+
var assembler = await AssembleAsync(reader, filename, config);
19+
return new AssemblyResult(assembler.Failed, assembler.Logs);
20+
}
21+
22+
/// <summary>
23+
/// Assembles a string.
24+
/// </summary>
25+
public static async Task<AssemblyResult<T>> AssembleAsync<T, TConfig>(string str, string? filename, TConfig config, Stream? outStream = null)
26+
where T : IBuildModule<T, TConfig>
27+
where TConfig : AssemblerConfig
28+
{
29+
using var reader = new StringReader(str);
30+
var assembler = await AssembleAsync(reader, filename, config);
31+
var obj = T.Create(assembler._module, config, outStream);
32+
return new AssemblyResult<T>(obj, assembler.Failed, assembler.Logs);
33+
}
34+
35+
/// <summary>
36+
/// Assembles a stream.
37+
/// </summary>
38+
public static async Task<AssemblyResult> AssembleAsync(Stream stream, string? filename, AssemblerConfig config)
39+
{
40+
using var reader = new StreamReader(stream);
41+
var assembler = await AssembleAsync(reader, filename, config);
42+
return new AssemblyResult(assembler.Failed, assembler.Logs);
43+
}
44+
45+
/// <summary>
46+
/// Assembles a stream.
47+
/// </summary>
48+
public static async Task<AssemblyResult<T>> AssembleAsync<T, TConfig>(Stream stream, string? filename, TConfig config, Stream? outStream = null)
49+
where T : IBuildModule<T, TConfig>
50+
where TConfig : AssemblerConfig
51+
{
52+
using var reader = new StreamReader(stream);
53+
var assembler = await AssembleAsync(reader, filename, config);
54+
var obj = T.Create(assembler._module, config, outStream);
55+
return new AssemblyResult<T>(obj, assembler.Failed, assembler.Logs);
56+
}
57+
}

src/MIPS.Assembler/Assembler.cs

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System.Collections.Generic;
1212
using System.IO;
1313
using System.Linq;
14+
using System.Net.Http.Headers;
1415
using System.Threading.Tasks;
1516

1617
namespace MIPS.Assembler;
@@ -59,12 +60,12 @@ private Assembler(AssemblerConfig config)
5960
/// <summary>
6061
/// Gets the assembler context for this assembler instance.
6162
/// </summary>
62-
public AssemblerContext Context { get; }
63+
internal AssemblerContext Context { get; }
6364

6465
/// <summary>
6566
/// Gets the assembler's configuration.
6667
/// </summary>
67-
public AssemblerConfig Config { get; }
68+
internal AssemblerConfig Config { get; }
6869

6970
/// <summary>
7071
/// Gets the current address.
@@ -89,29 +90,11 @@ internal Address CurrentAddress
8990
/// Gets whether or not the assembler failed to assemble a valid module.
9091
/// </summary>
9192
public bool Failed => _logger.Failed;
92-
93-
/// <summary>
94-
/// Assembles an object module from a stream of assembly.
95-
/// </summary>
96-
public static async Task<Assembler> AssembleAsync(string str, string? filename, AssemblerConfig config)
97-
{
98-
using var reader = new StringReader(str);
99-
return await AssembleAsync(reader, filename, config);
100-
}
101-
102-
/// <summary>
103-
/// Assembles an object module from a stream of assembly.
104-
/// </summary>
105-
public static async Task<Assembler> AssembleAsync(Stream stream, string? filename, AssemblerConfig config)
106-
{
107-
using var reader = new StreamReader(stream);
108-
return await AssembleAsync(reader, filename, config);
109-
}
11093

11194
/// <summary>
11295
/// Assembles an object module from a stream of assembly.
11396
/// </summary>
114-
public static async Task<Assembler> AssembleAsync(TextReader reader, string? filename, AssemblerConfig config)
97+
private static async Task<Assembler> AssembleAsync(TextReader reader, string? filename, AssemblerConfig config)
11598
{
11699
var assembler = new Assembler(config);
117100
var tokens = await Tokenizer.TokenizeAsync(reader, filename);
@@ -130,13 +113,4 @@ public static async Task<Assembler> AssembleAsync(TextReader reader, string? fil
130113

131114
return assembler;
132115
}
133-
134-
/// <summary>
135-
/// Writes the assembled module to a module stream of the provided format.
136-
/// </summary>
137-
/// <typeparam name="T">The format of module to write.</typeparam>
138-
/// <param name="stream">The stream to write the module to.</param>
139-
/// <returns>The module object.</returns>
140-
public T? CompleteModule<T>(Stream stream)
141-
where T : IBuildModule<T> => T.Create(_module, Config, stream);
142116
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Avishai Dernis 2025
2+
3+
using MIPS.Assembler.Logging;
4+
using System.Collections.Generic;
5+
6+
namespace MIPS.Assembler.Models;
7+
8+
/// <summary>
9+
/// A class containing the results of an assembler run.
10+
/// </summary>
11+
public class AssemblyResult
12+
{
13+
internal AssemblyResult(bool failed, IReadOnlyList<AssemblerLog> logs)
14+
{
15+
Failed = failed;
16+
Logs = logs;
17+
}
18+
19+
/// <summary>
20+
/// Gets whether or not the assembly failed.
21+
/// </summary>
22+
public bool Failed { get; }
23+
24+
/// <summary>
25+
/// Gets the assmembly log.
26+
/// </summary>
27+
public IReadOnlyList<AssemblerLog> Logs { get; }
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Avishai Dernis 2025
2+
3+
using MIPS.Assembler.Logging;
4+
using MIPS.Assembler.Models.Modules.Interfaces;
5+
using System.Collections.Generic;
6+
7+
namespace MIPS.Assembler.Models;
8+
9+
/// <summary>
10+
/// A <see cref="AssemblyResult"/> including the constructed object module.
11+
/// </summary>
12+
/// <typeparam name="TObject">The object module type.</typeparam>
13+
public class AssemblyResult<TObject> : AssemblyResult
14+
where TObject : IBuildModule
15+
{
16+
internal AssemblyResult(TObject? objectModule, bool failed, IReadOnlyList<AssemblerLog> logs)
17+
: base(failed, logs)
18+
{
19+
ObjectModule = objectModule;
20+
}
21+
22+
internal AssemblyResult(TObject? objectModule, AssemblyResult childResult)
23+
: this(objectModule, childResult.Failed, childResult.Logs)
24+
{
25+
}
26+
27+
/// <summary>
28+
/// Gets the object module result of the assembly.
29+
/// </summary>
30+
public TObject? ObjectModule { get; }
31+
}

0 commit comments

Comments
 (0)