bitr 0.1.1

Minimal BitScript interpreter.
Documentation
#![allow(clippy::module_inception)]

use std::io::{self, Write};

use crate::*;

pub fn run_line(line: &mut str, status: &mut Status) -> Result<Option<String>, String> {
    let code = line.split('#').next().unwrap();
    let mut code = code.trim().to_owned();

    if code.is_empty() {
        return Ok(None);
    }

    escape(&mut code);

    let mut splitted = code.split(' ').collect::<Vec<_>>();

    match splitted.as_slice() {
        ["dclr", ..] => {
            let (name, length, default) = match &splitted[1..] {
                [name, length, default] => (name, length, default),
                [name, length] => (name, length, &"0"),
                _ => return Err(String::from(
                    "incorrect usage of `dclr`, correct usage is `declr [name] [size] (default)",
                )),
            };

            let length = match length.parse::<usize>() {
                Ok(n) => n,
                Err(_e) => return Err(format!("unable to parse `{length}` as usize")),
            };

            if status
                .variables
                .insert(name.to_string(), vec![str_to_bool(default)?; length])
                .is_some()
            {
                return Err(format!("multiple declaration of variable `{name}`"));
            }
        }
        ["del", name] => {
            if status.variables.remove(*name).is_none() {
                return Err(format!("cannot delete undeclared variable `{name}`"));
            }
        }
        ["dbg"] => {
            return Err(String::from(
                "incorrect usage of `dbg`, correct usage is `dbg [expression]",
            ));
        }
        ["dbg", ..] => {
            let value_label = splitted[1..].join(" ");
            let mut value = value_label.chars().collect::<Vec<_>>();
            replace_variables(&mut value, status)?;
            eval(&mut value, status)?;
            println!("{value_label}: {}", value.into_iter().collect::<String>());
        }
        ["disp"] => {
            return Err(String::from(
                "incorrect usage of `disp`, correct usage is `disp [format string]",
            ));
        }
        ["disp", ..] => {
            let mut chars = splitted[1..].join(" ").chars().collect::<Vec<_>>();
            display_eval(&mut chars, status)?;
            print!("{}", chars.into_iter().collect::<String>());
            io::stdout().flush().expect("cannot write to stdout");
        }
        ["inpt"] => {
            return Err(String::from(
                "incorrect usage of `inpt`, correct usage is `inpt [variable] (base)`",
            ));
        }
        ["inpt", ..] => {
            let radix = if splitted.len() == 2 {
                2
            } else {
                match char_to_radix(splitted[1].chars().next().unwrap()) {
                    Ok(radix) => {
                        splitted.remove(1);
                        radix
                    }
                    Err(_) => 2,
                }
            };

            let chars = splitted[1..].join(" ").chars().collect::<Vec<_>>();
            input(&chars, status, radix)?;
        }
        ["lbl", _label] => {}
        ["goto", label] => match status.labels.get(label.to_string().as_str()) {
            Some(position) => status.line_no = *position,
            None => return Err(format!("use of undeclared label `{label}`")),
        },
        ["if", ..] => {
            let args_owner = splitted[1..].join(" ");
            let args = args_owner.splitn(2, ',').collect::<Vec<_>>();

            let mut condition = format!("AND({})", args[0]).chars().collect();
            let mut action = args[1].to_string();

            replace_variables(&mut condition, status)?;
            eval(&mut condition, status)?;

            if condition.as_slice() == ['1'] {
                return run_line(&mut action, status);
            }
        }
        ["exit", ..] => {
            let mut value = splitted[1..].join(" ").chars().collect();

            replace_variables(&mut value, status)?;
            eval(&mut value, status)?;
            return Ok(Some(value.iter().collect()));
        }
        ["reg", name, ..] => {
            if status.components.contains_key(*name) {
                return Err(format!("multiple registration of component `{name}`"));
            }

            let path = splitted[2..].join(" ");

            let (compnent_status, lines) =
                match Status::try_from_path(&path, Some(status.path.clone())) {
                    Some(status) => status,
                    None => return Err(format!("failed to load component {name}")),
                };

            status
                .components
                .insert(name.to_string(), (lines, compnent_status));
        }
        ["dereg", name] => {
            if status.components.remove(*name).is_none() {
                return Err(format!("cannot dereg unregistered component `{name}`"));
            }
        }
        _ => {
            if let Some(index) = code.find('=') {
                let (assigned, mut value) = code.split_at(index);
                value = &value[1..];

                if value.is_empty() {
                    return Err(String::from("empty right hand side value"));
                }

                let radix = match char_to_radix(value.chars().next().unwrap()) {
                    Ok(radix) => {
                        value = &value[1..];
                        radix
                    }
                    Err(_) => 2,
                };
                assign(assigned, value, status, radix)?;
                return Ok(None);
            } else {
                let mut chars = code.chars().collect();
                replace_variables(&mut chars, status)?;
                eval(&mut chars, status)?;
            }
        }
    };

    Ok(None)
}