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..b560ffac 100644 --- a/src/b.rs +++ b/src/b.rs @@ -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, @@ -998,6 +998,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 +1065,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)?; @@ -1387,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/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)] diff --git a/tests.json b/tests.json index d854ee2f..1e225471 100644 --- a/tests.json +++ b/tests.json @@ -1878,5 +1878,53 @@ "state": "Enabled", "comment": "" } + }, + "operator_overloading": { + "6502": { + "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": "" + } + } } diff --git a/tests/operator_overloading.b b/tests/operator_overloading.b new file mode 100644 index 00000000..4111224f --- /dev/null +++ b/tests/operator_overloading.b @@ -0,0 +1,30 @@ +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); + +__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; +}