use crate::{ ast::{Block, Expression, Program, Return, Statement, Type}, diagnostic::Diagnostic, verbose }; use askama::Template; use std::{borrow::Cow, fmt::Write as _}; mod assign; mod conditional; mod decl; mod expect_ty; mod expr; mod function; mod resolve_ty; mod state; mod ty; use assign::expand_assign; use conditional::{expand_if, expand_while}; use decl::expand_decl; use expect_ty::expect_tuple_exact; use expr::expand_expr_with_type; use function::expand_function; use state::State; use ty::expand_type; pub enum CompileResult { Ok { main_rs: syn::File, funs_rs: syn::File, types_rs: syn::File }, InternalErr(String, syn::Error) } mod tpl { use crate::ast::{Generics, Output}; use askama::Template; use proc_macro2::Ident; use std::borrow::Cow; mod filters { use std::fmt::Display; pub fn if_some(opt: &Option) -> askama::Result { Ok(if let Some(value) = opt.as_ref() { value.to_string() } else { String::new() }) } } #[derive(Template)] #[template(path = "main.rs.j2", escape = "none")] pub(super) struct MainRs<'a> { pub(super) inputs: Vec>, pub(super) outputs: &'a [Output] } #[derive(Template)] #[template(path = "types.rs.j2", escape = "none")] pub(super) struct TypesRs<'a> { pub(super) typedefs: Vec> } pub(super) struct TypeDef<'a> { pub(super) ident: &'a Ident, pub(super) generics: Option<&'a Generics>, pub(super) inner_ty: Cow<'static, str> } impl<'a> TypeDef<'a> { pub(super) fn new( ident: &'a Ident, generics: Option<&'a Generics>, inner_ty: Cow<'static, str> ) -> Self { Self { ident, generics, inner_ty } } } pub(super) struct Input<'a> { pub(super) ident: &'a Ident, short_long: &'static str, quote: char } impl<'a> Input<'a> { pub(super) fn new(ident: &'a Ident) -> Self { let (short_long, quote) = match ident.to_string() { str if str.len() == 1 && str != "h" && str != "n" && str != "v" => { ("short", '\'') }, _ => ("long", '"') }; Self { ident, short_long, quote } } } } #[allow(clippy::write_with_newline)] pub fn compile(input: &str) -> Result { let program: Program = syn::parse_str(input)?; let mut state = State::new(program.types, program.functions); let main_rs = tpl::MainRs { inputs: program .inputs .iter() .map(|arg| tpl::Input::new(&arg.ident)) .collect(), outputs: &program.outputs }; let main_rs = main_rs.render().unwrap(); let types_rs = tpl::TypesRs { typedefs: state .types() .into_iter() .map(|ty| { Ok(tpl::TypeDef::new( &ty.ident, ty.generics.as_ref(), expand_type(&state, &ty.ty)? )) }) .collect::, Diagnostic>>()? }; let types_rs = types_rs.render().unwrap(); let mut buf = String::new(); buf += "#![no_implicit_prelude]\n"; buf += "#![allow(unused_mut, while_true)]\n\n"; buf += "use crate::types::*;\n\n"; buf += "pub(crate) fn run(crate::Values {\n"; for input in program.inputs { write!(buf, " mut {},\n", input.ident).unwrap(); state.add_variable(input.ident, Type::Int(Default::default())); } buf += "}: crate::Values) -> crate::Output {\n"; for s in program.stmts { buf += " "; buf += &expand_stmt(&mut state, s)?; buf += "\n"; } buf += " crate::Output {\n"; for output in program.outputs { for ident in output.idents { write!(buf, " {ident}: ").unwrap(); buf += &expand_expr_with_type( &state, &Type::Int(Default::default()), Expression::Ident(ident) )?; buf += ",\n"; } } buf += " }\n"; buf += "}\n\n"; while let Some(idx) = state.next_function_instance() { state.with_function_instance(idx, |fun, generics, ident| { if verbose() { eprint!("[compile] Expanding function instance {ident} ..."); } let res = expand_function(&mut buf, &state, fun, generics, ident); if verbose() { eprintln!("done"); } res })?; } Ok(CompileResult::Ok { main_rs: match syn::parse_file(&main_rs) { Ok(main_rs) => main_rs, Err(err) => return Ok(CompileResult::InternalErr(main_rs, err)) }, funs_rs: match syn::parse_file(&buf) { Ok(funs_rs) => funs_rs, Err(err) => return Ok(CompileResult::InternalErr(buf, err)) }, types_rs: match syn::parse_file(&types_rs) { Ok(types_rs) => types_rs, Err(err) => return Ok(CompileResult::InternalErr(types_rs, err)) } }) } fn expand_stmt( state: &mut State<'_>, stmt: Statement ) -> Result, Diagnostic> { Ok(match stmt { Statement::Decl(decl) => expand_decl(state, decl)?.into(), Statement::Assign(assign) => expand_assign(state, assign)?.into(), Statement::If(if_expr) => expand_if(state, if_expr)?.into(), Statement::CoinFlip { head, prob, tail } => { let mut buf = format!("match ::stdlib::Coin::flip({}) {{", prob); buf += "::stdlib::Coin::Head => "; buf += &expand_block(state, head)?; buf += ", ::stdlib::Coin::Tail => "; buf += &expand_block(state, tail)?; buf += "}"; buf.into() }, Statement::While(while_expr) => expand_while(state, while_expr)?.into(), Statement::Block(block) => expand_block(state, block)?.into(), Statement::Return(Return { return_token, expr, .. }) => { let return_ty = state.return_ty().ok_or_else(|| { Diagnostic::new( return_token.span, "Cannot return", "Return statements may only be used in function bodies" ) })?; match expr { Some(expr) => { format!("return {};", expand_expr_with_type(state, return_ty, expr)?) .into() }, None => { expect_tuple_exact( return_ty, 0, return_token.span, return_token.span )?; "return;".into() } } }, Statement::Continue(cont) => { if !state.is_loop() { return Err(Diagnostic::new( cont.continue_token.span, "Not a loop", "You may only use `continue` in a loop body" )); } "continue;".into() }, Statement::Break(brake) => { if !state.is_loop() { return Err(Diagnostic::new( brake.break_token.span, "Not a loop", "You may only use `break` in a loop body" )); } "break;".into() } }) } fn expand_block(state: &State<'_>, block: Block) -> Result { let mut sub = state.substate(); let mut buf = "{".to_owned(); for s in block.stmts { buf += &expand_stmt(&mut sub, s)?; } buf += "}"; Ok(buf) }