lisette-emit 0.2.11

Little language inspired by Rust that compiles to Go
Documentation
use crate::Emitter;
use crate::bindings::BindingValue;
use crate::expressions::context::ExpressionContext;
use crate::names::go_name;
use syntax::ast::{Expression, Generic, Literal, UnaryOperator};
use syntax::types::Type;

impl Emitter<'_> {
    pub(crate) fn emit_type_alias(
        &mut self,
        name: &str,
        generics: &[Generic],
        ty: &Type,
    ) -> String {
        let is_fn_alias;
        let underlying = match ty {
            Type::Forall { body, .. } => match body.as_ref() {
                Type::Nominal {
                    underlying_ty: Some(inner),
                    ..
                } if matches!(inner.as_ref(), Type::Function { .. }) => {
                    is_fn_alias = true;
                    inner.as_ref()
                }
                other => {
                    is_fn_alias = false;
                    other
                }
            },
            Type::Nominal {
                underlying_ty: Some(inner),
                ..
            } if matches!(inner.as_ref(), Type::Function { .. }) => {
                is_fn_alias = true;
                inner.as_ref()
            }
            _ => {
                is_fn_alias = false;
                ty
            }
        };
        let ty_string = self.go_type_as_string(underlying);

        if let Type::Nominal { id, .. } = underlying
            && let Some(module) = self.facts.module_for_qualified_name(id.as_str())
            && !self.facts.is_current_module(module)
            && module != go_name::PRELUDE_MODULE
            && !go_name::is_go_import(module)
        {
            let module = module.to_string();
            self.require_module_import(&module);
        }

        let symbol = self.facts.qualified_current(name);
        let generics_string = self.generics_to_string_for_symbol(&symbol, generics);

        let separator = if is_fn_alias { " " } else { " = " };
        format!(
            "type {}{}{}{}",
            go_name::escape_keyword(name),
            generics_string,
            separator,
            ty_string
        )
    }

    pub(crate) fn emit_const(
        &mut self,
        identifier: &str,
        expression: &Expression,
        ty: &Type,
    ) -> String {
        let target_name = self
            .module
            .escape_remap(identifier)
            .unwrap_or(identifier)
            .to_string();
        let initial_go_name = self.scope.bind(identifier, target_name);
        let go_identifier = if self.try_declare(&initial_go_name) {
            initial_go_name
        } else {
            let fresh = self.fresh_var(Some(identifier));
            self.scope.bind(identifier, &fresh);
            self.try_declare(&fresh);
            fresh
        };
        let ty_str = self.go_type_as_string(ty);

        let mut output = String::new();
        let expression_string =
            self.emit_operand(&mut output, expression, ExpressionContext::value());
        let value = if expression_string.is_empty() {
            "struct{}{}"
        } else {
            &expression_string
        };
        let keyword = if self.is_go_const_eligible(expression) {
            self.record_go_const(go_identifier.clone());
            "const"
        } else {
            "var"
        };
        format!("{} {} {} = {}", keyword, go_identifier, ty_str, value)
    }

    fn is_go_const_eligible(&self, expression: &Expression) -> bool {
        match expression.unwrap_parens() {
            Expression::Literal { literal, .. } => matches!(
                literal,
                Literal::Integer { .. }
                    | Literal::Float { .. }
                    | Literal::Imaginary(_)
                    | Literal::Boolean(_)
                    | Literal::String { .. }
                    | Literal::Char(_)
            ),
            Expression::Identifier { value, .. } => {
                match self.scope.resolve_identifier_binding(value.as_str()) {
                    Some(BindingValue::GoName(name)) => self.is_go_const_binding(name),
                    Some(BindingValue::InlineExpr(_)) => false,
                    None => self.is_go_const_binding(value.as_str()),
                }
            }
            Expression::Binary { left, right, .. } => {
                self.is_go_const_eligible(left) && self.is_go_const_eligible(right)
            }
            Expression::Unary {
                operator: UnaryOperator::Negative | UnaryOperator::Not,
                expression,
                ..
            } => self.is_go_const_eligible(expression),
            _ => false,
        }
    }
}