mist-codegen 0.3.1-alpha.0

The Mist programming language Rust code generator
Documentation
pub mod class_decl;
pub mod expr;
pub mod statement;
pub mod top_level;

use mist_parser::ast::*;

pub struct Context {
    pub expr_ensure_semicolon: bool,
    pub expr_super: Option<ExprPath>,
}

pub trait GenRust {
    fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen);
}

pub trait GetRust {
    fn get_rust(&self) -> String;
    fn get_rust_ctx(&self, cx: &mut Context) -> String {
        let _ = cx;
        self.get_rust()
    }
}

#[derive(Default)]
pub struct RustCodegen {
    output: String,
    indent: usize,
}

impl RustCodegen {
    pub fn new() -> Self {
        Self {
            output: String::new(),
            indent: 0,
        }
    }

    fn indent_str(&self) -> String {
        "    ".repeat(self.indent)
    }

    fn add(&mut self, s: &str) {
        self.output.push_str(s);
    }

    fn addln(&mut self, s: &str) {
        self.add(s);
        self.add("\n");
    }

    fn add_indented(&mut self, s: &str) {
        let line = format!("{}{}", self.indent_str(), s);
        self.add(&line);
    }

    fn add_indentedln(&mut self, s: &str) {
        let line = format!("{}{}\n", self.indent_str(), s);
        self.add(&line);
    }

    pub fn generate(&mut self, toplevels: Vec<TopLevel>) -> String {
        let mut ctx = Context {
            expr_ensure_semicolon: true,
            expr_super: None,
        };

        for tl in toplevels {
            tl.gen_rust(&mut ctx, self);
        }

        self.output.clone()
    }

    pub fn ensure_brackets(&mut self, ctx: &mut Context, stmt: &Box<Statement>) {
        match &**stmt {
            Statement::Block(_) => stmt.gen_rust(ctx, self),
            _ => {
                self.add("{");
                self.indent += 1;
                stmt.gen_rust(ctx, self);
                self.indent -= 1;
                self.add("}");
            }
        }
    }

    pub fn ensure_brackets_expr(&mut self, ctx: &mut Context, expr: &Expression) {
        match expr {
            Expression::Statement(stmt) => self.ensure_brackets(ctx, stmt),
            _ => {
                self.add("{");
                self.indent += 1;
                expr.gen_rust(ctx, self);
                self.indent -= 1;
                self.add("}");
            }
        }
    }

    pub fn ensure_brackets_body(&mut self, ctx: &mut Context, body: &StatementBody) {
        match body {
            StatementBody::Expression(expr) => self.ensure_brackets_expr(ctx, expr),

            StatementBody::Statement(expr) => {
                ctx.expr_ensure_semicolon = true;
                self.ensure_brackets_expr(ctx, expr);
            }
        }
    }
}

impl GenRust for Attribute {
    fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
        match self {
            Self::Path(path) => cg.add(&path.get_rust()),
            Self::NameValue { path, value } => {
                cg.add(&format!("{} = ", path.get_rust()));
                value.gen_rust(ctx, cg);
            }
            Self::List { path, items } => {
                cg.add(&path.get_rust());
                cg.add("(");
                for (i, item) in items.iter().enumerate() {
                    if i > 0 {
                        cg.add(", ");
                    }

                    item.gen_rust(ctx, cg);
                }
                cg.add(")");
            }
        }
    }
}

impl<T: GetRust> GetRust for Spanned<T> {
    fn get_rust(&self) -> String {
        format!(
            "/* {}:{} */ {}",
            self.line,
            self.column,
            self.item.get_rust()
        )
    }
}

impl<T: GenRust> GenRust for Spanned<T> {
    fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
        cg.add_indentedln(&format!("/* {}:{} */", self.line, self.column));
        cg.add_indented("");
        self.item.gen_rust(ctx, cg);
    }
}

impl GetRust for Path {
    fn get_rust(&self) -> String {
        self.0
            .iter()
            .map(Identifier::get_rust)
            .collect::<Vec<String>>()
            .join("::")
    }
}

impl GetRust for Visibility {
    fn get_rust(&self) -> String {
        match self {
            Visibility::Public => "pub ".to_string(),
            Visibility::PublicTarget(path) => format!("pub({}) ", path.get_rust()),
            Visibility::Private => "".to_string(),
        }
    }
}

impl GetRust for Identifier {
    fn get_rust(&self) -> String {
        self.0.clone()
    }
}

impl GetRust for TypeExpr {
    fn get_rust(&self) -> String {
        match self {
            Self::Path(path, generics) => {
                if let Some(generics) = generics {
                    format!("{}{}", path.get_rust(), generics.get_rust())
                } else {
                    path.get_rust()
                }
            }
            Self::Lifetime(name) => format!("'{}", name.get_rust()),
            Self::Tuple(types) => format!(
                "({})",
                types
                    .into_iter()
                    .map(|t| t.get_rust())
                    .collect::<Vec<_>>()
                    .join(", ")
            ),
            Self::StaticFn(types, return_type) => {
                if let Some(return_type) = return_type {
                    format!(
                        "fn({}) -> {}",
                        types
                            .into_iter()
                            .map(|t| t.get_rust())
                            .collect::<Vec<_>>()
                            .join(", "),
                        return_type.get_rust()
                    )
                } else {
                    format!(
                        "fn({})",
                        types
                            .into_iter()
                            .map(|t| t.get_rust())
                            .collect::<Vec<_>>()
                            .join(", "),
                    )
                }
            }

            Self::UnsafePtr { mutable, ty } => {
                let mutable = if *mutable { "mut " } else { "const " };
                format!("*{mutable}{}", ty.get_rust())
            }

            Self::Ref {
                lifetime,
                mutable,
                ty,
            } => {
                let mutable = if *mutable { "mut " } else { "" };

                if let Some(lifetime) = lifetime {
                    format!("&'{} {mutable}{}", lifetime.get_rust(), ty.get_rust())
                } else {
                    format!("&{mutable}{}", ty.get_rust())
                }
            }

            Self::Dyn(ty) => {
                format!("dyn {}", ty.get_rust())
            }
        }
    }
}

impl GenRust for Pattern {
    fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
        match self {
            Self::Etc => cg.add(".."),
            Self::Literal(lit) => lit.gen_rust(ctx, cg),
            Self::Path(mutable, path) => {
                if *mutable {
                    cg.add("mut ");
                };

                cg.add(&path.get_rust())
            }
            Self::Struct(path, inner) => {
                cg.add(&path.get_rust());
                cg.add(" {");

                for (idx, i) in inner.iter().enumerate() {
                    if idx > 0 {
                        cg.add(", ");
                    }

                    if let Some((name, pat)) = i {
                        cg.add(&name.get_rust());
                        if let Some(pat) = pat {
                            cg.add(": ");
                            pat.gen_rust(ctx, cg);
                        }
                    } else {
                        cg.add("..");
                    }
                }

                cg.add("}");
            }

            Self::NamedTuple(path, inner) => {
                cg.add(&path.get_rust());
                cg.add(" (");
                for pat in inner {
                    pat.gen_rust(ctx, cg);
                    cg.add(",");
                }
                cg.add(")");
            }

            Self::Tuple(inner) => {
                cg.add("(");
                for pat in inner {
                    pat.gen_rust(ctx, cg);
                    cg.add(",");
                }
                cg.add(")");
            }
        }
    }
}