use std::collections::HashMap;
use crate::{
parser::{Command, CommandType},
scope::Scope,
};
#[derive(Clone, Debug)]
pub struct Function {
pub args: Vec<String>,
pub command: Command,
}
pub type Call = fn(&mut Context, &mut Scope) -> Result<(), String>;
#[derive(Clone)]
pub struct Builtin {
pub call: Call,
pub args: Vec<String>,
pub description: String,
}
#[derive(Clone)]
pub enum VariableValue {
List(Vec<String>),
Callable(Command),
}
pub struct Context {
pub(crate) builtins: HashMap<String, Builtin>,
pub(crate) last_command: Option<Command>,
pub(crate) last_call: Option<String>,
pub(crate) exit: bool,
variables: HashMap<*const (), HashMap<String, VariableValue>>,
}
impl Context {
pub fn new() -> Self {
Context {
builtins: HashMap::new(),
last_command: None,
last_call: None,
variables: HashMap::new(),
exit: false,
}
}
pub(crate) fn register_builtin(&mut self, name: &'static str, command: Builtin) {
self.builtins.insert(name.into(), command);
}
pub fn builtin(&mut self, name: &'static str, call: Call) {
self.register_builtin(
name,
Builtin {
call,
args: Vec::new(),
description: "".into(),
},
)
}
pub fn info(
&mut self,
name: &'static str,
args: Vec<&'static str>,
desc: &'static str,
) -> Result<(), ()> {
if let Some(builtin) = self.builtins.get_mut(name.into()) {
builtin.args = args.into_iter().map(|e| e.to_string()).collect();
builtin.description = desc.to_string();
Ok(())
} else {
Err(())
}
}
pub(crate) fn get_builtin(&self, name: &str) -> Option<Call> {
self.builtins.get(name).map(|c| c.call)
}
pub(crate) fn evaluate(&mut self, scope: &mut Scope) -> Result<Vec<String>, String> {
while let Some(command) = scope.commands.pop_front() {
self.last_command = Some(command.clone());
match command.kind {
CommandType::String(s) => scope.list.push(s),
CommandType::Number(n) => scope.list.push(format!("{}", n)),
CommandType::Chain(commands) => {
scope
.list
.append(&mut self.evaluate(&mut scope.child(commands.to_owned()))?);
}
CommandType::FunctionDefinition(name, args, command) => {
scope.functions.insert(
name.clone(),
Function {
args: args.clone(),
command: *command.clone(),
},
);
}
CommandType::Assign(name) => {
let val = scope.list.clone();
scope.list.clear();
let mut lookup_scope: Option<&Scope> = Some(scope);
while let Some(child) = lookup_scope {
let scope = child as *const Scope as *const ();
if self
.variables
.get(&scope)
.map(|e| e.contains_key(&name))
.unwrap_or_default()
{
break;
}
lookup_scope = child.parent
}
self.set_variable(
lookup_scope.unwrap_or(scope),
name,
VariableValue::List(val),
);
}
CommandType::Identifier(i) => {
self.handle_identifier(i, scope)?;
}
CommandType::Comment(_) => {}
};
if self.exit {
break;
}
}
Ok(scope.list.clone())
}
fn call_function(
&mut self,
function: &Function,
name: &String,
scope: &mut Scope,
) -> Result<(), String> {
let mut args = HashMap::new();
for (i, arg) in function.args.iter().enumerate() {
let Some(cmd) = scope.get_arg() else {
return Err(format!(
"Expected parameter {}, {} for function {}",
i + 1,
arg,
name
))
};
if arg.ends_with('!') {
args.insert(arg.clone(), VariableValue::Callable(cmd));
} else {
let mut arg_scope = scope.child(vec![cmd]);
let evaluated = self.evaluate(&mut arg_scope)?;
args.insert(arg.clone(), VariableValue::List(evaluated));
}
}
let mut fun_scope = scope.child(vec![function.command.clone()]);
for (k, v) in args.into_iter() {
self.set_variable(&fun_scope, k, v);
}
fun_scope.list = scope.list.clone();
scope.list = self.evaluate(&mut fun_scope)?;
Ok(())
}
fn set_variable(&mut self, scope: &Scope, name: String, value: VariableValue) {
let scope = scope as *const Scope as *const ();
match self.variables.get_mut(&scope) {
Some(map) => {
map.insert(name, value);
}
None => {
let mut map = HashMap::new();
map.insert(name, value);
self.variables.insert(scope, map);
}
};
}
fn collect_args(&self, scope: &Scope, vars: &mut HashMap<String, VariableValue>) {
if let Some(parent) = scope.parent {
self.collect_args(parent, vars)
};
let scope = scope as *const Scope as *const ();
if let Some(map) = self.variables.get(&scope) {
vars.extend(map.clone().into_iter())
};
}
pub(crate) fn get_variables(&self, scope: &Scope) -> HashMap<String, VariableValue> {
let mut vars = HashMap::new();
self.collect_args(scope, &mut vars);
vars
}
fn handle_identifier(&mut self, i: String, scope: &mut Scope) -> Result<(), String> {
let mut lookup_scope: Option<&Scope> = Some(scope);
let vars = self.get_variables(scope);
if let Some(var) = vars.get(&i) {
match var {
VariableValue::List(list) => scope.list.append(&mut list.clone()),
VariableValue::Callable(body) => scope
.list
.append(&mut self.evaluate(&mut scope.child(vec![body.clone()]))?),
};
return Ok(());
}
while let Some(child) = lookup_scope {
if let Some(function) = child.get_func(&i) {
self.call_function(&function.to_owned(), &i, scope)?;
return Ok(());
}
lookup_scope = child.parent
}
if let Some(builtin) = self.get_builtin(&i) {
self.last_call = Some(i.clone());
builtin(self, scope)?;
return Ok(());
}
Err(format!("No variable or function named \"{}\" declared", i))
}
}