Skip to content

Commit

Permalink
Merge pull request #471 from ratmice/clone_parse_param
Browse files Browse the repository at this point in the history
Relax `%parse-param` bounds from `Copy` to `Clone`
  • Loading branch information
ltratt authored Oct 8, 2024
2 parents 2140334 + c15507a commit 61913eb
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 16 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members=[
"lrpar/examples/calc_ast",
"lrpar/examples/calc_parsetree",
"lrpar/examples/start_states",
"lrpar/examples/mut_param",
"lrtable",
"nimbleparse",
]
Expand Down
6 changes: 3 additions & 3 deletions doc/src/actioncode.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ make use of the following:

A single extra parameter can be passed to action functions if the `%parse-param
<var>: <type>` declaration is used. The variable `<var>` is then visible in all
action code. `<type>` must implement the [`Copy`
trait](https://doc.rust-lang.org/std/marker/trait.Copy.html) (note that `&`
references implement `Copy`).
action code. `<type>` must implement the [`Clone`
trait](https://doc.rust-lang.org/stable/std/clone/trait.Clone.html) (note that `Copy`
bounds imply `Clone`, and `&` references implement `Copy`).

For example if a grammar has a declaration:

Expand Down
13 changes: 13 additions & 0 deletions lrpar/cttests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ lrpar_mod!("multitypes.y");
lrlex_mod!("parseparam.l");
lrpar_mod!("parseparam.y");

lrlex_mod!("parseparam_copy.l");
lrpar_mod!("parseparam_copy.y");

lrlex_mod!("passthrough.l");
lrpar_mod!("passthrough.y");

Expand Down Expand Up @@ -247,6 +250,16 @@ fn test_parseparam() {
}
}

#[test]
fn test_parseparam_copy() {
let lexerdef = parseparam_copy_l::lexerdef();
let lexer = lexerdef.lexer("101");
match parseparam_copy_y::parse(&lexer, 3) {
(Some(104), _) => (),
_ => unreachable!(),
}
}

