vyre-libs 0.7.2

vyre Category A library ecosystem - pure-IR compositions over foundation IR and primitive-owned kernels
Documentation
//! Object-like macro replacement: the `##` paste prefix, the replacement
//! walk over the macro body, and the pasted-token synthesis.

use crate::parsing::c::lex::tokens::TOK_HASHHASH;
use crate::parsing::c::preprocess::synthesis::*;
use vyre_foundation::ir::{Expr, Node};

use super::*;

pub(super) fn emit_object_like_token_paste_prefix(
    macro_vals: &str,
    macro_replacement_params: &str,
    out_tok_types: &str,
    synth_failure_trap: &'static str,
) -> Vec<Node> {
    vec![
        Node::if_then(
            Expr::eq(Expr::var("named_out_idx"), Expr::u32(0)),
            vec![Node::trap(
                Expr::var("named_repl_i"),
                "object-like-token-paste-missing-left-token",
            )],
        ),
        Node::if_then(
            Expr::ge(
                Expr::add(Expr::var("named_repl_i"), Expr::u32(1)),
                Expr::var("named_repl_size"),
            ),
            vec![Node::trap(
                Expr::var("named_repl_i"),
                "object-like-token-paste-missing-right-token",
            )],
        ),
        Node::let_bind(
            "macro_paste_next_offset",
            Expr::add(
                Expr::var("named_macro_idx"),
                Expr::add(Expr::var("named_repl_i"), Expr::u32(1)),
            ),
        ),
        Node::let_bind(
            "macro_paste_next_param",
            Expr::load(
                macro_replacement_params,
                Expr::var("macro_paste_next_offset"),
            ),
        ),
        Node::if_then(
            Expr::ne(
                Expr::var("macro_paste_next_param"),
                Expr::u32(C_MACRO_REPLACEMENT_LITERAL),
            ),
            vec![Node::trap(
                Expr::var("macro_paste_next_param"),
                "object-like-token-paste-cannot-reference-parameters",
            )],
        ),
        Node::let_bind(
            "macro_paste_left_tok",
            Expr::load(
                out_tok_types,
                Expr::sub(Expr::var("named_out_idx"), Expr::u32(1)),
            ),
        ),
        Node::let_bind(
            "macro_paste_right_tok",
            Expr::load(macro_vals, Expr::var("macro_paste_next_offset")),
        ),
        Node::let_bind(
            "macro_paste_synth_tok",
            synthesized_paste_token(
                Expr::var("macro_paste_left_tok"),
                Expr::var("macro_paste_right_tok"),
            ),
        ),
        Node::if_then(
            Expr::eq(
                Expr::var("macro_paste_synth_tok"),
                Expr::u32(EMPTY_MACRO_SLOT),
            ),
            vec![Node::trap(
                Expr::var("macro_paste_right_tok"),
                synth_failure_trap,
            )],
        ),
        Node::store(
            out_tok_types,
            Expr::sub(Expr::var("named_out_idx"), Expr::u32(1)),
            Expr::var("macro_paste_synth_tok"),
        ),
    ]
}

pub(super) fn emit_object_like_replacement_loop(
    macro_vals: &str,
    macro_replacement_params: &str,
    paste_branch: Vec<Node>,
    literal_branch: Vec<Node>,
) -> Vec<Node> {
    vec![
        Node::let_bind("named_skip_repl", Expr::u32(0)),
        Node::loop_for(
            "named_repl_i",
            Expr::u32(0),
            Expr::var("named_repl_size"),
            vec![Node::if_then_else(
                Expr::eq(Expr::var("named_skip_repl"), Expr::u32(1)),
                vec![Node::assign("named_skip_repl", Expr::u32(0))],
                {
                    let mut body = vec![
                        Node::let_bind(
                            "named_repl_offset",
                            Expr::add(Expr::var("named_macro_idx"), Expr::var("named_repl_i")),
                        ),
                        Node::let_bind(
                            "named_repl_param",
                            Expr::load(macro_replacement_params, Expr::var("named_repl_offset")),
                        ),
                        Node::if_then(
                            Expr::ne(
                                Expr::var("named_repl_param"),
                                Expr::u32(C_MACRO_REPLACEMENT_LITERAL),
                            ),
                            vec![Node::trap(
                                Expr::var("named_repl_param"),
                                "object-like-macro-replacement-cannot-reference-parameters",
                            )],
                        ),
                        Node::let_bind(
                            "named_repl_tok",
                            Expr::load(macro_vals, Expr::var("named_repl_offset")),
                        ),
                    ];
                    body.push(Node::if_then_else(
                        Expr::eq(Expr::var("named_repl_tok"), Expr::u32(TOK_HASHHASH)),
                        paste_branch,
                        literal_branch,
                    ));
                    body
                },
            )],
        ),
        Node::assign("named_i", Expr::add(Expr::var("named_i"), Expr::u32(1))),
    ]
}

pub(super) fn synthesized_paste_token(left: Expr, right: Expr) -> Expr {
    C_TOKEN_PASTE_RULES.iter().rev().fold(
        Expr::u32(EMPTY_MACRO_SLOT),
        |fallback, (left_tok, right_tok, out_tok)| {
            Expr::select(
                Expr::and(
                    Expr::eq(left.clone(), Expr::u32(*left_tok)),
                    Expr::eq(right.clone(), Expr::u32(*right_tok)),
                ),
                Expr::u32(*out_tok),
                fallback,
            )
        },
    )
}