Brass 3 takes some of Brass 2's ideas - ie, a user-extendable plugin-driven compiler - yet simplifies it quite a lot. The Brass 2 parser was pretty sophisticated, which meant that whilst it was based on very clean code trying to shoe-horn in additional functionality like a macro preprocessor (which modified the source) or various odd bits of syntax (like TASM's '.equ') was a nightmare and just didn't work.
The new parser is still light-years ahead of the original one in Brass (including much better operator support and functions), though!
There's a vast amount of stuff that needs to be implemented (modules, reusable labels, lots of missing directives) but at least the concept has stood up well thus far.
I've gone attribute-crazy (attributes are used in .NET to attach metadata to types/functions/&c) and so, for example, individual plugins can expose their documentation. From this you can get a nifty help viewer (that can be embedded into Latenite): Of course, command-line apps don't usually look very interesting, but I couldn't resist syntax-highlighting error reports (the compiler knows what each token is - a label, an operator, an instruction, a comment, a directive - so syntax highlighting is effectively built-in):
I post this to hopefully commit myself to the project, and also driesguldolf seems to want .while \ .loop functionality so at least this indicates that it's coming.
As far as plugins go, here's the source for the .incbin plugin:
Code: Select all
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.ComponentModel;
using Brass3;
using Brass3.Plugins;
using Brass3.Attributes;
namespace Core.Directives {
[Syntax(".incbin \"file\"")]
[Description("Insert all data from a binary file into the output at the current program counter position.")]
[Remarks("Use this to import precompiled resources from other sources into your project.")]
[CodeExample("MonsterSprite:\r\n.incbin \"Resources/Sprites/Monster.spr\"")]
[Category("Data")]
public class IncBin : IDirective {
public string[] Names { get { return new string[] { "incbin" }; } }
public string Name { get { return Names[0]; } }
public void Invoke(Compiler compiler, TokenisedSource source, int index, string directive) {
int[] Args = source.GetCommaDelimitedArguments(index + 1, 1);
if (!source.ExpressionIsStringConstant(Args[0])) throw new DirectiveArgumentException(source, "Filename expected.");
string Filename = compiler.ResolveFilename(source.GetExpressionStringConstant(Args[0], false));
if (!File.Exists(Filename)) throw new DirectiveArgumentException(source.Tokens[index], "File '" + Filename + "' not found.");
try {
using (FileStream FS = File.OpenRead(Filename)) {
switch (compiler.CurrentPass) {
case AssemblyPass.Pass1:
compiler.Labels.ProgramCounter.Value += FS.Length;
break;
case AssemblyPass.Pass2:
int Data = 0;
while ((Data = FS.ReadByte()) != -1) compiler.WriteOutput((byte)Data);
break;
}
}
} catch (Exception ex) {
throw new CompilerExpection(source, ex.Message);
}
}
}
}