/ cab / main.rs
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  }