c_a_l 0.1.4

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

use c_a_l::{
    input,
    macros::*,
    p::{parse, parse_txt},
    x::{eval_each, State},
};
use colored::Colorize;
use std::{borrow::Cow, env, fs};

/** log an error */
macro_rules! err {
    ($($t:tt)*) => {{
        eprintln!("{}: {}", "err".red().bold(), format!($($t)*));
    }};
}

/** display a help message */
macro_rules! help {
    { $($x:tt)* } => {{
        println!("{}", format!($($x)*).split("\n").map(|x| x.trim()).collect::<Vec<_>>().join("\n"))
    }};
}

fn repl() {
    let mut s = State::new();

    println!("cal repl. (c) skylar bleed 2024.\n");

    loop {
        match input(" ").as_str() {
            "\\h" => help! {
                r#"
                help menus:
                \+   verbs
                "#
            },
            "\\+" => help! {
                r#"
                verbs

                assignment
                x:y          x gets y
                (x;i)::y     x at index i modified to y

                m   say      does            ex
                !   iota     every idx 0..x  !5                           -> (0;1;2;3;4)
                -   negate   negate x        -2                           -> -2
                #   length   length of x     a~ # !a:5                    -> 1
                $   format   fmt x as str    $`({{x+1}})                    -> "`({{x+1}})" 
                |   reverse  reverse x       | !5                         -> (4;3;2;1;0)
                _   floor    truncate float  (_)'{{x%2}}'1+ !5              -> (0;1;1;2;2)
                ~   not      logical not     (~)' !2                      -> (1;0)
                @   type     typeof x        (@)' (1;2%1;"a";"abc")       -> (`(i);`(f);`(c);`(C))
                *   head     head/first      *"abc"                       -> "a"
                %   tail     tail/{{1_x}}      %"abc"                       -> "bc"
                <<  println  print a str     <<"hello world"              -> ()
                <   print    print w/o \n    <"hello world"               -> ()
                <<! eprintln print to stderr <<!"hello world"             -> ()
                <!  eprint   eprint w/o \n   <!"hello world"              -> ()
                >>  read     read textfile   >>f                          -> [ content of file f ]

                d   say      does            ex
                +   add      addition        1+1                          -> 2
                -   sub      subtraction     5- -10                       -> 15
                *   mul      multiplication  2* !3                        -> (0;2;4)
                %   div      division        2% 1+ 2! !5                  -> (2;1;2;1;2)
                !   mod      modulus         2! !5                        -> (0;1;0;1;1)
                &   min      minimum/and     {{x& !2}}' !2                  -> ( (0;0); (0;1) )
                |   max      maximum/or      {{x| !2}}' !2                  -> ( (0;1); (1;1) )
                ^   powf     power           2^10                         -> 1024
                #   take     take x from y   2# !10                       -> (0;1)
                _   drop     drop x from y   8# !10                       -> (8;9)
                .   apply n  apply y to x    (~). {{{{x+y*z}}.x}}' (a: !3;|a) -> 1
                ~   match    is x y?         {{(~).x}}'((1;1);(0;1);"aa")   -> (1;0;1)
                '   each     apply x to each (1+)'(1;2;3)                 -> (2;3;4)
                ||| cols     cut y to x cols 3||| !6                      -> ( (0;1;2); (3;4;5) )
                "#
            },
            i => match parse(i) {
                Ok(p) => match eval_each(&p, Cow::Borrowed(&s)) {
                    Ok((x, _s)) => {
                        // uhhh
                        s = (*_s).clone();
                        println!(
                            "{}",
                            x.iter()
                                .map(|x| format!("{x}"))
                                .collect::<Vec<_>>()
                                .join("\n")
                        )
                    }
                    Err(e) => {
                        err!("{e}")
                    }
                },
                Err(e) => err!("{e}"),
            },
        }
    }
}

fn strip_sh<T>(x: T) -> String 
where
    String: From<T>
{
    match String::from(x) {
        x if x.starts_with("#!") => {
            let mut c = x.chars().peekable();
            while let Some(i) = c.peek() && *i != '\n' {
                c.next();
            }
            c.collect::<String>()
        }
        x => x,
    }
}

fn file(s: &str) {
    let f = match fs::read_to_string(s) {
        Ok(x) => x,
        Err(e) => fatal!("'io: {e}"),
    };

    let p = match parse(f) {
        Ok(x) => x,
        Err(e) => fatal!("{e}"),
    };

    let _e = match eval_each(&p, Cow::Owned(State::new())) {
        Ok((x, _)) => x,
        Err(e) => fatal!("{e}"),
    };
}

fn txt(s: &str) {
    let f = match fs::read_to_string(s) {
        Ok(x) => strip_sh(x),
        Err(e) => fatal!("'io: {e}"),
    };

    let p = match parse_txt(f) {
        Ok(x) => x,
        Err(e) => fatal!("{e}"),
    };

    let _ = match eval_each(&p, Cow::Owned(State::new())) {
        Ok((x, _)) => x,
        Err(e) => fatal!("{e}"),
    };
}

fn main() {
    let a: Vec<_> = env::args().collect();
    if a.len() > 1 {
        match a
            .iter()
            .skip(1)
            .map(|x| &**x)
            .collect::<Vec<&str>>()
            .as_slice()
        {
            &[x] => file(x),
            &["-t", x] => txt(x),
            _ => fatal!(
                r#"
usage:
    % [rlwrap] cal  # repl
    % cal $f        # run file f
    % cal -t $f     # run file in text mode ( txt <? << $ 1+1; ?> txt )
"#
            ),
        };
    } else {
        repl();
    }
}