2021-03-13 18:19:44 +01:00
|
|
|
use nom::character::complete::{multispace0, multispace1};
|
2021-03-07 21:29:59 +01:00
|
|
|
use nom::error::{ErrorKind, ParseError};
|
2021-03-28 19:28:49 +02:00
|
|
|
use nom::{alt, char, complete, do_parse, eof, many0, named, separated_list0, tag, terminated};
|
2021-03-07 21:29:59 +01:00
|
|
|
|
2021-03-13 18:19:44 +01:00
|
|
|
#[macro_use]
|
2021-03-28 19:28:49 +02:00
|
|
|
pub(crate) mod macros;
|
2021-03-13 18:19:44 +01:00
|
|
|
mod expr;
|
2021-03-13 19:12:03 +01:00
|
|
|
mod type_;
|
2021-04-17 08:28:24 +02:00
|
|
|
mod util;
|
2021-03-07 21:29:59 +01:00
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
use crate::ast::{Arg, Decl, Fun, Ident};
|
2021-03-13 18:19:44 +01:00
|
|
|
pub use expr::expr;
|
2021-03-20 01:46:19 +01:00
|
|
|
use type_::function_type;
|
2021-03-13 19:12:03 +01:00
|
|
|
pub use type_::type_;
|
2021-03-07 21:29:59 +01:00
|
|
|
|
2021-03-13 18:19:44 +01:00
|
|
|
pub type Error = nom::Err<nom::error::Error<String>>;
|
2021-03-07 21:29:59 +01:00
|
|
|
|
2021-03-13 18:19:44 +01:00
|
|
|
pub(crate) fn is_reserved(s: &str) -> bool {
|
2021-03-14 03:57:27 +01:00
|
|
|
matches!(
|
|
|
|
s,
|
2021-03-14 16:53:13 +01:00
|
|
|
"if" | "then"
|
|
|
|
| "else"
|
|
|
|
| "let"
|
|
|
|
| "in"
|
|
|
|
| "fn"
|
2021-03-20 20:08:55 +01:00
|
|
|
| "ty"
|
2021-03-14 16:53:13 +01:00
|
|
|
| "int"
|
|
|
|
| "float"
|
|
|
|
| "bool"
|
|
|
|
| "true"
|
|
|
|
| "false"
|
|
|
|
| "cstring"
|
2021-03-14 03:57:27 +01:00
|
|
|
)
|
2021-03-08 06:04:44 +01:00
|
|
|
}
|
|
|
|
|
2021-03-13 18:19:44 +01:00
|
|
|
pub(crate) fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
|
2021-03-07 21:29:59 +01:00
|
|
|
where
|
|
|
|
E: ParseError<&'a str>,
|
|
|
|
{
|
|
|
|
let mut chars = i.chars();
|
|
|
|
if let Some(f) = chars.next() {
|
|
|
|
if f.is_alphabetic() || f == '_' {
|
|
|
|
let mut idx = 1;
|
|
|
|
for c in chars {
|
|
|
|
if !(c.is_alphanumeric() || c == '_') {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
idx += 1;
|
|
|
|
}
|
2021-03-08 06:04:44 +01:00
|
|
|
let id = &i[..idx];
|
|
|
|
if is_reserved(id) {
|
|
|
|
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
|
|
|
|
} else {
|
|
|
|
Ok((&i[idx..], Ident::from_str_unchecked(id)))
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
} else {
|
|
|
|
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Eof)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
named!(ascripted_arg(&str) -> Arg, do_parse!(
|
|
|
|
complete!(char!('(')) >>
|
|
|
|
multispace0 >>
|
|
|
|
ident: ident >>
|
|
|
|
multispace0 >>
|
|
|
|
complete!(char!(':')) >>
|
|
|
|
multispace0 >>
|
|
|
|
type_: type_ >>
|
|
|
|
multispace0 >>
|
|
|
|
complete!(char!(')')) >>
|
|
|
|
(Arg {
|
|
|
|
ident,
|
|
|
|
type_: Some(type_)
|
|
|
|
})
|
|
|
|
));
|
|
|
|
|
|
|
|
named!(arg(&str) -> Arg, alt!(
|
|
|
|
ident => { |ident| Arg {ident, type_: None}} |
|
|
|
|
ascripted_arg
|
|
|
|
));
|
|
|
|
|
2021-03-20 01:46:19 +01:00
|
|
|
named!(extern_decl(&str) -> Decl, do_parse!(
|
|
|
|
complete!(tag!("extern"))
|
|
|
|
>> multispace1
|
|
|
|
>> name: ident
|
|
|
|
>> multispace0
|
|
|
|
>> char!(':')
|
|
|
|
>> multispace0
|
|
|
|
>> type_: function_type
|
|
|
|
>> multispace0
|
|
|
|
>> (Decl::Extern {
|
|
|
|
name,
|
|
|
|
type_
|
|
|
|
})
|
|
|
|
));
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
named!(fun_decl(&str) -> Decl, do_parse!(
|
|
|
|
complete!(tag!("fn"))
|
2021-03-20 01:46:19 +01:00
|
|
|
>> multispace1
|
2021-03-07 21:29:59 +01:00
|
|
|
>> name: ident
|
|
|
|
>> multispace1
|
2021-03-14 21:43:47 +01:00
|
|
|
>> args: separated_list0!(multispace1, arg)
|
2021-03-07 21:29:59 +01:00
|
|
|
>> multispace0
|
|
|
|
>> char!('=')
|
|
|
|
>> multispace0
|
|
|
|
>> body: expr
|
2021-03-08 06:04:44 +01:00
|
|
|
>> (Decl::Fun {
|
2021-03-07 21:29:59 +01:00
|
|
|
name,
|
2021-03-08 06:04:44 +01:00
|
|
|
body: Fun {
|
|
|
|
args,
|
|
|
|
body
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
})
|
|
|
|
));
|
|
|
|
|
2021-03-14 22:01:25 +01:00
|
|
|
named!(ascription_decl(&str) -> Decl, do_parse!(
|
2021-03-20 20:08:55 +01:00
|
|
|
complete!(tag!("ty"))
|
|
|
|
>> multispace1
|
|
|
|
>> name: ident
|
2021-03-14 22:01:25 +01:00
|
|
|
>> multispace0
|
|
|
|
>> complete!(char!(':'))
|
|
|
|
>> multispace0
|
|
|
|
>> type_: type_
|
2021-03-20 20:08:55 +01:00
|
|
|
>> multispace0
|
2021-03-14 22:01:25 +01:00
|
|
|
>> (Decl::Ascription {
|
|
|
|
name,
|
|
|
|
type_
|
|
|
|
})
|
|
|
|
));
|
|
|
|
|
2021-03-07 21:29:59 +01:00
|
|
|
named!(pub decl(&str) -> Decl, alt!(
|
2021-03-14 22:01:25 +01:00
|
|
|
ascription_decl |
|
2021-03-20 01:46:19 +01:00
|
|
|
fun_decl |
|
|
|
|
extern_decl
|
2021-03-07 21:29:59 +01:00
|
|
|
));
|
|
|
|
|
2021-03-28 19:28:49 +02:00
|
|
|
named!(pub toplevel(&str) -> Vec<Decl>, do_parse!(
|
|
|
|
decls: many0!(decl)
|
|
|
|
>> multispace0
|
|
|
|
>> eof!()
|
|
|
|
>> (decls)));
|
2021-03-07 21:29:59 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-03-13 18:19:44 +01:00
|
|
|
use std::convert::TryInto;
|
2021-03-07 21:29:59 +01:00
|
|
|
|
2021-03-14 22:01:25 +01:00
|
|
|
use crate::ast::{BinaryOperator, Expr, FunctionType, Literal, Type};
|
2021-03-14 21:43:47 +01:00
|
|
|
|
2021-03-07 21:29:59 +01:00
|
|
|
use super::*;
|
2021-03-13 18:19:44 +01:00
|
|
|
use expr::tests::ident_expr;
|
2021-03-07 21:29:59 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fn_decl() {
|
|
|
|
let res = test_parse!(decl, "fn id x = x");
|
|
|
|
assert_eq!(
|
|
|
|
res,
|
2021-03-08 06:04:44 +01:00
|
|
|
Decl::Fun {
|
2021-03-07 21:29:59 +01:00
|
|
|
name: "id".try_into().unwrap(),
|
2021-03-08 06:04:44 +01:00
|
|
|
body: Fun {
|
|
|
|
args: vec!["x".try_into().unwrap()],
|
|
|
|
body: *ident_expr("x"),
|
|
|
|
}
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
)
|
|
|
|
}
|
2021-03-08 06:04:44 +01:00
|
|
|
|
2021-03-14 21:43:47 +01:00
|
|
|
#[test]
|
|
|
|
fn ascripted_fn_args() {
|
|
|
|
test_parse!(ascripted_arg, "(x : int)");
|
|
|
|
let res = test_parse!(decl, "fn plus1 (x : int) = x + 1");
|
|
|
|
assert_eq!(
|
|
|
|
res,
|
|
|
|
Decl::Fun {
|
|
|
|
name: "plus1".try_into().unwrap(),
|
|
|
|
body: Fun {
|
|
|
|
args: vec![Arg {
|
|
|
|
ident: "x".try_into().unwrap(),
|
|
|
|
type_: Some(Type::Int),
|
|
|
|
}],
|
|
|
|
body: Expr::BinaryOp {
|
|
|
|
lhs: ident_expr("x"),
|
|
|
|
op: BinaryOperator::Add,
|
|
|
|
rhs: Box::new(Expr::Literal(Literal::Int(1))),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-03-08 06:04:44 +01:00
|
|
|
#[test]
|
|
|
|
fn multiple_decls() {
|
|
|
|
let res = test_parse!(
|
|
|
|
toplevel,
|
|
|
|
"fn id x = x
|
|
|
|
fn plus x y = x + y
|
|
|
|
fn main = plus (id 2) 7"
|
|
|
|
);
|
|
|
|
assert_eq!(res.len(), 3);
|
2021-03-14 17:27:28 +01:00
|
|
|
let res = test_parse!(
|
|
|
|
toplevel,
|
|
|
|
"fn id x = x\nfn plus x y = x + y\nfn main = plus (id 2) 7\n"
|
|
|
|
);
|
|
|
|
assert_eq!(res.len(), 3);
|
2021-03-08 06:04:44 +01:00
|
|
|
}
|
2021-03-14 22:01:25 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn top_level_ascription() {
|
2021-03-20 20:08:55 +01:00
|
|
|
let res = test_parse!(toplevel, "ty id : fn a -> a");
|
2021-03-14 22:01:25 +01:00
|
|
|
assert_eq!(
|
|
|
|
res,
|
|
|
|
vec![Decl::Ascription {
|
|
|
|
name: "id".try_into().unwrap(),
|
|
|
|
type_: Type::Function(FunctionType {
|
|
|
|
args: vec![Type::Var("a".try_into().unwrap())],
|
|
|
|
ret: Box::new(Type::Var("a".try_into().unwrap()))
|
|
|
|
})
|
|
|
|
}]
|
|
|
|
)
|
|
|
|
}
|
2021-03-28 19:28:49 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn return_unit() {
|
|
|
|
assert_eq!(
|
|
|
|
test_parse!(decl, "fn g _ = ()"),
|
|
|
|
Decl::Fun {
|
|
|
|
name: "g".try_into().unwrap(),
|
|
|
|
body: Fun {
|
|
|
|
args: vec![Arg {
|
|
|
|
ident: "_".try_into().unwrap(),
|
|
|
|
type_: None,
|
|
|
|
}],
|
|
|
|
body: Expr::Literal(Literal::Unit),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
2021-03-07 21:29:59 +01:00
|
|
|
}
|