Factor out expr parser into its own module
This commit is contained in:
parent
1ea2d8ba9f
commit
3dff189499
4 changed files with 510 additions and 495 deletions
|
@ -6,6 +6,7 @@ pub(crate) mod commands;
|
||||||
pub(crate) mod common;
|
pub(crate) mod common;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
#[macro_use]
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
|
||||||
pub use common::{Error, Result};
|
pub use common::{Error, Result};
|
||||||
|
|
482
src/parser/expr.rs
Normal file
482
src/parser/expr.rs
Normal file
|
@ -0,0 +1,482 @@
|
||||||
|
use nom::character::complete::{digit1, multispace0, multispace1};
|
||||||
|
use nom::{
|
||||||
|
alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
|
||||||
|
separated_list0, separated_list1, tag, tuple,
|
||||||
|
};
|
||||||
|
use pratt::{Affix, Associativity, PrattParser, Precedence};
|
||||||
|
|
||||||
|
use crate::ast::{BinaryOperator, Expr, Fun, Ident, Literal, UnaryOperator};
|
||||||
|
use crate::parser::ident;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum TokenTree<'a> {
|
||||||
|
Prefix(UnaryOperator),
|
||||||
|
// Postfix(char),
|
||||||
|
Infix(BinaryOperator),
|
||||||
|
Primary(Expr<'a>),
|
||||||
|
Group(Vec<TokenTree<'a>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(prefix(&str) -> TokenTree, map!(alt!(
|
||||||
|
complete!(char!('-')) => { |_| UnaryOperator::Neg } |
|
||||||
|
complete!(char!('!')) => { |_| UnaryOperator::Not }
|
||||||
|
), TokenTree::Prefix));
|
||||||
|
|
||||||
|
named!(infix(&str) -> TokenTree, map!(alt!(
|
||||||
|
complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
|
||||||
|
complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
|
||||||
|
complete!(char!('+')) => { |_| BinaryOperator::Add } |
|
||||||
|
complete!(char!('-')) => { |_| BinaryOperator::Sub } |
|
||||||
|
complete!(char!('*')) => { |_| BinaryOperator::Mul } |
|
||||||
|
complete!(char!('/')) => { |_| BinaryOperator::Div } |
|
||||||
|
complete!(char!('^')) => { |_| BinaryOperator::Pow }
|
||||||
|
), TokenTree::Infix));
|
||||||
|
|
||||||
|
named!(primary(&str) -> TokenTree, alt!(
|
||||||
|
do_parse!(
|
||||||
|
multispace0 >>
|
||||||
|
char!('(') >>
|
||||||
|
multispace0 >>
|
||||||
|
group: group >>
|
||||||
|
multispace0 >>
|
||||||
|
char!(')') >>
|
||||||
|
multispace0 >>
|
||||||
|
(TokenTree::Group(group))
|
||||||
|
) |
|
||||||
|
delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(
|
||||||
|
rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
|
||||||
|
many0!(tuple!(
|
||||||
|
infix,
|
||||||
|
delimited!(multispace0, many0!(prefix), multispace0),
|
||||||
|
primary
|
||||||
|
// many0!(postfix)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
|
named!(group(&str) -> Vec<TokenTree>, do_parse!(
|
||||||
|
prefix: many0!(prefix)
|
||||||
|
>> primary: primary
|
||||||
|
// >> postfix: many0!(postfix)
|
||||||
|
>> rest: rest
|
||||||
|
>> ({
|
||||||
|
let mut res = prefix;
|
||||||
|
res.push(primary);
|
||||||
|
// res.append(&mut postfix);
|
||||||
|
for (infix, mut prefix, primary/*, mut postfix*/) in rest {
|
||||||
|
res.push(infix);
|
||||||
|
res.append(&mut prefix);
|
||||||
|
res.push(primary);
|
||||||
|
// res.append(&mut postfix);
|
||||||
|
}
|
||||||
|
res
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
|
||||||
|
group(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ExprParser;
|
||||||
|
|
||||||
|
impl<'a, I> PrattParser<I> for ExprParser
|
||||||
|
where
|
||||||
|
I: Iterator<Item = TokenTree<'a>>,
|
||||||
|
{
|
||||||
|
type Error = pratt::NoError;
|
||||||
|
type Input = TokenTree<'a>;
|
||||||
|
type Output = Expr<'a>;
|
||||||
|
|
||||||
|
fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
|
||||||
|
use BinaryOperator::*;
|
||||||
|
use UnaryOperator::*;
|
||||||
|
|
||||||
|
Ok(match input {
|
||||||
|
TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
|
||||||
|
TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
|
||||||
|
TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
|
||||||
|
TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
|
||||||
|
TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
|
||||||
|
TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
|
||||||
|
TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
|
||||||
|
TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
|
||||||
|
TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
|
||||||
|
TokenTree::Primary(_) => Affix::Nilfix,
|
||||||
|
TokenTree::Group(_) => Affix::Nilfix,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
|
||||||
|
Ok(match input {
|
||||||
|
TokenTree::Primary(expr) => expr,
|
||||||
|
TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn infix(
|
||||||
|
&mut self,
|
||||||
|
lhs: Self::Output,
|
||||||
|
op: Self::Input,
|
||||||
|
rhs: Self::Output,
|
||||||
|
) -> Result<Self::Output, Self::Error> {
|
||||||
|
let op = match op {
|
||||||
|
TokenTree::Infix(op) => op,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok(Expr::BinaryOp {
|
||||||
|
lhs: Box::new(lhs),
|
||||||
|
op,
|
||||||
|
rhs: Box::new(rhs),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
|
||||||
|
let op = match op {
|
||||||
|
TokenTree::Prefix(op) => op,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Expr::UnaryOp {
|
||||||
|
op,
|
||||||
|
rhs: Box::new(rhs),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn postfix(
|
||||||
|
&mut self,
|
||||||
|
_lhs: Self::Output,
|
||||||
|
_op: Self::Input,
|
||||||
|
) -> Result<Self::Output, Self::Error> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
|
||||||
|
|
||||||
|
named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
|
||||||
|
|
||||||
|
named!(binding(&str) -> (Ident, Expr), do_parse!(
|
||||||
|
multispace0
|
||||||
|
>> ident: ident
|
||||||
|
>> multispace0
|
||||||
|
>> char!('=')
|
||||||
|
>> multispace0
|
||||||
|
>> expr: expr
|
||||||
|
>> (ident, expr)
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(let_(&str) -> Expr, do_parse!(
|
||||||
|
tag!("let")
|
||||||
|
>> multispace0
|
||||||
|
>> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
|
||||||
|
>> multispace0
|
||||||
|
>> tag!("in")
|
||||||
|
>> multispace0
|
||||||
|
>> body: expr
|
||||||
|
>> (Expr::Let {
|
||||||
|
bindings,
|
||||||
|
body: Box::new(body)
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(if_(&str) -> Expr, do_parse! (
|
||||||
|
tag!("if")
|
||||||
|
>> multispace0
|
||||||
|
>> condition: expr
|
||||||
|
>> multispace0
|
||||||
|
>> tag!("then")
|
||||||
|
>> multispace0
|
||||||
|
>> then: expr
|
||||||
|
>> multispace0
|
||||||
|
>> tag!("else")
|
||||||
|
>> multispace0
|
||||||
|
>> else_: expr
|
||||||
|
>> (Expr::If {
|
||||||
|
condition: Box::new(condition),
|
||||||
|
then: Box::new(then),
|
||||||
|
else_: Box::new(else_)
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
|
||||||
|
|
||||||
|
named!(paren_expr(&str) -> Expr,
|
||||||
|
delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
|
||||||
|
|
||||||
|
named!(funcref(&str) -> Expr, alt!(
|
||||||
|
ident_expr |
|
||||||
|
paren_expr
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(no_arg_call(&str) -> Expr, do_parse!(
|
||||||
|
fun: funcref
|
||||||
|
>> multispace0
|
||||||
|
>> complete!(tag!("()"))
|
||||||
|
>> (Expr::Call {
|
||||||
|
fun: Box::new(fun),
|
||||||
|
args: vec![],
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(fun_expr(&str) -> Expr, do_parse!(
|
||||||
|
tag!("fn")
|
||||||
|
>> multispace1
|
||||||
|
>> args: separated_list0!(multispace1, ident)
|
||||||
|
>> multispace0
|
||||||
|
>> char!('=')
|
||||||
|
>> multispace0
|
||||||
|
>> body: expr
|
||||||
|
>> (Expr::Fun(Box::new(Fun {
|
||||||
|
args,
|
||||||
|
body
|
||||||
|
})))
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(arg(&str) -> Expr, alt!(
|
||||||
|
ident_expr |
|
||||||
|
literal |
|
||||||
|
paren_expr
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(call_with_args(&str) -> Expr, do_parse!(
|
||||||
|
fun: funcref
|
||||||
|
>> multispace1
|
||||||
|
>> args: separated_list1!(multispace1, arg)
|
||||||
|
>> (Expr::Call {
|
||||||
|
fun: Box::new(fun),
|
||||||
|
args
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(simple_expr(&str) -> Expr, alt!(
|
||||||
|
let_ |
|
||||||
|
if_ |
|
||||||
|
fun_expr |
|
||||||
|
literal |
|
||||||
|
ident_expr
|
||||||
|
));
|
||||||
|
|
||||||
|
named!(pub expr(&str) -> Expr, alt!(
|
||||||
|
no_arg_call |
|
||||||
|
call_with_args |
|
||||||
|
map!(token_tree, |tt| {
|
||||||
|
ExprParser.parse(&mut tt.into_iter()).unwrap()
|
||||||
|
}) |
|
||||||
|
simple_expr));
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use BinaryOperator::*;
|
||||||
|
use Expr::{BinaryOp, If, Let, UnaryOp};
|
||||||
|
use UnaryOperator::*;
|
||||||
|
|
||||||
|
pub(crate) fn ident_expr(s: &str) -> Box<Expr> {
|
||||||
|
Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
mod operators {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_plus() {
|
||||||
|
let (rem, res) = expr("x*y+z").unwrap();
|
||||||
|
assert!(rem.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
BinaryOp {
|
||||||
|
lhs: Box::new(BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: ident_expr("y")
|
||||||
|
}),
|
||||||
|
op: Add,
|
||||||
|
rhs: ident_expr("z")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_plus_ws() {
|
||||||
|
let (rem, res) = expr("x * y + z").unwrap();
|
||||||
|
assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
BinaryOp {
|
||||||
|
lhs: Box::new(BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: ident_expr("y")
|
||||||
|
}),
|
||||||
|
op: Add,
|
||||||
|
rhs: ident_expr("z")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unary() {
|
||||||
|
let (rem, res) = expr("x * -z").unwrap();
|
||||||
|
assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: Box::new(UnaryOp {
|
||||||
|
op: Neg,
|
||||||
|
rhs: ident_expr("z"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mul_literal() {
|
||||||
|
let (rem, res) = expr("x * 3").unwrap();
|
||||||
|
assert!(rem.is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(3))),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn equ() {
|
||||||
|
let res = test_parse!(expr, "x * 7 == 7");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
BinaryOp {
|
||||||
|
lhs: Box::new(BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
||||||
|
}),
|
||||||
|
op: Equ,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_complex() {
|
||||||
|
let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Let {
|
||||||
|
bindings: vec![
|
||||||
|
(
|
||||||
|
Ident::try_from("x").unwrap(),
|
||||||
|
Expr::Literal(Literal::Int(1))
|
||||||
|
),
|
||||||
|
(
|
||||||
|
Ident::try_from("y").unwrap(),
|
||||||
|
Expr::BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Mul,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
],
|
||||||
|
body: Box::new(Expr::BinaryOp {
|
||||||
|
lhs: Box::new(Expr::BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Add,
|
||||||
|
rhs: ident_expr("y"),
|
||||||
|
}),
|
||||||
|
op: Mul,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(4))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn if_simple() {
|
||||||
|
let res = test_parse!(expr, "if x == 8 then 9 else 20");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
If {
|
||||||
|
condition: Box::new(BinaryOp {
|
||||||
|
lhs: ident_expr("x"),
|
||||||
|
op: Equ,
|
||||||
|
rhs: Box::new(Expr::Literal(Literal::Int(8))),
|
||||||
|
}),
|
||||||
|
then: Box::new(Expr::Literal(Literal::Int(9))),
|
||||||
|
else_: Box::new(Expr::Literal(Literal::Int(20)))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn no_arg_call() {
|
||||||
|
let res = test_parse!(expr, "f()");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Expr::Call {
|
||||||
|
fun: ident_expr("f"),
|
||||||
|
args: vec![]
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_with_args() {
|
||||||
|
let res = test_parse!(expr, "f x 1");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Expr::Call {
|
||||||
|
fun: ident_expr("f"),
|
||||||
|
args: vec![*ident_expr("x"), Expr::Literal(Literal::Int(1))]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_funcref() {
|
||||||
|
let res = test_parse!(expr, "(let x = 1 in x) 2");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Expr::Call {
|
||||||
|
fun: Box::new(Expr::Let {
|
||||||
|
bindings: vec![(
|
||||||
|
Ident::try_from("x").unwrap(),
|
||||||
|
Expr::Literal(Literal::Int(1))
|
||||||
|
)],
|
||||||
|
body: ident_expr("x")
|
||||||
|
}),
|
||||||
|
args: vec![Expr::Literal(Literal::Int(2))]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anon_function() {
|
||||||
|
let res = test_parse!(expr, "let id = fn x = x in id 1");
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
Expr::Let {
|
||||||
|
bindings: vec![(
|
||||||
|
Ident::try_from("id").unwrap(),
|
||||||
|
Expr::Fun(Box::new(Fun {
|
||||||
|
args: vec![Ident::try_from("x").unwrap()],
|
||||||
|
body: *ident_expr("x")
|
||||||
|
}))
|
||||||
|
)],
|
||||||
|
body: Box::new(Expr::Call {
|
||||||
|
fun: ident_expr("id"),
|
||||||
|
args: vec![Expr::Literal(Literal::Int(1))],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
15
src/parser/macros.rs
Normal file
15
src/parser/macros.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#[macro_use]
|
||||||
|
macro_rules! test_parse {
|
||||||
|
($parser: ident, $src: expr) => {{
|
||||||
|
let res = $parser($src);
|
||||||
|
nom_trace::print_trace!();
|
||||||
|
let (rem, res) = res.unwrap();
|
||||||
|
assert!(
|
||||||
|
rem.is_empty(),
|
||||||
|
"non-empty remainder: \"{}\", parsed: {:?}",
|
||||||
|
rem,
|
||||||
|
res
|
||||||
|
);
|
||||||
|
res
|
||||||
|
}};
|
||||||
|
}
|
|
@ -1,166 +1,21 @@
|
||||||
use nom::character::complete::{digit1, multispace0, multispace1};
|
use nom::character::complete::{multispace0, multispace1};
|
||||||
use nom::error::{ErrorKind, ParseError};
|
use nom::error::{ErrorKind, ParseError};
|
||||||
use nom::{
|
use nom::{alt, char, complete, do_parse, many0, named, separated_list0, tag};
|
||||||
alt, char, complete, delimited, do_parse, flat_map, many0, map, named, parse_to,
|
|
||||||
separated_list0, separated_list1, tag, tuple,
|
|
||||||
};
|
|
||||||
use pratt::{Affix, Associativity, PrattParser, Precedence};
|
|
||||||
|
|
||||||
use crate::ast::{BinaryOperator, Decl, Expr, Fun, Ident, Literal, UnaryOperator};
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
mod expr;
|
||||||
|
|
||||||
|
use crate::ast::{Decl, Fun, Ident};
|
||||||
|
pub use expr::expr;
|
||||||
|
|
||||||
pub type Error = nom::Err<nom::error::Error<String>>;
|
pub type Error = nom::Err<nom::error::Error<String>>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub(crate) fn is_reserved(s: &str) -> bool {
|
||||||
enum TokenTree<'a> {
|
|
||||||
Prefix(UnaryOperator),
|
|
||||||
// Postfix(char),
|
|
||||||
Infix(BinaryOperator),
|
|
||||||
Primary(Expr<'a>),
|
|
||||||
Group(Vec<TokenTree<'a>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
named!(prefix(&str) -> TokenTree, map!(alt!(
|
|
||||||
complete!(char!('-')) => { |_| UnaryOperator::Neg } |
|
|
||||||
complete!(char!('!')) => { |_| UnaryOperator::Not }
|
|
||||||
), TokenTree::Prefix));
|
|
||||||
|
|
||||||
named!(infix(&str) -> TokenTree, map!(alt!(
|
|
||||||
complete!(tag!("==")) => { |_| BinaryOperator::Equ } |
|
|
||||||
complete!(tag!("!=")) => { |_| BinaryOperator::Neq } |
|
|
||||||
complete!(char!('+')) => { |_| BinaryOperator::Add } |
|
|
||||||
complete!(char!('-')) => { |_| BinaryOperator::Sub } |
|
|
||||||
complete!(char!('*')) => { |_| BinaryOperator::Mul } |
|
|
||||||
complete!(char!('/')) => { |_| BinaryOperator::Div } |
|
|
||||||
complete!(char!('^')) => { |_| BinaryOperator::Pow }
|
|
||||||
), TokenTree::Infix));
|
|
||||||
|
|
||||||
named!(primary(&str) -> TokenTree, alt!(
|
|
||||||
do_parse!(
|
|
||||||
multispace0 >>
|
|
||||||
char!('(') >>
|
|
||||||
multispace0 >>
|
|
||||||
group: group >>
|
|
||||||
multispace0 >>
|
|
||||||
char!(')') >>
|
|
||||||
multispace0 >>
|
|
||||||
(TokenTree::Group(group))
|
|
||||||
) |
|
|
||||||
delimited!(multispace0, simple_expr, multispace0) => { |s| TokenTree::Primary(s) }
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(
|
|
||||||
rest(&str) -> Vec<(TokenTree, Vec<TokenTree>, TokenTree)>,
|
|
||||||
many0!(tuple!(
|
|
||||||
infix,
|
|
||||||
delimited!(multispace0, many0!(prefix), multispace0),
|
|
||||||
primary
|
|
||||||
// many0!(postfix)
|
|
||||||
))
|
|
||||||
);
|
|
||||||
|
|
||||||
named!(group(&str) -> Vec<TokenTree>, do_parse!(
|
|
||||||
prefix: many0!(prefix)
|
|
||||||
>> primary: primary
|
|
||||||
// >> postfix: many0!(postfix)
|
|
||||||
>> rest: rest
|
|
||||||
>> ({
|
|
||||||
let mut res = prefix;
|
|
||||||
res.push(primary);
|
|
||||||
// res.append(&mut postfix);
|
|
||||||
for (infix, mut prefix, primary/*, mut postfix*/) in rest {
|
|
||||||
res.push(infix);
|
|
||||||
res.append(&mut prefix);
|
|
||||||
res.push(primary);
|
|
||||||
// res.append(&mut postfix);
|
|
||||||
}
|
|
||||||
res
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
fn token_tree(i: &str) -> nom::IResult<&str, Vec<TokenTree>> {
|
|
||||||
group(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ExprParser;
|
|
||||||
|
|
||||||
impl<'a, I> PrattParser<I> for ExprParser
|
|
||||||
where
|
|
||||||
I: Iterator<Item = TokenTree<'a>>,
|
|
||||||
{
|
|
||||||
type Error = pratt::NoError;
|
|
||||||
type Input = TokenTree<'a>;
|
|
||||||
type Output = Expr<'a>;
|
|
||||||
|
|
||||||
fn query(&mut self, input: &Self::Input) -> Result<Affix, Self::Error> {
|
|
||||||
use BinaryOperator::*;
|
|
||||||
use UnaryOperator::*;
|
|
||||||
|
|
||||||
Ok(match input {
|
|
||||||
TokenTree::Infix(Add) => Affix::Infix(Precedence(6), Associativity::Left),
|
|
||||||
TokenTree::Infix(Sub) => Affix::Infix(Precedence(6), Associativity::Left),
|
|
||||||
TokenTree::Infix(Mul) => Affix::Infix(Precedence(7), Associativity::Left),
|
|
||||||
TokenTree::Infix(Div) => Affix::Infix(Precedence(7), Associativity::Left),
|
|
||||||
TokenTree::Infix(Pow) => Affix::Infix(Precedence(8), Associativity::Right),
|
|
||||||
TokenTree::Infix(Equ) => Affix::Infix(Precedence(4), Associativity::Right),
|
|
||||||
TokenTree::Infix(Neq) => Affix::Infix(Precedence(4), Associativity::Right),
|
|
||||||
TokenTree::Prefix(Neg) => Affix::Prefix(Precedence(6)),
|
|
||||||
TokenTree::Prefix(Not) => Affix::Prefix(Precedence(6)),
|
|
||||||
TokenTree::Primary(_) => Affix::Nilfix,
|
|
||||||
TokenTree::Group(_) => Affix::Nilfix,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn primary(&mut self, input: Self::Input) -> Result<Self::Output, Self::Error> {
|
|
||||||
Ok(match input {
|
|
||||||
TokenTree::Primary(expr) => expr,
|
|
||||||
TokenTree::Group(group) => self.parse(&mut group.into_iter()).unwrap(),
|
|
||||||
_ => unreachable!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn infix(
|
|
||||||
&mut self,
|
|
||||||
lhs: Self::Output,
|
|
||||||
op: Self::Input,
|
|
||||||
rhs: Self::Output,
|
|
||||||
) -> Result<Self::Output, Self::Error> {
|
|
||||||
let op = match op {
|
|
||||||
TokenTree::Infix(op) => op,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
Ok(Expr::BinaryOp {
|
|
||||||
lhs: Box::new(lhs),
|
|
||||||
op,
|
|
||||||
rhs: Box::new(rhs),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prefix(&mut self, op: Self::Input, rhs: Self::Output) -> Result<Self::Output, Self::Error> {
|
|
||||||
let op = match op {
|
|
||||||
TokenTree::Prefix(op) => op,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Expr::UnaryOp {
|
|
||||||
op,
|
|
||||||
rhs: Box::new(rhs),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn postfix(
|
|
||||||
&mut self,
|
|
||||||
_lhs: Self::Output,
|
|
||||||
_op: Self::Input,
|
|
||||||
) -> Result<Self::Output, Self::Error> {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_reserved(s: &str) -> bool {
|
|
||||||
matches!(s, "if" | "then" | "else" | "let" | "in" | "fn")
|
matches!(s, "if" | "then" | "else" | "let" | "in" | "fn")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
|
pub(crate) fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
|
||||||
where
|
where
|
||||||
E: ParseError<&'a str>,
|
E: ParseError<&'a str>,
|
||||||
{
|
{
|
||||||
|
@ -188,121 +43,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
named!(int(&str) -> Literal, map!(flat_map!(digit1, parse_to!(u64)), Literal::Int));
|
|
||||||
|
|
||||||
named!(literal(&str) -> Expr, map!(alt!(int), Expr::Literal));
|
|
||||||
|
|
||||||
named!(binding(&str) -> (Ident, Expr), do_parse!(
|
|
||||||
multispace0
|
|
||||||
>> ident: ident
|
|
||||||
>> multispace0
|
|
||||||
>> char!('=')
|
|
||||||
>> multispace0
|
|
||||||
>> expr: expr
|
|
||||||
>> (ident, expr)
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(let_(&str) -> Expr, do_parse!(
|
|
||||||
tag!("let")
|
|
||||||
>> multispace0
|
|
||||||
>> bindings: separated_list1!(alt!(char!(';') | char!('\n')), binding)
|
|
||||||
>> multispace0
|
|
||||||
>> tag!("in")
|
|
||||||
>> multispace0
|
|
||||||
>> body: expr
|
|
||||||
>> (Expr::Let {
|
|
||||||
bindings,
|
|
||||||
body: Box::new(body)
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(if_(&str) -> Expr, do_parse! (
|
|
||||||
tag!("if")
|
|
||||||
>> multispace0
|
|
||||||
>> condition: expr
|
|
||||||
>> multispace0
|
|
||||||
>> tag!("then")
|
|
||||||
>> multispace0
|
|
||||||
>> then: expr
|
|
||||||
>> multispace0
|
|
||||||
>> tag!("else")
|
|
||||||
>> multispace0
|
|
||||||
>> else_: expr
|
|
||||||
>> (Expr::If {
|
|
||||||
condition: Box::new(condition),
|
|
||||||
then: Box::new(then),
|
|
||||||
else_: Box::new(else_)
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(ident_expr(&str) -> Expr, map!(ident, Expr::Ident));
|
|
||||||
|
|
||||||
named!(paren_expr(&str) -> Expr,
|
|
||||||
delimited!(complete!(tag!("(")), expr, complete!(tag!(")"))));
|
|
||||||
|
|
||||||
named!(funcref(&str) -> Expr, alt!(
|
|
||||||
ident_expr |
|
|
||||||
paren_expr
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(no_arg_call(&str) -> Expr, do_parse!(
|
|
||||||
fun: funcref
|
|
||||||
>> multispace0
|
|
||||||
>> complete!(tag!("()"))
|
|
||||||
>> (Expr::Call {
|
|
||||||
fun: Box::new(fun),
|
|
||||||
args: vec![],
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(fun_expr(&str) -> Expr, do_parse!(
|
|
||||||
tag!("fn")
|
|
||||||
>> multispace1
|
|
||||||
>> args: separated_list0!(multispace1, ident)
|
|
||||||
>> multispace0
|
|
||||||
>> char!('=')
|
|
||||||
>> multispace0
|
|
||||||
>> body: expr
|
|
||||||
>> (Expr::Fun(Box::new(Fun {
|
|
||||||
args,
|
|
||||||
body
|
|
||||||
})))
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(arg(&str) -> Expr, alt!(
|
|
||||||
ident_expr |
|
|
||||||
literal |
|
|
||||||
paren_expr
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(call_with_args(&str) -> Expr, do_parse!(
|
|
||||||
fun: funcref
|
|
||||||
>> multispace1
|
|
||||||
>> args: separated_list1!(multispace1, arg)
|
|
||||||
>> (Expr::Call {
|
|
||||||
fun: Box::new(fun),
|
|
||||||
args
|
|
||||||
})
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(simple_expr(&str) -> Expr, alt!(
|
|
||||||
let_ |
|
|
||||||
if_ |
|
|
||||||
fun_expr |
|
|
||||||
literal |
|
|
||||||
ident_expr
|
|
||||||
));
|
|
||||||
|
|
||||||
named!(pub expr(&str) -> Expr, alt!(
|
|
||||||
no_arg_call |
|
|
||||||
call_with_args |
|
|
||||||
map!(token_tree, |tt| {
|
|
||||||
ExprParser.parse(&mut tt.into_iter()).unwrap()
|
|
||||||
}) |
|
|
||||||
simple_expr));
|
|
||||||
|
|
||||||
//////
|
|
||||||
|
|
||||||
named!(fun_decl(&str) -> Decl, do_parse!(
|
named!(fun_decl(&str) -> Decl, do_parse!(
|
||||||
complete!(tag!("fn"))
|
complete!(tag!("fn"))
|
||||||
>> multispace0
|
>> multispace0
|
||||||
|
@ -330,170 +70,10 @@ named!(pub toplevel(&str) -> Vec<Decl>, many0!(decl));
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use nom_trace::print_trace;
|
use std::convert::TryInto;
|
||||||
use std::convert::{TryFrom, TryInto};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use BinaryOperator::*;
|
use expr::tests::ident_expr;
|
||||||
use Expr::{BinaryOp, If, Let, UnaryOp};
|
|
||||||
use UnaryOperator::*;
|
|
||||||
|
|
||||||
fn ident_expr(s: &str) -> Box<Expr> {
|
|
||||||
Box::new(Expr::Ident(Ident::try_from(s).unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! test_parse {
|
|
||||||
($parser: ident, $src: expr) => {{
|
|
||||||
let res = $parser($src);
|
|
||||||
print_trace!();
|
|
||||||
let (rem, res) = res.unwrap();
|
|
||||||
assert!(
|
|
||||||
rem.is_empty(),
|
|
||||||
"non-empty remainder: \"{}\", parsed: {:?}",
|
|
||||||
rem,
|
|
||||||
res
|
|
||||||
);
|
|
||||||
res
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
mod operators {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mul_plus() {
|
|
||||||
let (rem, res) = expr("x*y+z").unwrap();
|
|
||||||
assert!(rem.is_empty());
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
BinaryOp {
|
|
||||||
lhs: Box::new(BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: ident_expr("y")
|
|
||||||
}),
|
|
||||||
op: Add,
|
|
||||||
rhs: ident_expr("z")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mul_plus_ws() {
|
|
||||||
let (rem, res) = expr("x * y + z").unwrap();
|
|
||||||
assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
BinaryOp {
|
|
||||||
lhs: Box::new(BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: ident_expr("y")
|
|
||||||
}),
|
|
||||||
op: Add,
|
|
||||||
rhs: ident_expr("z")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unary() {
|
|
||||||
let (rem, res) = expr("x * -z").unwrap();
|
|
||||||
assert!(rem.is_empty(), "non-empty remainder: \"{}\"", rem);
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: Box::new(UnaryOp {
|
|
||||||
op: Neg,
|
|
||||||
rhs: ident_expr("z"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mul_literal() {
|
|
||||||
let (rem, res) = expr("x * 3").unwrap();
|
|
||||||
assert!(rem.is_empty());
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(3))),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn equ() {
|
|
||||||
let res = test_parse!(expr, "x * 7 == 7");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
BinaryOp {
|
|
||||||
lhs: Box::new(BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
|
||||||
}),
|
|
||||||
op: Equ,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn let_complex() {
|
|
||||||
let res = test_parse!(expr, "let x = 1; y = x * 7 in (x + y) * 4");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Let {
|
|
||||||
bindings: vec![
|
|
||||||
(
|
|
||||||
Ident::try_from("x").unwrap(),
|
|
||||||
Expr::Literal(Literal::Int(1))
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Ident::try_from("y").unwrap(),
|
|
||||||
Expr::BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Mul,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(7)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
],
|
|
||||||
body: Box::new(Expr::BinaryOp {
|
|
||||||
lhs: Box::new(Expr::BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Add,
|
|
||||||
rhs: ident_expr("y"),
|
|
||||||
}),
|
|
||||||
op: Mul,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(4))),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn if_simple() {
|
|
||||||
let res = test_parse!(expr, "if x == 8 then 9 else 20");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
If {
|
|
||||||
condition: Box::new(BinaryOp {
|
|
||||||
lhs: ident_expr("x"),
|
|
||||||
op: Equ,
|
|
||||||
rhs: Box::new(Expr::Literal(Literal::Int(8))),
|
|
||||||
}),
|
|
||||||
then: Box::new(Expr::Literal(Literal::Int(9))),
|
|
||||||
else_: Box::new(Expr::Literal(Literal::Int(20)))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fn_decl() {
|
fn fn_decl() {
|
||||||
|
@ -510,69 +90,6 @@ mod tests {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn no_arg_call() {
|
|
||||||
let res = test_parse!(expr, "f()");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Expr::Call {
|
|
||||||
fun: ident_expr("f"),
|
|
||||||
args: vec![]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn call_with_args() {
|
|
||||||
let res = test_parse!(expr, "f x 1");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Expr::Call {
|
|
||||||
fun: ident_expr("f"),
|
|
||||||
args: vec![*ident_expr("x"), Expr::Literal(Literal::Int(1))]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn call_funcref() {
|
|
||||||
let res = test_parse!(expr, "(let x = 1 in x) 2");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Expr::Call {
|
|
||||||
fun: Box::new(Expr::Let {
|
|
||||||
bindings: vec![(
|
|
||||||
Ident::try_from("x").unwrap(),
|
|
||||||
Expr::Literal(Literal::Int(1))
|
|
||||||
)],
|
|
||||||
body: ident_expr("x")
|
|
||||||
}),
|
|
||||||
args: vec![Expr::Literal(Literal::Int(2))]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn anon_function() {
|
|
||||||
let res = test_parse!(expr, "let id = fn x = x in id 1");
|
|
||||||
assert_eq!(
|
|
||||||
res,
|
|
||||||
Expr::Let {
|
|
||||||
bindings: vec![(
|
|
||||||
Ident::try_from("id").unwrap(),
|
|
||||||
Expr::Fun(Box::new(Fun {
|
|
||||||
args: vec![Ident::try_from("x").unwrap()],
|
|
||||||
body: *ident_expr("x")
|
|
||||||
}))
|
|
||||||
)],
|
|
||||||
body: Box::new(Expr::Call {
|
|
||||||
fun: ident_expr("id"),
|
|
||||||
args: vec![Expr::Literal(Literal::Int(1))],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn multiple_decls() {
|
fn multiple_decls() {
|
||||||
let res = test_parse!(
|
let res = test_parse!(
|
||||||
|
|
Loading…
Reference in a new issue