lmntalc 0.13.1

A compiler for the LMNtal language
Documentation
use std::collections::HashMap;

use lmntalc_core::model::guard::{self, ProcessConstraint};

use crate::ir::{BinaryOperator, Literal, Operation, UnaryOperator, VarSource};

use super::BackendError;

#[derive(Debug, Default)]
pub struct BackendState {
    pub var_type: HashMap<usize, ProcessConstraint>,
    pub name_map: HashMap<String, usize>,
}

impl BackendState {
    pub fn get_name(&mut self, name: &str) -> usize {
        if let Some(id) = self.name_map.get(name) {
            *id
        } else {
            let id = self.name_map.len();
            self.name_map.insert(name.to_string(), id);
            id
        }
    }
}

pub trait Dialect {
    fn string_literal(&self, value: &str) -> String;
    fn char_literal(&self, value: char) -> String;
    fn type_name(&self, ty: ProcessConstraint) -> Result<&'static str, BackendError>;
    fn port_value_expr(
        &self,
        atom_id: usize,
        port: usize,
        ty: ProcessConstraint,
    ) -> Result<String, BackendError>;
    fn temp_variable_expr(&self, id: usize) -> String {
        format!("var_{}", id)
    }
    fn binary_operator(&self, op: BinaryOperator) -> Result<&'static str, BackendError>;
    fn unary_operator(&self, op: UnaryOperator) -> &'static str;
    fn reserved_function(
        &self,
        name: &str,
        args: &[String],
        ty: ProcessConstraint,
    ) -> Result<String, BackendError>;
}

pub fn print_operation<D: Dialect>(dialect: &D, op: &Operation) -> Result<String, BackendError> {
    match op {
        Operation::Literal(literal) => Ok(match literal {
            Literal::Int(i) => i.to_string(),
            Literal::Float(f) => f.to_string(),
            Literal::Char(c) => dialect.char_literal(*c),
            Literal::String(s) => dialect.string_literal(s),
        }),
        Operation::Variable { source, ty_ } => match source {
            VarSource::Head(id, port) | VarSource::Body(id, port) => {
                dialect.port_value_expr(*id, *port, *ty_)
            }
            VarSource::Variable(id) => Ok(dialect.temp_variable_expr(*id)),
        },
        Operation::BinaryOP { op, lhs, rhs } => Ok(format!(
            "({} {} {})",
            print_operation(dialect, lhs)?,
            dialect.binary_operator(*op)?,
            print_operation(dialect, rhs)?
        )),
        Operation::UnaryOP { op, operand } => Ok(format!(
            "{}{}",
            dialect.unary_operator(*op),
            print_operation(dialect, operand)?
        )),
        Operation::FunctionCall { name, args, ty_ } => {
            if guard::RESERVED_FUNC.iter().any(|func| func.name == name) {
                let rendered = args
                    .iter()
                    .map(|arg| print_operation(dialect, arg))
                    .collect::<Result<Vec<_>, _>>()?;
                dialect.reserved_function(name, &rendered, *ty_)
            } else {
                Err(BackendError::UnsupportedFunction {
                    function: name.clone(),
                })
            }
        }
    }
}