atomsh/
env.rs

1use std::{
2    thread::sleep,
3    time::Duration,
4    collections::BTreeMap,
5    process::exit,
6    path::{PathBuf, Component},
7    env::current_exe,
8    io::{stdin, stdout, Write},
9    fs::{read_to_string, write}
10};
11use super::{Value, Error, VERSION, PRELUDE_FILENAME};
12
13use rand::{seq::SliceRandom, Rng, thread_rng, distributions::Uniform};
14use chrono::{Local, Timelike, Datelike};
15use os_info::Type;
16use asciicolor::Colorize;
17
18use lazy_static::lazy_static;
19lazy_static! {
20    static ref SPADES: Vec<Value> = {
21        vec![
22            Value::string("🂡"),
23            Value::string("🂢"),
24            Value::string("🂣"),
25            Value::string("🂤"),
26            Value::string("🂥"),
27            Value::string("🂦"),
28            Value::string("🂧"),
29            Value::string("🂨"),
30            Value::string("🂩"),
31            Value::string("🂪"),
32            Value::string("🂫"),
33            Value::string("🂭"),
34            Value::string("🂮")
35        ]
36    };
37    
38    static ref HEARTS: Vec<Value> = {
39        vec![
40            Value::string("🂱"),
41            Value::string("🂲"),
42            Value::string("🂳"),
43            Value::string("🂴"),
44            Value::string("🂵"),
45            Value::string("🂶"),
46            Value::string("🂷"),
47            Value::string("🂸"),
48            Value::string("🂹"),
49            Value::string("🂺"),
50            Value::string("🂻"),
51            Value::string("🂽"),
52            Value::string("🂾")
53        ]
54    };
55
56    static ref DIAMONDS: Vec<Value> = {
57        vec![
58            Value::string("🃁"),
59            Value::string("🃂"),
60            Value::string("🃃"),
61            Value::string("🃄"),
62            Value::string("🃅"),
63            Value::string("🃆"),
64            Value::string("🃇"),
65            Value::string("🃈"),
66            Value::string("🃉"),
67            Value::string("🃊"),
68            Value::string("🃋"),
69            Value::string("🃍"),
70            Value::string("🃎")
71        ]
72    };
73
74    static ref CLUBS: Vec<Value> = {
75        vec![
76            Value::string("🃑"),
77            Value::string("🃒"),
78            Value::string("🃓"),
79            Value::string("🃔"),
80            Value::string("🃕"),
81            Value::string("🃖"),
82            Value::string("🃗"),
83            Value::string("🃘"),
84            Value::string("🃙"),
85            Value::string("🃚"),
86            Value::string("🃛"),
87            Value::string("🃝"),
88            Value::string("🃞")
89        ]
90    };
91
92    static ref CARDS: Value = {
93        let mut cards = BTreeMap::new();
94        let mut deck = BTreeMap::new();
95        deck.insert("all".to_string(), Value::List(vec![
96            Value::string("🂡"),
97            Value::string("🂱"),
98            Value::string("🃁"),
99            Value::string("🃑"),
100            
101            Value::string("🂢"),
102            Value::string("🂲"),
103            Value::string("🃂"),
104            Value::string("🃒"),
105            
106            Value::string("🂣"),
107            Value::string("🂳"),
108            Value::string("🃃"),
109            Value::string("🃓"),
110            
111            Value::string("🂤"),
112            Value::string("🂴"),
113            Value::string("🃄"),
114            Value::string("🃔"),
115            
116            Value::string("🂥"),
117            Value::string("🂵"),
118            Value::string("🃅"),
119            Value::string("🃕"),
120            
121            Value::string("🂦"),
122            Value::string("🂶"),
123            Value::string("🃆"),
124            Value::string("🃖"),
125            
126            Value::string("🂧"),
127            Value::string("🂷"),
128            Value::string("🃇"),
129            Value::string("🃗"),
130            
131            Value::string("🂨"),
132            Value::string("🂸"),
133            Value::string("🃈"),
134            Value::string("🃘"),
135            
136            Value::string("🂩"),
137            Value::string("🂹"),
138            Value::string("🃉"),
139            Value::string("🃙"),
140            
141            Value::string("🂪"),
142            Value::string("🂺"),
143            Value::string("🃊"),
144            Value::string("🃚"),
145            
146            Value::string("🂫"),
147            Value::string("🂻"),
148            Value::string("🃋"),
149            Value::string("🃛"),
150            
151            Value::string("🂭"),
152            Value::string("🂽"),
153            Value::string("🃍"),
154            Value::string("🃝"),
155            
156            Value::string("🂮"),
157            Value::string("🂾"),
158            Value::string("🃎"),
159            Value::string("🃞"),
160        ]));
161
162
163        deck.insert("aces".to_string(), Value::List(vec![
164            Value::string("🂡"),
165            Value::string("🂱"),
166            Value::string("🃁"),
167            Value::string("🃑"),
168        ]));
169
170        deck.insert("kings".to_string(), Value::List(vec![
171            Value::string("🂮"),
172            Value::string("🂾"),
173            Value::string("🃎"),
174            Value::string("🃞"),
175        ]));
176
177        deck.insert("queens".to_string(), Value::List(vec![
178            Value::string("🂭"),
179            Value::string("🂽"),
180            Value::string("🃍"),
181            Value::string("🃝"),
182        ]));
183
184        deck.insert("jacks".to_string(), Value::List(vec![
185            Value::string("🂫"),
186            Value::string("🂻"),
187            Value::string("🃋"),
188            Value::string("🃛"),
189        ]));
190
191        deck.insert("faces".to_string(), Value::List(vec![
192            Value::string("🂫"),
193            Value::string("🂻"),
194            Value::string("🃋"),
195            Value::string("🃛"),
196            Value::string("🂭"),
197            Value::string("🂽"),
198            Value::string("🃍"),
199            Value::string("🃝"),
200            Value::string("🂮"),
201            Value::string("🂾"),
202            Value::string("🃎"),
203            Value::string("🃞"),
204        ]));
205
206        deck.insert("numbers".to_string(), Value::List(vec![
207            Value::string("🂢"),
208            Value::string("🂲"),
209            Value::string("🃂"),
210            Value::string("🃒"),
211            
212            Value::string("🂣"),
213            Value::string("🂳"),
214            Value::string("🃃"),
215            Value::string("🃓"),
216            
217            Value::string("🂤"),
218            Value::string("🂴"),
219            Value::string("🃄"),
220            Value::string("🃔"),
221            
222            Value::string("🂥"),
223            Value::string("🂵"),
224            Value::string("🃅"),
225            Value::string("🃕"),
226            
227            Value::string("🂦"),
228            Value::string("🂶"),
229            Value::string("🃆"),
230            Value::string("🃖"),
231            
232            Value::string("🂧"),
233            Value::string("🂷"),
234            Value::string("🃇"),
235            Value::string("🃗"),
236            
237            Value::string("🂨"),
238            Value::string("🂸"),
239            Value::string("🃈"),
240            Value::string("🃘"),
241            
242            Value::string("🂩"),
243            Value::string("🂹"),
244            Value::string("🃉"),
245            Value::string("🃙"),
246            
247            Value::string("🂪"),
248            Value::string("🂺"),
249            Value::string("🃊"),
250            Value::string("🃚"),
251        ]));
252
253        deck.insert("spades".to_string(), Value::List(SPADES.clone()));
254        deck.insert("hearts".to_string(), Value::List(HEARTS.clone()));
255        deck.insert("diamonds".to_string(), Value::List(DIAMONDS.clone()));
256        deck.insert("clubs".to_string(), Value::List(CLUBS.clone()));
257        cards.insert("deck".to_string(), Value::Table(deck));
258        
259        let mut suites = BTreeMap::new();
260        suites.insert("spades".to_string(),   Value::string("♠"));
261        suites.insert("clubs".to_string(),    Value::string("♣"));
262        suites.insert("hearts".to_string(),   Value::string("♥"));
263        suites.insert("diamonds".to_string(), Value::string("♦"));
264        cards.insert("suites".to_string(), Value::Table(suites));
265
266        cards.insert("suite".to_string(), Value::builtin("cards@suite", |args, env| {
267            check_args_len(Value::symbol("cards@suite"), &args, 1)?;
268            let card = args[0].eval(env)?;
269            Ok(if SPADES.contains(&card) {
270                Value::string("♠")
271            } else if HEARTS.contains(&card) {
272                Value::string("♥")
273            } else if DIAMONDS.contains(&card) {
274                Value::string("♦")
275            } else if CLUBS.contains(&card) {
276                Value::string("♣")
277            } else {
278                return Err(Error::CustomError(format!("{} does not belong to any suite", card)))
279            })
280        }));
281
282        fn value(card: &Value) -> Result<i32, Error> {
283            if let Some(i) = SPADES.iter().position(|x| x.clone() == card.clone()) {
284                Ok((i + 1) as i32)
285            } else if let Some(i) = HEARTS.iter().position(|x| x.clone() == card.clone()) {
286                Ok((i + 1) as i32)
287            } else if let Some(i) = DIAMONDS.iter().position(|x| x.clone() == card.clone()) {
288                Ok((i + 1) as i32)
289            } else if let Some(i) = CLUBS.iter().position(|x| x.clone() == card.clone()) {
290                Ok((i + 1) as i32)
291            } else {
292                return Err(Error::CustomError(format!("{} does not belong to any suite", card)))
293            }
294        }
295
296        cards.insert("value".to_string(), Value::builtin("cards@value", |args, env| {
297            check_args_len(Value::symbol("cards@value"), &args, 1)?;
298            let card = args[0].eval(env)?;
299            Ok(Value::Integer(value(&card)?))
300        }));
301
302        cards.insert("name".to_string(), Value::builtin("cards@name", |args, env| {
303            check_args_len(Value::symbol("cards@name"), &args, 1)?;
304            let card = args[0].eval(env)?;
305            Ok(Value::string(
306                format!("{} of {}",
307                    match value(&card)? {
308                        1 => "ace",
309                        2 => "two",
310                        3 => "three",
311                        4 => "four",
312                        5 => "five",
313                        6 => "six",
314                        7 => "seven",
315                        8 => "eight",
316                        9 => "nine",
317                        10 => "ten",
318                        11 => "jack",
319                        12 => "queen",
320                        13 => "king",
321                        _ => return Err(Error::CustomError(format!("invalid card value for {}", card)))
322                    },
323                    if SPADES.contains(&card) {
324                        "spades"
325                    } else if HEARTS.contains(&card) {
326                        "hearts"
327                    } else if DIAMONDS.contains(&card) {
328                        "diamonds"
329                    } else if CLUBS.contains(&card) {
330                        "clubs"
331                    } else {
332                        return Err(Error::CustomError(format!("{} does not belong to any suite", card)))
333                    }
334                )
335            ))
336        }));
337
338        cards.insert("from-name".to_string(), Value::builtin("cards@from-name", |args, env| {
339            check_args_len(Value::symbol("cards@from-name"), &args, 1)?;
340            let name = args[0].eval(env)?.to_string();
341            let words = name.trim().split_whitespace().map(ToString::to_string).collect::<Vec<String>>();
342
343            if words.len() != 3 || words[1] != "of" {
344                return Err(Error::CustomError(format!("\"{}\" is not a valid card name", name)))
345            }
346
347            let val = match words[0].as_str() {
348                "ace" => 1,
349                "two" | "2" => 2,
350                "three" | "3" => 3,
351                "four" | "4" => 4,
352                "five" | "5" => 5,
353                "six" | "6" => 6,
354                "seven" | "7" => 7,
355                "eight" | "8" => 8,
356                "nine" | "9" => 9,
357                "ten" | "10" => 10,
358                "jack" => 11,
359                "queen" => 12,
360                "king" => 13,
361                e => return Err(Error::CustomError(format!("invalid card value \"{}\"", e)))
362            };
363
364            let suite = match words[2].as_str() {
365                "spades"   => SPADES.clone(),
366                "hearts"   => HEARTS.clone(),
367                "diamonds" => DIAMONDS.clone(),
368                "clubs"    => CLUBS.clone(),
369                e => return Err(Error::CustomError(format!("invalid card suite \"{}\"", e)))
370            };
371            
372            Ok(Value::string(suite[val-1].clone()))
373        }));
374
375        cards.insert("back".to_string(), Value::string("🂠"));
376        
377        Value::Table(cards)
378    };
379
380    static ref CHESS: Value = {
381        let mut chess = BTreeMap::new();
382        let mut white = BTreeMap::new();
383        let mut black = BTreeMap::new();
384
385        white.insert("king".to_string(),   Value::string("♔"));
386        white.insert("queen".to_string(),  Value::string("♕"));
387        white.insert("rook".to_string(),   Value::string("♖"));
388        white.insert("bishop".to_string(), Value::string("♗"));
389        white.insert("knight".to_string(), Value::string("♘"));
390        white.insert("pawn".to_string(),   Value::string("♙"));
391        chess.insert("white".to_string(),  Value::Table(white));
392        
393        black.insert("king".to_string(),   Value::string("♚"));
394        black.insert("queen".to_string(),  Value::string("♛"));
395        black.insert("rook".to_string(),   Value::string("♜"));
396        black.insert("bishop".to_string(), Value::string("♝"));
397        black.insert("knight".to_string(), Value::string("♞"));
398        black.insert("pawn".to_string(),   Value::string("♟"));
399        chess.insert("black".to_string(),  Value::Table(black));
400
401        chess.insert("space".to_string(),  Value::string("."));
402
403        fn is_piece(piece: &String) -> bool {
404            ["♚", "♛", "♜", "♝", "♞", "♟", "♔", "♕", "♖", "♗", "♘", "♙"].contains(&piece.as_str())
405        }
406
407        fn is_white(piece: &String) -> bool {
408            ["♔", "♕", "♖", "♗", "♘", "♙"].contains(&piece.as_str())
409        }
410
411        fn is_black(piece: &String) -> bool {
412            ["♚", "♛", "♜", "♝", "♞", "♟"].contains(&piece.as_str())
413        }
414
415        fn is_space(piece: &String) -> bool {
416            !is_piece(piece)
417        }
418
419        chess.insert("is-piece".to_string(), Value::builtin("chess@is-piece", |args, env| {
420            check_args_len(Value::Symbol("chess@is-piece".to_string()), &args, 1)?;
421            if let Value::String(piece) = args[0].eval(env)? {
422                Ok(Value::Boolean(is_piece(&piece)))
423            } else {
424                Err(Error::InvalidArguments(Value::Symbol("chess@is-piece".to_string()), args.clone()))
425            }
426        }));
427
428        chess.insert("is-space".to_string(), Value::builtin("chess@is-space", |args, env| {
429            check_args_len(Value::Symbol("chess@is-space".to_string()), &args, 1)?;
430            if let Value::String(piece) = args[0].eval(env)? {
431                Ok(Value::Boolean(is_space(&piece)))
432            } else {
433                Err(Error::InvalidArguments(Value::Symbol("chess@is-space".to_string()), args.clone()))
434            }
435        }));
436
437        chess.insert("is-white".to_string(), Value::builtin("chess@is-white", |args, env| {
438            check_args_len(Value::Symbol("chess@is-white".to_string()), &args, 1)?;
439            if let Value::String(piece) = args[0].eval(env)? {
440                Ok(Value::Boolean(is_white(&piece)))
441            } else {
442                Err(Error::InvalidArguments(Value::Symbol("chess@is-white".to_string()), args.clone()))
443            }
444        }));
445
446        chess.insert("is-black".to_string(), Value::builtin("chess@is-black", |args, env| {
447            check_args_len(Value::Symbol("chess@is-black".to_string()), &args, 1)?;
448            if let Value::String(piece) = args[0].eval(env)? {
449                Ok(Value::Boolean(is_black(&piece)))
450            } else {
451                Err(Error::InvalidArguments(Value::Symbol("chess@is-black".to_string()), args.clone()))
452            }
453        }));
454
455        chess.insert("create".to_string(), Value::builtin("chess@create", |args, _| {
456            check_args_len(Value::symbol("chess@create"), &args, 0)?;
457            Ok(Value::List(vec![
458                Value::List(["♜", "♞", "♝", "♛", "♚", "♝", "♞", "♜"].iter().map(Value::string).collect()),
459                Value::List(vec!["♟"].repeat(8).iter().map(Value::string).collect()),
460                Value::List(vec!["."].repeat(8).iter().map(Value::string).collect()),
461                Value::List(vec!["."].repeat(8).iter().map(Value::string).collect()),
462                Value::List(vec!["."].repeat(8).iter().map(Value::string).collect()),
463                Value::List(vec!["."].repeat(8).iter().map(Value::string).collect()),
464                Value::List(vec!["♙"].repeat(8).iter().map(Value::string).collect()),
465                Value::List(["♖", "♘", "♗", "♕", "♔", "♗", "♘", "♖"].iter().map(Value::string).collect()),
466            ]))
467        }));
468
469        chess.insert("flip".to_string(), Value::builtin("chess@flip", |args, env| {
470            check_args_len(Value::symbol("chess@flip"), &args, 1)?;
471
472            if let Value::List(mut board) = args[0].eval(env)? {
473                board.reverse();
474                Ok(Value::List(board))
475            } else {
476                Err(Error::InvalidArguments(Value::symbol("chess@flip"), args.clone()))
477            }
478        }));
479
480        fn get(board: Vec<Vec<Value>>, col: usize, row: usize) -> Value {
481            board[row][col].clone()
482        }
483
484        fn to_coords(pos: &String) -> Result<(usize, usize), Error> {
485            let tmp = pos.trim().to_lowercase();
486            let mut chars = tmp.chars();
487            if let Some(col) = chars.next() {
488                if let Some(row) = chars.next() {
489                    if chars.next() == None {
490                        Ok((match col {
491                            'a' => 0,
492                            'b' => 1,
493                            'c' => 2,
494                            'd' => 3,
495                            'e' => 4,
496                            'f' => 5,
497                            'g' => 6,
498                            'h' => 7,
499                            _ => return Err(Error::CustomError(format!("invalid notation for piece position `{}`", pos)))
500                        }, match row.to_string().parse::<usize>() {
501                            Ok(n) if 1 <= 1 && n <= 8 => 8 - n,
502                            _ => return Err(Error::CustomError(format!("invalid notation for piece position `{}`", pos)))
503                        }))
504                    } else {
505                        Err(Error::CustomError(format!("invalid notation for piece position `{}`", pos)))
506                    }
507                } else {
508                    Err(Error::CustomError(format!("invalid notation for piece position `{}`", pos)))
509                }
510            } else {
511                Err(Error::CustomError(format!("invalid notation for piece position `{}`", pos)))
512            }
513        }
514
515        chess.insert("get".to_string(), Value::builtin("chess@get", |args, env| {
516            check_args_len(Value::symbol("chess@get"), &args, 2)?;
517
518            match (args[0].eval(env)?, args[1].eval(env)?) {
519                (Value::List(val_board), Value::String(pos)) => {
520                    let mut board = vec![];
521                    for val_row in val_board {
522                        if let Value::List(row) = val_row {
523                            board.push(row);
524                        } else {
525                            return Err(Error::InvalidArguments(Value::Symbol("chess@get".to_string()), args.clone()))
526                        }
527                    }
528                    let (col, row) = to_coords(&pos)?;
529                    Ok(get(board, col, row))
530                }
531                _ => Err(Error::InvalidArguments(Value::Symbol("chess@get".to_string()), args.clone()))
532            }
533        }));
534
535        fn to_board(val_board: Vec<Value>) -> Result<Vec<Vec<Value>>, Error> {
536            let mut board = vec![];
537            for val_row in val_board {
538                if let Value::List(row) = val_row {
539                    board.push(row);
540                } else {
541                    return Err(Error::CustomError("malformed board".to_string()))
542                }
543            }
544
545            Ok(board)
546        }
547
548        fn format_board(val_board: Vec<Value>) -> Result<Value, Error> {
549            let mut result = String::new();
550            let board = to_board(val_board)?;
551
552            let mut is_black = false;
553            for (i, row) in board.iter().enumerate() {
554                for col in row {
555                    result += &if is_black && is_space(&col.to_string()) {
556                        "░".to_string()
557                    } else if !is_black && is_space(&col.to_string()) {
558                        " ".to_string()
559                    } else {
560                        format!("{}", col)
561                    };
562
563                    is_black = !is_black
564                }
565                is_black = !is_black;
566                if i+1 < 8 { result.push('\n') }
567            }
568
569            Ok(Value::String(result))
570        }
571
572        chess.insert("fmt".to_string(), Value::builtin("chess@fmt", |args, env| {
573            check_args_len(Value::symbol("chess@fmt"), &args, 1)?;
574
575            match args[0].eval(env)? {
576                Value::List(val_board) => format_board(val_board),
577                _ => Err(Error::InvalidArguments(Value::Symbol("chess@fmt".to_string()), args.clone()))
578            }
579        }));
580
581        chess.insert("print".to_string(), Value::builtin("chess@print", |args, env| {
582            check_args_len(Value::symbol("chess@print"), &args, 1)?;
583
584            match args[0].eval(env)? {
585                Value::List(val_board) => {
586                    println!("{}", format_board(val_board)?);
587                    Ok(Value::Nil)
588                }
589                _ => Err(Error::InvalidArguments(Value::Symbol("chess@print".to_string()), args.clone()))
590            }
591        }));
592
593        chess.insert("mv".to_string(), Value::builtin("chess@mv", |args, env| {
594            check_args_len(Value::symbol("chess@mv"), &args, 3)?;
595
596            match (args[0].eval(env)?, args[1].eval(env)?, args[2].eval(env)?) {
597                (Value::List(val_board), Value::String(from), Value::String(to)) => {
598                    let mut board = to_board(val_board)?;
599                    let (src_col, src_row) = to_coords(&from)?;
600                    let (dst_col, dst_row) = to_coords(&to)?;
601                    
602                    let src_piece = &board[src_row][src_col].to_string();
603                    let dst_piece = &board[dst_row][dst_col].to_string();
604                    
605                    if is_piece(&src_piece) {
606                        if (is_white(&src_piece) != is_white(&dst_piece)) || is_space(&dst_piece) {
607                            board[dst_row][dst_col] = board[src_row][src_col].clone();
608                            board[src_row][src_col] = Value::string(".");
609
610                            Ok(Value::List(board.iter().map(|x| Value::List(x.clone())).collect()))
611                        } else {
612                            Err(Error::CustomError("cannot capture same color piece".to_string()))
613                        }
614                    } else {
615                        Err(Error::CustomError("cannot move non-piece".to_string()))
616                    }
617                }
618                _ => Err(Error::InvalidArguments(Value::Symbol("chess@mv".to_string()), args.clone()))
619            }
620        }));
621
622        chess.insert("add".to_string(), Value::builtin("chess@add", |args, env| {
623            check_args_len(Value::symbol("chess@add"), &args, 3)?;
624
625            match (args[0].eval(env)?, args[1].eval(env)?, args[2].eval(env)?) {
626                (Value::List(val_board), Value::String(dst), Value::String(piece)) => {
627                    let mut board = to_board(val_board)?;
628                    let (dst_col, dst_row) = to_coords(&dst)?;
629                    
630                    board[dst_row][dst_col] = Value::String(piece);
631                    Ok(Value::List(board.iter().map(|x| Value::List(x.clone())).collect()))
632                }
633                _ => Err(Error::InvalidArguments(Value::Symbol("chess@add".to_string()), args.clone()))
634            }
635        }));
636
637        chess.insert("rm".to_string(), Value::builtin("chess@rm", |args, env| {
638            check_args_len(Value::symbol("chess@rm"), &args, 2)?;
639
640            match (args[0].eval(env)?, args[1].eval(env)?) {
641                (Value::List(val_board), Value::String(rm)) => {
642                    let mut board = to_board(val_board)?;
643                    let (rm_col, rm_row) = to_coords(&rm)?;
644                    
645                    board[rm_row][rm_col] = Value::string(".");
646                    Ok(Value::List(board.iter().map(|x| Value::List(x.clone())).collect()))
647                }
648                _ => Err(Error::InvalidArguments(Value::Symbol("chess@rm".to_string()), args.clone()))
649            }
650        }));
651
652        Value::Table(chess)
653    };
654
655    static ref FMT: Value = {
656        let mut colorize = BTreeMap::new();
657        let mut dark = BTreeMap::new();
658        macro_rules! make_color {
659            ($color:expr) => {|args, env| {
660                let mut result = String::new();
661                for (i, arg) in args.iter().enumerate() {
662                    result += &format!("{}", arg.eval(env)?);
663                    if i < args.len()-1 {
664                        result += " ";
665                    }
666                }
667                
668                Ok(Value::String($color(result)))
669            }};
670        }
671
672        dark.insert(String::from("red"), Value::builtin("fmt@dark@red", make_color!(Colorize::red)));
673        dark.insert(String::from("green"), Value::builtin("fmt@dark@green", make_color!(Colorize::green)));
674        dark.insert(String::from("blue"), Value::builtin("fmt@dark@blue", make_color!(Colorize::blue)));
675        dark.insert(String::from("cyan"), Value::builtin("fmt@dark@cyan", make_color!(Colorize::cyan)));
676        dark.insert(String::from("yellow"), Value::builtin("fmt@dark@yellow", make_color!(Colorize::yellow)));
677        dark.insert(String::from("magenta"), Value::builtin("fmt@dark@magenta", make_color!(Colorize::magenta)));
678        colorize.insert(String::from("dark"), Value::Table(dark));
679        
680        colorize.insert(String::from("red"), Value::builtin("fmt@red", make_color!(Colorize::bright_red)));
681        colorize.insert(String::from("green"), Value::builtin("fmt@green", make_color!(Colorize::bright_green)));
682        colorize.insert(String::from("blue"), Value::builtin("fmt@blue", make_color!(Colorize::bright_blue)));
683        colorize.insert(String::from("yellow"), Value::builtin("fmt@yellow", make_color!(Colorize::bright_yellow)));
684        colorize.insert(String::from("magenta"), Value::builtin("fmt@magenta", make_color!(Colorize::bright_magenta)));
685        colorize.insert(String::from("cyan"), Value::builtin("fmt@cyan", make_color!(Colorize::bright_cyan)));
686        colorize.insert(String::from("black"), Value::builtin("fmt@black", make_color!(Colorize::black)));
687        colorize.insert(String::from("gray"), Value::builtin("fmt@gray", make_color!(Colorize::bright_black)));
688        colorize.insert(String::from("grey"), Value::builtin("fmt@grey", make_color!(Colorize::bright_black)));
689        colorize.insert(String::from("white"), Value::builtin("fmt@white", make_color!(Colorize::bright_white)));
690
691        colorize.insert(String::from("bold"), Value::builtin("fmt@bold", make_color!(Colorize::bold)));
692        colorize.insert(String::from("invert"), Value::builtin("fmt@invert", make_color!(Colorize::invert)));
693        colorize.insert(String::from("underline"), Value::builtin("fmt@underline", make_color!(Colorize::underline)));
694        
695        Value::Table(colorize)
696    };
697
698    static ref MATH: Value = {
699        let mut math = BTreeMap::new();
700        math.insert("E".to_string(), Value::Float(std::f64::consts::E));
701        math.insert("PI".to_string(), Value::Float(std::f64::consts::PI));
702        math.insert("TAU".to_string(), Value::Float(std::f64::consts::TAU));
703
704        math.insert("pow".to_string(), Value::builtin("pow", |args, env| {
705            check_args_len(Value::symbol("math@pow"), &args, 2)?;
706
707            match args[0].eval(env)? {
708                Value::Float(base) => {
709                    match args[1].eval(env)? {
710                        Value::Float(n)   => Ok(Value::Float(base.powf(n))),
711                        Value::Integer(n) => Ok(Value::Float(base.powi(n))),
712                        _ => Err(Error::InvalidArguments(Value::symbol("math@pow"), args.clone()))
713                    }
714                }
715                Value::Integer(base) => {
716                    match args[1].eval(env)? {
717                        Value::Float(n)   => Ok(Value::Float((base as f64).powf(n))),
718                        Value::Integer(n) if n > 0 => Ok(Value::Integer(base.pow(n as u32))),
719                        Value::Integer(n) => Ok(Value::Float((base as f64).powi(n))),
720                        _ => Err(Error::InvalidArguments(Value::symbol("math@pow"), args.clone()))
721                    }
722                }
723                _ => Err(Error::InvalidArguments(Value::symbol("math@pow"), args.clone()))
724            }
725        }));
726
727        math.insert("log".to_string(), Value::builtin("log", |args, env| {
728            check_args_len(Value::symbol("math@log"), &args, 2)?;
729
730            let base = match args[0].eval(env)? {
731                Value::Float(n)   => Ok(n),
732                Value::Integer(n) => Ok(n as f64),
733                _ => Err(Error::InvalidArguments(Value::symbol("math@log"), args.clone()))
734            }?;
735
736            let x = match args[1].eval(env)? {
737                Value::Float(n)   => Ok(n),
738                Value::Integer(n) => Ok(n as f64),
739                _ => Err(Error::InvalidArguments(Value::symbol("math@log"), args.clone()))
740            }?;
741            
742            Ok(Value::Float(x.log(base)))
743        }));
744
745        math.insert("log10".to_string(), Value::builtin("log10", |args, env| {
746            check_args_len(Value::symbol("math@log10"), &args, 1)?;
747
748            let x = match args[0].eval(env)? {
749                Value::Float(n)   => Ok(n),
750                Value::Integer(n) => Ok(n as f64),
751                _ => Err(Error::InvalidArguments(Value::symbol("math@log10"), args.clone()))
752            }?;
753            
754            Ok(Value::Float(x.log10()))
755        }));
756
757        math.insert("log2".to_string(), Value::builtin("log2", |args, env| {
758            check_args_len(Value::symbol("math@log2"), &args, 1)?;
759
760            let x = match args[0].eval(env)? {
761                Value::Float(n)   => Ok(n),
762                Value::Integer(n) => Ok(n as f64),
763                _ => Err(Error::InvalidArguments(Value::symbol("math@log2"), args.clone()))
764            }?;
765            
766            Ok(Value::Float(x.log2()))
767        }));
768
769        math.insert("sqrt".to_string(), Value::builtin("sqrt", |args, env| {
770            check_args_len(Value::symbol("math@sqrt"), &args, 1)?;
771
772            match args[0].eval(env)? {
773                Value::Float(n)   => Ok(Value::Float(n.sqrt())),
774                Value::Integer(n) => Ok(Value::Float((n as f64).sqrt())),
775                _ => Err(Error::InvalidArguments(Value::symbol("math@sqrt"), args.clone()))
776            }
777        }));
778
779
780        math.insert("cbrt".to_string(), Value::builtin("cbrt", |args, env| {
781            check_args_len(Value::symbol("math@cbrt"), &args, 1)?;
782
783            match args[0].eval(env)? {
784                Value::Float(n)   => Ok(Value::Float(n.cbrt())),
785                Value::Integer(n) => Ok(Value::Float((n as f64).cbrt())),
786                _ => Err(Error::InvalidArguments(Value::symbol("math@cbrt"), args.clone()))
787            }
788        }));
789
790        math.insert("sin".to_string(), Value::builtin("sin", |args, env| {
791            check_args_len(Value::symbol("math@sin"), &args, 1)?;
792
793            match args[0].eval(env)? {
794                Value::Float(n)   => Ok(Value::Float(n.sin())),
795                Value::Integer(n) => Ok(Value::Float((n as f64).sin())),
796                _ => Err(Error::InvalidArguments(Value::symbol("math@sin"), args.clone()))
797            }
798        }));
799
800        math.insert("cos".to_string(), Value::builtin("cos", |args, env| {
801            check_args_len(Value::symbol("math@cos"), &args, 1)?;
802
803            match args[0].eval(env)? {
804                Value::Float(n)   => Ok(Value::Float(n.cos())),
805                Value::Integer(n) => Ok(Value::Float((n as f64).cos())),
806                _ => Err(Error::InvalidArguments(Value::symbol("math@cos"), args.clone()))
807            }
808        }));
809
810        math.insert("tan".to_string(), Value::builtin("tan", |args, env| {
811            check_args_len(Value::symbol("math@tan"), &args, 1)?;
812
813            match args[0].eval(env)? {
814                Value::Float(n)   => Ok(Value::Float(n.tan())),
815                Value::Integer(n) => Ok(Value::Float((n as f64).tan())),
816                _ => Err(Error::InvalidArguments(Value::symbol("math@tan"), args.clone()))
817            }
818        }));
819
820        math.insert("asin".to_string(), Value::builtin("asin", |args, env| {
821            check_args_len(Value::symbol("math@asin"), &args, 1)?;
822
823            match args[0].eval(env)? {
824                Value::Float(n)   => Ok(Value::Float(n.asin())),
825                Value::Integer(n) => Ok(Value::Float((n as f64).asin())),
826                _ => Err(Error::InvalidArguments(Value::symbol("math@asin"), args.clone()))
827            }
828        }));
829
830        math.insert("acos".to_string(), Value::builtin("acos", |args, env| {
831            check_args_len(Value::symbol("math@acos"), &args, 1)?;
832
833            match args[0].eval(env)? {
834                Value::Float(n)   => Ok(Value::Float(n.acos())),
835                Value::Integer(n) => Ok(Value::Float((n as f64).acos())),
836                _ => Err(Error::InvalidArguments(Value::symbol("math@acos"), args.clone()))
837            }
838        }));
839
840        math.insert("atan".to_string(), Value::builtin("atan", |args, env| {
841            check_args_len(Value::symbol("math@atan"), &args, 1)?;
842
843            match args[0].eval(env)? {
844                Value::Float(n)   => Ok(Value::Float(n.atan())),
845                Value::Integer(n) => Ok(Value::Float((n as f64).atan())),
846                _ => Err(Error::InvalidArguments(Value::symbol("math@atan"), args.clone()))
847            }
848        }));
849
850        Value::Table(math)
851    };
852}
853
854#[derive(Clone)]
855pub struct Environment {
856    symbols: BTreeMap<String, Value>
857}
858
859const TYPES: &'static [Type] = &[
860    Type::Alpine,
861    Type::Amazon,
862    Type::Android,
863    Type::Arch,
864    Type::CentOS,
865    Type::Debian,
866    Type::Emscripten,
867    Type::EndeavourOS,
868    Type::Fedora,
869    Type::Linux,
870    Type::Macos,
871    Type::Manjaro,
872    Type::Mint,
873    Type::openSUSE,
874    Type::OracleLinux,
875    Type::Pop,
876    Type::Redhat,
877    Type::RedHatEnterprise,
878    Type::Redox,
879    Type::Solus,
880    Type::SUSE,
881    Type::Ubuntu,
882    Type::Unknown,
883    Type::Windows
884];
885
886fn get_os_name(t: &Type) -> String {
887    match t {
888        Type::Alpine           => "alpine",
889        Type::Amazon           => "amazon",
890        Type::Android          => "android",
891        Type::Arch             => "arch",
892        Type::CentOS           => "centos",
893        Type::Debian           => "debian",
894        Type::Macos            => "macos",
895        Type::Fedora           => "fedora",
896        Type::Linux            => "linux",
897        Type::Manjaro          => "manjaro",
898        Type::Mint             => "mint",
899        Type::openSUSE         => "opensuse",
900        Type::EndeavourOS      => "endeavouros",
901        Type::OracleLinux      => "oraclelinux",
902        Type::Pop              => "pop",
903        Type::Redhat           => "redhat",
904        Type::RedHatEnterprise => "redhatenterprise",
905        Type::Redox            => "redox",
906        Type::Solus            => "solus",
907        Type::SUSE             => "suse",
908        Type::Ubuntu           => "ubuntu",
909        Type::Windows          => "windows",
910        Type::Unknown | _ => "unknown",
911    }.to_string()
912}
913
914fn get_os_family(t: &Type) -> String {
915    match t {
916        Type::Amazon
917        | Type::Android => "android",
918        Type::Alpine
919        | Type::Arch
920        | Type::CentOS
921        | Type::Debian
922        | Type::Fedora
923        | Type::Linux
924        | Type::Manjaro
925        | Type::Mint
926        | Type::openSUSE
927        | Type::EndeavourOS
928        | Type::OracleLinux
929        | Type::Pop
930        | Type::Redhat
931        | Type::RedHatEnterprise
932        | Type::SUSE
933        | Type::Ubuntu
934         => "linux",
935        
936        Type::Macos
937        | Type::Solus
938        | Type::Redox => "unix",
939
940        Type::Windows => "windows",
941
942        Type::Unknown | _ => "unknown",
943    }.to_string()
944}
945
946pub const REPORT: &str = "report";
947pub const PROMPT: &str = "prompt";
948pub const INCOMPLETE_PROMPT: &str = "incomplete-prompt";
949
950pub const CWD:   &str = "CWD";
951const HOME:      &str = "HOME";
952const VIDEOS:    &str = "VIDS";
953const DESKTOP:   &str = "DESK";
954const PICTURES:  &str = "PICS";
955const DOCUMENTS: &str = "DOCS";
956const DOWNLOADS: &str = "DOWN";
957
958
959fn check_args_len(func: Value, args: &Vec<Value>, len: usize) -> Result<(), Error> {
960    if args.len() > len { Err(Error::TooManyArguments(func, args.clone())) }
961    else if args.len() < len { Err(Error::TooFewArguments(func, args.clone())) }
962    else { Ok(()) }
963}
964
965impl Environment {
966    pub fn new() -> Self {
967        let mut result = Self { symbols: BTreeMap::new() };
968        result.define(CWD, Value::Path(if let Ok(path) = result.get_cwd() {
969            path
970        } else {
971            PathBuf::new()
972        }));
973        result
974    }
975
976    pub fn get(&self, name: impl ToString) -> Result<Value, Error> {
977        let name = name.to_string();
978        if let Some(value) = self.symbols.get(&name) {
979            Ok(value.clone())
980        } else {
981            Ok(match name.as_str() {
982                REPORT => Value::builtin(REPORT, |args, env| {
983                    check_args_len(env.get(REPORT)?, &args, 1)?;
984
985                    let val = args[0].eval(env)?;
986
987                    match val {
988                        Value::Nil | Value::Integer(0) => {}
989                        Value::Error(e) => println!("error: {}", e),
990                        other => println!(" => {:?}", other),
991                    }
992
993                    Ok(Value::Nil)
994                }),
995
996                PROMPT => Value::builtin(PROMPT, |args, env| {
997                    check_args_len(env.get(PROMPT)?, &args, 1)?;
998                    Ok(Value::String(format!("{}> ", args[0].eval(env)?)))
999                }),
1000
1001                INCOMPLETE_PROMPT => Value::builtin(INCOMPLETE_PROMPT, |args, env| {
1002                    check_args_len(env.get(INCOMPLETE_PROMPT)?, &args, 1)?;
1003                    Ok(Value::String(format!("{}> ", " ".repeat(format!("{}", args[0].eval(env)?).len()))))
1004                }),
1005
1006                "absolute" => Value::builtin("absolute", |args, env| {
1007                    check_args_len(env.get("absolute")?, &args, 1)?;
1008
1009                    match args[0].eval(env)? {
1010                        Value::Path(path) => if let Ok(result) = dunce::canonicalize(path) {
1011                            Ok(Value::Path(result))
1012                        } else {
1013                            Err(Error::CustomError(String::from("could not canonicalize path")))
1014                        },
1015
1016                        Value::Symbol(path) | Value::String(path) => if let Ok(result) = dunce::canonicalize(path) {
1017                            Ok(Value::Path(result))
1018                        } else {
1019                            Err(Error::CustomError(String::from("could not canonicalize path")))
1020                        },
1021
1022                        _ => Err(Error::InvalidArguments(env.get("absolute")?, args.clone()))
1023                    }
1024                }),
1025
1026                "exists" => Value::builtin("exists", |args, env| {
1027                    check_args_len(env.get("exists")?, &args, 1)?;
1028                    match args[0].eval(env)? {
1029                        Value::Path(path) => Ok(Value::Boolean(path.exists())),
1030                        Value::String(path) | Value::Symbol(path) => Ok(Value::Boolean(PathBuf::from(path).exists())),
1031                        _ => Err(Error::InvalidArguments(env.get("exists")?, args.clone()))
1032                    }
1033                }),
1034
1035                "cards" => CARDS.clone(),
1036                "chess" => CHESS.clone(),
1037
1038                "rand" => {
1039                    let mut random = BTreeMap::new();
1040                    random.insert("int".to_string(), Value::builtin("rand@int", |args, env| {
1041                        check_args_len(Value::Symbol("rand@int".to_string()), &args, 2)?;
1042                        if let (Value::Integer(l), Value::Integer(h)) = (args[0].eval(env)?, args[1].eval(env)?) {
1043                            let mut rng = thread_rng();
1044                            let n = Uniform::new(l, h);
1045                            Ok(Value::Integer(rng.sample(n)))
1046                        } else {
1047                            Err(Error::InvalidArguments(Value::Symbol("rand@int".to_string()), args.clone()))
1048                        }
1049                    }));
1050                    
1051                    random.insert("shuffle".to_string(), Value::builtin("rand@shuffle", |args, env| {
1052                        check_args_len(Value::Symbol("rand@shuffle".to_string()), &args, 1)?;
1053
1054                        if let Value::List(mut list) = args[0].eval(env)? {
1055                            let mut rng = thread_rng();
1056                            list.shuffle(&mut rng);
1057                            Ok(Value::List(list))
1058                        } else {
1059                            Err(Error::InvalidArguments(Value::Symbol("rand@shuffle".to_string()), args.clone()))
1060                        }
1061                    }));
1062                    
1063                    random.insert("choose".to_string(), Value::builtin("rand@choose", |args, env| {
1064                        check_args_len(Value::Symbol("rand@choose".to_string()), &args, 1)?;
1065
1066                        if let Value::List(list) = args[0].eval(env)? {
1067                            let mut rng = thread_rng();
1068                            let n = Uniform::new(0, list.len());
1069                            Ok(list[rng.sample(n)].clone())
1070                        } else {
1071                            Err(Error::InvalidArguments(Value::Symbol("rand@choose".to_string()), args.clone()))
1072                        }
1073                    }));
1074
1075                    Value::Table(random)
1076                },
1077
1078                "file" => {
1079                    let mut file = BTreeMap::new();
1080                    file.insert("read".to_string(), Value::builtin("file@read", |args, env| {
1081                        check_args_len(Value::Symbol("file@read".to_string()), &args, 1)?;
1082    
1083                        match args[0].eval(env)? {
1084                            Value::Path(path) => {
1085                                if let Ok(contents) = read_to_string(&env.get_cwd()?.join(&path)) {
1086                                    Ok(Value::String(contents))
1087                                } else {
1088                                    Err(Error::CustomError(format!("could not read file {:?}", path)))
1089                                }
1090                            },
1091    
1092                            Value::String(path) | Value::Symbol(path) => {
1093                                if let Ok(contents) = read_to_string(&env.get_cwd()?.join(&path)) {
1094                                    Ok(Value::String(contents))
1095                                } else {
1096                                    Err(Error::CustomError(format!("could not read file {:?}", path)))
1097                                }
1098                            },
1099                            _ => Err(Error::InvalidArguments(Value::Symbol("file@read".to_string()), args.clone()))
1100                        }
1101                    }));
1102
1103                    file.insert("write".to_string(), Value::builtin("file@write", |args, env| {
1104                        check_args_len(Value::Symbol("file@write".to_string()), &args, 2)?;
1105    
1106                        if let Value::String(contents) = args[1].eval(env)? {
1107                            match args[0].eval(env)? {
1108                                Value::Path(path) => match write(&env.get_cwd()?.join(&path), contents) {
1109                                    Ok(_) => Ok(Value::Nil),
1110                                    _ => Err(Error::CustomError(format!("could not write to file {:?}", path)))
1111                                },
1112                                Value::String(path) | Value::Symbol(path) => match write(&env.get_cwd()?.join(&path), contents) {
1113                                    Ok(_) => Ok(Value::Nil),
1114                                    _ => Err(Error::CustomError(format!("could not write to file {:?}", path)))
1115                                },
1116
1117                                _ => Err(Error::InvalidArguments(Value::Symbol("file@write".to_string()), args.clone()))
1118                            }
1119                        } else {
1120                            Err(Error::InvalidArguments(Value::Symbol("file@write".to_string()), args.clone()))
1121                        }
1122                    }));
1123
1124                    file.insert("append".to_string(), Value::builtin("file@append", |args, env| {
1125                        check_args_len(Value::Symbol("file@append".to_string()), &args, 2)?;
1126    
1127                        let contents = match args[0].eval(env)? {
1128                            Value::Path(path) => {
1129                                if let Ok(contents) = read_to_string(&env.get_cwd()?.join(&path)) {
1130                                    contents
1131                                } else {
1132                                    String::new()
1133                                }
1134                            },
1135    
1136                            Value::String(path) | Value::Symbol(path) => {
1137                                if let Ok(contents) = read_to_string(&env.get_cwd()?.join(path)) {
1138                                    contents
1139                                } else {
1140                                    String::new()
1141                                }
1142                            },
1143                            _ => return Err(Error::InvalidArguments(Value::Symbol("file@append".to_string()), args.clone()))
1144                        };
1145
1146                        if let Value::String(new_contents) = args[1].eval(env)? {
1147                            match args[0].eval(env)? {
1148                                Value::Path(path) => match write(&env.get_cwd()?.join(&path), contents.to_string() + &new_contents) {
1149                                    Ok(_) => Ok(Value::Nil),
1150                                    _ => Err(Error::CustomError(format!("could not write to file {:?}", path)))
1151                                },
1152                                Value::String(path) | Value::Symbol(path) => match write(&env.get_cwd()?.join(&path), contents.to_string() + &new_contents) {
1153                                    Ok(_) => Ok(Value::Nil),
1154                                    _ => Err(Error::CustomError(format!("could not write to file {:?}", path)))
1155                                },
1156
1157                                _ => Err(Error::InvalidArguments(Value::Symbol("file@append".to_string()), args.clone()))
1158                            }
1159                        } else {
1160                            Err(Error::InvalidArguments(Value::Symbol("file@append".to_string()), args.clone()))
1161                        }
1162                    }));
1163
1164                    Value::Table(file)
1165                }
1166
1167                "is-err" => Value::builtin("is-err", |args, env| {
1168                    check_args_len(env.get("is-err")?, &args, 1)?;
1169
1170                    Ok(Value::Boolean(match args[0].eval(env) {
1171                        Ok(Value::Error(_)) | Err(_) => true,
1172                        _ => false
1173                    }))
1174                }),
1175
1176                "is-syntax-err" => Value::builtin("is-syntax-err", |args, env| {
1177                    check_args_len(env.get("is-syntax-err")?, &args, 1)?;
1178
1179                    Ok(Value::Boolean(match args[0].eval(env) {
1180                        Ok(Value::Error(e)) => match *e {
1181                            Error::SyntaxError(_) => true,
1182                            _ => false,
1183                        },
1184                        _ => false
1185                    }))
1186                }),
1187
1188
1189                "widget" => {
1190                    let mut widget = BTreeMap::new();
1191
1192                    widget.insert(String::from("create"), Value::builtin("widget@create", |args, env| {
1193                        check_args_len(Value::Symbol("widget@create".to_string()), &args, 4)?;
1194                        // print(widget@create("♔", "testing", 7))
1195                        // print(widget@create("Chess", "testing!", 8))
1196                        // print(widget@add-vertical(widget@create("Chess", "testing!", 8), widget@create("♔", "hmm", 8)))
1197                        let title = match args[0].eval(env)? {
1198                            Value::String(x) => x,
1199                            _ => return Err(Error::InvalidArguments(Value::Symbol("widget@create".to_string()), args.clone()))
1200                        };
1201                        
1202                        let text = match args[1].eval(env)? {
1203                            Value::String(x) => x,
1204                            _ => return Err(Error::InvalidArguments(Value::Symbol("widget@create".to_string()), args.clone())),
1205                        };
1206                        
1207                        let text_width = match args[2].eval(env)? {
1208                            Value::Integer(x) if x > 4 => x as usize,
1209                            _ => return Err(Error::InvalidArguments(Value::Symbol("widget@create".to_string()), args.clone())),
1210                        } - 2;
1211                        
1212                        let widget_height = match args[3].eval(env)? {
1213                            Value::Integer(x) if x >= 3 => x as usize,
1214                            _ => return Err(Error::InvalidArguments(Value::Symbol("widget@create".to_string()), args.clone())),
1215                        };
1216
1217                        if text_width < title.len() {
1218                            Err(Error::CustomError(String::from("width is less than title length")))
1219                        } else {
1220                            let title_len = title.chars().collect::<Vec<char>>().len();
1221                            let mut left_border_half = "─".repeat(((text_width - title_len) as f64 / 2.0).round() as usize);
1222                            let right_border_half = left_border_half.clone();
1223                            let left_len = left_border_half.chars().collect::<Vec<char>>().len();
1224                            if (left_len * 2 + title_len + 2) > text_width + 2 {
1225                                left_border_half.pop();
1226                            }
1227
1228                            let mut result = format!("┌{left_side}{}{right_side}┐\n", title, left_side=left_border_half, right_side=right_border_half);
1229                            let width = result.chars().collect::<Vec<char>>().len() - 1;
1230                            
1231                            let mut i = 0;
1232                            for ch in text.chars() {
1233                                if i == 0 {
1234                                    result.push(' ');
1235                                    i += 1;
1236                                }
1237
1238                                if ch == '\n' {
1239                                    result += &" ".repeat(width-i);
1240                                    i = width;
1241                                } else {
1242                                    result.push(ch);
1243                                }
1244                                
1245                                if i >= width-1 {
1246                                    result += "\n";
1247                                    i = 0;
1248                                } else {
1249                                    i += 1;
1250                                }
1251                            }
1252
1253
1254                            result += &" ".repeat(width-i);
1255
1256                            while result.lines().collect::<Vec<&str>>().len() < widget_height - 1 {
1257                                result += &(String::from("\n") + &" ".repeat(width));
1258                            }
1259
1260                            result += &format!("\n└{left_side}{}{right_side}┘", "─".repeat(title_len), left_side=left_border_half, right_side=right_border_half);
1261
1262                            Ok(Value::String(result))
1263                        }
1264                    }));
1265
1266                    widget.insert(String::from("add-horizontal"), Value::builtin("widget@add-horizontal", |args, env| {
1267                        if args.is_empty() {
1268                            Err(Error::TooFewArguments(Value::Symbol("widget@add-horizontal".to_string()), args.clone()))
1269                        } else {
1270                            let mut string_args = vec![];
1271                            let mut height = 0;
1272                            for (i, arg) in args.iter().enumerate() {
1273                                if let Value::String(s) = arg.eval(env)? {
1274                                    let lines = s.lines().map(ToString::to_string).collect::<Vec<String>>();
1275                                    string_args.push(lines.clone());
1276
1277                                    height = string_args[0].len();
1278                                    
1279                                    if height != lines.len() {
1280                                        return Err(Error::CustomError(format!("Heights of horizontally added widgets must be equal, 0={}, {}={}", height, i, lines.len())))
1281                                    }
1282                                } else {
1283                                    return Err(Error::InvalidArguments(Value::Symbol("widget@add-horizontal".to_string()), args.clone()));
1284                                }
1285                            }
1286
1287                            let mut result = String::new();
1288
1289                            for line_n in 0..height {
1290                                for arg in &string_args {
1291                                    result += &arg[line_n];
1292                                }
1293                                result += "\n";
1294                            }
1295
1296
1297                            Ok(Value::String(result))
1298                        }
1299                    }));
1300
1301                    widget.insert(String::from("add-vertical"), Value::builtin("widget@add-vertical", |args, env| {
1302                        if args.is_empty() {
1303                            Err(Error::TooFewArguments(Value::Symbol("widget@add-vertical".to_string()), args.clone()))
1304                        } else {
1305                            let mut string_args = vec![];
1306                            for (i, arg) in args.iter().enumerate() {
1307                                if let Value::String(s) = arg.eval(env)? {
1308                                    string_args.push(s.trim().to_string());
1309
1310                                    let width = string_args[0].lines().next().unwrap().chars().collect::<Vec<char>>().len();
1311                                    
1312
1313                                    let this_width = string_args[i].lines().next().unwrap().chars().collect::<Vec<char>>().len();
1314                                    if width != this_width {
1315                                        return Err(Error::CustomError(format!("Widths of vertically added widgets must be equal, 0={}, {}={}", width, i, this_width)))
1316                                    }
1317                                } else {
1318                                    return Err(Error::InvalidArguments(Value::Symbol("widget@add-vertical".to_string()), args.clone()));
1319                                }
1320                            }
1321
1322                            Ok(Value::String(string_args.join("\n")))
1323                        }
1324                    }));
1325
1326                    Value::Table(widget)
1327                },
1328
1329                "fmt" => FMT.clone(),
1330                "math" => MATH.clone(),
1331
1332                "sleep" => Value::builtin("sleep", |args, env| {
1333                    check_args_len(env.get("sleep")?, &args, 1)?;
1334
1335                    match args[0].eval(env)? {
1336                        Value::Float(n)   => sleep(Duration::from_millis((n.abs() * 1000.0) as u64)),
1337                        Value::Integer(n) => sleep(Duration::from_millis((n.abs() * 1000) as u64)),
1338                        _ => return Err(Error::InvalidArguments(env.get("sleep")?, args.clone()))
1339                    }
1340                    
1341                    Ok(Value::Nil)
1342                }),
1343
1344                "to-path" => Value::builtin("to-path", |args, env| {
1345                    check_args_len(env.get("to-path")?, &args, 1)?;
1346                    
1347                    match args[0].eval(env)? {
1348                        Value::Path(x) => Ok(Value::Path(x)),
1349                        Value::String(s) | Value::Symbol(s) => Ok(Value::Path(PathBuf::from(s))),
1350                        _ => Err(Error::InvalidArguments(env.get("to-path")?, args.clone()))
1351                    }
1352                }),
1353
1354                "to-str" => Value::builtin("to-str", |args, env| {
1355                    
1356                    let mut result = String::new();
1357                    for (i, arg) in args.iter().enumerate() {
1358                        result += &format!("{}", arg.eval(env)?);
1359                        if i < args.len()-1 {
1360                            result += " ";
1361                        }
1362                    }
1363                    
1364                    Ok(Value::String(result))
1365                }),
1366
1367                "to-float" => Value::builtin("to-float", |args, env| {
1368                    check_args_len(env.get("to-float")?, &args, 1)?;
1369                    match args[0].eval(env)? {
1370                        Value::String(s) => match s.parse::<f64>() {
1371                            Ok(n) => Ok(Value::Float(n)),
1372                            Err(_) => Err(Error::CouldNotParseInteger(Value::String(s))),
1373                        },
1374                        Value::Float(x) => Ok(Value::Float(x)),
1375                        Value::Integer(x) => Ok(Value::Float(x as f64)),
1376                        Value::Boolean(x) => Ok(Value::Float(if x { 1.0 } else { 0.0 })),
1377                        _ => Err(Error::InvalidArguments(env.get("to-float")?, args.clone()))
1378                    }
1379                }),
1380
1381                "to-int" => Value::builtin("to-int", |args, env| {
1382                    check_args_len(env.get("to-int")?, &args, 1)?;
1383                    match args[0].eval(env)? {
1384                        Value::String(s) => match s.parse::<i32>() {
1385                            Ok(i) => Ok(Value::Integer(i)),
1386                            Err(_) => Err(Error::CouldNotParseInteger(Value::String(s))),
1387                        },
1388                        Value::Float(x) => Ok(Value::Integer(x as i32)),
1389                        Value::Integer(x) => Ok(Value::Integer(x)),
1390                        Value::Boolean(x) => Ok(Value::Integer(if x { 1 } else { 0 })),
1391                        _ => Err(Error::InvalidArguments(env.get("to-int")?, args.clone()))
1392                    }
1393                }),
1394
1395                "input" => Value::builtin("input", |args, env| {
1396                    let mut result = String::new();
1397
1398                    for (i, arg) in args.iter().enumerate() {
1399                        print!("{}", arg.eval(env)?);
1400                        if i < args.len()-1 {
1401                            print!(" ");
1402                        }
1403                    }
1404                    let _ = stdout().flush();
1405                    if stdin().read_line(&mut result).is_err() {
1406                        Err(Error::ReadInputError)
1407                    } else {
1408                        Ok(Value::String(result.trim_end_matches("\n").trim_end_matches("\r").to_string()))
1409                    }
1410                }),
1411
1412                "rev" => Value::builtin("rev", |args, env| {
1413                    check_args_len(env.get("rev")?, &args, 1)?;
1414                    match args[0].eval(env)? {
1415                        Value::String(s) => Ok(Value::String(s.chars().rev().collect())),
1416                        Value::List(l) => Ok(Value::List(l.into_iter().rev().collect())),
1417                        _ => Err(Error::InvalidArguments(env.get("rev")?, args.clone()))
1418                    }
1419                }),
1420
1421                "split" => Value::builtin("split", |args, env| {
1422                    check_args_len(env.get("split")?, &args, 2)?;
1423
1424                    if let Value::String(s) = args[0].eval(env)? {
1425                        Ok(Value::List(
1426                            s.split(&args[1].eval(env)?.to_string())
1427                                .map(|x| Value::String(x.to_string()))
1428                                .collect()
1429                        ))
1430                    } else {
1431                        Err(Error::InvalidArguments(env.get("join")?, args.clone()))
1432                    }
1433                }),
1434
1435                "sort" => Value::builtin("sort", |args, env| {
1436                    check_args_len(env.get("sort")?, &args, 1)?;
1437
1438                    if let Value::List(list) = args[0].eval(env)? {
1439                        let mut num_list = vec![];
1440                        for item in list {
1441                            match item {
1442                                Value::Integer(i) => num_list.push(i),
1443                                _ => return Err(Error::InvalidArguments(env.get("sort")?, args.clone()))
1444                            }
1445                        }
1446                        num_list.sort();
1447                        Ok(Value::List(num_list.iter().map(|x| Value::Integer(*x)).collect()))
1448                    } else {
1449                        Err(Error::InvalidArguments(env.get("sort")?, args.clone()))
1450                    }
1451                }),
1452
1453                "join" => Value::builtin("join", |args, env| {
1454                    check_args_len(env.get("join")?, &args, 2)?;
1455
1456                    if let Value::List(list) = args[0].eval(env)? {
1457                        Ok(Value::String(
1458                            list
1459                                .iter()
1460                                .map(|x| x.to_string())
1461                                .collect::<Vec<String>>()
1462                                .join(&args[1].eval(env)?.to_string())
1463                        ))
1464                    } else {
1465                        Err(Error::InvalidArguments(env.get("join")?, args.clone()))
1466                    }
1467                }),
1468
1469                "date" => {
1470                    let now = Local::now().date();
1471
1472                    let mut date = BTreeMap::new();
1473                    date.insert(String::from("day"),     Value::Integer(now.day() as i32));
1474                    date.insert(String::from("weekday"), Value::Integer(now.weekday().num_days_from_sunday() as i32));
1475                    date.insert(String::from("month"),   Value::Integer(now.month() as i32));
1476                    date.insert(String::from("year"),    Value::Integer(now.year() as i32));
1477                    date.insert(String::from("str"),     Value::String(now.format("%D").to_string()));
1478                    Value::Table(date)
1479                }
1480
1481                "time" => {
1482                    let now = Local::now();
1483
1484                    let mut time = BTreeMap::new();
1485                    time.insert(String::from("hour"),   Value::Integer(now.hour() as i32));
1486                    time.insert(String::from("minute"), Value::Integer(now.minute() as i32));
1487                    time.insert(String::from("second"), Value::Integer(now.second() as i32));
1488                    time.insert(String::from("str"),    Value::String(now.time().format("%-I:%M %p").to_string()));
1489                    Value::Table(time)
1490                }
1491
1492                "sh" => {
1493                    let mut shell = BTreeMap::new();
1494                    if let Ok(path) = current_exe() {
1495                        shell.insert(String::from("exe"), Value::Path(path.clone()));
1496                        if let Some(parent) = path.parent() {
1497                            shell.insert(String::from("dir"), Value::Path(PathBuf::from(parent)));
1498                        }
1499                    }
1500
1501                    shell.insert(String::from("version"), Value::List(VERSION.iter().map(|x| Value::Integer(*x as i32)).collect()));
1502                    if let Ok(path) = self.get_home_dir() {
1503                        shell.insert(String::from("prelude"), Value::Path(path.join(PRELUDE_FILENAME)));
1504                    }
1505                    Value::Table(shell)
1506                }
1507
1508                "os" => {
1509                    let os = os_info::get();
1510                    let mut os_table = BTreeMap::new();
1511                    os_table.insert(String::from("name"),   Value::String(get_os_name(&os.os_type())));
1512                    os_table.insert(String::from("family"), Value::String(get_os_family(&os.os_type())));
1513                    os_table.insert(String::from("version"), Value::String(format!("{}", os.version())));
1514                    Value::Table(os_table)
1515                }
1516
1517                "env" => Value::Table(self.get_symbols().clone()),
1518
1519                HOME => Value::Path(self.get_home_dir()?),
1520                VIDEOS => Value::Path(self.get_vids_dir()?),
1521                DESKTOP => Value::Path(self.get_desk_dir()?),
1522                PICTURES => Value::Path(self.get_pics_dir()?),
1523                DOCUMENTS => Value::Path(self.get_docs_dir()?),
1524                DOWNLOADS => Value::Path(self.get_down_dir()?),
1525                
1526                "home" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(HOME)?)))),
1527                "vids" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(VIDEOS)?)))),
1528                "desk" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(DESKTOP)?)))),
1529                "pics" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(PICTURES)?)))),
1530                "docs" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(DOCUMENTS)?)))),
1531                "down" => Value::Macro(vec![], Box::new(Value::Define(CWD.to_string(), Box::new(self.get(DOWNLOADS)?)))),
1532
1533                "exit" | "quit" => Value::builtin("exit", |_, _| exit(0)),
1534
1535                "unbind" => Value::builtin("unbind", |args, env| {
1536                    check_args_len(env.get("unbind")?, &args, 1)?;
1537                    match args[0].eval(env)? {
1538                        Value::String(name) => {
1539                            if env.is_defined(&name) {
1540                                let result = env.get(&name)?;
1541                                env.symbols.remove(&name);
1542                                Ok(result)
1543                            } else {
1544                                Err(Error::SymbolNotDefined(name))
1545                            }
1546                        }
1547                        _ => Err(Error::InvalidArguments(env.get("unbind")?, args.clone()))
1548                    }
1549                }),
1550
1551                "print" => Value::builtin("print", |args, env| {
1552                    let mut acc = Value::Nil;
1553                    for (i, arg) in args.iter().enumerate() {
1554                        acc = arg.eval(env)?;
1555                        print!("{}", acc);
1556                        if i < args.len()-1 {
1557                            print!(" ");
1558                        } else {
1559                            println!("");
1560                        }
1561                    }
1562        
1563                    Ok(acc)
1564                }),
1565
1566                "echo" => Value::builtin("print", |args, env| {
1567                    for (i, arg) in args.iter().enumerate() {
1568                        print!("{}", arg.eval(env)?);
1569                        if i < args.len()-1 {
1570                            print!(" ");
1571                        } else {
1572                            println!("");
1573                        }
1574                    }
1575        
1576                    Ok(Value::Nil)
1577                }),
1578                
1579                "pwd" | "cwd" => Value::builtin("pwd", |args, env| {
1580                    check_args_len(env.get("pwd")?, &args, 0)?;
1581                    println!("{}", env.get("CWD")?);
1582                    Ok(Value::Nil)
1583                }),
1584
1585                
1586                "cd-eval" => Value::builtin("cd-eval", |args, env| {
1587                    if args.is_empty() {
1588                        env.define(CWD, Value::Path(env.get_home_dir()?));
1589                        Ok(Value::Integer(0))
1590                    } else {
1591                        check_args_len(env.get("cd-eval")?, &args, 1)?;
1592                        let mut cwd = env.get_cwd()?;
1593                            
1594                        cwd.push(PathBuf::from(args[0].eval(env)?.to_string()));
1595                        if cwd.exists() && cwd.is_dir() {
1596                            env.define(CWD, Value::Path(match dunce::canonicalize(&cwd) {
1597                                Ok(path) => path,
1598                                Err(_) => cwd.clone()
1599                            }));
1600
1601                            Ok(Value::Integer(0))
1602                        } else {
1603                            Err(Error::CannotChangeDir(cwd))
1604                        }
1605                    }
1606                }),
1607
1608                "cd" => Value::builtin("cd", |args, env| {
1609                    if args.is_empty() {
1610                        env.define(CWD, Value::Path(env.get_home_dir()?));
1611                        Ok(Value::Integer(0))
1612                    } else {
1613                        check_args_len(env.get("cd")?, &args, 1)?;
1614                        let mut cwd = env.get_cwd()?;
1615                        
1616                        cwd.push(PathBuf::from(args[0].to_string()));
1617                        
1618                        if cwd.exists() && cwd.is_dir() {
1619                            env.define(CWD, Value::Path(match dunce::canonicalize(&cwd) {
1620                                Ok(path) => path,
1621                                Err(_) => cwd.clone()
1622                            }));
1623                            
1624                            Ok(Value::Integer(0))
1625                        } else {
1626                            Err(Error::CannotChangeDir(cwd))
1627                        }
1628                    }
1629                }),
1630
1631                "clear" | "cls" => Value::builtin("cls", move |args, env| {
1632                    check_args_len(env.get("cls")?, &args, 0)?;
1633
1634                    let family = get_os_family(&os_info::get().os_type());
1635                    if family == "linux" || family == "unix" {
1636                        Value::Run(Box::new(Value::Path(PathBuf::from("clear"))), vec![]).eval(env)
1637                    } else if family == "windows" {
1638                        Value::Run(Box::new(Value::Path(PathBuf::from("cls"))), vec![]).eval(env)
1639                    } else {
1640                        println!("{}", "\n".repeat(255));
1641                        Ok(Value::Nil)
1642                    }
1643                }),
1644
1645                "keys" => Value::builtin("keys", |args, env| {
1646                    check_args_len(env.get("keys")?, &args, 1)?;
1647
1648                    if let Value::Table(table) = args[0].eval(env)? {
1649                        Ok(Value::List(table.keys().map(|x| Value::String(x.clone())).collect()))
1650                    } else {
1651                        Err(Error::InvalidArguments(env.get("keys")?, args.clone()))
1652                    }
1653                }),
1654
1655                "vals" => Value::builtin("vals", |args, env| {
1656                    check_args_len(env.get("vals")?, &args, 1)?;
1657
1658                    if let Value::Table(table) = args[0].eval(env)? {
1659                        Ok(Value::List(table.values().map(|x| x.clone()).collect()))
1660                    } else {
1661                        Err(Error::InvalidArguments(env.get("vals")?, args.clone()))
1662                    }
1663                }),
1664                
1665                "insert" => Value::builtin("insert", |args, env| {
1666                    check_args_len(env.get("insert")?, &args, 3)?;
1667
1668                    if let Value::Table(mut t) = args[0].eval(env)? {
1669                        if let Value::String(key) = args[1].eval(env)? {
1670                            t.insert(key, args[2].eval(env)?);
1671                            Ok(Value::Table(t))
1672                        } else {
1673                            Err(Error::InvalidArguments(env.get("insert")?, args.clone()))
1674                        }
1675                    } else {
1676                        Err(Error::InvalidArguments(env.get("insert")?, args.clone()))
1677                    }
1678                }),
1679
1680                "remove" => Value::builtin("remove", |args, env| {
1681                    check_args_len(env.get("remove")?, &args, 2)?;
1682
1683                    if let Value::Table(mut t) = args[0].eval(env)? {
1684                        if let Value::String(key) = args[1].eval(env)? {
1685                            t.remove(&key);
1686                            Ok(Value::Table(t))
1687                        } else {
1688                            Err(Error::InvalidArguments(env.get("remove")?, args.clone()))
1689                        }
1690                    } else {
1691                        Err(Error::InvalidArguments(env.get("remove")?, args.clone()))
1692                    }
1693                }),
1694
1695                "len" => Value::builtin("len", |args, env| {
1696                    check_args_len(env.get("len")?, &args, 1)?;
1697
1698                    match args[0].eval(env)? {
1699                        Value::List(list) => Ok(Value::Integer(list.len() as i32)),
1700                        Value::Table(t) => Ok(Value::Integer(t.len() as i32)),
1701                        Value::String(s) => Ok(Value::Integer(s.chars().collect::<Vec<char>>().len() as i32)),
1702                        Value::Path(path) => Ok(Value::Integer(path.components().collect::<Vec<Component>>().len() as i32)),
1703                        _ => Err(Error::InvalidArguments(env.get("len")?, args.clone()))
1704                    }
1705                }),
1706
1707                "push" => Value::builtin("push", |args, env| {
1708                    check_args_len(env.get("push")?, &args, 2)?;
1709                    if let Value::List(mut list) = args[0].eval(env)? {
1710                        for arg in &args[1..] {
1711                            list.push(arg.eval(env)?);
1712                        }
1713                        
1714                        Ok(Value::List(list))
1715                    } else {
1716                        Err(Error::InvalidArguments(env.get("push")?, args.clone()))
1717                    }
1718                }),
1719                
1720                "pop" => Value::builtin("pop", |args, env| {
1721                    check_args_len(env.get("pop")?, &args, 1)?;
1722                    match args[0].eval(env)? {
1723                        Value::List(mut list) => {
1724                            Ok(match list.pop() {
1725                                Some(val) => val,
1726                                None => Value::Nil
1727                            })
1728                        }
1729
1730                        Value::String(mut s) => {
1731                            Ok(if let Some(ch) = s.pop() {
1732                                Value::String(ch.to_string())
1733                            } else {
1734                                Value::Nil
1735                            })
1736                        }
1737
1738                        Value::Path(path) => {
1739                            Ok(Value::Path(if let Some(parent) = path.parent() {
1740                                PathBuf::from(parent)
1741                            } else {
1742                                path
1743                            }))
1744                        }
1745
1746                        _ => Err(Error::InvalidArguments(env.get("pop")?, args.clone()))
1747                    }
1748                }),
1749                
1750                "zip" => Value::builtin("zip", |args, env| {
1751                    check_args_len(env.get("zip")?, &args, 2)?;
1752                    match (args[0].eval(env)?, args[1].eval(env)?) {
1753                        (Value::List(a), Value::List(b)) => Ok(Value::List(a.into_iter().zip(b.into_iter()).map(|(a, b)| Value::List(vec![a, b])).collect())),
1754                        _ => Err(Error::InvalidArguments(env.get("zip")?, args.clone()))
1755                    }
1756                }),
1757
1758                "head" => Value::builtin("head", |args, env| {
1759                    check_args_len(env.get("head")?, &args, 1)?;
1760                    if let Value::List(list) = args[0].eval(env)? {
1761                        if list.is_empty() {
1762                            Err(Error::IndexNotFound(Value::List(list), Value::Integer(0)))
1763                        } else {
1764                            Ok(list[0].clone())
1765                        }
1766                    } else {
1767                        Err(Error::InvalidArguments(env.get("head")?, args.clone()))
1768                    }
1769                }),
1770
1771                "tail" => Value::builtin("tail", |args, env| {
1772                    check_args_len(env.get("tail")?, &args, 1)?;
1773                    if let Value::List(list) = args[0].eval(env)? {
1774                        if list.is_empty() {
1775                            Ok(Value::List(vec![]))
1776                        } else {
1777                            Ok(Value::List(list[1..].to_vec()))
1778                        }
1779                    } else {
1780                        Err(Error::InvalidArguments(env.get("tail")?, args.clone()))
1781                    }
1782                }),
1783
1784                "map" => Value::builtin("map", |args, env| {
1785                    check_args_len(env.get("map")?, &args, 2)?;
1786                    let func = args[0].eval(env)?;
1787                    if let Value::List(list) = args[1].eval(env)? {
1788                        let mut result = vec![];
1789                        for item in list {
1790                            result.push(Value::Apply(Box::new(func.clone()), vec![item]).eval(env)?)
1791                        }
1792                        Ok(Value::List(result))
1793                    } else {
1794                        Err(Error::InvalidArguments(env.get("map")?, args.clone()))
1795                    }
1796                }),
1797
1798                "filter" => Value::builtin("filter", |args, env| {
1799                    check_args_len(env.get("filter")?, &args, 2)?;
1800                    let func = args[0].eval(env)?;
1801        
1802                    if let Value::List(list) = args[1].eval(env)? {
1803                        let mut result = vec![];
1804                        for item in list {
1805                            let cond = Value::Apply(Box::new(func.clone()), vec![item.clone()]).eval(env)?;
1806                            if let Value::Boolean(b) = cond {
1807                                if b {
1808                                    result.push(item)
1809                                }
1810                            } else {
1811                                return Err(Error::InvalidCondition(cond))
1812                            }
1813                        }
1814                        Ok(Value::List(result))
1815                    } else {
1816                        Err(Error::InvalidArguments(env.get("map")?, args.clone()))
1817                    }
1818                }),
1819
1820                "reduce" => Value::builtin("reduce", |args, env| {
1821                    check_args_len(env.get("reduce")?, &args, 3)?;
1822                    let func = args[0].eval(env)?;
1823                    let mut acc = args[1].eval(env)?;
1824        
1825                    if let Value::List(list) = args[2].eval(env)? {
1826                        for item in list {
1827                            acc = Value::Apply(Box::new(func.clone()), vec![acc.clone(), item.clone()]).eval(env)?;
1828                        }
1829                        Ok(acc)
1830                    } else {
1831                        Err(Error::InvalidArguments(env.get("reduce")?, args.clone()))
1832                    }
1833                }),
1834
1835                "back" => Value::Macro(vec![], Box::new(Value::Apply(Box::new(Value::Symbol("cd".to_string())), vec![Value::String("..".to_string())]))),
1836
1837                "add" => Value::Lambda(vec!["x".to_string(), "y".to_string()], Box::new(Value::Add(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Symbol("y".to_string())))), Self::new()),
1838                "mul" => Value::Lambda(vec!["x".to_string(), "y".to_string()], Box::new(Value::Multiply(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Symbol("y".to_string())))), Self::new()),
1839                "sub" => Value::Lambda(vec!["x".to_string(), "y".to_string()], Box::new(Value::Subtract(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Symbol("y".to_string())))), Self::new()),
1840                "div" => Value::Lambda(vec!["x".to_string(), "y".to_string()], Box::new(Value::Divide(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Symbol("y".to_string())))), Self::new()),
1841                "rem" => Value::Lambda(vec!["x".to_string(), "y".to_string()], Box::new(Value::Remainder(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Symbol("y".to_string())))), Self::new()),
1842
1843                "sum" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Apply(Box::new(Value::Symbol("reduce".to_string())), vec![Value::Symbol("add".to_string()), Value::Integer(0), Value::Symbol("x".to_string())])), Self::new()),
1844                "prod" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Apply(Box::new(Value::Symbol("reduce".to_string())), vec![Value::Symbol("mul".to_string()), Value::Integer(1), Value::Symbol("x".to_string())])), Self::new()),
1845
1846                "inc" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Add(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(1)))), Self::new()),
1847                "dec" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Subtract(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(1)))), Self::new()),
1848                
1849                "double" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Multiply(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(2)))), Self::new()),
1850                "triple" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Multiply(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(3)))), Self::new()),
1851                "quadruple" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Multiply(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(4)))), Self::new()),
1852                "quintuple" => Value::Lambda(vec!["x".to_string()], Box::new(Value::Multiply(Box::new(Value::Symbol("x".to_string())), Box::new(Value::Integer(5)))), Self::new()),
1853
1854                x => {
1855                    for t in TYPES {
1856                        if x == get_os_name(t) {
1857                            return Ok(Value::String(x.to_lowercase()))
1858                        } else if x == get_os_family(t) {
1859                            return Ok(Value::String(x.to_lowercase()))
1860                        }
1861                    }
1862
1863                    return Err(Error::SymbolNotDefined(name.clone()))
1864                }
1865            })
1866        }
1867    }
1868
1869    pub fn get_pics_dir(&self) -> Result<PathBuf, Error> {
1870        dirs::picture_dir().ok_or(Error::PicturesDirectoryNotFound)
1871    }
1872
1873    pub fn get_vids_dir(&self) -> Result<PathBuf, Error> {
1874        dirs::video_dir().ok_or(Error::VideosDirectoryNotFound)
1875    }
1876
1877    pub fn get_down_dir(&self) -> Result<PathBuf, Error> {
1878        dirs::download_dir().ok_or(Error::DownloadsDirectoryNotFound)
1879    }
1880
1881    pub fn get_desk_dir(&self) -> Result<PathBuf, Error> {
1882        dirs::desktop_dir().ok_or(Error::DesktopDirectoryNotFound)
1883    }
1884
1885    pub fn get_docs_dir(&self) -> Result<PathBuf, Error> {
1886        dirs::document_dir().ok_or(Error::DocumentsDirectoryNotFound)
1887    }
1888
1889    pub fn get_home_dir(&self) -> Result<PathBuf, Error> {
1890        dirs::home_dir().ok_or(Error::HomeDirectoryNotFound)
1891    }
1892
1893    pub fn get_cwd(&self) -> Result<PathBuf, Error> {
1894        if let Ok(Value::Path(result)) = self.get(CWD) {
1895            Ok(result)
1896        } else {
1897            Ok(PathBuf::from(self.get_home_dir()?))
1898        }
1899    }
1900
1901    pub(crate) fn get_symbols(&self) -> &BTreeMap<String, Value> {
1902        &self.symbols
1903    }
1904
1905    pub fn is_defined(&self, name: &String) -> bool {
1906        self.symbols.contains_key(name)
1907    }
1908
1909    pub fn define(&mut self, name: impl ToString, value: Value) {
1910        self.symbols.insert(name.to_string(), value);
1911    }
1912
1913    pub fn combine(&self, other: &Self) -> Self {
1914        let mut result = self.clone();
1915        result.symbols.extend(other.symbols.clone());
1916        result
1917    }
1918}