airlang_bin 0.0.8

The Air Programming Language
use {
    airlang::semantics::{
        generate,
        Interpreter,
        Val,
    },
    std::{
        collections::HashMap,
        error::Error,
        fmt::Display,
    },
};

pub(crate) struct ConstCtx {
    pub(crate) cmd_map: HashMap<String, Box<dyn Cmd>>,
}

pub(crate) struct DynCtx {
    pub(crate) interpreter: Interpreter,
    pub(crate) meta_interpreter: Interpreter,

    pub(crate) multiline_mode: bool,
}

pub(crate) trait Cmd = Fn(&ConstCtx, &mut DynCtx, Val) -> Output;

pub(crate) enum Output {
    Break,
    Ok(Box<dyn Display>),
    Err(Box<dyn Error>),
}

impl ConstCtx {
    pub(crate) fn eval(&self, dyn_ctx: &mut DynCtx, input: Val) -> Output {
        let Val::Call(call) = input else {
            return self.eval_air(dyn_ctx, input);
        };
        let Val::Symbol(func) = &call.func else {
            return self.eval_air(dyn_ctx, Val::Call(call));
        };
        match &**func {
            CMD => self.eval_cmd(dyn_ctx, call.input),
            AIR => self.eval_air(dyn_ctx, call.input),
            _ => self.eval_air(dyn_ctx, Val::Call(call)),
        }
    }

    pub(crate) fn eval_cmd(&self, dyn_ctx: &mut DynCtx, input: Val) -> Output {
        match input {
            Val::Symbol(ref s) => {
                let Some(f) = self.cmd_map.get(&**s) else {
                    return self.eval_meta(dyn_ctx, input);
                };
                f(self, dyn_ctx, Val::default())
            }
            Val::Call(call) => {
                let Val::Symbol(func) = &call.func else {
                    return self.eval_meta(dyn_ctx, Val::Call(call));
                };
                if &**func == AIR {
                    return self.eval_meta(dyn_ctx, call.input);
                }
                let Some(f) = self.cmd_map.get(&**func) else {
                    return self.eval_meta(dyn_ctx, Val::Call(call));
                };
                f(self, dyn_ctx, call.input)
            }
            input => self.eval_meta(dyn_ctx, input),
        }
    }

    pub(crate) fn eval_air(&self, dyn_ctx: &mut DynCtx, val: Val) -> Output {
        Self::eval_interpret(&mut dyn_ctx.interpreter, val)
    }

    pub(crate) fn eval_meta(&self, dyn_ctx: &mut DynCtx, val: Val) -> Output {
        Self::eval_interpret(&mut dyn_ctx.meta_interpreter, val)
    }

    fn eval_interpret(interpreter: &mut Interpreter, val: Val) -> Output {
        let output = interpreter.interpret(val);
        match generate(&output) {
            Ok(output) => Output::Ok(Box::new(output)),
            Err(err) => Output::Err(Box::new(err)),
        }
    }
}

const CMD: &str = ":cmd";
const AIR: &str = ":air";