Created
March 31, 2021 22:09
-
-
Save curlpipe/f6aa99435dbe5626ebc31419ba30507c to your computer and use it in GitHub Desktop.
A simple pratt parser in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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