diff --git a/src/b.rs b/src/b.rs index 121ef5ce..69dba1da 100644 --- a/src/b.rs +++ b/src/b.rs @@ -456,6 +456,84 @@ pub unsafe fn compile_primary_expression(l: *mut Lexer, c: *mut Compiler) -> Opt Some((arg, false)) } Token::CharLit | Token::IntLit => Some((Arg::Literal((*l).int_number), false)), + Token::Iota => { + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + None + } else { + // TODO: If we get a = or a =+ with a constant, consider that + let prev = (*c).iota_counter; + let save_point = (*l).parse_point; + lexer::get_token(l)?; + if let Some(_) = Binop::from_assign_token((*l).token) { + match (*l).token { + Token::Eq => { + // Now, except a literal + get_and_expect_token_but_continue(l, c, Token::IntLit)?; + let v = (*l).int_number; + (*c).iota_counter = (v as usize) + 1; + + Some((Arg::Literal(v as u64), false)) + } + Token::PlusEq => { + get_and_expect_token_but_continue(l, c, Token::IntLit)?; + let v = (*l).int_number; + (*c).iota_counter += v as usize; + + Some((Arg::Literal(prev as u64), false)) + } + _ => { + diagf!((*l).loc, c!("ERROR: iota does not support '%s' operator\n"), lexer::display_token((*l).token)); + None + } + } + } else { + // Sorry. Return Everything + (*l).parse_point = save_point; + + (*c).iota_counter += 1; + Some((Arg::Literal(prev as u64), false)) + } + } + } + Token::IotaReset => { + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + None + } else { + (*c).iota_counter = 1; + // Only iota itself get to be an lvalue + Some((Arg::Literal(((*c).iota_counter-1) as u64), false)) + } + } + Token::IotaSkip => { + let prev = (*c).iota_counter; + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + None + } else { + get_and_expect_token_but_continue(l, c, Token::OParen)?; + get_and_expect_token_but_continue(l, c, Token::IntLit)?; + let v = (*l).int_number; + get_and_expect_token_but_continue(l, c, Token::CParen)?; + (*c).iota_counter += v as usize; + // Only iota itself get to be an lvalue + Some((Arg::Literal(prev as u64), false)) + } + } + Token::IotaSet => { + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + None + } else { + get_and_expect_token_but_continue(l, c, Token::OParen)?; + get_and_expect_token_but_continue(l, c, Token::IntLit)?; + let v = (*l).int_number; + get_and_expect_token_but_continue(l, c, Token::CParen)?; + (*c).iota_counter = (v as usize) + 1; + Some((Arg::Literal(v), false)) + } + } Token::ID => { let name = arena::strdup(&mut (*c).arena, (*l).string); @@ -596,7 +674,7 @@ pub unsafe fn compile_assign_expression(l: *mut Lexer, c: *mut Compiler) -> Opti while let Some(binop) = Binop::from_assign_token((*l).token) { let binop_loc = (*l).loc; let (rhs, _) = compile_assign_expression(l, c)?; - + if !lvalue { diagf!(binop_loc, c!("ERROR: cannot assign to rvalue\n")); return bump_error_count(c).map(|()| (Arg::Bogus, false)); @@ -994,6 +1072,7 @@ pub struct Compiler { pub func_gotos: Array, pub used_funcs: Array, pub op_label_count: usize, + pub iota_counter: usize, pub switch_stack: Array, pub data: Array, pub extrns: Array<*const c_char>, @@ -1119,6 +1198,9 @@ pub unsafe fn compile_program(l: *mut Lexer, c: *mut Compiler) -> Option<()> { (*(*c).func_body.items.add(used_label.addr)).opcode = Op::JmpLabel {label: (*existing_label).label}; } + // Automatically clear the iota counter + (*c).iota_counter = 0; + da_append(&mut (*c).funcs, Func { name, name_loc, @@ -1149,7 +1231,7 @@ pub unsafe fn compile_program(l: *mut Lexer, c: *mut Compiler) -> Option<()> { // TODO: This code is ugly // couldn't find a better way to write it while keeping accurate error messages - get_and_expect_tokens(l, &[Token::Minus, Token::IntLit, Token::CharLit, Token::String, Token::ID, Token::SemiColon, Token::OBracket])?; + get_and_expect_tokens(l, &[Token::Minus, Token::IntLit, Token::CharLit, Token::String, Token::ID, Token::SemiColon, Token::OBracket, Token::Iota, Token::IotaSkip, Token::IotaSet, Token::IotaReset])?; if (*l).token == Token::OBracket { global.is_vec = true; @@ -1167,6 +1249,53 @@ pub unsafe fn compile_program(l: *mut Lexer, c: *mut Compiler) -> Option<()> { get_and_expect_token(l, Token::IntLit)?; ImmediateValue::Literal(!(*l).int_number + 1) } + Token::Iota => { + let prev = (*c).iota_counter; + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + bump_error_count(c)?; + } + (*c).iota_counter += 1; + ImmediateValue::Literal(prev as u64) + } + Token::IotaReset => { + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + bump_error_count(c)?; + } + (*c).iota_counter = 1; + ImmediateValue::Literal(((*c).iota_counter-1) as u64) + } + Token::IotaSkip => { + let prev = (*c).iota_counter; + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + bump_error_count(c)?; + } + + get_and_expect_token(l, Token::OParen)?; + get_and_expect_token(l, Token::IntLit)?; + let v = (*l).int_number; + get_and_expect_token(l, Token::CParen)?; + + (*c).iota_counter += v as usize; + ImmediateValue::Literal(prev as u64) + } + Token::IotaSet => { + let prev = (*c).iota_counter; + if (*c).historical { + diagf!((*l).loc, c!("ERROR: iota is not allowed in historical mode\n")); + bump_error_count(c)?; + } + + get_and_expect_token(l, Token::OParen)?; + get_and_expect_token(l, Token::IntLit)?; + let v = (*l).int_number; + get_and_expect_token(l, Token::CParen)?; + + (*c).iota_counter = (v as usize) + 1; + ImmediateValue::Literal(prev as u64) + } Token::IntLit | Token::CharLit => ImmediateValue::Literal((*l).int_number), Token::String => ImmediateValue::DataOffset(compile_string((*l).string, c)), Token::ID => { diff --git a/src/lexer.rs b/src/lexer.rs index 926d6472..640f6b45 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -93,6 +93,10 @@ pub enum Token { Return, Asm, Variadic, + Iota, + IotaReset, + IotaSkip, + IotaSet, } pub unsafe fn display_token(token: Token) -> *const c_char { @@ -160,6 +164,10 @@ 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::Iota => c!("keyword `iota`"), + Token::IotaReset => c!("keyword `iota_reset`"), + Token::IotaSkip => c!("keyword `iota_skip`"), + Token::IotaSet => c!("keyword `iota_set`"), } } @@ -264,6 +272,10 @@ const KEYWORDS: *const [(*const c_char, Token)] = &[ (c!("return"), Token::Return), (c!("__asm__"), Token::Asm), (c!("__variadic__"), Token::Variadic), + (c!("iota"), Token::Iota), + (c!("iota_reset"), Token::IotaReset), + (c!("iota_skip"), Token::IotaSkip), + (c!("iota_set"), Token::IotaSet), ]; #[derive(Clone, Copy)] diff --git a/tests.json b/tests.json index d854ee2f..06b76745 100644 --- a/tests.json +++ b/tests.json @@ -1597,6 +1597,53 @@ "comment": "" } }, + "iota": { + "gas-x86_64-windows": { + "expected_stdout": "0 1 2\r\n1 0 2\r\n4 2\r\n0\r\n", + "state": "Enabled", + "comment": "" + }, + "gas-x86_64-linux": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + }, + "gas-x86_64-darwin": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + }, + "gas-aarch64-linux": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + }, + "gas-aarch64-darwin": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + }, + "uxn": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + }, + "6502": { + "expected_stdout": "0 1 2\r\n1 0 2\r\n4 2\r\n0\r\n", + "state": "Enabled", + "comment": "" + }, + "fasm-x86_64-windows": { + "expected_stdout": "0 1 2\r\n1 0 2\r\n4 2\r\n0\r\n", + "state": "Enabled", + "comment": "" + }, + "fasm-x86_64-linux": { + "expected_stdout": "0 1 2\n1 0 2\n4 2\n0\n", + "state": "Enabled", + "comment": "" + } + }, "inc_dec": { "fasm-x86_64-windows": { "expected_stdout": "x: 3\r\n++x: 4\r\nx++: 4\r\nx: 5\r\nx--: 5\r\n--x: 3\r\n", diff --git a/tests/iota.b b/tests/iota.b new file mode 100644 index 00000000..a7533a56 --- /dev/null +++ b/tests/iota.b @@ -0,0 +1,28 @@ +STRUCT_OKINA_FOO iota_set(0); +STRUCT_OKINA_BAR iota_skip(1); +/* iota_skip can generally be used to create an n-element wordlist */ +STRUCT_OKINA_BAZ iota_skip(12); + +/* Try to reset an iota to start a new struct */ +STRUCT_YUKARI_BAR iota_reset; +STRUCT_YUKARI_FOO iota; +STRUCT_YUKARI_BAZ iota_skip(128); +STRUCT_YUKARI_BARRIER iota; + +list[4]; + +main() { + printf("%d %d %d\n", STRUCT_OKINA_FOO, STRUCT_OKINA_BAR, STRUCT_OKINA_BAZ); + printf("%d %d %d\n", STRUCT_YUKARI_FOO, STRUCT_YUKARI_BAR, STRUCT_YUKARI_BAZ); + + /* Using iotas in general expressions */ + list[iota_reset] = 1; + list[iota] = 2; + list[iota] = 3; + list[iota] = 4; + printf("%d %d\n", iota, list[1]); + + /* iota = N <=> iota_set(N) */ + printf("%d\n", iota = 0); + return (0); +}