Crates.io | TSPL |
lib.rs | TSPL |
version | 0.0.13 |
source | src |
created_at | 2024-02-23 21:45:40.558555 |
updated_at | 2024-08-06 21:38:15.37485 |
description | The Simplest Parser Library |
homepage | |
repository | https://github.com/HigherOrderCO/TSPL |
max_upload_size | |
id | 1151016 |
size | 13,046 |
TSPL is the The Simplest Parser Library that works in Rust.
In pure functional languages like Haskell, a Parser can be represented as a function:
Parser<A> ::= String -> Reply<A, Error>
This allows us to implement a Monad instance for Parser<A>
, letting us use the do-notation
to
create simple and elegant parsers for our own types. Sadly, Rust doesn't have an equivalent. Yet,
we can easily emulate it by:
Using structs and impl
to manage the cursor state internally.
Returning a Result
, which allows us to use Rust's ?
to emulate monadic blocks.
This library merely exposes some functions to implement parsers that way, and nothing else.
As an example, let's create a λ-Term parser using TSPL.
enum Term {
Lam { name: String, body: Box<Term> },
App { func: Box<Term>, argm: Box<Term> },
Var { name: String },
}
<term> ::= <lam> | <app> | <var>
<lam> ::= "λ" <name> " " <term>
<app> ::= "(" <term> " " <term> ")"
<var> ::= alphanumeric_string
new_parser()!
macro.TSPL::new_parser!(TermParser);
impl TermParser
, with your grammar:impl<'i> TermParser<'i> {
fn parse(&mut self) -> Result<Term, String> {
self.skip_trivia();
match self.peek_one() {
Some('λ') => {
self.consume("λ")?;
let name = self.parse_name()?;
let body = Box::new(self.parse()?);
Ok(Term::Lam { name, body })
}
Some('(') => {
self.consume("(")?;
let func = Box::new(self.parse()?);
let argm = Box::new(self.parse()?);
self.consume(")")?;
Ok(Term::App { func, argm })
}
_ => {
let name = self.parse_name()?;
Ok(Term::Var { name })
}
}
}
}
fn main() {
let mut parser = TermParser::new("λx(λy(x y) λz z)");
match parser.parse() {
Ok(term) => println!("{:?}", term),
Err(err) => eprintln!("{}", err),
}
}
The complete example is available in ./examples/lambda_term.rs. Run it with:
cargo run --example lambda_term
This design is based on T6's new parser for HVM-Core, and is much cleaner than the old HOPA approach.