From 95a665495471117fe7aaa74f3b2d86742edad262 Mon Sep 17 00:00:00 2001 From: Emmett Brown <79384480+Emmett81@users.noreply.github.com> Date: Sat, 23 Oct 2021 00:14:21 +0200 Subject: [PATCH] Fix for issue #115: label support inside asm{} --- converge.asm | 24 +++++ src/asm/state.rs | 241 ++++++++++++++++++++++++++----------------- tests/issue115/3.asm | 29 ++++++ tests/issue115/4.asm | 26 +++++ 4 files changed, 225 insertions(+), 95 deletions(-) create mode 100644 converge.asm create mode 100644 tests/issue115/3.asm create mode 100644 tests/issue115/4.asm diff --git a/converge.asm b/converge.asm new file mode 100644 index 00000000..8a876a65 --- /dev/null +++ b/converge.asm @@ -0,0 +1,24 @@ +#ruledef test +{ + ld {x} => + { + assert(x <= 0x8) + 0x11 @ x`16 + } + + ld {x} => + { + assert(x > 0x8) + 0x22 @ x`8 + } + + test => asm + { + ld label ; error: converge + ld label ; error: converge + ld label ; error: converge + label: + } +} + +test \ No newline at end of file diff --git a/src/asm/state.rs b/src/asm/state.rs index 7f0d8898..36c42918 100644 --- a/src/asm/state.rs +++ b/src/asm/state.rs @@ -1331,128 +1331,179 @@ impl State fileserver: &dyn util::FileServer) -> Result { - // Clone the context in order to advance the logical address - // between instructions. - let mut inner_ctx = ctx.clone(); - let mut result = util::BigInt::new(0, Some(0)); - - let mut parser = syntax::Parser::new(Some(info.report.clone()), info.tokens); - //println!("asm block `{}`", fileserver.get_excerpt(&parser.get_full_span())); - - while !parser.is_over() - { - // Substitute `{x}` occurrences with tokens from the argument - let mut subs_parser = parser.slice_until_linebreak_over_nested_braces(); - let subparser_span = subs_parser.get_full_span(); + let mut size_guess = 0; - //println!("> instr `{}`", fileserver.get_excerpt(&subparser_span)); + // Parse the tokens twice, once to find the labels and guess + // size of result, then to assemble the output + for iterations in 0..2 { - let mut subs_tokens: Vec = Vec::new(); - while !subs_parser.is_over() - { - if let Some(open_token) = subs_parser.maybe_expect(syntax::TokenKind::BraceOpen) - { - let arg_name_token = subs_parser.expect(syntax::TokenKind::Identifier)?; - let arg_name = arg_name_token.excerpt.as_ref().unwrap(); + // Clone the context in order to advance the logical address + // between instructions. + let mut inner_ctx = ctx.clone(); - let token_sub = match info.args.get_token_sub(&arg_name) - { - None => - { - info.report.error_span("unknown argument", &arg_name_token.span); - return Err(()); - } - Some(t) => t - }; + let mut parser = syntax::Parser::new(Some(info.report.clone()), info.tokens); - let close_token = subs_parser.expect(syntax::TokenKind::BraceClose)?; - let sub_span = open_token.span.join(&close_token.span); + //println!("asm block `{}`", fileserver.get_excerpt(&parser.get_full_span())); - for token in token_sub - { - let mut sub_token = token.clone(); - sub_token.span = sub_span.clone(); - subs_tokens.push(sub_token); - } - } - else - { - subs_tokens.push(subs_parser.advance()); - } - } - - let mut subparser = syntax::Parser::new(Some(info.report.clone()), &subs_tokens); - subparser.suppress_reports(); + while !parser.is_over() + { + // Substitute `{x}` occurrences with tokens from the argument + let mut subs_parser = parser.slice_until_linebreak_over_nested_braces(); + let subparser_span = subs_parser.get_full_span(); - //println!("> after subs `{:?}`", subs_tokens); - - let matches = asm::parser::match_rule_invocation( - &self, - subparser, - inner_ctx.clone(), - fileserver, - info.report.clone())?; + //println!("> instr `{}`", fileserver.get_excerpt(&subparser_span)); - let value = self.resolve_rule_invocation( - info.report.clone(), - &matches, - fileserver, - true, - info.args)?; - - //println!(" value = {:?}", value); - - let (bigint, size) = match value.get_bigint() - { - Some(bigint) => + let mut subs_tokens: Vec = Vec::new(); + while !subs_parser.is_over() { - match bigint.size + if let Some(open_token) = subs_parser.maybe_expect(syntax::TokenKind::BraceOpen) { - Some(size) => (bigint, size), - None => + let arg_name_token = subs_parser.expect(syntax::TokenKind::Identifier)?; + let arg_name = arg_name_token.excerpt.as_ref().unwrap(); + + let token_sub = match info.args.get_token_sub(&arg_name) { - info.report.error_span( - "cannot infer size of instruction", - &subparser_span); + None => + { + info.report.error_span("unknown argument", &arg_name_token.span); + return Err(()); + } + Some(t) => t + }; + + let close_token = subs_parser.expect(syntax::TokenKind::BraceClose)?; + let sub_span = open_token.span.join(&close_token.span); - return Err(()); + for token in token_sub + { + let mut sub_token = token.clone(); + sub_token.span = sub_span.clone(); + subs_tokens.push(sub_token); } } + else + { + subs_tokens.push(subs_parser.advance()); + } } - _ => + let mut subparser = syntax::Parser::new(Some(info.report.clone()), &subs_tokens); + subparser.suppress_reports(); + + //println!("> after subs `{:?}`", subs_tokens); + + if subparser.next_is(0, syntax::TokenKind::Identifier) && subparser.next_is(1, syntax::TokenKind::Colon) { - info.report.error_span( - "wrong type returned from instruction", - &subparser_span); + let label_tk = subparser.expect(syntax::TokenKind::Identifier)?; + + let label_name = label_tk.excerpt.as_ref().unwrap(); - return Err(()); - } - }; + info.args.set_local( + label_name, + expr::Value::make_integer( + self.get_addr(info.report.clone(), &inner_ctx, &subparser_span)?)); - if size > 0 - { - if result.size.unwrap() == 0 - { - result = bigint; + subparser.expect(syntax::TokenKind::Colon)?; } - else + else { - result = result.concat( - (result.size.unwrap(), 0), - &bigint, - (size, 0)); + let matches = asm::parser::match_rule_invocation( + &self, + subparser, + inner_ctx.clone(), + fileserver, + info.report.clone())?; + + if iterations == 0 + { + let value = self.resolve_rule_invocation( + info.report.clone(), + &matches, + fileserver, + false, + info.args).unwrap_or(expr::Value::make_integer(0)); + + if value.get_bigint().is_some() && value.get_bigint().unwrap().size.is_some() { + let size = value.get_bigint().unwrap().size.unwrap(); + size_guess += size; + inner_ctx.bit_offset += size; + } + } + + if iterations == 1 + { + let value = self.resolve_rule_invocation( + info.report.clone(), + &matches, + fileserver, + true, + info.args)?; + + //println!(" value = {:?}", value); + + let (bigint, size) = match value.get_bigint() + { + Some(bigint) => + { + match bigint.size + { + Some(size) => (bigint, size), + None => + { + info.report.error_span( + "cannot infer size of instruction", + &subparser_span); + + return Err(()); + } + } + } + + _ => + { + info.report.error_span( + "wrong type returned from instruction", + &subparser_span); + + return Err(()); + } + }; + + if size > 0 + { + if result.size.unwrap() == 0 + { + result = bigint; + } + else + { + result = result.concat( + (result.size.unwrap(), 0), + &bigint, + (size, 0)); + } + + inner_ctx.bit_offset += size; + } + } } - - inner_ctx.bit_offset += size; + parser.expect_linebreak()?; } - - parser.expect_linebreak()?; } + //println!(" result size guess = {:?}", size_guess); //println!(" result size = {:?}", result.size); + + if size_guess != result.size.unwrap() + { + info.report.error_span( + "Size of instruction did not converge after iterations", + &syntax::Parser::new(Some(info.report.clone()), info.tokens).get_full_span()); + return Err(()); + } + Ok(expr::Value::make_integer(result)) } } diff --git a/tests/issue115/3.asm b/tests/issue115/3.asm new file mode 100644 index 00000000..53c337ea --- /dev/null +++ b/tests/issue115/3.asm @@ -0,0 +1,29 @@ +#ruledef { + emit {x:u8} => x + + nested_test => asm + { + label: + emit $ + label2: + emit $ + emit label + emit label2 + emit $ + test + } + + test => asm + { + label: + emit $ + label2: + emit $ + emit label + emit label2 + emit $ + } +} + +test ; = 0x00_01_00_01_04 +nested_test ; = 0x05_06_05_06_09_0a_0b_0a_0b_0e \ No newline at end of file diff --git a/tests/issue115/4.asm b/tests/issue115/4.asm new file mode 100644 index 00000000..b229fd15 --- /dev/null +++ b/tests/issue115/4.asm @@ -0,0 +1,26 @@ +#ruledef { + emit {x:u8} => x + + test => asm + { + test2 end + test2 end + end: + } + + test2 {l: u32} => asm + { + emit l + emit end + end: + } + + run => asm + { + test + emit 0x10 + } +} + +run ; = 0x04_02_04_04_10 +emit $ ; = 0x05 \ No newline at end of file