msbash 0.0.0

Bash interpreter/compiler. Will not support all the functionalities.
#![allow(dead_code)]

#[derive(Clone)]
pub struct UDF {
    pub nbr_args: u8,
    pub instructions: Vec<IR>,
    pub default_args: Vec<u16>,
}

pub struct Cmd {
    idx: u16, //constant defining the command to call
    args: Vec<Index>,
}

#[derive(Clone, Debug)]
pub enum Index {
    Loc(u8),
    Arg(u8),
    Cmd(u16),
    Expr(u16),
    Udf(u16),
    Const(u16),
}

pub const GLOB_VAR_INDIC: u8 = '\\' as u8;
pub const LOCL_VAR_INDIC: u8 = '$' as u8;

#[derive(Clone)]
pub enum IR {
    LocalAssignement(u8, Index),   // can be anything
    GlobalAssignement(u16, Index), // can only assign const or var
    UdfCall(u16, Vec<Index>),
    CmdCall(u16, Vec<Index>),
    BuiltinCall(u16, Vec<Index>),
}

#[derive(Debug, Clone)]
pub struct Expr {
    pub data: Vec<u8>,
}

impl Expr {
    pub fn new(data: Vec<u8>) -> Self {
        Self { data }
    }

    pub fn from_str(
        value: &str,
        locals: &NameToLocalVar,
        globals: &NameToIndex,
    ) -> Result<Expr, String> {
        let mut idx = 0;
        let val = value.as_bytes();

        let len = val.len();
        let mut res: Vec<u8> = Vec::with_capacity(len);

        while idx < len {
            // replace all ${} with a loc or glob variable index
            while idx < len && val[idx] != '$' as u8 {
                res.push(val[idx]);
                idx += 1;
            }
            idx += 1;
            if idx >= len {
                break;
            }

            if val[idx] == '{' as u8 {
                idx += 1;
                let start = idx;
                while val[idx] != '}' as u8 {
                    idx += 1;
                }

                let var_name = &val[start..idx];
                let var_name = std::str::from_utf8(var_name).unwrap();

                // ignore the }
                idx += 1;
                if let Some(idx) = globals.get(var_name) {
                    res.push(GLOB_VAR_INDIC);
                    let bytes = idx.to_be_bytes();
                    res.push(bytes[0]);
                    res.push(bytes[1]);
                } else if let Some(idx) = locals.get(var_name) {
                    res.push(LOCL_VAR_INDIC);
                    res.push(*idx);
                } else {
                    return Err(format!("undefined variable {}", var_name));
                }
            }
        }
        Ok(Expr::new(res))
    }
}

pub type NameToLocalVar = std::collections::HashMap<String, u8>;
pub type NameToIndex = std::collections::HashMap<String, u16>;
pub type IndexResult = Result<Index, String>;
pub type IRResult = Result<IR, String>;

impl UDF {
    pub fn defaults() -> Self {
        let nbr_args = 0;
        let instructions: Vec<IR> = Vec::new();
        let default_args: Vec<u16> = Vec::new();
        Self {
            nbr_args,
            instructions,
            default_args,
        }
    }
    pub fn min_args(&self) -> u8 {
        // can crash on invalid functions
        self.nbr_args - self.default_args.len() as u8
    }
}