1use clap::Parser;
2use mimalloc::MiMalloc;
3use std::rc::Rc;
4use std::{fs, path::PathBuf};
5use tvix_cli::args::Args;
6use tvix_cli::repl::Repl;
7use tvix_cli::{init_io_handle, interpret, AllowIncomplete};
8use tvix_eval::observer::DisassemblingObserver;
9use tvix_eval::EvalMode;
10use tvix_glue::tvix_store_io::TvixStoreIO;
11
12#[global_allocator]
13static GLOBAL: MiMalloc = MiMalloc;
14
15fn lint(code: &str, path: Option<PathBuf>, args: &Args) -> bool {
18 let mut eval_builder = tvix_eval::Evaluation::builder_impure();
19
20 if args.strict {
21 eval_builder = eval_builder.mode(EvalMode::Strict);
22 }
23
24 let source_map = eval_builder.source_map().clone();
25
26 let mut compiler_observer = DisassemblingObserver::new(source_map.clone(), std::io::stderr());
27
28 if args.dump_bytecode {
29 eval_builder.set_compiler_observer(Some(&mut compiler_observer));
30 }
31
32 if args.trace_runtime {
33 eprintln!("warning: --trace-runtime has no effect with --compile-only!");
34 }
35
36 let eval = eval_builder.build();
37 let result = eval.compile_only(code, path);
38
39 if args.display_ast {
40 if let Some(ref expr) = result.expr {
41 eprintln!("AST: {}", tvix_eval::pretty_print_expr(expr));
42 }
43 }
44
45 for error in &result.errors {
46 error.fancy_format_stderr();
47 }
48
49 for warning in &result.warnings {
50 warning.fancy_format_stderr(&source_map);
51 }
52
53 result.errors.is_empty()
55}
56
57fn main() {
58 let args = Args::parse();
59 let io_handle = init_io_handle(&args);
60
61 if let Some(file) = &args.script {
62 run_file(io_handle, file.clone(), &args)
63 } else if let Some(expr) = &args.expr {
64 if !interpret(
65 io_handle,
66 expr,
67 None,
68 &args,
69 false,
70 AllowIncomplete::RequireComplete,
71 None, None,
73 None,
74 )
75 .unwrap()
76 .finalize()
77 {
78 std::process::exit(1);
79 }
80 } else {
81 let mut repl = Repl::new(io_handle, &args);
82 repl.run()
83 }
84}
85
86fn run_file(io_handle: Rc<TvixStoreIO>, mut path: PathBuf, args: &Args) {
87 if path.is_dir() {
88 path.push("default.nix");
89 }
90 let contents = fs::read_to_string(&path).expect("failed to read the input file");
91
92 let success = if args.compile_only {
93 lint(&contents, Some(path), args)
94 } else {
95 interpret(
96 io_handle,
97 &contents,
98 Some(path),
99 args,
100 false,
101 AllowIncomplete::RequireComplete,
102 None,
103 None,
104 None,
105 )
106 .unwrap()
107 .finalize()
108 };
109
110 if !success {
111 std::process::exit(1);
112 }
113}