spl 0.4.2

Stack Pogramming Language: A simple, concise scripting language.
Documentation
use std::sync::Arc;

use crate::sasm::sasm_read;
use crate::*;
use crate::{lexer, runtime::*};

pub fn dyn_dump(stack: &mut Stack) -> OError {
    Words {
        words: vec![Word::Key(Keyword::Dump)],
    }
    .exec(stack)
}

pub fn dyn_def(stack: &mut Stack) -> OError {
    let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-def".to_owned()));
    };
    Words {
        words: vec![Word::Key(Keyword::Def(s))],
    }
    .exec(stack)?;
    Ok(())
}

pub fn dyn_func(stack: &mut Stack) -> OError {
    let (Value::Str(s), Value::Func(f)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-func".to_owned()));
    };
    stack.define_func(s, f);
    Ok(())
}

pub fn dyn_construct(stack: &mut Stack) -> OError {
    let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-construct".to_owned()));
    };
    Words {
        words: vec![Word::Key(Keyword::Construct(
            s,
            Vec::new(),
            Vec::new(),
            ConstructKind::Thing,
        ))],
    }
    .exec(stack)?;
    Ok(())
}

pub fn dyn_namespace(stack: &mut Stack) -> OError {
    let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-construct".to_owned()));
    };
    Words {
        words: vec![Word::Key(Keyword::Construct(
            s,
            Vec::new(),
            Vec::new(),
            ConstructKind::Namespace,
        ))],
    }
    .exec(stack)?;
    Ok(())
}

pub fn dyn_def_field(stack: &mut Stack) -> OError {
    let (Value::Str(s), Value::Str(name)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-def-field".to_owned()));
    };
    runtime(|rt| rt.get_type_by_name(&s))
        .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?
        .lock()
        .add_property(name, stack.get_frame())?;
    Ok(())
}

pub fn dyn_def_method(stack: &mut Stack) -> OError {
    let (Value::Str(s), Value::Str(name), Value::Func(f)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-def-method".to_owned()));
    };
    runtime(|rt| rt.get_type_by_name(&s))
        .ok_or_else(|| stack.error(ErrorKind::TypeNotFound(s)))?
        .lock()
        .functions
        .insert(name, f);
    Ok(())
}

pub fn dyn_include(stack: &mut Stack) -> OError {
    let (Value::Str(b), Value::Str(a)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-include".to_owned()));
    };
    Words {
        words: vec![Word::Key(Keyword::Include(a, b))],
    }
    .exec(stack)?;
    Ok(())
}

pub fn dyn_while(stack: &mut Stack) -> OError {
    let (Value::Func(blk), Value::Func(cond)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-while".to_owned()));
    };
    loop {
        cond.to_call.call(stack)?;
        if !stack.pop().lock_ro().is_truthy() {
            break;
        }
        blk.to_call.call(stack)?;
    }
    Ok(())
}

pub fn dyn_if(stack: &mut Stack) -> OError {
    let Value::Func(blk) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-if".to_owned()));
    };
    if stack.pop().lock_ro().is_truthy() {
        blk.to_call.call(stack)?;
    }
    Ok(())
}

pub fn dyn_call(stack: &mut Stack) -> OError {
    let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-call".to_owned()));
    };
    let mut words = Vec::new();
    let mut ra = 0;
    while s.starts_with('&') {
        ra += 1;
        s = s[1..].to_owned();
    }
    if s.ends_with(';') {
        words.push(Word::Call(s[..s.len() - 1].to_owned(), true, ra));
    } else {
        words.push(Word::Call(s, false, ra));
    }
    Words { words }.exec(stack)?;
    Ok(())
}

pub fn dyn_objcall(stack: &mut Stack) -> OError {
    let Value::Str(mut s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-objcall".to_owned()));
    };
    let mut words = Vec::new();
    let mut ra = 0;
    while s.starts_with('&') {
        ra += 1;
        s = s[1..].to_owned();
    }
    if s.ends_with(';') {
        words.push(Word::ObjCall(s[..s.len() - 1].to_owned(), true, ra));
    } else {
        words.push(Word::ObjCall(s, false, ra));
    }
    Words { words }.exec(stack)?;
    Ok(())
}

pub fn dyn_all_types(stack: &mut Stack) -> OError {
    stack.push(
        Value::Array(
            runtime(|rt| rt.get_types())
                .into_iter()
                .map(|x| Value::Str(x.lock_ro().get_name()).spl())
                .collect(),
        )
        .spl(),
    );
    Ok(())
}

