From b997cce5c2c08c88710e01add5078c43d45b6d20 Mon Sep 17 00:00:00 2001 From: matt rice Date: Tue, 8 Oct 2024 06:53:33 -0700 Subject: [PATCH] Add a mut_param example This shows using the `%parse-param` with interior mutability. --- Cargo.toml | 1 + lrpar/examples/mut_param/Cargo.toml | 20 ++++++++++++++ lrpar/examples/mut_param/README.md | 20 ++++++++++++++ lrpar/examples/mut_param/build.rs | 20 ++++++++++++++ lrpar/examples/mut_param/src/main.rs | 40 ++++++++++++++++++++++++++++ lrpar/examples/mut_param/src/param.l | 5 ++++ lrpar/examples/mut_param/src/param.y | 21 +++++++++++++++ lrpar/src/lib/parser.rs | 2 +- 8 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 lrpar/examples/mut_param/Cargo.toml create mode 100644 lrpar/examples/mut_param/README.md create mode 100644 lrpar/examples/mut_param/build.rs create mode 100644 lrpar/examples/mut_param/src/main.rs create mode 100644 lrpar/examples/mut_param/src/param.l create mode 100644 lrpar/examples/mut_param/src/param.y diff --git a/Cargo.toml b/Cargo.toml index 24286930e..4fb67f257 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members=[ "lrpar/examples/calc_ast", "lrpar/examples/calc_parsetree", "lrpar/examples/start_states", + "lrpar/examples/mut_param", "lrtable", "nimbleparse", ] diff --git a/lrpar/examples/mut_param/Cargo.toml b/lrpar/examples/mut_param/Cargo.toml new file mode 100644 index 000000000..9008252d9 --- /dev/null +++ b/lrpar/examples/mut_param/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "mut_param" +version = "0.1.0" +authors = ["Laurence Tratt "] +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="../.." } diff --git a/lrpar/examples/mut_param/README.md b/lrpar/examples/mut_param/README.md new file mode 100644 index 000000000..650570f47 --- /dev/null +++ b/lrpar/examples/mut_param/README.md @@ -0,0 +1,20 @@ +# `mut_param` + +## Description +Example which shows how to use interior mutability with the `%parse-param` directive. +As a parameter the parse function accepts a `Rc>`. + +## 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 } +``` diff --git a/lrpar/examples/mut_param/build.rs b/lrpar/examples/mut_param/build.rs new file mode 100644 index 000000000..2bca2a146 --- /dev/null +++ b/lrpar/examples/mut_param/build.rs @@ -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(); +} diff --git a/lrpar/examples/mut_param/src/main.rs b/lrpar/examples/mut_param/src/main.rs new file mode 100644 index 000000000..82aed5461 --- /dev/null +++ b/lrpar/examples/mut_param/src/main.rs @@ -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, ¶m_y::token_epp)); + } + println!("Evaluated: {:?}", ¶m); + } + _ => break, + } + } +} diff --git a/lrpar/examples/mut_param/src/param.l b/lrpar/examples/mut_param/src/param.l new file mode 100644 index 000000000..0b3447a79 --- /dev/null +++ b/lrpar/examples/mut_param/src/param.l @@ -0,0 +1,5 @@ +%% +(\-?)[0-9]+ "INT" +\- "Decr" +\+ "Incr" +[\n\t\ ] ; diff --git a/lrpar/examples/mut_param/src/param.y b/lrpar/examples/mut_param/src/param.y new file mode 100644 index 000000000..6dd21fc7d --- /dev/null +++ b/lrpar/examples/mut_param/src/param.y @@ -0,0 +1,21 @@ +%token Incr Decr +%parse-param val: Rc> +%% +Expr -> () : "INT" Ops { + *val.borrow_mut() += parse_int($lexer.span_str($1.map_err(|_| "").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> { + match s.parse::() { + Ok(val) => Ok(val), + Err(_) => { + Err(Box::from(format!("{} cannot be represented as a i64", s))) + } + } +} diff --git a/lrpar/src/lib/parser.rs b/lrpar/src/lib/parser.rs index 7252b48be..d6a1ca241 100644 --- a/lrpar/src/lib/parser.rs +++ b/lrpar/src/lib/parser.rs @@ -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>],