c_a_l 0.1.4

cool array language
Documentation
#![feature(let_chains)]
#![feature(ascii_char)]

pub mod p;
pub mod x;

pub mod macros {
    pub use crate::{err_fmt, fatal, puts, eputs};
}

use lazy_static::lazy_static;
use std::{collections::HashMap, io};

#[macro_export]
macro_rules! err_fmt {
    ($($t:tt)*) => {{
        Err(format!($($t)*))
    }};
}

#[macro_export]
macro_rules! fatal {
    ($($t:tt)*) => {{
        eprintln!($($t)*);
        std::process::exit(-1);
    }};
}

#[macro_export]
macro_rules! puts {
    ($($t:tt)*) => {{
        use std::io::Write;
        print!($($t)*);
        match std::io::stdout().flush() {
            Ok(x) => x,
            Err(e) => fatal!("failed to flush stdout: {e}"),
        }
    }};
}

#[macro_export]
macro_rules! eputs {
    ($($t:tt)*) => {{
        use std::io::{Write, stderr};
        eprint!($($t)*);
        match stderr().flush() {
            Ok(x) => x,
            Err(e) => fatal!("failed to flush stderr: {e}")
        }
    }};
}

pub fn btoi(x: bool) -> i64 {
    if x {
        1
    } else {
        0
    }
}

/** take n items from vector v, mutating v and removing
 * a vec of the removed items */
pub fn take<T>(v: &mut Vec<T>, n: i64) -> Vec<T> {
    let mut r = Vec::new();

    for _ in 0..n {
        if v.len() > 0 {
            r.push(v.remove(0));
        } else {
            break;
        }
    }

    r
}

/** take but from the end */
pub fn take_end<T>(v: &mut Vec<T>, n: i64) -> Vec<T> {
    let mut r = Vec::new();

    for _ in 0..n {
        match v.pop() {
            Some(x) => r.push(x),
            None => break,
        }
    }

    r
}

/** unlike take, this does not modify the original vec */
pub fn drop<T>(v: Vec<T>, n: i64) -> Vec<T> {
    v.into_iter().skip(n as usize).collect()
}

pub fn drop_end<T: Clone>(v: &mut Vec<T>, n: i64) -> Vec<T> {
    for _ in 0..n {
        match v.pop() {
            Some(_) => {}
            None => break,
        }
    }

    v.clone()
}

/** read line from stdin, taking prompt
 * like the python thing, yk? */
pub fn input(p: &str) -> String {
    puts!("{p}");

    /* read into b */
    let mut b = String::new();
    let stdin = io::stdin();
    match stdin.read_line(&mut b) {
        Ok(x) => x,
        Err(e) => fatal!("failed to read line from stdin: {e}"),
    };

    /* trim writespace bc the newline is tacked
     * on and we don't need it anyway */
    b.trim().to_string()
}

lazy_static! {
    static ref ESCS: HashMap<char, char> = HashMap::from([
        ('n', '\n'),
        ('t', '\t'),
        ('0', '\0'),
        ('b', '\x08'),
        ('a', '\x07'),
        ('v', '\x0b'),
        ('f', '\x0c'),
        ('r', '\x0d'),
        ('"', '"'),
        ('\\', '\\'),
    ]);
}

pub fn esc<T>(x: T) -> String
where
    String: From<T>,
{
    String::from(x)
        .chars()
        .map(|x| {
            for (c, e) in ESCS.iter() {
                if x == *e {
                    return format!("\\{c}");
                }
            }
            x.to_string()
        })
        .collect::<Vec<_>>()
        .join("")
}

pub fn un_esc<T: Clone>(x: T) -> Result<String, String>
where
    String: From<T>,
{
    let mut s = String::new();
    let mut i = 0;
    let x = String::from(x).chars().collect::<Vec<_>>();

    while i < x.len() {
        s.push(if x[i] == '\\' {
            if i + 1 < x.len() {
                let t = x[i + 1];
                i += 1;
                match ESCS.get(&t) {
                    Some(x) => *x,
                    None => return err_fmt!("invalid escape \\{t}"),
                }
            } else {
                return err_fmt!(
                    "escape at end of string: {}",
                    x.into_iter().collect::<String>()
                );
            }
        } else {
            x[i]
        });
        i += 1;
    }

    Ok(s)
}

#[test]
fn esc_unesc() {
    for (x, y) in ESCS.iter() {
        println!("\\{x}");
        assert_eq!(esc(un_esc(match *y {
            /* \ breaks this test for some reason but still actually works */
            '\\' => continue,
            x => x
        }).unwrap()), format!("\\{x}"));
        assert_eq!(un_esc(format!("\\{x}")), Ok(y.to_string()));
    }
}