Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 51 additions & 20 deletions peg-macros/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -878,20 +878,36 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
));
}
(&Expr::Marker(la), &Expr::Marker(ra)) if op.elements.len() >= 3 => {
//infix
let new_prec = match (la, ra) {
(true, false) => prec + 1, // left associative
(false, true) => prec, // right associative
// infix
let (assoc, new_prec) = match (la, ra) {
(true, false) => (true, prec + 1), // left associative
(false, true) => (true, prec), // right associative
(false, false) => (false, prec + 1), // no associative
_ => return report_error(op_span, "precedence rules must use `@` and `(@)` to indicate associativity".to_string())
};
let assoc_prec = if assoc { -1 } else { prec };

let no_assoc_trap = if !assoc {
quote_spanned! { op_span =>
if #prec == __assoc_prec {
::peg::RuleResult::Failed
} else
}
} else {
quote!()
};

post_rules.push(
labeled_seq(context, &op.elements[1..op.elements.len()-1], {
quote_spanned!{ op_span =>
if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
#no_assoc_trap
if let ::peg::RuleResult::Matched(__pos, (__assoc_prec_bound, #r_arg)) = __recurse(__pos, #new_prec, __state, __err_state) {
let #l_arg = __infix_result;
__infix_result = #action;
::peg::RuleResult::Matched(__pos, ())
::peg::RuleResult::Matched(
__pos,
__assoc_prec.max(__assoc_prec_bound).max(#assoc_prec),
)
} else { ::peg::RuleResult::Failed }
}
})
Expand All @@ -906,7 +922,7 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
quote_spanned! { op_span =>
let #l_arg = __infix_result;
__infix_result = #action;
::peg::RuleResult::Matched(__pos, ())
::peg::RuleResult::Matched(__pos, -1)
}
},
));
Expand All @@ -920,8 +936,8 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
pre_rules.push(
labeled_seq(context, &op.elements[..op.elements.len()-1], {
quote_spanned!{ op_span =>
if let ::peg::RuleResult::Matched(__pos, #r_arg) = __recurse(__pos, #new_prec, __state, __err_state) {
::peg::RuleResult::Matched(__pos, #action)
if let ::peg::RuleResult::Matched(__pos, (__assoc_prec, #r_arg)) = __recurse(__pos, #new_prec, __state, __err_state) {
::peg::RuleResult::Matched(__pos, (__assoc_prec, #action))
} else { ::peg::RuleResult::Failed }
}
})
Expand All @@ -930,7 +946,7 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
_ => {
// atom
pre_rules.push(labeled_seq(context, &op.elements, {
quote_spanned! { op_span => ::peg::RuleResult::Matched(__pos, #action) }
quote_spanned! { op_span => ::peg::RuleResult::Matched(__pos, (-1, #action)) }
}));
}
};
Expand All @@ -940,8 +956,8 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
level_code.push(quote_spanned! { span =>
if #prec >= __min_prec {
#(
if let ::peg::RuleResult::Matched(__pos, ()) = #post_rules {
return (__infix_result, ::peg::RuleResult::Matched(__pos, ()));
if let ::peg::RuleResult::Matched(__pos, __assoc_prec) = #post_rules {
return (__infix_result, ::peg::RuleResult::Matched(__pos, __assoc_prec));
}
)*
}
Expand All @@ -967,23 +983,24 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
err_state: &mut ::peg::error::ErrorState,
min_prec: i32,
lpos: usize,
prefix_atom: &dyn Fn(usize, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> ::peg::RuleResult<T>,
level_code: &dyn Fn(usize, usize, i32, T, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<T>) -> (T, ::peg::RuleResult<()>),
) -> ::peg::RuleResult<T> {
prefix_atom: &dyn Fn(usize, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<(i32, T)>) -> ::peg::RuleResult<(i32, T)>,
level_code: &dyn Fn(usize, usize, i32, i32, T, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<(i32, T)>) -> (T, ::peg::RuleResult<i32>),
) -> ::peg::RuleResult<(i32, T)> {
let initial = {
prefix_atom(lpos, state, err_state, &|pos, min_prec, state, err_state| {
__infix_parse(state, err_state, min_prec, pos, prefix_atom, level_code)
})
};

if let ::peg::RuleResult::Matched(pos, mut infix_result) = initial {
if let ::peg::RuleResult::Matched(pos, (mut assoc_prec, mut infix_result)) = initial {
#enter
let mut repeat_pos = pos;
loop {
let (val, res) = level_code(
repeat_pos,
lpos,
min_prec,
assoc_prec,
infix_result,
state,
err_state,
Expand All @@ -993,21 +1010,35 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS
);
infix_result = val;

if let ::peg::RuleResult::Matched(pos, ()) = res {
if let ::peg::RuleResult::Matched(pos, assoc_prec_bound) = res {
repeat_pos = pos;
assoc_prec = assoc_prec_bound;
continue;
}

break;
}
#leave
::peg::RuleResult::Matched(repeat_pos, infix_result)
::peg::RuleResult::Matched(repeat_pos, (assoc_prec, infix_result))
} else {
::peg::RuleResult::Failed
}
}
fn __infix_parse_result<T, S>(
state: &mut S,
err_state: &mut ::peg::error::ErrorState,
min_prec: i32,
lpos: usize,
prefix_atom: &dyn Fn(usize, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<(i32, T)>) -> ::peg::RuleResult<(i32, T)>,
level_code: &dyn Fn(usize, usize, i32, i32, T, &mut S, &mut ::peg::error::ErrorState, &dyn Fn(usize, i32, &mut S, &mut ::peg::error::ErrorState) -> ::peg::RuleResult<(i32, T)>) -> (T, ::peg::RuleResult<i32>),
) -> ::peg::RuleResult<T> {
match __infix_parse(state, err_state, 0, lpos, prefix_atom, level_code) {
::peg::RuleResult::Matched(__pos, (_, __v)) => ::peg::RuleResult::Matched(__pos, __v),
::peg::RuleResult::Failed => ::peg::RuleResult::Failed,
}
}

__infix_parse(__state, __err_state, 0, __pos,
__infix_parse_result(__state, __err_state, 0, __pos,
&|__pos, __state, __err_state, __recurse| {
let __lpos = __pos;
#(
Expand All @@ -1018,7 +1049,7 @@ fn compile_expr(context: &Context, e: &SpannedExpr, result_used: bool) -> TokenS

::peg::RuleResult::Failed
},
&|__pos, __lpos, __min_prec, mut __infix_result, __state, __err_state, __recurse| {
&|__pos, __lpos, __min_prec, __assoc_prec, mut __infix_result, __state, __err_state, __recurse| {
#(#level_code)*
(__infix_result, ::peg::RuleResult::Failed)
}
Expand Down
87 changes: 87 additions & 0 deletions tests/pass/arithmetic_no_associative.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
extern crate peg;

peg::parser!(grammar arithmetic() for str {
pub(crate) rule no_assoc() = precedence! {
(@) "||" @ {}
@ "@@" (@) {}
--
@ "<" @ {}
@ ">" @ {}
--
@ "?" {}
--
"-" @ {}
--
@ "." @ {}
--
"x" {}
}
});

#[test]
fn main() {
assert!(arithmetic::no_assoc("x<x").is_ok());
assert!(arithmetic::no_assoc("x>x").is_ok());
assert!(arithmetic::no_assoc("-x").is_ok());
assert!(arithmetic::no_assoc("x?").is_ok());

assert!(arithmetic::no_assoc("x||x").is_ok());
assert!(arithmetic::no_assoc("x<x||x").is_ok());
assert!(arithmetic::no_assoc("x||x<x").is_ok());
assert!(arithmetic::no_assoc("x<x||x<x").is_ok());
assert!(arithmetic::no_assoc("x<x||x<x||x<x").is_ok());
assert!(arithmetic::no_assoc("x@@x").is_ok());
assert!(arithmetic::no_assoc("x<x@@x").is_ok());
assert!(arithmetic::no_assoc("x@@x<x").is_ok());
assert!(arithmetic::no_assoc("x<x@@x<x").is_ok());
assert!(arithmetic::no_assoc("x<x@@x<x@@x<x").is_ok());

assert!(arithmetic::no_assoc("-x?").is_ok());
assert!(arithmetic::no_assoc("x<-x").is_ok());
assert!(arithmetic::no_assoc("-x<x").is_ok());
assert!(arithmetic::no_assoc("-x<-x").is_ok());
assert!(arithmetic::no_assoc("x<x?").is_ok());
assert!(arithmetic::no_assoc("x?<x").is_ok());
assert!(arithmetic::no_assoc("x?<x?").is_ok());
assert!(arithmetic::no_assoc("x<-x?").is_ok());
assert!(arithmetic::no_assoc("x?<-x?").is_ok());
assert!(arithmetic::no_assoc("-x?<-x?").is_ok());

assert!(arithmetic::no_assoc("x.x").is_ok());
assert!(arithmetic::no_assoc("x.x<x").is_ok());
assert!(arithmetic::no_assoc("x<x.x").is_ok());
assert!(arithmetic::no_assoc("x.x<x.x").is_ok());
assert!(arithmetic::no_assoc("x||x.x<x.x").is_ok());
assert!(arithmetic::no_assoc("x.x||x.x<x.x").is_ok());
assert!(arithmetic::no_assoc("x.x||x<x.x").is_ok());

assert!(arithmetic::no_assoc("x<x<x").is_err());
assert!(arithmetic::no_assoc("x<x>x").is_err());
assert!(arithmetic::no_assoc("x?<x<x").is_err());
assert!(arithmetic::no_assoc("-x<x<x").is_err());
assert!(arithmetic::no_assoc("x<-x<x").is_err());
assert!(arithmetic::no_assoc("x<x?<x").is_err());
assert!(arithmetic::no_assoc("x<x?<-x").is_err());
assert!(arithmetic::no_assoc("x<x?<x?").is_err());

assert!(arithmetic::no_assoc("x<x<x||x").is_err());
assert!(arithmetic::no_assoc("x<x<x||x<x").is_err());
assert!(arithmetic::no_assoc("x||x<x<x").is_err());
assert!(arithmetic::no_assoc("x<x||x<x<x").is_err());
assert!(arithmetic::no_assoc("x<x<x@@x").is_err());
assert!(arithmetic::no_assoc("x<x<x@@x<x").is_err());
assert!(arithmetic::no_assoc("x@@x<x<x").is_err());
assert!(arithmetic::no_assoc("x<x@@x<x<x").is_err());

assert!(arithmetic::no_assoc("x<x.x.x").is_err());
assert!(arithmetic::no_assoc("x||x<x.x.x").is_err());
assert!(arithmetic::no_assoc("x@@x<x.x.x").is_err());
assert!(arithmetic::no_assoc("x.x<x.x.x").is_err());
assert!(arithmetic::no_assoc("x.x<x||x.x.x").is_err());
assert!(arithmetic::no_assoc("x.x<x@@x.x.x").is_err());
assert!(arithmetic::no_assoc("x.x.x<x").is_err());
assert!(arithmetic::no_assoc("x.x.x<x.x").is_err());
assert!(arithmetic::no_assoc("x.x.x<x.x||x").is_err());
assert!(arithmetic::no_assoc("x.x.x<x.x||x.x").is_err());
assert!(arithmetic::no_assoc("x.x.x<x||x.x").is_err());
}
1 change: 1 addition & 0 deletions tests/pass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod arithmetic_ast;
mod arithmetic_infix;
mod arithmetic_infix_ast;
mod arithmetic_infix_ast_span;
mod arithmetic_no_associative;
mod arithmetic_with_left_recursion;
mod assembly_ast_dyn_type_param_bounds;
mod borrow_from_input;
Expand Down