Created
April 13, 2019 01:57
-
-
Save shanemikel/7d2d05059a99ed6573805818162c1b3f to your computer and use it in GitHub Desktop.
Lua parsing in Rust with Pest (operator precedence)
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
#[derive(Parser)] | |
#[grammar = "lua.pest"] | |
pub struct LuaParser; | |
lazy_static! { | |
static ref PREC_CLIMBER: PrecClimber<Rule> = { | |
use Rule::*; | |
use Operator as O; | |
use Assoc::Left as L; | |
PrecClimber::new(vec![ | |
O::new(Or, L), | |
O::new(And, L), | |
O::new(Eq, L) | O::new(Neq, L), | |
O::new(Lt, L) | O::new(Leq, L) | O::new(Gt, L) | O::new(Geq, L), | |
O::new(Add, L) | O::new(Sub, L), | |
O::new(Mul, L) | O::new(Div, L), | |
]) | |
}; | |
} | |
#[derive(Debug)] | |
pub enum Exp { | |
Val(Val), | |
Var(Var), | |
BinOp(Box<Exp>, Op, Box<Exp>), | |
} | |
impl<'a> FromPest<'a> for Exp { | |
type Rule = Rule; | |
type FatalError = ErrorContext; | |
fn from_pest(mut pairs: &mut Pairs<'a, Rule>) -> ParseResult<Self> { | |
fn map(pair: Pair<Rule>) -> ParseResult<Exp> { | |
let rule = pair.as_rule(); | |
let body = pair.as_str(); | |
log::debug!("`Exp::from_pest::map` called on `{:?}`: {:?}", rule, body); | |
let mut pairs = pair.into_inner(); | |
match rule { | |
Rule::Val => Val::from_pest(&mut pairs).map(Exp::Val), | |
Rule::Var => Var::from_pest(&mut pairs).map(Exp::Var), | |
Rule::Exp => Exp::from_pest(&mut pairs), | |
_rule => unreachable!(), | |
} | |
} | |
fn reduce(lhs: ParseResult<Exp>, op: Pair<Rule>, rhs: ParseResult<Exp>) -> | |
ParseResult<Exp> | |
{ | |
let lhs = lhs?; | |
let rhs = rhs?; | |
let rule = op.as_rule(); | |
let body = op.as_str(); | |
let args = format!("({:?}, {:?}: \"{}\", {:?}", lhs, rule, body, rhs); | |
log::debug!("`Exp::from_pest::reduce` called with {}", args); | |
let mut pairs = op.into_inner(); | |
let op = Op::from_pest(&mut pairs)?; | |
Ok(Exp::BinOp(Box::new(lhs), op, Box::new(rhs))) | |
} | |
let pair = pairs.peek().ok_or(NoMatch)?; | |
let rule = pair.as_rule(); | |
let body = pair.as_str(); | |
log::debug!("`Exp::from_pest` called on `{:?}`: {:?}", rule, body); | |
match rule { | |
Rule::Val => Val::from_pest(&mut pairs).map(Exp::Val), | |
Rule::Var => Var::from_pest(&mut pairs).map(Exp::Var), | |
Rule::Exp => { | |
let pairs = pairs.next().unwrap().into_inner(); | |
PREC_CLIMBER.climb(pairs, map, reduce) | |
}, | |
_rule => Err(NoMatch), | |
} | |
} | |
} |
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
Script = { SOI ~ Block ~ EOI } | |
Block = { (Stat ~ ";"?)* } | |
Stat = { Assignment | Do | While } | |
Assignment = { VarList1 ~ "=" ~ ExpList1 } | |
Do = { "do" ~ Block ~ "end" } | |
While = { "while" ~ Exp ~ "do" ~ Block ~ "end" } | |
VarList1 = _{ Var ~ ("," ~ Var)* } | |
ExpList1 = _{ Exp ~ ("," ~ Exp)* } | |
Exp = { Term ~ (Op ~ Term)* } | |
Term = _{ Atom | "(" ~ Exp ~ ")" } | |
Atom = _{ Val | Var } | |
Var = { Name } | |
Val = { Nil | Bool | Num } | |
Nil = @{ "nil" } | |
Bool = { True | False } | |
Num = _{ Float | Int } | |
True = @{ "true" } | |
False = @{ "false" } | |
Op = { OpArith | OpComp | OpLogic } | |
OpArith = _{ Pow | Mul | Div | Add | Sub } | |
OpComp = _{ Lt | Leq | Gt | Geq | Eq | Neq } | |
OpLogic = _{ Not | And | Or } | |
Not = { "not" } | |
Pow = { "^" } | |
Mul = { "*" } | |
Div = { "/" } | |
Add = { "+" } | |
Sub = { "-" } | |
Lt = { "<" } | |
Leq = { "<=" } | |
Gt = { ">" } | |
Geq = { ">=" } | |
Eq = { "==" } | |
Neq = { "~=" } | |
And = { "and" } | |
Or = { "or" } | |
Name = @{ !Keyword ~ NameSeq } | |
Keyword = _{ "do" | "while" | "end" } | |
NameSeq = _{ (Lower | "_") ~ (Alpha | Digit | "_")* } | |
Float = @{ NumSign? ~ Int ~ "." ~ Digit+ } | |
Int = @{ NumSign? ~ Digit+ } | |
NumSign = _{ ("+" | "-") } | |
Lower = _{ 'a' .. 'z' } | |
Upper = _{ 'A' .. 'Z' } | |
Alpha = _{ Lower | Upper } | |
Digit = _{ '0' .. '9' } | |
WHITESPACE = _{ " " | "\t" | Newline } | |
Newline = _{ "\n" | "\r\n" } | |
COMMENT = _{ BlockComment | ("--" ~ (!Newline ~ ANY)*) } | |
BlockComment = _{ "--[[" ~ (BlockComment | !"]]" ~ ANY)* ~ "]]" } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For some reason it's not getting past the first terminal rule
EDIT in case it isn't clear, that output syntax tree at the end is supposed to be the final result of parsing the entire
Exp