datex_macros 0.1.8

The public macros for the DATEX Core Rust implementation
Documentation
use crate::utils::expr_to_value_container;
use datex_core::{
    self,
    compiler::{CompileOptions, compile_template},
    core_compiler::core_compilation_context::DXBWithSharedValues,
    prelude::*,
    runtime::Runtime,
    values::value_container::ValueContainer,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    Expr, LitStr, Result, Token,
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
};

enum Placeholder {
    ValueContainer(ValueContainer),
    Expression(Expr),
}

pub struct ExecuteMacroInput {
    program: String,
    args: Vec<Placeholder>,
}

impl Parse for ExecuteMacroInput {
    fn parse(input: ParseStream) -> Result<Self> {
        let program: LitStr = input.parse()?;
        let mut tokened_args = Punctuated::<Expr, Token![,]>::new();

        if input.peek(Token![,]) {
            let _comma: Token![,] = input.parse()?;
            while !input.is_empty() {
                tokened_args.push_value(input.parse()?);
                if input.peek(Token![,]) {
                    tokened_args.push_punct(input.parse()?);
                } else {
                    break;
                }
            }
        }
        let mut args = Vec::new();
        for arg in tokened_args.into_iter() {
            if let Ok(value) = expr_to_value_container(&arg) {
                args.push(Placeholder::ValueContainer(value));
            } else {
                args.push(Placeholder::Expression(arg));
            }
        }

        Ok(Self {
            program: program.value(),
            args,
        })
    }
}

fn prepare_setup(input: ExecuteMacroInput) -> TokenStream {
    let runtime = Runtime::stub();
    let script = input.program;

    let placeholder_count = script.chars().filter(|&c| c == '?').count();
    let arg_count = input.args.len();

    if placeholder_count != arg_count {
        return syn::Error::new_spanned(
            script,
            format!(
                "execute!: placeholder count ({}) != argument count ({})",
                placeholder_count, arg_count
            ),
        )
        .to_compile_error();
    }

    let stack_init = input
        .args
        .iter()
        .map(|placeholder| match placeholder {
            Placeholder::ValueContainer(_) => quote! {
                stack_values.push(None);
            },
            Placeholder::Expression(expr) => quote! {
                stack_values.push(Some(ValueContainer::from(#expr)));
            },
        })
        .collect::<Vec<_>>();

    let placeholders: Vec<Option<ValueContainer>> = input
        .args
        .into_iter()
        .map(|p| match p {
            Placeholder::ValueContainer(v) => Some(v),
            Placeholder::Expression(_) => None,
        })
        .collect::<Vec<_>>();

    let dxb = compile_template(
        &script,
        placeholders,
        CompileOptions::default(),
        runtime,
    );
    if let Err(e) = dxb {
        return syn::Error::new_spanned(
            script,
            format!("Failed to compile template: {}", e),
        )
        .to_compile_error();
    }
    let DXBWithSharedValues { dxb, shared_values } = dxb.unwrap().0;

    if dxb.is_empty() {
        return syn::Error::new(
            proc_macro2::Span::call_site(),
            format!(
                "execute!: compiled DXB body is empty for script `{}`",
                script
            ),
        )
        .to_compile_error();
    }
    println!("compiled DXB length = {}", dxb.len());
    println!("shared values length = {}", shared_values.len());

    quote! {{
        use datex_core::runtime::execution::execution_loop::state::RuntimeExecutionStack;
        use datex_core::runtime::execution::execution_input::ExecutionCallerMetadata;
        use datex_core::values::value_container::ValueContainer;
        use datex_core::collections::HashMap;
        use datex_core::runtime::execution::{ExecutionInput, ExecutionOptions};
        use datex_core::runtime::Runtime;
        use datex_core::prelude::*;
        use datex_core::core_compiler::core_compilation_context::DXBWithSharedValues;

        let mut stack_values: Vec<Option<ValueContainer>> = Vec::new();
        #(#stack_init)*

        let runtime_execution_stack = RuntimeExecutionStack { values: stack_values };
        let dxb_with_shared_values = DXBWithSharedValues {
            dxb: vec![#(#dxb),*],
            shared_values: vec![],
        };

        // println!("runtime_execution_stack.values.len() = {}", datex_core::decompiler::decompile_body(
        //     &dxb_with_shared_values.dxb,
        //     datex_core::decompiler::DecompileOptions::colorized(),
        // ).unwrap());

        let runtime = Runtime::stub();
        ExecutionInput::new_with_stack(
            dxb_with_shared_values,
            ExecutionCallerMetadata::local_default(),
            ExecutionOptions::default(),
            runtime,
            runtime_execution_stack
        )
    }}
}

pub fn execute_sync(input: ExecuteMacroInput) -> TokenStream {
    let setup = prepare_setup(input);
    quote! {{
        datex_core::runtime::execution::execute_dxb_sync(#setup)
    }}
}
pub fn execute_async(input: ExecuteMacroInput) -> TokenStream {
    let setup = prepare_setup(input);
    quote! {{
        async move {
            datex_core::runtime::execution::execute_dxb(#setup).await
        }
    }}
}
pub fn execute_sync_unchecked(input: ExecuteMacroInput) -> TokenStream {
    let setup = prepare_setup(input);
    quote! {{
        datex_core::runtime::execution::execute_dxb_sync(#setup).expect("Failed to execute DXB")
    }}
}
pub fn execute_async_unchecked(input: ExecuteMacroInput) -> TokenStream {
    let setup = prepare_setup(input);
    quote! {{
        async move {
            datex_core::runtime::execution::execute_dxb(#setup).await.expect("Failed to execute DXB")
        }
    }}
}