Skip to content

Instantly share code, notes, and snippets.

@curlpipe
Created March 31, 2021 22:09
Show Gist options
  • Save curlpipe/f6aa99435dbe5626ebc31419ba30507c to your computer and use it in GitHub Desktop.
Save curlpipe/f6aa99435dbe5626ebc31419ba30507c to your computer and use it in GitHub Desktop.
A simple pratt parser in Rust
// A simple pratt parser in Rust
use std::fmt;
// Tree enum, for representing trees
pub enum Tree {
// This holds a number e.g. `3`
Number(usize),
// This holds an operation e.g. `1 + 2`
Operation(Box<Tree>, char, Box<Tree>),
}
// Nice way to display a tree
impl fmt::Display for Tree {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Match this tree
match self {
// If tree part is a number, display it
Tree::Number(n) => write!(f, "{}", n),
// If tree part is an operation, put brackets around it
Tree::Operation(lhs, op, rhs) => write!(
f, "({} {} {})",
lhs, op, rhs
),
}
}
}
// Pratt struct to hold source code and operate on it
pub struct Pratt {
chars: Vec<char>,
ptr: usize,
}
impl Pratt {
pub fn new(src: &str) -> Self {
// Create new instance
Self { chars: src.chars().collect(), ptr: 0 }
}
pub fn expr(&mut self, power: u8) -> Tree {
// Parse an expression
let n = self.next();
// Get the left hand side
let mut left = self.part(n);
// Check precedence is still OK
while Pratt::prec(self.peek()) > power {
// Recurse into binary operation
let op = self.next();
left = self.bin_op(left, op);
}
// Return resultant tree
left
}
pub fn part(&mut self, tok: char) -> Tree {
// Parse number and create Number node
Tree::Number(tok.to_string().parse().unwrap())
}
fn bin_op(&mut self, left: Tree, op: char) -> Tree {
// Take left hand side, calculate right hand side, return tree
let right = self.expr(Pratt::prec(op));
Tree::Operation(Box::new(left), op, Box::new(right))
}
fn prec(tok: char) -> u8 {
// Assigns a numeric value to each operator
match tok {
'^' => 3, // God
'*' | '/' => 2, // Highest
'+' | '-' => 1, // Middle
_ => 0, // Lowest
}
}
fn next(&mut self) -> char {
// Get the current character and move on
self.ptr += 1;
self.chars[self.ptr - 1]
}
fn peek(&self) -> char {
// Look at the current character without affecting the pointer
*self.chars.get(self.ptr).unwrap_or(&' ')
}
}
fn main() {
// Do parse and display it nicely
let arithmetic = "1/4+2";
let mut p = Pratt::new(arithmetic);
println!("input: {:?}", arithmetic);
println!("output: {}", p.expr(0));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment