aiken-lang 1.0.22-alpha

The Aiken compiler
Documentation
use chumsky::prelude::*;

use crate::{
    ast,
    expr::UntypedExpr,
    parser::{annotation, error::ParseError, pattern, token::Token},
};

pub fn let_(
    r: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
    just(Token::Let)
        .ignore_then(pattern())
        .then(just(Token::Colon).ignore_then(annotation()).or_not())
        .then_ignore(just(Token::Equal))
        .then(r.clone())
        .validate(move |((pattern, annotation), value), span, emit| {
            if matches!(value, UntypedExpr::Assignment { .. }) {
                emit(ParseError::invalid_assignment_right_hand_side(span))
            }

            UntypedExpr::Assignment {
                location: span,
                value: Box::new(value),
                pattern,
                kind: ast::AssignmentKind::Let,
                annotation,
            }
        })
}

pub fn expect(
    r: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
    just(Token::Expect)
        .ignore_then(
            pattern()
                .then(just(Token::Colon).ignore_then(annotation()).or_not())
                .then_ignore(just(Token::Equal))
                .or_not(),
        )
        .then(r.clone())
        .validate(move |(opt_pattern, value), span, emit| {
            let (pattern, annotation) = opt_pattern.unwrap_or_else(|| {
                (
                    ast::UntypedPattern::Constructor {
                        is_record: false,
                        location: span,
                        name: "True".to_string(),
                        arguments: vec![],
                        module: None,
                        constructor: (),
                        with_spread: false,
                        tipo: (),
                    },
                    None,
                )
            });

            if matches!(value, UntypedExpr::Assignment { .. }) {
                emit(ParseError::invalid_assignment_right_hand_side(span))
            }

            UntypedExpr::Assignment {
                location: span,
                value: Box::new(value),
                pattern,
                kind: ast::AssignmentKind::Expect,
                annotation,
            }
        })
}

#[cfg(test)]
mod tests {
    use crate::assert_expr;

    #[test]
    fn let_bindings() {
        assert_expr!("let thing = [ 1, 2, a ]");
    }

    #[test]
    fn expect() {
        assert_expr!("expect Some(x) = something.field");
    }

    #[test]
    fn expect_bool_sugar() {
        assert_expr!("expect something.field == wow");
    }

    #[test]
    fn expect_trace_if_false() {
        assert_expr!("expect foo?");
    }

    #[test]
    fn expect_unfinished_let() {
        assert_expr!(
            "
            let a =
            // foo
            let b = 42
            "
        );
    }

    #[test]
    fn expect_let_in_let() {
        assert_expr!("let a = { let b = 42 }");
    }

    #[test]
    fn expect_let_in_let_return() {
        assert_expr!(
            "
            let a = {
              let b = 42
              b
            }
            "
        );
    }

    #[test]
    fn expect_let_in_let_parens() {
        assert_expr!("let a = ( let b = 42 )");
    }

    #[test]
    fn expect_expect_let() {
        assert_expr!("expect { let a = 42 } = foo");
    }
}