/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2023 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
use std::{
fs::{read_to_string, File},
io::Write,
process::exit,
};
use clap::Parser as ClapParser;
use darkfi::{
cli_desc,
zkas::{Analyzer, Compiler, Lexer, Parser, ZkBinary},
};
#[derive(clap::Parser)]
#[clap(name = "zkas", about = cli_desc!(), version)]
struct Args {
/// Place the output into ``
#[clap(short = 'o', value_name = "FILE")]
output: Option,
/// Strip debug symbols
#[clap(short = 's')]
strip: bool,
/// Preprocess only; do not compile
#[clap(short = 'E')]
evaluate: bool,
/// Interactive semantic analysis
#[clap(short = 'i')]
interactive: bool,
/// Examine decoded bytecode
#[clap(short = 'e')]
examine: bool,
/// ZK script to compile
input: String,
}
fn main() {
let args = Args::parse();
let filename = args.input.as_str();
let source = match read_to_string(filename) {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed reading from \"{}\". {}", filename, e);
exit(1);
}
};
// Clean up tabs, and convert CRLF to LF.
let source = source.replace('\t', " ").replace("\r\n", "\n");
// ANCHOR: zkas
// The lexer goes over the input file and separates its content into
// tokens that get fed into a parser.
let lexer = Lexer::new(filename, source.chars());
let tokens = lexer.lex();
// The parser goes over the tokens provided by the lexer and builds
// the initial AST, not caring much about the semantics, just enforcing
// syntax and general structure.
let parser = Parser::new(filename, source.chars(), tokens);
let (namespace, constants, witnesses, statements) = parser.parse();
// The analyzer goes through the initial AST provided by the parser and
// converts return and variable types to their correct forms, and also
// checks that the semantics of the ZK script are correct.
let mut analyzer = Analyzer::new(filename, source.chars(), constants, witnesses, statements);
analyzer.analyze_types();
if args.interactive {
analyzer.analyze_semantic();
}
if args.evaluate {
println!("{:#?}", analyzer.constants);
println!("{:#?}", analyzer.witnesses);
println!("{:#?}", analyzer.statements);
println!("{:#?}", analyzer.heap);
exit(0);
}
let compiler = Compiler::new(
filename,
source.chars(),
namespace,
analyzer.constants,
analyzer.witnesses,
analyzer.statements,
analyzer.literals,
!args.strip,
);
let bincode = compiler.compile();
// ANCHOR_END: zkas
let output = match args.output {
Some(o) => o,
None => format!("{}.bin", args.input),
};
let mut file = match File::create(&output) {
Ok(v) => v,
Err(e) => {
eprintln!("Error: Failed to create \"{}\". {}", output, e);
exit(1);
}
};
if let Err(e) = file.write_all(&bincode) {
eprintln!("Error: Failed to write bincode to \"{}\". {}", output, e);
exit(1);
};
println!("Wrote output to {}", &output);
if args.examine {
let zkbin = ZkBinary::decode(&bincode).unwrap();
println!("{:#?}", zkbin);
}
}