handle more edge cases

This commit is contained in:
jjanzen 2025-02-27 17:40:58 -06:00
parent a316be56e2
commit 4af81abcc1
2 changed files with 124 additions and 188 deletions

View file

@ -240,6 +240,16 @@ pub const Parser = struct {
const SymbolError = error{
UnexpectedSymbol,
ForwardReference0,
ForwardReference1,
ForwardReference2,
ForwardReference3,
ForwardReference4,
ForwardReference5,
ForwardReference6,
ForwardReference7,
ForwardReference8,
ForwardReference9,
};
/// Determine whether the cursor points at a symbol
@ -270,8 +280,25 @@ pub const Parser = struct {
}
/// Get the number associated with a given symbol
fn handleSymbol(self: *Parser, symbol: []const u8) SymbolError!NumberValue {
// TODO: handle xH, XF, xB
fn readSymbol(self: *Parser, symbol: []const u8) SymbolError!NumberValue {
if (symbol.len == 2 and symbol[0] >= '0' and symbol[0] <= '9' and symbol[1] != 'B') {
if (symbol[1] != 'F') return SymbolError.UnexpectedSymbol;
return switch (symbol[0]) {
'0' => SymbolError.ForwardReference0,
'1' => SymbolError.ForwardReference1,
'2' => SymbolError.ForwardReference2,
'3' => SymbolError.ForwardReference3,
'4' => SymbolError.ForwardReference4,
'5' => SymbolError.ForwardReference5,
'6' => SymbolError.ForwardReference6,
'7' => SymbolError.ForwardReference7,
'8' => SymbolError.ForwardReference8,
'9' => SymbolError.ForwardReference9,
else => unreachable,
};
}
const n = self.symbols.get(symbol);
if (n == null) return SymbolError.UnexpectedSymbol;
return n.?;
@ -289,7 +316,7 @@ pub const Parser = struct {
NoEndingDelimiter,
UnaryOperationOnString,
BinaryOperationOnString,
InvalidOperationOnRegister,
IllegalOperationOnRegister,
DivisionByZero,
FractionalDivisionOversizedNumerator,
};
@ -300,16 +327,23 @@ pub const Parser = struct {
var result: u64 = 0;
var last_op = WeakOp.none;
var done = false;
var started = false;
var register_result: ?u8 = null;
var string_result: ?[]const u8 = null;
while (!done) {
if (string_result != null) return ExpressionError.BinaryOperationOnString;
if (register_result != null) return ExpressionError.InvalidOperationOnRegister;
if (register_result != null) return ExpressionError.IllegalOperationOnRegister;
const term = try self.identifyTerm();
switch (term) {
.string => string_result = term.string,
.string => {
if (!started) {
string_result = term.string;
} else {
return ExpressionError.BinaryOperationOnString;
}
},
.number => |nv| {
switch (nv) {
.pure => |n| {
@ -321,7 +355,13 @@ pub const Parser = struct {
WeakOp.none => n,
};
},
.register => register_result = nv.register,
.register => {
if (!started) {
register_result = nv.register;
} else {
return ExpressionError.IllegalOperationOnRegister;
}
},
}
},
}
@ -339,6 +379,8 @@ pub const Parser = struct {
} else {
self.ch_pos += 1;
}
started = true;
}
if (string_result != null) {
@ -373,7 +415,7 @@ pub const Parser = struct {
var done = false;
while (!done) {
if (string_result != null) return ExpressionError.BinaryOperationOnString;
if (register_result != null) return ExpressionError.InvalidOperationOnRegister;
if (register_result != null) return ExpressionError.IllegalOperationOnRegister;
const primary = try self.identifyPrimary();
@ -415,7 +457,7 @@ pub const Parser = struct {
if (!started) {
register_result = nv.register;
} else {
return ExpressionError.InvalidOperationOnRegister;
return ExpressionError.IllegalOperationOnRegister;
}
},
}
@ -485,7 +527,7 @@ pub const Parser = struct {
const symbol = try self.identifySymbol();
if (symbol.len != 2) return SymbolError.UnexpectedSymbol;
if (symbol[1] == 'H') return SymbolError.UnexpectedSymbol;
const symbol_val = try self.handleSymbol(symbol);
const symbol_val = try self.readSymbol(symbol);
return ExpressionResult{ .number = symbol_val };
}
@ -555,7 +597,7 @@ pub const Parser = struct {
},
else => {
const symbol = try self.identifySymbol();
const symbol_value = try self.handleSymbol(symbol);
const symbol_value = try self.readSymbol(symbol);
return ExpressionResult{ .number = symbol_value };
},
}
@ -574,8 +616,8 @@ pub const Parser = struct {
return opcodes.parseOp(self.allocator, self.input[start..end]);
}
pub fn init(allocator: std.mem.Allocator, input: []const u8) Parser {
return Parser{
pub fn init(allocator: std.mem.Allocator, input: []const u8) !Parser {
var p = Parser{
.allocator = allocator,
.input = input,
.location = 0,
@ -583,6 +625,19 @@ pub const Parser = struct {
.symbols = std.StringHashMap(NumberValue).init(allocator),
.object = std.ArrayList(u8).init(allocator),
};
try p.symbols.put("0B", NumberValue{ .pure = 0 });
try p.symbols.put("1B", NumberValue{ .pure = 0 });
try p.symbols.put("2B", NumberValue{ .pure = 0 });
try p.symbols.put("3B", NumberValue{ .pure = 0 });
try p.symbols.put("4B", NumberValue{ .pure = 0 });
try p.symbols.put("5B", NumberValue{ .pure = 0 });
try p.symbols.put("6B", NumberValue{ .pure = 0 });
try p.symbols.put("7B", NumberValue{ .pure = 0 });
try p.symbols.put("8B", NumberValue{ .pure = 0 });
try p.symbols.put("9B", NumberValue{ .pure = 0 });
return p;
}
pub fn deinit(self: *Parser) void {
@ -635,7 +690,7 @@ test "symbols are identified" {
};
for (0..6) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = try parser.identifySymbol();
try std.testing.expect(std.mem.eql(u8, expected[i], symbol));
parser.deinit();
@ -650,7 +705,7 @@ test "no symbols are found successfully" {
};
for (test_cases) |case| {
var parser = Parser.init(std.testing.allocator, case);
var parser = try Parser.init(std.testing.allocator, case);
const symbol = parser.identifySymbol();
try std.testing.expectEqual(error.UnexpectedSymbol, symbol);
parser.deinit();
@ -675,7 +730,7 @@ test "opcodes are identified" {
};
for (0..5) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const op = try parser.identifyOperation();
try std.testing.expectEqual(expected[i], op);
parser.deinit();
@ -691,7 +746,7 @@ test "no opcodes are found successfully" {
};
for (test_cases) |case| {
var parser = Parser.init(std.testing.allocator, case);
var parser = try Parser.init(std.testing.allocator, case);
const symbol = parser.identifyOperation();
try std.testing.expectEqual(error.NoOpcode, symbol);
parser.deinit();
@ -712,7 +767,7 @@ test "decimals are recognized" {
};
for (0..3) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = try parser.identifyDecimal();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -735,7 +790,7 @@ test "malformed decimals are not recognized" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = parser.identifyDecimal();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -756,7 +811,7 @@ test "hexadecimals are recognized" {
};
for (0..3) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = try parser.identifyHexadecimal();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -781,7 +836,7 @@ test "malformed hexadecimals are not recognized" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = parser.identifyHexadecimal();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -802,7 +857,7 @@ test "characters are recognized" {
};
for (0..3) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = try parser.identifyChar();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -829,7 +884,7 @@ test "invalid unicode sequences are not characters" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = parser.identifyChar();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -848,7 +903,7 @@ test "strings are recognized" {
};
for (0..2) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = try parser.identifyString();
try std.testing.expect(std.mem.eql(u8, expected[i], symbol));
parser.deinit();
@ -871,7 +926,7 @@ test "invalid strings are not recognized" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
const symbol = parser.identifyString();
try std.testing.expectEqual(expected[i], symbol);
parser.deinit();
@ -894,7 +949,7 @@ test "constants are recognized" {
};
for (0..4) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = try parser.identifyConstant();
switch (symbol) {
@ -918,7 +973,7 @@ test "invalid constants are recognized" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = parser.identifyConstant();
try std.testing.expectEqual(expected[i], symbol);
@ -949,7 +1004,7 @@ test "basic primaries are identified" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = try parser.identifyPrimary();
switch (symbol) {
@ -973,7 +1028,7 @@ test "invalid primaries are detected" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = parser.identifyPrimary();
try std.testing.expectEqual(expected[i], symbol);
@ -1006,7 +1061,7 @@ test "valid terms are recognized" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = try parser.identifyTerm();
@ -1031,7 +1086,7 @@ test "terms compute the entire chain" {
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = try parser.identifyTerm();
try std.testing.expectEqual(expected[i], symbol);
@ -1049,10 +1104,10 @@ test "strong operations do not work on registers" {
};
for (test_cases) |case| {
var parser = Parser.init(std.testing.allocator, case);
var parser = try Parser.init(std.testing.allocator, case);
defer parser.deinit();
const symbol = parser.identifyTerm();
try std.testing.expectEqual(Parser.ExpressionError.InvalidOperationOnRegister, symbol);
try std.testing.expectEqual(Parser.ExpressionError.IllegalOperationOnRegister, symbol);
}
}
@ -1063,7 +1118,7 @@ test "strong operations do not work on strings" {
};
for (test_cases) |case| {
var parser = Parser.init(std.testing.allocator, case);
var parser = try Parser.init(std.testing.allocator, case);
defer parser.deinit();
const symbol = parser.identifyTerm();
try std.testing.expectEqual(Parser.ExpressionError.BinaryOperationOnString, symbol);
@ -1079,9 +1134,10 @@ test "expressions are recognized" {
"#AA^#FF",
"5*5+5",
"5*5+5*5",
"#ab<<32+#cdef00&~(#cdef00-1)",
"#ab<<32+k&~(k-1)",
"\"hello\"",
"$12",
"$$12",
};
const expected = [_]ExpressionResult{
@ -1095,11 +1151,15 @@ test "expressions are recognized" {
ExpressionResult{ .number = NumberValue{ .pure = 0xab00000100 } },
ExpressionResult{ .string = "hello" },
ExpressionResult{ .number = NumberValue{ .register = 12 } },
ExpressionResult{ .number = NumberValue{ .register = 12 } },
};
for (0..test_cases.len) |i| {
var parser = Parser.init(std.testing.allocator, test_cases[i]);
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
try parser.symbols.put("k", NumberValue{ .pure = 0xcdef00 });
const symbol = try parser.identifyExpression();
switch (symbol) {
@ -1108,3 +1168,32 @@ test "expressions are recognized" {
}
}
}
test "weak operations cannot be applied to strings and registers" {
const test_cases = [_][]const u8{
"$1+$1",
"2-$2",
"$3|3",
"$4^$4",
"1+\"hello\"",
"\"hello\"-2",
"3|\"hello\"^4",
};
const expected = [_]Parser.ExpressionError{
Parser.ExpressionError.IllegalOperationOnRegister,
Parser.ExpressionError.IllegalOperationOnRegister,
Parser.ExpressionError.IllegalOperationOnRegister,
Parser.ExpressionError.IllegalOperationOnRegister,
Parser.ExpressionError.BinaryOperationOnString,
Parser.ExpressionError.BinaryOperationOnString,
Parser.ExpressionError.BinaryOperationOnString,
};
for (0..test_cases.len) |i| {
var parser = try Parser.init(std.testing.allocator, test_cases[i]);
defer parser.deinit();
const symbol = parser.identifyExpression();
try std.testing.expectEqual(expected[i], symbol);
}
}

View file

@ -1,153 +0,0 @@
x = [
"TRAP",
"FCMP",
"FUN",
"FEQL",
"FADD",
"FIX",
"FSUB",
"FIXU",
"FLOT",
"FLOTU",
"SFLOT",
"SFLOTU",
"FMUL",
"FCMPE",
"FUNE",
"FEQLE",
"FDIV",
"FSQRT",
"FREM",
"FINT",
"MUL",
"MULU",
"DIV",
"DIVU",
"ADD",
"ADDU",
"SUB",
"SUBU",
"2ADDU",
"4ADDU",
"8ADDU",
"16ADDU",
"CMP",
"CMPU",
"NEG",
"NEGU",
"SL",
"SLU",
"SR",
"SRU",
"BN",
"BZ",
"BP",
"BOD",
"BNN",
"BNZ",
"BNP",
"BEV",
"PBN",
"PBZ",
"PBP",
"PBOD",
"PBNN",
"PBNZ",
"PBNP",
"PBEV",
"CSN",
"CSZ",
"CSP",
"CSOD",
"CSNN",
"CSNZ",
"CSNP",
"CSEV",
"ZSN",
"ZSZ",
"ZSP",
"ZSOD",
"ZSNN",
"ZSNZ",
"ZSNP",
"ZSEV",
"LDB",
"LDBU",
"LDW",
"LDWU",
"LDT",
"LDTU",
"LDO",
"LDOU",
"LDSF",
"LDHT",
"CSWAP",
"LDUNC",
"LDVTS",
"PRELD",
"PREGO",
"GO",
"STB",
"STBU",
"STW",
"STWU",
"STT",
"STTU",
"STO",
"STOU",
"STSF",
"STHT",
"STCO",
"STUNC",
"SYNCD",
"PREST",
"SYNCID",
"PUSHGO",
"OR",
"ORN",
"NOR",
"XOR",
"AND",
"ANDN",
"NAND",
"NXOR",
"BDIF",
"WDIF",
"TDIF",
"ODIF",
"MUX",
"SADD",
"MOR",
"MXOR",
"SETH",
"SETMH",
"SETML",
"SETL",
"INCH",
"INCMH",
"INCML",
"INCL",
"ORH",
"ORMH",
"ORML",
"ORL",
"ANDNH",
"ANDNMH",
"ANDNML",
"ANDNL",
"JMP",
"PUSHJ",
"GETA",
"PUT",
"POP",
"RESUME",
"SAVE",
"UNSAVE",
"SYNC",
"SWYM",
"GET",
"TRIP",
]
for val in x:
print(f"Operation{{.opcode = Opcode.{val}}},")