This commit is contained in:
jjanzen 2025-03-04 20:40:26 -06:00
parent 4af81abcc1
commit 2ee94b9594
2 changed files with 94 additions and 78 deletions

View file

@ -15,10 +15,3 @@ pub fn main() !void {
try bw.flush(); // don't forget to flush!
}
test "simple test" {
var list = std.ArrayList(i32).init(std.testing.allocator);
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
try list.append(42);
try std.testing.expectEqual(@as(i32, 42), list.pop());
}

View file

@ -1,36 +1,6 @@
const std = @import("std");
const opcodes = @import("opcodes.zig");
/// A number's value can be pure or point to a register
const NumberType = enum {
pure,
register,
};
const NumberValue = union(NumberType) {
pure: u64,
register: u8,
};
/// An expression's result can be a NumberValue or a string
const ExpressionResultType = enum {
number,
string,
};
const ExpressionResult = union(ExpressionResultType) {
number: NumberValue,
string: []const u8,
};
/// A constant can be a number of a string
const ConstantType = enum {
number,
string,
};
const ConstantValue = union(ConstantType) {
number: u64,
string: []const u8,
};
/// The Parser reads a provided input and assembles it into MMIX object code
pub const Parser = struct {
allocator: std.mem.Allocator,
@ -84,6 +54,16 @@ pub const Parser = struct {
}
}
/// A number's value can be pure or point to a register
const NumberType = enum {
pure,
register,
};
const NumberValue = union(NumberType) {
pure: u64,
register: u8,
};
const DecimalError = std.fmt.ParseIntError;
/// Determine whether the cursor points at a valid integer in base 10
@ -213,6 +193,16 @@ pub const Parser = struct {
UnexpectedSymbol,
};
/// A constant can be a number of a string
const ConstantType = enum {
number,
string,
};
const ConstantValue = union(ConstantType) {
number: u64,
string: []const u8,
};
/// Determine whether the cursor points at a valid constant
/// The constant may be a string or a number
/// Move the cursor past the constant and return it
@ -321,6 +311,16 @@ pub const Parser = struct {
FractionalDivisionOversizedNumerator,
};
/// An expression's result can be a NumberValue or a string
const ExpressionResultType = enum {
number,
string,
};
const ExpressionResult = union(ExpressionResultType) {
number: NumberValue,
string: []const u8,
};
/// Determine whether the cursor points at a valid expression
/// Move the cursor past the expression, evaluate it, and return its value
fn identifyExpression(self: *Parser) ExpressionError!ExpressionResult {
@ -616,6 +616,19 @@ pub const Parser = struct {
return opcodes.parseOp(self.allocator, self.input[start..end]);
}
const LineContents = struct {
allocator: std.mem.Allocator,
symbol_text: ?[]const u8,
opcode: opcodes.Operation,
expressions: std.ArrayList(?ExpressionResult),
unresolved_expressions: std.AutoHashMap(usize, u4),
};
fn identifyLine(self: *Parser) !LineContents {
_ = self;
return error.NotImplemented;
}
pub fn init(allocator: std.mem.Allocator, input: []const u8) !Parser {
var p = Parser{
.allocator = allocator,
@ -626,6 +639,7 @@ pub const Parser = struct {
.object = std.ArrayList(u8).init(allocator),
};
// Populate the symbol table with backwards references
try p.symbols.put("0B", NumberValue{ .pure = 0 });
try p.symbols.put("1B", NumberValue{ .pure = 0 });
try p.symbols.put("2B", NumberValue{ .pure = 0 });
@ -646,6 +660,15 @@ pub const Parser = struct {
}
};
// ===========================================================================//
// _____ _____ _____ _____ _____ //
// |_ _| ___/ ___|_ _/ ___| //
// | | | |__ \ `--. | | \ `--. //
// | | | __| `--. \ | | `--. \ //
// | | | |___/\__/ / | | /\__/ / //
// \_/ \____/\____/ \_/ \____/ //
// ===========================================================================//
test "normal ascii characters are recognized as symbol chars" {
const chars = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM_";
@ -941,11 +964,11 @@ test "constants are recognized" {
"\"hello \"world",
};
const expected = [_]ConstantValue{
ConstantValue{ .number = 1234567890 },
ConstantValue{ .number = 0x1234567890abcdef },
ConstantValue{ .number = 'a' },
ConstantValue{ .string = "hello " },
const expected = [_]Parser.ConstantValue{
Parser.ConstantValue{ .number = 1234567890 },
Parser.ConstantValue{ .number = 0x1234567890abcdef },
Parser.ConstantValue{ .number = 'a' },
Parser.ConstantValue{ .string = "hello " },
};
for (0..4) |i| {
@ -992,15 +1015,15 @@ test "basic primaries are identified" {
"$123",
};
const expected = [_]ExpressionResult{
ExpressionResult{ .number = NumberValue{ .pure = 1234 } },
ExpressionResult{ .number = NumberValue{ .pure = 0 } },
ExpressionResult{ .number = NumberValue{ .pure = 'a' } },
ExpressionResult{ .string = "hello world" },
ExpressionResult{ .number = NumberValue{ .pure = 1234 } },
ExpressionResult{ .number = NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
ExpressionResult{ .number = NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
ExpressionResult{ .number = NumberValue{ .register = 123 } },
const expected = [_]Parser.ExpressionResult{
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 1234 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 'a' } },
Parser.ExpressionResult{ .string = "hello world" },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 1234 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .register = 123 } },
};
for (0..test_cases.len) |i| {
@ -1048,16 +1071,16 @@ test "valid terms are recognized" {
"\"hello\"",
};
const expected = [_]ExpressionResult{
ExpressionResult{ .number = NumberValue{ .pure = 408 } },
ExpressionResult{ .number = NumberValue{ .pure = 11 } },
ExpressionResult{ .number = NumberValue{ .pure = 12297829382473034410 } },
ExpressionResult{ .number = NumberValue{ .pure = 1 } },
ExpressionResult{ .number = NumberValue{ .pure = 4 } },
ExpressionResult{ .number = NumberValue{ .pure = 1 } },
ExpressionResult{ .number = NumberValue{ .pure = 0xAA } },
ExpressionResult{ .number = NumberValue{ .register = 12 } },
ExpressionResult{ .string = "hello" },
const expected = [_]Parser.ExpressionResult{
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 408 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 11 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 12297829382473034410 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 1 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 4 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 1 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xAA } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .register = 12 } },
Parser.ExpressionResult{ .string = "hello" },
};
for (0..test_cases.len) |i| {
@ -1079,10 +1102,10 @@ test "terms compute the entire chain" {
"3*3%8<<2>>1",
};
const expected = [_]ExpressionResult{
ExpressionResult{ .number = NumberValue{ .pure = 120 } },
ExpressionResult{ .number = NumberValue{ .pure = 3 } },
ExpressionResult{ .number = NumberValue{ .pure = 2 } },
const expected = [_]Parser.ExpressionResult{
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 120 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 3 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 2 } },
};
for (0..test_cases.len) |i| {
@ -1140,25 +1163,25 @@ test "expressions are recognized" {
"$$12",
};
const expected = [_]ExpressionResult{
ExpressionResult{ .number = NumberValue{ .pure = 3 } },
ExpressionResult{ .number = NumberValue{ .pure = 1 } },
ExpressionResult{ .number = NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
ExpressionResult{ .number = NumberValue{ .pure = 0xAA } },
ExpressionResult{ .number = NumberValue{ .pure = 0x55 } },
ExpressionResult{ .number = NumberValue{ .pure = 30 } },
ExpressionResult{ .number = NumberValue{ .pure = 50 } },
ExpressionResult{ .number = NumberValue{ .pure = 0xab00000100 } },
ExpressionResult{ .string = "hello" },
ExpressionResult{ .number = NumberValue{ .register = 12 } },
ExpressionResult{ .number = NumberValue{ .register = 12 } },
const expected = [_]Parser.ExpressionResult{
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 3 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 1 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xFFFFFFFFFFFFFFFF } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xAA } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0x55 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 30 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 50 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .pure = 0xab00000100 } },
Parser.ExpressionResult{ .string = "hello" },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .register = 12 } },
Parser.ExpressionResult{ .number = Parser.NumberValue{ .register = 12 } },
};
for (0..test_cases.len) |i| {
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
try parser.symbols.put("k", NumberValue{ .pure = 0xcdef00 });
try parser.symbols.put("k", Parser.NumberValue{ .pure = 0xcdef00 });
const symbol = try parser.identifyExpression();