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 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}