prattle/lib.rs
1// lib.rs - MIT License
2// MIT License
3// Copyright (c) 2018 Tyler Laing (ZerothLaw)
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23//! # Introduction
24//!
25//! This crate implements a configurable and general-purpose Pratt parser.
26//!
27//! A Pratt parser is also known as a Top-Down Operator Precedence parser,
28//! aka TDOP parser. The general algorithm was discovered and outlined by
29//! Vaughn Pratt in 1973 in his paper[1].
30//!
31//! It differs from recursive-descent classes of parsers by associating
32//! parsing rules to tokens rather than grammar rules.
33//!
34//! This is especially valuable when parsing expression grammars with
35//! operator precedence without significant descent and type costs.
36//!
37//! Consider the following expression:
38//!
39//! > a + b * c
40//!
41//! So which variables are to be associated with which operators?
42//! A recursive-descent parser would need to implement two layers of
43//! grammar rules which are recursive with each other. This leads to
44//! potential infinite loops if your parser follows the wrong rule and
45//! doesn't backtrack effectively.
46//!
47//! Whereas with a Pratt Parser, you need just three rules and three
48//! binding powers:
49//! null denotation of Variable => Node::Simple(token);
50//! left denotation of Add => Node::Composite(token: token, children: vec![node,
51//! parser.parse_expr(5)]);
52//! left denotation of Mul => Node::Composite(token: token, children: vec![node,
53//! parser.parse_expr(10)]);
54//! lbp of Variable = 0;
55//! lbp of Add = 5;
56//! lbp of Mul = 10;
57//!
58//! And now it will correctly associate 'b' and 'c' tokens with the mul operator, and
59//! then the result of that with the 'a' token and the add operator.
60//!
61//! It also executes with far fewer parse calls, creating a minimal stack depth
62//! during execution.
63//!
64//! ## Example
65//!
66//! ```rust
67//! use std::fmt::{Display, Formatter, Error};
68//!
69//! extern crate prattle;
70//! use prattle::prelude::*;
71//!
72//! #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
73//! enum CToken {
74//! Var(String),
75//! Add,
76//! Mul
77//! }
78//!
79//! impl Display for CToken {
80//! fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
81//! write!(f, "{:?}", self)
82//! }
83//! }
84//!
85//! fn main() {
86//! let mut spec = ParserSpec::new();
87//!
88//! spec.add_null_assoc(
89//! CToken::Var("".to_string()),
90//! PrecedenceLevel::Root,
91//! |parser: &mut dyn Parser<CToken>, token, _| {
92//! Ok(Node::Simple(token.clone()))
93//! }
94//! );
95//! spec.add_left_assoc(
96//! CToken::Add,
97//! PrecedenceLevel::First,
98//! |parser, token, lbp, node| {
99//! Ok(Node::Composite{token: token.clone(), children: vec![
100//! node,
101//! parser.parse_expr(lbp)?]})
102//! });
103//! spec.add_left_assoc(
104//! CToken::Mul,
105//! PrecedenceLevel::Second,
106//! |parser, token, lbp, node| {
107//! Ok(Node::Composite{token: token.clone(), children: vec![
108//! node,
109//! parser.parse_expr(lbp)?]})
110//! });
111//!
112//! let lexer = LexerVec::new(vec![
113//! CToken::Var("a".to_string()),
114//! CToken::Add,
115//! CToken::Var("b".to_string()),
116//! CToken::Mul,
117//! CToken::Var("c".to_string())
118//! ]);
119//!
120//! let mut parser = GeneralParser::new(spec, lexer);
121//!
122//! let res = parser.parse();
123//! println!("{:?}", res);
124//! }
125//! ```
126//!
127//! ## Capabilities
128//!
129//! This crate enables a very fast and simple parser, with simple rules, that is
130//! easy to understand and maintain. Most of the work is in implementing the
131//! required traits on your Token type.
132//!
133//! ## More complex examples
134//!
135//! Run:
136//! > cargo run --example token_spec
137//!
138//! examples/token_spec.rs shows an example of how to implement the traits for
139//! the token type so it can be used to lookup the parse rules (uses HashMap).
140//!
141//! ## Citations
142//! > [1] Vaughan R. Pratt. 1973. Top down operator precedence. In Proceedings
143//! > of the 1st annual ACM SIGACT-SIGPLAN symposium on Principles of
144//! > programming languages (POPL '73). ACM, New York, NY, USA, 41-51.
145//! > DOI=http://dx.doi.org/10.1145/512927.512931
146//!
147
148#[macro_use] extern crate failure;
149
150#[macro_use] pub mod macros;
151
152pub mod errors;
153pub mod lexer;
154pub mod node;
155pub mod parser;
156pub mod precedence;
157pub mod spec;
158pub mod token;
159
160/// Handy prelude mod containing everything you need to get started.
161pub mod prelude {
162 pub use errors::ParseError;
163 pub use lexer::{Lexer, LexerVec};
164 pub use node::Node;
165 pub use parser::{Parser, GeneralParser};
166 pub use precedence::PrecedenceLevel;
167 pub use spec::{ParserSpec, SpecificationError};
168 pub use token::Token;
169}
170
171//Little container mod for type aliases that are convenient and short
172pub mod types {
173 use super::prelude::*;
174 pub type NullDenotation<T> = fn(&mut dyn Parser<T>, T, PrecedenceLevel) -> Result<Node<T>, ParseError<T>>;
175 pub type LeftDenotation<T> = fn(&mut dyn Parser<T>, T, PrecedenceLevel, Node<T>) -> Result<Node<T>, ParseError<T>>;
176
177 pub type NullInfo<T> = (PrecedenceLevel, NullDenotation<T>);
178 pub type LeftInfo<T> = (PrecedenceLevel, PrecedenceLevel, LeftDenotation<T>);
179}