Lines
100 %
Functions
58.33 %
Branches
use num_rational::Ratio;
pub type Fraction = Ratio<i64>;
#[derive(Debug, Clone, PartialEq)]
pub struct LambdaParams {
pub required: Vec<String>,
pub optional: Vec<(String, Option<Expr>)>,
pub rest: Option<String>,
pub key: Vec<(String, Option<Expr>)>,
pub aux: Vec<(String, Option<Expr>)>,
}
impl LambdaParams {
pub fn simple(params: Vec<String>) -> Self {
Self {
required: params,
optional: Vec::new(),
rest: None,
key: Vec::new(),
aux: Vec::new(),
pub enum Expr {
Nil,
Bool(bool),
Number(Fraction),
String(String),
Symbol(String),
Keyword(String),
Cons(Box<Expr>, Box<Expr>),
List(Vec<Expr>),
Quote(Box<Expr>),
Quasiquote(Box<Expr>),
Unquote(Box<Expr>),
UnquoteSplicing(Box<Expr>),
Lambda(LambdaParams, Box<Expr>),
RuntimeValue(crate::runtime::Value),
impl Expr {
#[must_use]
pub fn cons(car: Expr, cdr: Expr) -> Self {
Expr::Cons(Box::new(car), Box::new(cdr))
pub struct Annotation {
pub name: String,
pub value: Expr,
pub fn is_atom(&self) -> bool {
!matches!(self, Expr::List(_) | Expr::Cons(_, _))
pub fn as_symbol(&self) -> Option<&str> {
match self {
Expr::Symbol(s) => Some(s),
_ => None,
pub fn as_list(&self) -> Option<&[Expr]> {
Expr::List(l) => Some(l),
#[derive(Debug, Clone, Default)]
pub struct Program {
pub exprs: Vec<Expr>,
pub annotations: Vec<Annotation>,
impl Program {
pub fn new(exprs: Vec<Expr>) -> Self {
exprs,
annotations: Vec::new(),
pub fn with_annotations(exprs: Vec<Expr>, annotations: Vec<Annotation>) -> Self {
Self { exprs, annotations }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_expr_is_atom() {
assert!(Expr::Nil.is_atom());
assert!(Expr::Bool(true).is_atom());
assert!(Expr::Number(Fraction::from_integer(42)).is_atom());
assert!(Expr::String("hello".into()).is_atom());
assert!(Expr::Symbol("foo".into()).is_atom());
assert!(Expr::Keyword("bar".into()).is_atom());
assert!(!Expr::List(vec![]).is_atom());
assert!(!Expr::cons(Expr::Nil, Expr::Nil).is_atom());
assert!(Expr::Lambda(LambdaParams::simple(vec![]), Box::new(Expr::Nil)).is_atom());
fn test_expr_as_symbol() {
assert_eq!(Expr::Symbol("foo".into()).as_symbol(), Some("foo"));
assert_eq!(Expr::Number(Fraction::from_integer(1)).as_symbol(), None);
fn test_expr_as_list() {
let list = Expr::List(vec![Expr::Symbol("a".into())]);
assert!(list.as_list().is_some());
assert_eq!(list.as_list().unwrap().len(), 1);
assert!(Expr::Symbol("a".into()).as_list().is_none());