diff --git a/Cargo.toml b/Cargo.toml index 57ee7e31..29c9764b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "customasm" -version = "0.13.5" +version = "0.13.6" edition = "2021" authors = ["hlorenzi "] description = "An assembler for custom, user-defined instruction sets!" diff --git a/src/asm/matcher/mod.rs b/src/asm/matcher/mod.rs index 43838a67..d70a5b82 100644 --- a/src/asm/matcher/mod.rs +++ b/src/asm/matcher/mod.rs @@ -24,6 +24,21 @@ pub struct InstructionMatch } +impl InstructionMatch +{ + pub fn is_same(&self, other: &InstructionMatch) -> bool + { + self.ruledef_ref.0 == other.ruledef_ref.0 && + self.rule_ref.0 == other.rule_ref.0 && + self.args.len() == other.args.len() && + self.args + .iter() + .zip(&other.args) + .all(|(a, b)| a.is_same(&b)) + } +} + + #[derive(Clone, Debug)] pub enum InstructionMatchResolution { @@ -78,6 +93,32 @@ pub struct InstructionArgument } +impl InstructionArgument +{ + pub fn is_same(&self, other: &InstructionArgument) -> bool + { + if self.span != other.span + { + return false; + } + + match (&self.kind, &other.kind) + { + (InstructionArgumentKind::Expr(_), + InstructionArgumentKind::Expr(_)) => + true, + + (InstructionArgumentKind::Nested(ref a), + InstructionArgumentKind::Nested(ref b)) => + a.is_same(b), + + _ => + false, + } + } +} + + #[derive(Clone, Debug)] pub enum InstructionArgumentKind { @@ -379,7 +420,7 @@ pub fn match_instr( working_matches.extend(ruledef_matches); } } - + if working_matches.len() == 0 { return vec![]; @@ -392,6 +433,10 @@ pub fn match_instr( .collect::>(); + // Identical duplicate matches are already sorted consecutively + matches.dedup_by(|a, b| a.is_same(b)); + + // Calculate recursive "exact" pattern-part count for // each match for mtch in &mut matches @@ -546,25 +591,45 @@ fn match_with_rule<'src>( asm::RuleParameterType::Signed(_) | asm::RuleParameterType::Integer(_) => { - return match_with_expr( - defs, - rule, - walker, - needs_consume_all_tokens, - part_index, - match_so_far); + let mut result = vec![]; + + // Try both with and without lookahead character + for enable_lookahead in [false, true] + { + result.extend( + match_with_expr( + defs, + rule, + walker.clone(), + needs_consume_all_tokens, + part_index, + enable_lookahead, + match_so_far.clone())); + } + + return result; } asm::RuleParameterType::RuledefRef(ruledef_ref) => { - return match_with_nested_ruledef( - defs, - ruledef_ref, - rule, - walker, - needs_consume_all_tokens, - part_index, - match_so_far); + let mut result = vec![]; + + // Try both with and without lookahead character + for enable_lookahead in [false, true] + { + result.extend( + match_with_nested_ruledef( + defs, + ruledef_ref, + rule, + walker.clone(), + needs_consume_all_tokens, + part_index, + enable_lookahead, + match_so_far.clone())); + } + + return result; } } } @@ -585,19 +650,23 @@ fn match_with_rule<'src>( fn match_with_expr<'src>( defs: &asm::ItemDefs, rule: &asm::Rule, - walker: &mut syntax::Walker<'src>, + mut walker: syntax::Walker<'src>, needs_consume_all_tokens: bool, at_pattern_part: usize, - match_so_far: &mut InstructionMatch) + enable_lookahead: bool, + mut match_so_far: InstructionMatch) -> WorkingMatches<'src> { let walker_start = walker.next_useful_index(); - let maybe_expr = parse_with_lookahead( - &rule.pattern, - at_pattern_part, - walker, - |walker| expr::parse_optional(walker)); + let Some(maybe_expr) = + parse_with_lookahead( + &rule.pattern, + at_pattern_part, + enable_lookahead, + &mut walker, + |walker| expr::parse_optional(walker)) + else { return vec![] }; let walker_end = walker.get_cursor_index(); let walker_start = std::cmp::min(walker_start, walker_end); @@ -627,10 +696,10 @@ fn match_with_expr<'src>( match_with_rule( defs, rule, - walker, + &mut walker, needs_consume_all_tokens, at_pattern_part + 1, - match_so_far) + &mut match_so_far) } @@ -638,24 +707,28 @@ fn match_with_nested_ruledef<'src>( defs: &asm::ItemDefs, nested_ruledef_ref: util::ItemRef, rule: &asm::Rule, - walker: &mut syntax::Walker<'src>, + mut walker: syntax::Walker<'src>, needs_consume_all_tokens: bool, at_pattern_part: usize, - match_so_far: &mut InstructionMatch) + enable_lookahead: bool, + match_so_far: InstructionMatch) -> WorkingMatches<'src> { let walker_start = walker.next_useful_index(); let walker_limit_prev = walker.get_cursor_limit(); - let nested_matches = parse_with_lookahead( - &rule.pattern, - at_pattern_part, - walker, - |walker| match_with_ruledef( - defs, - nested_ruledef_ref, - walker, - false)); + let Some(nested_matches) = + parse_with_lookahead( + &rule.pattern, + at_pattern_part, + enable_lookahead, + &mut walker, + |walker| match_with_ruledef( + defs, + nested_ruledef_ref, + walker, + false)) + else { return vec![] }; let mut matches = WorkingMatches::new(); @@ -729,11 +802,17 @@ fn match_with_nested_ruledef<'src>( fn parse_with_lookahead<'src, F, T>( pattern: &asm::RulePattern, at_pattern_part: usize, + enable_lookahead: bool, walker: &mut syntax::Walker<'src>, parse_fn: F) - -> T + -> Option where F: FnOnce(&mut syntax::Walker<'src>) -> T { + if !enable_lookahead + { + return Some(parse_fn(walker)); + } + let maybe_lookahead_char = find_lookahead_char( pattern, at_pattern_part); @@ -749,11 +828,11 @@ fn parse_with_lookahead<'src, F, T>( walker.set_cursor_limit(limit); let result = parse_fn(walker); walker.set_cursor_limit(prev_limit); - return result; + return Some(result); } } - parse_fn(walker) + None } diff --git a/tests/issue203/ok.asm b/tests/issue203/ok.asm new file mode 100644 index 00000000..fea54ec6 --- /dev/null +++ b/tests/issue203/ok.asm @@ -0,0 +1,15 @@ +#subruledef foo +{ + add => 0xaa + sub => 0xbb +} + +#ruledef Bar +{ + {f: foo}d => f +} + +addd ; = 0xaa +add d ; = 0xaa +subd ; = 0xbb +sub d ; = 0xbb \ No newline at end of file diff --git a/tests/rule_nested_ambiguous/err_no_match2.asm b/tests/rule_nested_ambiguous/err_no_match2.asm deleted file mode 100644 index a32f3ac7..00000000 --- a/tests/rule_nested_ambiguous/err_no_match2.asm +++ /dev/null @@ -1,12 +0,0 @@ -#subruledef inner -{ - {x} => 0x11 @ x`8 - {x}$ => 0x22 @ x`8 -} - -#ruledef test -{ - ld {x: inner}$ => 0x55 @ x`16 -} - -ld 0xaa$$ ; error: no match \ No newline at end of file diff --git a/tests/rule_nested_ambiguous/ok.asm b/tests/rule_nested_ambiguous/ok1.asm similarity index 60% rename from tests/rule_nested_ambiguous/ok.asm rename to tests/rule_nested_ambiguous/ok1.asm index 68f4acb4..6ec38a61 100644 --- a/tests/rule_nested_ambiguous/ok.asm +++ b/tests/rule_nested_ambiguous/ok1.asm @@ -10,5 +10,8 @@ } ld 0xaa$ ; = 0x5511aa +ld 0xaa$$ ; = 0x5522aa ld 0x99 + 0x11$ ; = 0x5511aa -ld (0x99 + 0x11)$ ; = 0x5511aa \ No newline at end of file +ld 0x99 + 0x11$$ ; = 0x5522aa +ld (0x99 + 0x11)$ ; = 0x5511aa +ld (0x99 + 0x11)$$ ; = 0x5522aa \ No newline at end of file diff --git a/tests/rule_nested_ambiguous/ok2.asm b/tests/rule_nested_ambiguous/ok2.asm new file mode 100644 index 00000000..6ca10c8d --- /dev/null +++ b/tests/rule_nested_ambiguous/ok2.asm @@ -0,0 +1,9 @@ +#ruledef +{ + move to {x: u8} if {y: u8} => 0x55 @ x @ y +} + +input: +.input: +move to input if 0xaa ; = 0x5500aa +move to .input if 0xaa ; = 0x5500aa \ No newline at end of file