From ca57f6bd77a1a4e02adf1468b914f3e0ac650aca Mon Sep 17 00:00:00 2001 From: miezekatze Date: Tue, 8 Jul 2025 16:29:24 +0200 Subject: [PATCH 1/5] Add operator overloading using __operator__ --- libb/6502.b | 13 +++++-------- src/b.rs | 44 ++++++++++++++++++++++++++++++++++++++---- src/codegen/mos6502.rs | 32 ++++++------------------------ src/lexer.rs | 3 +++ 4 files changed, 54 insertions(+), 38 deletions(-) diff --git a/libb/6502.b b/libb/6502.b index 234b283b..7d5c055f 100644 --- a/libb/6502.b +++ b/libb/6502.b @@ -1,3 +1,6 @@ +__operator__(/, _div); +__operator__(%, _rem); + exit(code) { 0(code); } @@ -33,12 +36,6 @@ realloc(ptr, size) { return (malloc(size)); } -/* TODO: Try to implement this function with assembly - Problem with this implementation is that it is not - mapped to the operator - We cannot call this function `div` as it conflicts - with the `divmod` test -*/ _div(a, b) { auto d; d = 0; while(a >= b) { @@ -67,9 +64,9 @@ printn(n, b, sign) { n = -n; } - if(a=_div(n, b)) /* assignment, not test for equality */ + if(a=n/b) /* assignment, not test for equality */ printn(a, b, 0); /* recursive */ - c = _rem(n,b) + '0'; + c = n%b + '0'; if (c > '9') c += 7; putchar(c); } diff --git a/src/b.rs b/src/b.rs index 121ef5ce..858650bf 100644 --- a/src/b.rs +++ b/src/b.rs @@ -531,21 +531,36 @@ pub unsafe fn compile_primary_expression(l: *mut Lexer, c: *mut Compiler) -> Opt } } +pub unsafe fn push_binop_opcode_or_overload_call(binop: Binop, result: usize, lhs: Arg, rhs: Arg, loc: Loc, + c: *mut Compiler) { + for i in 0..(*c).op_overloads.count { + let (bop, fun) = *(*c).op_overloads.items.add(i); + if binop == bop { + let mut args: Array = zeroed(); + da_append(&mut args, lhs); + da_append(&mut args, rhs); + push_opcode(Op::Funcall {result, fun: Arg::External(fun), args}, loc, c); + return; + } + } + push_opcode(Op::Binop {binop, index: result, lhs, rhs}, loc, c); +} + // TODO: communicate to the caller of this function that it expects `lhs` to be an lvalue pub unsafe fn compile_binop(lhs: Arg, rhs: Arg, binop: Binop, loc: Loc, c: *mut Compiler) { match lhs { Arg::Deref(index) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop {binop, index: tmp, lhs, rhs}, loc, c); + push_binop_opcode_or_overload_call(binop, tmp, lhs, rhs, loc, c); push_opcode(Op::Store {index, arg: Arg::AutoVar(tmp)}, loc, c); }, Arg::External(name) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop {binop, index: tmp, lhs, rhs}, loc, c); + push_binop_opcode_or_overload_call(binop, tmp, lhs, rhs, loc, c); push_opcode(Op::ExternalAssign {name, arg: Arg::AutoVar(tmp)}, loc, c) } Arg::AutoVar(index) => { - push_opcode(Op::Binop {binop, index, lhs, rhs}, loc, c) + push_binop_opcode_or_overload_call(binop, index, lhs, rhs, loc, c) } Arg::Bogus => { // Bogus value does not compile to anything @@ -572,7 +587,7 @@ pub unsafe fn compile_binop_expression(l: *mut Lexer, c: *mut Compiler, preceden let (rhs, _) = compile_binop_expression(l, c, precedence + 1)?; let index = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop {binop, index, lhs, rhs}, (*l).loc, c); + push_binop_opcode_or_overload_call(binop, index, lhs, rhs, (*l).loc, c); lhs = Arg::AutoVar(index); lvalue = false; @@ -998,6 +1013,7 @@ pub struct Compiler { pub data: Array, pub extrns: Array<*const c_char>, pub variadics: Array<(*const c_char, Variadic)>, + pub op_overloads: Array<(Binop, *const c_char)>, pub globals: Array, pub asm_funcs: Array, /// Arena into which the Compiler allocates all the names and @@ -1064,6 +1080,26 @@ pub unsafe fn compile_program(l: *mut Lexer, c: *mut Compiler) -> Option<()> { get_and_expect_token_but_continue(l, c, Token::CParen)?; get_and_expect_token_but_continue(l, c, Token::SemiColon)?; } + Token::Operator => { + get_and_expect_token_but_continue(l, c, Token::OParen)?; + lexer::get_token(l)?; + let binop = match Binop::from_token((*l).token) { + Some(binop) => Some(binop), + None => { + diagf!((*l).loc, c!("ERROR: expected binary operator, but got %s\n"), + lexer::display_token((*l).token)); + None + } + }?; + get_and_expect_token_but_continue(l, c, Token::Comma)?; + get_and_expect_token_but_continue(l, c, Token::ID)?; + + let func = arena::strdup(&mut (*c).arena, (*l).string); + da_append(&mut (*c).op_overloads, (binop, func)); + + get_and_expect_token_but_continue(l, c, Token::CParen)?; + get_and_expect_token_but_continue(l, c, Token::SemiColon)?; + }, Token::Extrn => { while (*l).token != Token::SemiColon { get_and_expect_token(l, Token::ID)?; diff --git a/src/codegen/mos6502.rs b/src/codegen/mos6502.rs index 6608419e..dce6d3c4 100644 --- a/src/codegen/mos6502.rs +++ b/src/codegen/mos6502.rs @@ -924,32 +924,12 @@ pub unsafe fn generate_function(name: *const c_char, params_count: usize, auto_v instr(out, TXA); }, Binop::Mod => { - // !! TODO !! this should be implemented here and not as a B functions. - // TODO: current mod implementation is linear, we can do better. - load_arg(rhs, op.loc, out, asm); - push16(out, asm); - load_arg(lhs, op.loc, out, asm); - - instr0(out, JSR, ABS); - add_reloc(out, RelocationKind::External{name: c!("_rem"), offset: 0, - byte: Byte::Both}, asm); - instr(out, TAX); - pop16_discard(out, asm); - instr(out, TXA); + diagf!(op.loc, c!("FATAL: tried to use Mod operation, define using __operator__ instead\n")); + abort(); }, Binop::Div => { - // !! TODO !! this should be implemented here and not as a B functions. - // TODO: current div implementation is linear, we can do better. - load_arg(rhs, op.loc, out, asm); - push16(out, asm); - load_arg(lhs, op.loc, out, asm); - - instr0(out, JSR, ABS); - add_reloc(out, RelocationKind::External{name: c!("_div"), offset: 0, - byte: Byte::Both}, asm); - instr(out, TAX); - pop16_discard(out, asm); - instr(out, TXA); + diagf!(op.loc, c!("FATAL: tried to use Div operation, define using __operator__ instead\n")); + abort(); }, Binop::Mult => { load_two_args(out, lhs, rhs, op, asm); @@ -1313,7 +1293,7 @@ pub unsafe fn apply_relocations(out: *mut String_Builder, data_start: u16, asm: } } printf(c!("linking failed. could not find label `%s.%u'\n"), name, label); - unreachable!(); + abort(); }, RelocationKind::External{name, offset, byte} => { for i in 0..(*asm).externals.count { @@ -1329,7 +1309,7 @@ pub unsafe fn apply_relocations(out: *mut String_Builder, data_start: u16, asm: } } printf(c!("linking failed. could not find extern `%s'\n"), name); - unreachable!(); + abort(); }, RelocationKind::AddressRel{idx} => { let jaddr = *(*asm).addresses.items.add(idx); diff --git a/src/lexer.rs b/src/lexer.rs index 926d6472..fffcc802 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -93,6 +93,7 @@ pub enum Token { Return, Asm, Variadic, + Operator, } pub unsafe fn display_token(token: Token) -> *const c_char { @@ -160,6 +161,7 @@ pub unsafe fn display_token(token: Token) -> *const c_char { // TODO: document all this magical extension keywords somewhere Token::Asm => c!("keyword `__asm__`"), Token::Variadic => c!("keyword `__variadic__`"), + Token::Operator => c!("keyword `__operator__`"), } } @@ -264,6 +266,7 @@ const KEYWORDS: *const [(*const c_char, Token)] = &[ (c!("return"), Token::Return), (c!("__asm__"), Token::Asm), (c!("__variadic__"), Token::Variadic), + (c!("__operator__"), Token::Operator), ]; #[derive(Clone, Copy)] From dc3db6faa244e830a975c9c28fe90f2e39d70010 Mon Sep 17 00:00:00 2001 From: miezekatze Date: Tue, 8 Jul 2025 17:06:59 +0200 Subject: [PATCH 2/5] add tests, fix operator overloading order --- src/b.rs | 43 ++++++++++++++++++++---------------- tests.json | 9 +++++++- tests/operator_overloading.b | 20 +++++++++++++++++ 3 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 tests/operator_overloading.b diff --git a/src/b.rs b/src/b.rs index 858650bf..7c333a4f 100644 --- a/src/b.rs +++ b/src/b.rs @@ -531,36 +531,21 @@ pub unsafe fn compile_primary_expression(l: *mut Lexer, c: *mut Compiler) -> Opt } } -pub unsafe fn push_binop_opcode_or_overload_call(binop: Binop, result: usize, lhs: Arg, rhs: Arg, loc: Loc, - c: *mut Compiler) { - for i in 0..(*c).op_overloads.count { - let (bop, fun) = *(*c).op_overloads.items.add(i); - if binop == bop { - let mut args: Array = zeroed(); - da_append(&mut args, lhs); - da_append(&mut args, rhs); - push_opcode(Op::Funcall {result, fun: Arg::External(fun), args}, loc, c); - return; - } - } - push_opcode(Op::Binop {binop, index: result, lhs, rhs}, loc, c); -} - // TODO: communicate to the caller of this function that it expects `lhs` to be an lvalue pub unsafe fn compile_binop(lhs: Arg, rhs: Arg, binop: Binop, loc: Loc, c: *mut Compiler) { match lhs { Arg::Deref(index) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_binop_opcode_or_overload_call(binop, tmp, lhs, rhs, loc, c); + push_opcode(Op::Binop{binop, index: tmp, lhs, rhs}, loc, c); push_opcode(Op::Store {index, arg: Arg::AutoVar(tmp)}, loc, c); }, Arg::External(name) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_binop_opcode_or_overload_call(binop, tmp, lhs, rhs, loc, c); + push_opcode(Op::Binop{binop, index: tmp, lhs, rhs}, loc, c); push_opcode(Op::ExternalAssign {name, arg: Arg::AutoVar(tmp)}, loc, c) } Arg::AutoVar(index) => { - push_binop_opcode_or_overload_call(binop, index, lhs, rhs, loc, c) + push_opcode(Op::Binop{binop, index, lhs, rhs}, loc, c) } Arg::Bogus => { // Bogus value does not compile to anything @@ -587,7 +572,7 @@ pub unsafe fn compile_binop_expression(l: *mut Lexer, c: *mut Compiler, preceden let (rhs, _) = compile_binop_expression(l, c, precedence + 1)?; let index = allocate_auto_var(&mut (*c).auto_vars_ator); - push_binop_opcode_or_overload_call(binop, index, lhs, rhs, (*l).loc, c); + push_opcode(Op::Binop{binop, index, lhs, rhs}, (*l).loc, c); lhs = Arg::AutoVar(index); lvalue = false; @@ -1423,6 +1408,26 @@ pub unsafe fn main(mut argc: i32, mut argv: *mut*mut c_char) -> Option<()> { } } + // resolve operators + for i in 0..c.funcs.count { + let f = *c.funcs.items.add(i); + for j in 0..f.body.count { + let op = f.body.items.add(j); + if let Op::Binop {binop, index, lhs, rhs} = (*op).opcode { + for i in 0..c.op_overloads.count { + let (bop, fun) = *c.op_overloads.items.add(i); + if binop == bop { + let mut args: Array = zeroed(); + da_append(&mut args, lhs); + da_append(&mut args, rhs); + (*op).opcode = Op::Funcall {result: index, fun: Arg::External(fun), args}; + break; + } + } + } + } + } + scope_pop(&mut c.vars); // end global scope if c.error_count > 0 { diff --git a/tests.json b/tests.json index d854ee2f..57c36d72 100644 --- a/tests.json +++ b/tests.json @@ -1878,5 +1878,12 @@ "state": "Enabled", "comment": "" } + }, + "operator_overloading": { + "6502": { + "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\n", + "state": "Enabled", + "comment": "" + } } -} +} \ No newline at end of file diff --git a/tests/operator_overloading.b b/tests/operator_overloading.b new file mode 100644 index 00000000..6e3a601b --- /dev/null +++ b/tests/operator_overloading.b @@ -0,0 +1,20 @@ +f1(a, b) { + printf("1: %d * %d\n", a, b); + return (a+b); +} +f2(a, b) { + printf("2: %d + %d\n", a, b); + return (a-b); +} +f3(a, b) { + printf("3: %d - %d\n", a, b); + return (b+a+b); +} + +__operator__(*, f1); +__operator__(>>, f2); +__operator__(|, f3); + +main() { + printf("result = %d\n", ((2 * 3) >> 4) | 5); +} From b05dda07747ada7f68cbc64c1a1fe3f0800b08e9 Mon Sep 17 00:00:00 2001 From: miezekatze Date: Tue, 8 Jul 2025 17:09:00 +0200 Subject: [PATCH 3/5] add accidentally deleted spaces back in --- src/b.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/b.rs b/src/b.rs index 7c333a4f..b560ffac 100644 --- a/src/b.rs +++ b/src/b.rs @@ -536,16 +536,16 @@ pub unsafe fn compile_binop(lhs: Arg, rhs: Arg, binop: Binop, loc: Loc, c: *mut match lhs { Arg::Deref(index) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop{binop, index: tmp, lhs, rhs}, loc, c); + push_opcode(Op::Binop {binop, index: tmp, lhs, rhs}, loc, c); push_opcode(Op::Store {index, arg: Arg::AutoVar(tmp)}, loc, c); }, Arg::External(name) => { let tmp = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop{binop, index: tmp, lhs, rhs}, loc, c); + push_opcode(Op::Binop {binop, index: tmp, lhs, rhs}, loc, c); push_opcode(Op::ExternalAssign {name, arg: Arg::AutoVar(tmp)}, loc, c) } Arg::AutoVar(index) => { - push_opcode(Op::Binop{binop, index, lhs, rhs}, loc, c) + push_opcode(Op::Binop {binop, index, lhs, rhs}, loc, c) } Arg::Bogus => { // Bogus value does not compile to anything @@ -572,7 +572,7 @@ pub unsafe fn compile_binop_expression(l: *mut Lexer, c: *mut Compiler, preceden let (rhs, _) = compile_binop_expression(l, c, precedence + 1)?; let index = allocate_auto_var(&mut (*c).auto_vars_ator); - push_opcode(Op::Binop{binop, index, lhs, rhs}, (*l).loc, c); + push_opcode(Op::Binop {binop, index, lhs, rhs}, (*l).loc, c); lhs = Arg::AutoVar(index); lvalue = false; @@ -870,7 +870,7 @@ pub unsafe fn compile_statement(l: *mut Lexer, c: *mut Compiler) -> Option<()> { label: (*switch_frame).label }, case_loc, c); - push_opcode(Op::Binop{ + push_opcode(Op::Binop { binop: Binop::Equal, index: (*switch_frame).cond, lhs: (*switch_frame).value, From 6f2b2358402f791c6a20909269fc23becc40a74a Mon Sep 17 00:00:00 2001 From: miezekatze Date: Tue, 8 Jul 2025 17:15:37 +0200 Subject: [PATCH 4/5] add B++ to test case --- tests.json | 2 +- tests/operator_overloading.b | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tests.json b/tests.json index 57c36d72..9d99dc8d 100644 --- a/tests.json +++ b/tests.json @@ -1881,7 +1881,7 @@ }, "operator_overloading": { "6502": { - "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\n", + "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\nTesting B++\n", "state": "Enabled", "comment": "" } diff --git a/tests/operator_overloading.b b/tests/operator_overloading.b index 6e3a601b..6682d34a 100644 --- a/tests/operator_overloading.b +++ b/tests/operator_overloading.b @@ -15,6 +15,16 @@ __operator__(*, f1); __operator__(>>, f2); __operator__(|, f3); +__operator__(<<, f); + +f(s, w) { + printf("%s", w); + return (s); +} +cout 1; +endl "\n"; + main() { printf("result = %d\n", ((2 * 3) >> 4) | 5); + cout << "Testing B++" << endl; } From ec39a4fafe02500b10761e7fc6dbc67b1c3e7e74 Mon Sep 17 00:00:00 2001 From: miezekatze Date: Tue, 8 Jul 2025 17:23:09 +0200 Subject: [PATCH 5/5] updated tests --- tests.json | 43 +++++++++++++++++++++++++++++++++++- tests/operator_overloading.b | 4 ++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/tests.json b/tests.json index 9d99dc8d..1e225471 100644 --- a/tests.json +++ b/tests.json @@ -1884,6 +1884,47 @@ "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\nTesting B++\n", "state": "Enabled", "comment": "" + }, + "gas-x86_64-windows": { + "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\nTesting B++\r\n", + "state": "Enabled", + "comment": "" + }, + "gas-x86_64-linux": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" + } + "gas-x86_64-darwin": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" + }, + "gas-aarch64-linux": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" + }, + "gas-aarch64-darwin": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" + }, + "uxn": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" + }, + "fasm-x86_64-windows": { + "expected_stdout": "1: 2 * 3\r\n2: 5 + 4\r\n3: 1 - 5\r\nresult = 11\r\nTesting B++\r\n", + "state": "Enabled", + "comment": "" + }, + "fasm-x86_64-linux": { + "expected_stdout": "1: 2 * 3\n2: 5 + 4\n3: 1 - 5\nresult = 11\nTesting B++\n", + "state": "Enabled", + "comment": "" } + } -} \ No newline at end of file +} diff --git a/tests/operator_overloading.b b/tests/operator_overloading.b index 6682d34a..4111224f 100644 --- a/tests/operator_overloading.b +++ b/tests/operator_overloading.b @@ -13,7 +13,7 @@ f3(a, b) { __operator__(*, f1); __operator__(>>, f2); -__operator__(|, f3); +__operator__(<=, f3); __operator__(<<, f); @@ -25,6 +25,6 @@ cout 1; endl "\n"; main() { - printf("result = %d\n", ((2 * 3) >> 4) | 5); + printf("result = %d\n", ((2 * 3) >> 4) <= 5); cout << "Testing B++" << endl; }