reic 0.1.0

A compiler that just works
Documentation
use crate::{
    ast::AST,
    expr::{
        ArgList, Content, Expr, Fn, FnCall, ModExpr, ParamList, Primitive, ReiType, TypeModifier,
        UseExpr, VarDef,
    },
    lexer::Parser,
    lexer::Token,
    symtab::Identifier,
};
use log::info;

// ----------------
// PARSING
// ----------------

pub type ExprRes<T> = Result<T, Token>;
macro_rules! expr_none {
    () => {
        Err(Token::EOF)
    };
}

fn var_def_expr(parser: &mut Parser) -> ExprRes<VarDef> {
    let mut res = VarDef {
        var_type_modifier: TypeModifier::Let,
        lhs: Expr::Empty,
        rhs: Expr::Empty,
    };

    let mut flag = false;

    if parser.accept_ok(Token::Let) {
        res.var_type_modifier = TypeModifier::Let;
        flag = true;
    } else if parser.accept_ok(Token::Const) {
        res.var_type_modifier = TypeModifier::Const;
        flag = true;
    } else if parser.accept_ok(Token::Static) {
        res.var_type_modifier = TypeModifier::Static;
        flag = true;
    } else if parser.accept_ok(Token::Mut) {
        res.var_type_modifier = TypeModifier::Mut;
        flag = true;
    }

    if flag {
        // ? I think we should do either expr or partialexpr without an assignment operator
        // or just make that exprs dont include =

        res.lhs = expr(parser)?;
        parser.expect(Token::Equals);
        res.rhs = expr(parser)?;

        return Ok(res);
    }

    expr_none!()
}

/// Use statement: relative use x::* or absolute project::/project_name::
fn use_expr(parser: &mut Parser) -> ExprRes<UseExpr> {
    match parser.accept(Token::Use) {
        Ok(p) => {
            info!("Found the use keyword");

            // if ident, check if thats it, or there is a ::
            let r = parser.expect(Token::Identifier);
            let res = UseExpr {
                namespace: r,
                items: vec![],
            };

            if parser.accept(Token::DoubleColon).is_ok() {
                parser.expect(Token::Star);
            }

            // should you do nextsym?
            // accept() already does nextsym

            Ok(res)
        }
        Err(e) => expr_none!(),
    }
}

pub const MAX_PARAMS: usize = 512;

fn param_list_expr(parser: &mut Parser) -> ExprRes<ParamList> {
    let mut paramlist = ParamList { params: vec![] };

    parser.accept(Token::ParenLeft)?;

    for i in 0..MAX_PARAMS {
        let name = parser.accept(Token::Identifier)?;

        // instead of expecting a rei type, just do ident

        parser.expect(Token::Colon);
        let param_type = parser.expect(Token::Identifier);

        paramlist.params.push((name, param_type, None));

        // if no more args, then break. No trailing commas allowed in rei (although I do kinda like it)
        if !parser.accept_ok(Token::Comma) {
            break;
        }
    }

    parser.accept(Token::ParenRight)?;

    return Ok(paramlist);
}

// I think a function call is clearly a special expr function call expr

fn arg_list_expr(parser: &mut Parser) -> ExprRes<ArgList> {
    let mut arglist = ArgList { args: vec![] };

    parser.accept(Token::ParenLeft)?;

    loop {
        let res = parser.accept(Token::Identifier)?;
        arglist.args.push(res);

        // if no more args, then break. No trailing commas allowed in rei (although I do kinda like it)
        if !parser.accept_ok(Token::Comma) {
            break;
        }
    }

    parser.accept(Token::ParenRight)?;

    return Ok(arglist);
}

fn fn_call_expr(parser: &mut Parser) -> ExprRes<FnCall> {
    let ident = parser.accept(Token::Identifier)?;

    // check arg list
    let arg_list = arg_list_expr(parser)?;

    Ok(FnCall {
        fn_name: ident,
        args: arg_list,
    })
}

// NOTE: the Result<> is used in place of Option, since its great with the propagation

fn fn_expr(parser: &mut Parser) -> ExprRes<Fn> {
    // what about 'export'? an export expression prob is needed then
    // I dunno about templates. Should you annotate them?

    if parser.accept_ok(Token::Function) {
        info!("Found the fn keyword");

        // collect identifier of the function
        let name = parser.expect(Token::Identifier);

        // collect any post id stuff like template parameters

        // collect param list (same for classes)
        // NOTE: a function (non anon) needs a paramlist. An empty paramlist is still a paramlist
        let paramlist = param_list_expr(parser)?;

        // parse -> Identifier. If no "->", assume () return
        if parser.accept_ok(Token::RightArrow) {}

        // parse scope, if theres a scope, treat the next as a body
        // if no scope, only get next "line". MAYBE have a line expr?

        // collect body, there should always be a body, an empty {} body is still an expr
        let expr_body = expr(parser)?;

        let res = Fn {
            export: false,
            fn_name: name,
            fn_return_type: todo!(),
            args: todo!(),
            body: todo!(),
        };
    }

    expr_none!()
}

fn mod_expr(parser: &mut Parser) -> ExprRes<Expr> {
    if parser.accept_ok(Token::Module) {
        // NOTE: this accept() actually increments the current token
        // which prob shouldnt happen

        info!("Found the mod keyword");

        parser.expect(Token::Identifier);
        parser.expect(Token::CurlyBraceLeft);

        // do this. Oh, so this is returning none instead of something like empty
        let res = expr(parser)?;

        info!("Returning ok = {res:?}");

        return Ok(ModExpr { body: res }.into());
    }

    expr_none!()
}

// NOTE: have to .into() for expressions to work
macro_rules! ret_expr {
    ($res:expr) => {
        match $res {
            Ok(ex) => return Ok(ex.into()),
            Err(_) => (),
        }
    };
}

fn expr(parser: &mut Parser) -> ExprRes<Expr> {
    // prob just match then, for OR
    ret_expr!(mod_expr(parser));
    ret_expr!(use_expr(parser));
    ret_expr!(fn_expr(parser));
    ret_expr!(fn_call_expr(parser));

    expr_none!()
}

fn content(parser: &mut Parser) -> Content {
    let mut content = Content::default();

    loop {
        match expr(parser) {
            Ok(r) => {
                // if doesnt get here, cant find an expr
                info!("Found an expression: {r:?}");
                // Add as a child to symtab or something, later
                content.expressions.push(r);
            }
            Err(e) => break,
        }
    }

    content
}

pub fn parse_tokens(mut parser: Parser) -> AST {
    // in rei, Content::default() or Content::new() would BE macro'd or aliased to be Content()
    content(&mut parser)
}

// ------------
// TESTS
// ------------

#[test]
fn test_use_expr() {}