Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relax %parse-param bounds from Copy to Clone #471

Merged
merged 1 commit into from
Oct 9, 2024
Merged
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
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/clone_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 = "clone_param"
version = "0.1.0"
authors = ["Matt Rice <[email protected]>"]
edition = "2021"
license = "Apache-2.0/MIT"

[[bin]]
doc = false
name = "clone_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 lrlex::lrlex_mod;
use lrpar::lrpar_mod;
use std::io::{self, BufRead, Write};
use std::{cell::RefCell, rc::Rc};

// 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