pub fn dyn_read(stack: &mut Stack) -> OError {
    let Value::Str(s) = stack.pop().lock_ro().native.clone() else {
        return stack.err(ErrorKind::InvalidCall("dyn-read".to_owned()));
    };
    stack.push(
        Value::Func(AFunc::new(Func {
            ret_count: 0,
            to_call: FuncImpl::SPL(
                lexer::lex(false, s)
                    .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?,
            ),
            run_as_base: false,
            origin: stack.get_frame(),
            fname: None,
            name: "(dyn-read)".to_owned(),
        }))
        .spl(),
    );
    Ok(())
}

pub fn dyn_readf(stack: &mut Stack) -> OError {
    let (Value::Str(s), Value::Str(n)) = (
        stack.pop().lock_ro().native.clone(),
        stack.pop().lock_ro().native.clone(),
    ) else {
        return stack.err(ErrorKind::InvalidCall("dyn-readf".to_owned()));
    };
    stack.push(
        Value::Func(AFunc::new(Func {
            ret_count: 0,
            to_call: FuncImpl::SPL(
                lexer::lex(n.ends_with(".isbpl"), s)
                    .map_err(|x| stack.error(ErrorKind::LexError(format!("{x:?}"))))?,
            ),
            run_as_base: true,
            origin: stack.get_frame(),
            fname: Some(n),
            name: "root".to_owned(),
        }))
        .spl(),
    );
    Ok(())
}

pub fn dyn_sasm(stack: &mut Stack) -> OError {
    require_on_stack!(sasm, Str, stack, "dyn-sasm");
    stack.push(
        Value::Func(AFunc::new(Func {
            ret_count: 0,
            to_call: FuncImpl::SPL(sasm_read(sasm)),
            origin: stack.get_frame(),
            fname: None,
            name: "(dyn-read)".to_owned(),
            run_as_base: false,
        }))
        .spl(),
    );
    Ok(())
}

pub fn dyn_sasmf(stack: &mut Stack) -> OError {
    require_on_stack!(sasm, Str, stack, "dyn-sasmf");
    require_on_stack!(n, Str, stack, "dyn-sasmf");
    stack.push(
        Value::Func(AFunc::new(Func {
            ret_count: 0,
            to_call: FuncImpl::SPL(sasm_read(sasm)),
            origin: stack.get_frame(),
            fname: Some(n),
            name: "root".to_owned(),
            run_as_base: true,
        }))
        .spl(),
    );
    Ok(())
}

pub fn dyn_catch(stack: &mut Stack) -> OError {
    require_on_stack!(ctch, Func, stack, "dyn-catch");
    require_on_stack!(blk, Func, stack, "dyn-catch");
    require_on_stack!(types, Array, stack, "dyn-catch");
    if let Err(e) = blk.to_call.call(stack) {
        if types.is_empty() || types.contains(&e.kind.to_string().spl()) {
            stack.push(e.spl());
            ctch.to_call.call(stack)
        } else {
            Err(e)
        }
    } else {
        Ok(())
    }
}

pub fn dyn_use(stack: &mut Stack) -> OError {
    require_on_stack!(item, Str, stack, "dyn-use");
    Words::new(vec![Word::Key(Keyword::Use(item))]).exec(stack)
}

pub(crate) fn wrap(f: fn(&mut Stack) -> OError) -> impl Fn(&mut Stack) -> OError {
    move |stack| unsafe {
        let frame = stack.pop_frame(0);
        let r = f(stack);
        stack.push_frame(frame);
        r
    }
}

pub fn register(r: &mut Stack, o: Arc<Frame>) {
    type Fn = fn(&mut Stack) -> OError;
    let fns: [(&str, Fn, u32); 19] = [
        ("dyn-__dump", dyn_dump, 0),
        ("dyn-def", dyn_def, 0),
        ("dyn-func", dyn_func, 0),
        ("dyn-construct", dyn_construct, 0),
        ("dyn-namespace", dyn_namespace, 0),
        ("dyn-def-field", dyn_def_field, 0),
        ("dyn-def-method", dyn_def_method, 0),
        ("dyn-include", dyn_include, 0),
        ("dyn-while", dyn_while, 0),
        ("dyn-if", dyn_if, 0),
        ("dyn-call", dyn_call, 0),
        ("dyn-objcall", dyn_objcall, 0),
        ("dyn-all-types", dyn_all_types, 1),
        ("dyn-read", dyn_read, 1),
        ("dyn-readf", dyn_readf, 1),
        ("dyn-sasm", dyn_sasm, 1),
        ("dyn-sasmf", dyn_sasmf, 1),
        ("dyn-catch", dyn_catch, 0),
        ("dyn-use", dyn_use, 0),
    ];
    for f in fns {
        r.define_func(
            f.0.to_owned(),
            AFunc::new(Func {
                ret_count: f.2,
                to_call: FuncImpl::NativeDyn(Arc::new(Box::new(wrap(f.1)))),
                run_as_base: false,
                origin: o.clone(),
                fname: None,
                name: f.0.to_owned(),
            }),
        );
    }
}