use std::{
fs::{read_to_string, File},
io::Write,
process::ExitCode,
};
use arg::Args;
use darkfi::{
zkas::{Analyzer, Compiler, Lexer, Parser, ZkBinary},
ANSI_LOGO,
};
const ABOUT: &str =
concat!("zkas ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION"));
const USAGE: &str = r#"
Usage: zkas [OPTIONS] <INPUT>
Arguments:
<INPUT> ZK script to compile
Options:
-o <FILE> Place the output into <FILE>
-s Strip debug symbols
-p Preprocess only; do not compile
-i Interactive semantic analysis
-e Examine decoded bytecode
-h Print this help
"#;
fn usage() {
print!("{}{}\n{}", ANSI_LOGO, ABOUT, USAGE);
}
fn main() -> ExitCode {
let argv;
let mut pflag = false;
let mut iflag = false;
let mut eflag = false;
let mut sflag = false;
let mut hflag = false;
let mut output = String::new();
{
let mut args = Args::new().with_cb(|args, flag| match flag {
'p' => pflag = true,
'i' => iflag = true,
'e' => eflag = true,
's' => sflag = true,
'o' => output = args.eargf().to_string(),
_ => hflag = true,
});
argv = args.parse();
}
if hflag || argv.is_empty() {
usage();
return ExitCode::FAILURE
}
let filename = argv[0].as_str();
let source = match read_to_string(filename) {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed reading from \"{}\". {}", filename, e);
return ExitCode::FAILURE
}
};
let source = source.replace('\t', " ").replace("\r\n", "\n");
let lexer = Lexer::new(filename, source.chars());
let tokens = match lexer.lex() {
Ok(v) => v,
Err(_) => return ExitCode::FAILURE,
};
let parser = Parser::new(filename, source.chars(), tokens);
let (namespace, k, constants, witnesses, statements) = match parser.parse() {
Ok(v) => v,
Err(_) => return ExitCode::FAILURE,
};
let mut analyzer = Analyzer::new(filename, source.chars(), constants, witnesses, statements);
if analyzer.analyze_types().is_err() {
return ExitCode::FAILURE
}
if iflag && analyzer.analyze_semantic().is_err() {
return ExitCode::FAILURE
}
if pflag {
println!("{:#?}", analyzer.constants);
println!("{:#?}", analyzer.witnesses);
println!("{:#?}", analyzer.statements);
println!("{:#?}", analyzer.heap);
return ExitCode::SUCCESS
}
let compiler = Compiler::new(
filename,
source.chars(),
namespace,
k,
analyzer.constants,
analyzer.witnesses,
analyzer.statements,
analyzer.literals,
!sflag,
);
let bincode = match compiler.compile() {
Ok(v) => v,
Err(_) => return ExitCode::FAILURE,
};
let output = if output.is_empty() { format!("{}.bin", filename) } else { output };
let mut file = match File::create(&output) {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed to create \"{}\". {}", output, e);
return ExitCode::FAILURE
}
};
if let Err(e) = file.write_all(&bincode) {
eprintln!("Error: Failed to write bincode to \"{}\". {}", output, e);
return ExitCode::FAILURE
};
println!("Wrote output to {}", &output);
if eflag {
let zkbin = ZkBinary::decode(&bincode).unwrap();
println!("{:#?}", zkbin);
}
ExitCode::SUCCESS
}