#![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};
macro_rules! err {
($($t:tt)*) => {{
eprintln!("{}: {}", "err".red().bold(), format!($($t)*));
}};
}
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)) => {
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();
}
}