#[test]
fn test_passthrough() {
let lexerdef = passthrough_l::lexerdef();
Expand Down
18 changes: 18 additions & 0 deletions lrpar/cttests/src/parseparam_copy.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Test %parse-param copy
yacckind: Grmtools
grammar: |
%start S
%parse-param p: u64
%%
S -> u64:
// Previously %parse-param required a `Copy` bounds.
// Since then we relaxed the bounds to require `Clone`.
// This tests backwards compatibility of actions that
// rely on the older copy bounds.
'INT' { (move |_| {})(p); check_copy(p); p + $lexer.span_str($1.unwrap().span()).parse::<u64>().unwrap() }
;
%%
fn check_copy<T: Copy>(_: T){}
lexer: |
%%
[0-9]+ 'INT'
20 changes: 20 additions & 0 deletions lrpar/examples/clone_param/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "mut_param"
version = "0.1.0"
authors = ["Matt Rice <[email protected]>"]
edition = "2021"
license = "Apache-2.0/MIT"

[[bin]]
doc = false
name = "mut_param"

[build-dependencies]
cfgrammar = { path="../../../cfgrammar" }
lrlex = { path="../../../lrlex" }
lrpar = { path="../.." }

[dependencies]
cfgrammar = { path="../../../cfgrammar" }
lrlex = { path="../../../lrlex" }
lrpar = { path="../.." }
20 changes: 20 additions & 0 deletions lrpar/examples/clone_param/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# `clone_param`

## Description
Example which shows how to use interior mutability with the `%parse-param` directive.
As a parameter the parse function accepts a `Rc<RefCell<u64>>`.

## Input
For input the parser accepts a positive or negative integer e.g. `-1`, `42`, etc followed
by any sequence of `+`, or `-` characters. Except for the initial `-` on a negative integer,
`+` or `-` are treated as `Increment` and `Decrement` operators.

## Evaluation
Rather than building an AST, the param is directly mutated by the actions.
As such an input sequence like `-3++-` will evalute to `-2`.

## Example
```
>>> -3++-
Evaluated: RefCell { value: -2 }
```
20 changes: 20 additions & 0 deletions lrpar/examples/clone_param/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#![deny(rust_2018_idioms)]
use cfgrammar::yacc::YaccKind;
use lrlex::CTLexerBuilder;

fn main() {
// Since we're using both lrlex and lrpar, we use lrlex's `lrpar_config` convenience function
// that makes it easy to a) create a lexer and parser and b) link them together.
CTLexerBuilder::new()
.rust_edition(lrlex::RustEdition::Rust2021)
.lrpar_config(|ctp| {
ctp.yacckind(YaccKind::Grmtools)
.rust_edition(lrpar::RustEdition::Rust2021)
.grammar_in_src_dir("param.y")
.unwrap()
})
.lexer_in_src_dir("param.l")
.unwrap()
.build()
.unwrap();
}
40 changes: 40 additions & 0 deletions lrpar/examples/clone_param/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![allow(clippy::unnecessary_wraps)]

use std::io::{self, BufRead, Write};
use std::{ rc::Rc, cell::RefCell };
use lrlex::lrlex_mod;
use lrpar::lrpar_mod;

// Using `lrlex_mod!` brings the lexer for `param.l` into scope. By default the module name will be
// `param_l` (i.e. the file name, minus any extensions, with a suffix of `_l`).
lrlex_mod!("param.l");
// Using `lrpar_mod!` brings the parser for `param.y` into scope. By default the module name will be
// `param_y` (i.e. the file name, minus any extensions, with a suffix of `_y`).
lrpar_mod!("param.y");

fn main() {
// Get the `LexerDef` for the `param` language.
let lexerdef = param_l::lexerdef();
let stdin = io::stdin();
loop {
print!(">>> ");
io::stdout().flush().ok();
match stdin.lock().lines().next() {
Some(Ok(ref l)) => {
if l.trim().is_empty() {
continue;
}
// Now we create a lexer with the `lexer` method with which we can lex an input.
let lexer = lexerdef.lexer(l);
let param = Rc::new(RefCell::new(0));
// Pass the lexer to the parser and lex and parse the input.
let (_opt , errs) = param_y::parse(&lexer, param.clone());
for e in errs {
println!("{}", e.pp(&lexer, &param_y::token_epp));
}
println!("Evaluated: {:?}", &param);
}
_ => break,
}
}
}
5 changes: 5 additions & 0 deletions lrpar/examples/clone_param/src/param.l
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
%%
(\-?)[0-9]+ "INT"
\- "Decr"
\+ "Incr"
[\n\t\ ] ;
21 changes: 21 additions & 0 deletions lrpar/examples/clone_param/src/param.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
%token Incr Decr
%parse-param val: Rc<RefCell<i64>>
%%
Expr -> () : "INT" Ops {
*val.borrow_mut() += parse_int($lexer.span_str($1.map_err(|_| "<evaluation aborted>").unwrap().span())).unwrap()
};
Ops -> ():
%empty {}
| Ops Incr { *val.borrow_mut() += 1; }
| Ops Decr { *val.borrow_mut() -= 1; };
%%
use std::{ rc::Rc, cell::RefCell, error::Error };

fn parse_int(s: &str) -> Result<i64, Box<dyn Error>> {
match s.parse::<i64>() {
Ok(val) => Ok(val),
Err(_) => {
Err(Box::from(format!("{} cannot be represented as a i64", s)))
}
}
}
14 changes: 7 additions & 7 deletions lrpar/src/lib/cpctplus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ struct CPCTPlus<
StorageT: 'static + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
> where
usize: AsPrimitive<StorageT>,
{
Expand All @@ -118,7 +118,7 @@ pub(super) fn recoverer<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
>(
parser: &'a Parser<StorageT, LexerTypesT, ActionT, ParamT>,
) -> Box<dyn Recoverer<StorageT, LexerTypesT, ActionT, ParamT> + 'a>
Expand All @@ -135,7 +135,7 @@ impl<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
> Recoverer<StorageT, LexerTypesT, ActionT, ParamT>
for CPCTPlus<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>
where
Expand Down Expand Up @@ -270,7 +270,7 @@ impl<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
> CPCTPlus<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>
where
usize: AsPrimitive<StorageT>,
Expand Down Expand Up @@ -457,7 +457,7 @@ fn apply_repairs<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
>(
parser: &Parser<StorageT, LexerTypesT, ActionT, ParamT>,
mut laidx: usize,
Expand Down Expand Up @@ -496,7 +496,7 @@ fn simplify_repairs<
StorageT: 'static + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT,
ParamT: Copy,
ParamT: Clone,
>(
parser: &Parser<StorageT, LexerTypesT, ActionT, ParamT>,
all_rprs: &mut Vec<Vec<ParseRepair<LexerTypesT::LexemeT, StorageT>>>,
Expand Down Expand Up @@ -557,7 +557,7 @@ fn rank_cnds<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
>(
parser: &Parser<StorageT, LexerTypesT, ActionT, ParamT>,
finish_by: Instant,
Expand Down
12 changes: 6 additions & 6 deletions lrpar/src/lib/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ pub(super) struct Parser<
StorageT: 'static + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
> where
usize: AsPrimitive<StorageT>,
{
Expand Down Expand Up @@ -241,7 +241,7 @@ impl<
StorageT: 'static + Debug + Eq + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT: 'a,
ParamT: Copy,
ParamT: Clone,
> Parser<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>
where
usize: AsPrimitive<StorageT>,
Expand Down Expand Up @@ -329,7 +329,7 @@ where
self.lexer,
span,
astack.drain(pop_idx - 1..),
self.param,
self.param.clone(),
));
astack.push(v);
}
Expand Down Expand Up @@ -447,7 +447,7 @@ where
self.lexer,
span,
astack_uw.drain(pop_idx - 1..),
self.param,
self.param.clone(),
));
astack_uw.push(v);
} else {
Expand Down Expand Up @@ -595,7 +595,7 @@ pub(super) trait Recoverer<
StorageT: 'static + Debug + Hash + PrimInt + Unsigned,
LexerTypesT: LexerTypes<StorageT = StorageT>,
ActionT,
ParamT: Copy,
ParamT: Clone,
> where
usize: AsPrimitive<StorageT>,
{
Expand Down Expand Up @@ -877,7 +877,7 @@ where
/// (`None, [...]`), errors and a value (`Some(...), [...]`), as well as a value and no errors
/// (`Some(...), []`). Errors are sorted by the position they were found in the input and can
/// be a mix of lexing and parsing errors.
pub fn parse_actions<'b: 'a, 'input: 'b, ActionT: 'a, ParamT: Copy>(
pub fn parse_actions<'b: 'a, 'input: 'b, ActionT: 'a, ParamT: Clone>(
&self,
lexer: &'b dyn NonStreamingLexer<'input, LexerTypesT>,
actions: &'a [ActionFn<'a, 'b, 'input, StorageT, LexerTypesT, ActionT, ParamT>],
Expand Down

0 comments on commit 61913eb

Please sign in to comment.