tvix/
main.rs

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
15/// Interpret the given code snippet, but only run the Tvix compiler
16/// on it and return errors and warnings.
17fn 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    // inform the caller about any errors
54    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, // TODO(aspen): Pass in --arg/--argstr here
72            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}