use std::rc::Rc;
use pest::{
iterators::{Pair, Pairs},
Parser,
};
use pest_derive::Parser;
use thiserror::Error;
use crate::cst::{self, Catch, Comment, Cst, Expr, Ident, Literal, Stmt};
#[derive(Parser)]
#[grammar = "hurl_grammar.pest"]
pub struct HurlParser;
#[derive(Debug, Error)]
pub enum ParseError {
#[error("unexpected token")]
UnexpectedToken,
#[error("unexpected end of file")]
UnexpectedEof,
#[error("underlying error: {0}")]
PestError(#[from] Box<pest::error::Error<Rule>>),
}
impl From<pest::error::Error<Rule>> for ParseError {
fn from(e: pest::error::Error<Rule>) -> Self {
ParseError::PestError(Box::new(e))
}
}
pub fn parse(contents: &str) -> Result<Cst, ParseError> {
let mut parsed = HurlParser::parse(Rule::program, contents)?;
let pair = parsed.next().ok_or(ParseError::UnexpectedEof)?;
parse_program(pair)
}
pub fn parse_program(pair: Pair<Rule>) -> Result<Cst, ParseError> {
assert_eq!(pair.as_rule(), Rule::program);
let mut inner = pair.into_inner();
let stmts = parse_stmt_list(&mut inner)?;
Ok(Cst { stmts })
}
pub fn parse_leading_comments(pairs: &mut Pairs<Rule>) -> Result<Vec<Comment>, ParseError> {
let mut comments = vec![];
while let Some(pair) = pairs.peek()
&& pair.as_rule() == Rule::COMMENT
{
let pair = pairs.next().unwrap();
comments.push(parse_comment(pair)?);
}
Ok(comments)
}
pub fn parse_stmt_list(pairs: &mut Pairs<Rule>) -> Result<Vec<Stmt>, ParseError> {
let mut stmts: Vec<Stmt> = parse_leading_comments(pairs)?
.iter()
.cloned()
.map(Stmt::Comment)
.collect();
assert_eq!(pairs.peek().unwrap().as_rule(), Rule::stmt_list);
let mut prev_line = None;
let inner = pairs.next().unwrap().into_inner();
for pair in inner {
let (stmt, line) = parse_stmt(pair)?;
if let Some(prev_line) = prev_line {
if line - prev_line > 1 {
stmts.push(Stmt::BlankLines)
}
}
stmts.push(stmt);
prev_line = Some(line);
}
Ok(stmts)
}
pub fn parse_comment(pair: Pair<Rule>) -> Result<Comment, ParseError> {
assert_eq!(pair.as_rule(), Rule::COMMENT);
let comment = pair
.as_str()
.trim_start_matches('#')
.trim_start()
.to_string();
Ok(Comment(comment))
}
pub fn parse_stmt(pair: Pair<Rule>) -> Result<(Stmt, usize), ParseError> {
let line = pair.line_col().0;
if pair.as_rule() == Rule::COMMENT {
return Ok((Stmt::Comment(parse_comment(pair)?), line));
}
assert_eq!(pair.as_rule(), Rule::stmt);
let inner = pair.into_inner().next().unwrap();
let stmt = match inner.as_rule() {
Rule::import => parse_import(inner),
Rule::declaration => parse_declaration(inner),
Rule::assignment => parse_assignment(inner),
Rule::exception_handling => parse_try_catch(inner),
Rule::exception => parse_exception(inner),
Rule::return_stmt => Ok(Stmt::Return),
Rule::expr0 => Ok(Stmt::Expr(parse_expr(inner)?)),
_ => Err(ParseError::UnexpectedToken),
}?;
Ok((stmt, line))
}
pub fn parse_assignment(pair: Pair<Rule>) -> Result<Stmt, ParseError> {
assert_eq!(pair.as_rule(), Rule::assignment);
let mut inner = pair.into_inner();
let ident = parse_ident(inner.next().unwrap())?;
let expr = parse_expr(inner.next().unwrap())?;
Ok(Stmt::Assignment(ident, expr))
}
pub fn parse_exception(pair: Pair<Rule>) -> Result<Stmt, ParseError> {
assert_eq!(pair.as_rule(), Rule::exception);
let mut inner = pair.into_inner();
let toss_how = inner.next().unwrap();
let expr = parse_expr(inner.next().unwrap())?;
if toss_how.as_str() == "toss" {
Ok(Stmt::Toss(expr))
} else {
Ok(Stmt::Hurl(expr))
}
}
pub fn parse_try_catch(pair: Pair<Rule>) -> Result<Stmt, ParseError> {
assert_eq!(pair.as_rule(), Rule::exception_handling);
let mut inner = pair.into_inner();
let body = parse_stmt_list(&mut inner)?;
let handlers = parse_catch_list(inner.next().unwrap())?;
Ok(Stmt::TryCatch(body, Rc::new(handlers)))
}
fn parse_catch_list(pair: Pair<Rule>) -> Result<Vec<Catch>, ParseError> {
assert_eq!(pair.as_rule(), Rule::catch_list);
let inner = pair.into_inner();
inner
.map(|pair| parse_catch(pair))
.collect::<Result<Vec<_>, _>>()
}
fn parse_catch(pair: Pair<Rule>) -> Result<Catch, ParseError> {
assert_eq!(pair.as_rule(), Rule::catch);
let inner = pair.into_inner().next().unwrap();
match inner.as_rule() {
Rule::catch_as => {
let mut inner = inner.into_inner();
let ident = parse_ident(inner.next().unwrap())?;
let body = parse_stmt_list(&mut inner)?;
Ok(Catch::As(ident, body))
}
Rule::catch_expr => {
let mut inner = inner.into_inner();
let expr = parse_expr(inner.next().unwrap())?;
let body = parse_stmt_list(&mut inner)?;
Ok(Catch::Matching(expr, body))
}
Rule::catch_into => {
let mut inner = inner.into_inner();
let ident = parse_ident(inner.next().unwrap())?;
Ok(Catch::Into(ident))
}
_ => Err(ParseError::UnexpectedToken),
}
}
pub fn parse_import(pair: Pair<Rule>) -> Result<Stmt, ParseError> {
assert_eq!(pair.as_rule(), Rule::import);
let inner = pair.into_inner().next().unwrap();
let expr = parse_expr(inner)?;
Ok(Stmt::Import(expr))
}
pub fn parse_declaration(pair: Pair<Rule>) -> Result<Stmt, ParseError> {
assert_eq!(pair.as_rule(), Rule::declaration);
let mut inner = pair.into_inner();
let ident = parse_ident(inner.next().unwrap())?;
let expr = parse_expr(inner.next().unwrap())?;
Ok(Stmt::Declaration(ident, expr))
}
pub fn parse_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::expr0);
let inner: Vec<Pair<Rule>> = pair.into_inner().collect();
let lhs = parse_expr1(inner[0].clone())?;
if inner.len() == 1 {
Ok(lhs)
} else {
let op = parse_bin_op(inner[1].clone())?;
let rhs = parse_expr(inner[2].clone())?;
Ok(Expr::Bin(Box::new(lhs), op, Box::new(rhs)))
}
}
fn parse_ident(pair: Pair<Rule>) -> Result<Ident, ParseError> {
assert_eq!(pair.as_rule(), Rule::identifier);
let ident = pair.as_str().to_string();
Ok(Ident(ident))
}
fn parse_expr1(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::expr1);
let inner: Vec<Pair<Rule>> = pair.into_inner().collect();
let lhs = parse_expr2(inner[0].clone())?;
if inner.len() == 1 {
Ok(lhs)
} else {
let op = parse_bin_op(inner[1].clone())?;
let rhs = parse_expr1(inner[2].clone())?;
Ok(Expr::Bin(Box::new(lhs), op, Box::new(rhs)))
}
}
fn parse_expr2(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::expr2);
let inner: Vec<Pair<Rule>> = pair.into_inner().collect();
let lhs = parse_term(inner[0].clone())?;
if inner.len() == 1 {
Ok(lhs)
} else {
let op = parse_bin_op(inner[1].clone())?;
let rhs = parse_expr2(inner[2].clone())?;
Ok(Expr::Bin(Box::new(lhs), op, Box::new(rhs)))
}
}
fn parse_bin_op(pair: Pair<Rule>) -> Result<cst::BinOp, ParseError> {
match pair.as_rule() {
Rule::add => Ok(cst::BinOp::Add),
Rule::sub => Ok(cst::BinOp::Sub),
Rule::mul => Ok(cst::BinOp::Mul),
Rule::div => Ok(cst::BinOp::Div),
Rule::modu => Ok(cst::BinOp::Mod),
Rule::pow => Ok(cst::BinOp::Pow),
Rule::eq => Ok(cst::BinOp::Eq),
Rule::neq => Ok(cst::BinOp::Neq),
Rule::lt => Ok(cst::BinOp::Lt),
Rule::gt => Ok(cst::BinOp::Gt),
Rule::leq => Ok(cst::BinOp::Leq),
Rule::geq => Ok(cst::BinOp::Geq),
Rule::and => Ok(cst::BinOp::And),
Rule::or => Ok(cst::BinOp::Or),
_ => Err(ParseError::UnexpectedToken),
}
}
fn parse_term(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::term);
let inner: Vec<Pair<Rule>> = pair.into_inner().collect();
match inner[0].as_rule() {
Rule::index_access => parse_index_access(inner[0].clone()),
Rule::literal => {
let literal = parse_literal(inner[0].clone())?;
Ok(Expr::Literal(literal))
}
Rule::paren_expr => Ok(Expr::Paren(Box::new(parse_expr(
inner[0].clone().into_inner().next().unwrap(),
)?))),
Rule::func_expr => parse_func_expr(inner[0].clone()),
Rule::function_call => parse_function_call(inner[0].clone()),
Rule::identifier => parse_ident(inner[0].clone()).map(Expr::Ident),
Rule::not_expr => parse_not_expr(inner[0].clone()),
Rule::list_expr => parse_list_expr(inner[0].clone()),
_ => Err(ParseError::UnexpectedToken),
}
}
fn parse_not_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::not_expr);
let inner = pair.into_inner().next().unwrap();
let expr = parse_expr(inner)?;
Ok(Expr::Not(Box::new(expr)))
}
fn parse_list_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::list_expr);
let inner = pair.into_inner();
let exprs = inner.map(parse_expr).collect::<Result<Vec<_>, _>>()?;
Ok(Expr::List(exprs))
}
fn parse_function_call(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::function_call);
let mut inner = pair.into_inner();
let ident = parse_ident(inner.next().unwrap())?;
let args = if let Some(pairs) = inner.next() {
pairs
.into_inner()
.map(parse_expr)
.collect::<Result<Vec<_>, _>>()?
} else {
vec![]
};
Ok(Expr::Call(ident, args))
}
fn parse_literal(pair: Pair<Rule>) -> Result<Literal, ParseError> {
assert_eq!(pair.as_rule(), Rule::literal);
let inner = pair.into_inner().next().unwrap();
match inner.as_rule() {
Rule::number => parse_number(inner),
Rule::float => {
let float = inner.as_str().parse::<f64>().unwrap();
Ok(Literal::Float(float))
}
Rule::string => {
let string = inner
.as_str()
.strip_prefix('\"')
.unwrap()
.strip_suffix('\"')
.unwrap()
.to_string();
Ok(Literal::String(string))
}
Rule::boolean => {
let boolean = inner.as_str().parse::<bool>().unwrap();
Ok(Literal::Bool(boolean))
}
_ => Err(ParseError::UnexpectedToken),
}
}
fn parse_number(pair: Pair<Rule>) -> Result<Literal, ParseError> {
assert_eq!(pair.as_rule(), Rule::number);
let inner = pair.into_inner().next().unwrap();
match inner.as_rule() {
Rule::integer => {
let int = inner.as_str().parse::<i32>().unwrap();
Ok(Literal::Int(int))
}
Rule::float => {
let float = inner.as_str().parse::<f64>().unwrap();
Ok(Literal::Float(float))
}
_ => Err(ParseError::UnexpectedToken),
}
}
fn parse_func_expr(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::func_expr);
let mut inner = pair.into_inner();
let params = parse_params(inner.next().unwrap())?;
let body = parse_stmt_list(&mut inner)?;
Ok(Expr::Function(params, body))
}
fn parse_params(pair: Pair<Rule>) -> Result<Vec<Ident>, ParseError> {
assert_eq!(pair.as_rule(), Rule::params);
let mut params = Vec::new();
for param in pair.into_inner() {
params.push(parse_ident(param)?);
}
Ok(params)
}
fn parse_index_access(pair: Pair<Rule>) -> Result<Expr, ParseError> {
assert_eq!(pair.as_rule(), Rule::index_access);
let inner: Vec<Pair<Rule>> = pair.into_inner().collect();
let ident = parse_ident(inner[0].clone())?;
let index = parse_integer(inner[1].clone())?;
Ok(Expr::Index(ident, index))
}
fn parse_integer(pair: Pair<Rule>) -> Result<usize, ParseError> {
assert_eq!(pair.as_rule(), Rule::integer);
let int = pair.as_str().parse::<usize>().unwrap();
Ok(int)
}