main.rs
1 use std::{ 2 fmt::Write as _, 3 sync::Arc, 4 }; 5 6 use cab::{ 7 runtime, 8 syntax, 9 }; 10 use clap::Parser as _; 11 use cyn::ResultExt as _; 12 use dup::Dupe as _; 13 use ranged::Span; 14 use rpds::ListSync as List; 15 use runtime::{ 16 Value, 17 value, 18 }; 19 use ust::{ 20 COLORS, 21 Display as _, 22 report, 23 style::StyledExt as _, 24 terminal, 25 write, 26 }; 27 28 #[derive(clap::Parser)] 29 #[command(version, about)] 30 struct Cli { 31 /// Print the result of every `Language.tokenize` call. 32 #[arg(long, default_value = "false")] 33 dump_token: DumpToken, 34 35 /// Print the result of every `Language.parse` call. 36 #[arg(long, default_value = "false")] 37 dump_syntax: bool, 38 39 /// Print the result of every `Language.compile` call. 40 #[arg(long, default_value = "false")] 41 dump_code: bool, 42 43 /// The expression to `evaluate`. 44 expression: Vec<String>, 45 } 46 47 #[derive(clap::ValueEnum, Debug, Clone, Copy)] 48 enum DumpToken { 49 False, 50 True, 51 Color, 52 } 53 54 #[tokio::main] 55 async fn main() -> cyn::Termination { 56 let cli = Cli::parse(); 57 58 let out = &mut terminal::stdout(); 59 let err = &mut terminal::stderr(); 60 61 let expression = match &*cli.expression { 62 &[] => unimplemented!("repl"), 63 parts => parts.join(" "), 64 }; 65 66 let path = value::Path::new() 67 .root(Arc::new(value::path::blob(Value::from( 68 value::SString::from(&*expression), 69 )))) 70 .subpath(List::new_sync()); 71 72 // TODO: position_cache in Path. 73 let source = path.read().await?.to_vec(); 74 let source = String::from_utf8(source).expect("source was created from UTF-8 string"); 75 let source = report::PositionStr::new(&source); 76 77 let parse_oracle = syntax::ParseOracle::new(); 78 let parse = parse_oracle.parse(syntax::tokenize(&source).inspect(|&(kind, slice)| { 79 match cli.dump_token { 80 DumpToken::False => {}, 81 DumpToken::True => { 82 writeln!(out, "{kind:?} {slice:?}").expect("TODO move this inside the runtime"); 83 }, 84 DumpToken::Color => { 85 let style = COLORS[kind as usize]; 86 87 write(out, &slice.style(style)).expect("TODO move inside the runtime"); 88 }, 89 } 90 })); 91 92 if let DumpToken::True | DumpToken::Color = cli.dump_token { 93 writeln!(out).expect("TODO move inside the runtime"); 94 } 95 96 if cli.dump_syntax { 97 // The Display of this already has a newline. So use write! instead. 98 write!(out, "{node:#?}", node = &parse.node).expect("TODO move inside the runtime"); 99 } 100 101 let expression = parse.extractlnln(err, &path, &source)?; 102 103 let compile_oracle = runtime::CompileOracle::new(); 104 let code = compile_oracle 105 .compile(expression.as_ref()) 106 .path(path.dupe()) 107 .extractlnln(err, &path, &source)?; 108 109 if cli.dump_code { 110 code 111 .display_styled(out) 112 .expect("TODO move inside the runtime"); 113 writeln!(out).expect("TODO move inside the runtime"); 114 } 115 116 let thunk = value::Thunk::suspended(Arc::new(code)) 117 .scopes(List::new_sync().push_front(value::attributes::new! { 118 "foo": Value::from(value::string::new!("AAAA")), 119 "bar": Value::from(value::attributes::new! { 120 "baz": Value::Boolean(false), 121 }), 122 "true": Value::Boolean(true), 123 "false": Value::Boolean(false), 124 // "fee": Value::from(value::Thunk::suspended_native(|| { 125 // eprintln!("[BACKGROUND PROCESS] Selling personal data to mastercard..."); 126 // Value::from(value::string::new!("Your transaction has been successful.")) 127 // })), 128 })) 129 .is_lambda(false) 130 .location(value::Location::new(path, Span::at(0_u32, source.len()))); 131 132 thunk 133 .force(&runtime::State { 134 parse_oracle, 135 compile_oracle, 136 }) 137 .await; 138 139 let (_, value) = thunk 140 .get() 141 .await 142 .expect("thunk must have value after forcing"); 143 144 value 145 .display_styled(out) 146 .chain_err("failed to display value")?; 147 148 cyn::Termination::success() 149 } 150 151 #[cfg(test)] 152 mod tests { 153 use clap::CommandFactory as _; 154 155 use super::*; 156 157 #[test] 158 fn cli() { 159 Cli::command().debug_assert(); 160 } 161 }