main.rs 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. fs::{read_to_string, File},
  20. io::Write,
  21. process::ExitCode,
  22. };
  23. use arg::Args;
  24. use darkfi::{
  25. zkas::{Analyzer, Compiler, Lexer, Parser, ZkBinary},
  26. ANSI_LOGO,
  27. };
  28. const ABOUT: &str =
  29. concat!("zkas ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION"));
  30. const USAGE: &str = r#"
  31. Usage: zkas [OPTIONS] <INPUT>
  32. Arguments:
  33. <INPUT> ZK script to compile
  34. Options:
  35. -o <FILE> Place the output into <FILE>
  36. -s Strip debug symbols
  37. -p Preprocess only; do not compile
  38. -i Interactive semantic analysis
  39. -e Examine decoded bytecode
  40. -h Print this help
  41. "#;
  42. fn usage() {
  43. print!("{}{}\n{}", ANSI_LOGO, ABOUT, USAGE);
  44. }
  45. fn main() -> ExitCode {
  46. let argv;
  47. let mut pflag = false;
  48. let mut iflag = false;
  49. let mut eflag = false;
  50. let mut sflag = false;
  51. let mut hflag = false;
  52. let mut output = String::new();
  53. {
  54. let mut args = Args::new().with_cb(|args, flag| match flag {
  55. 'p' => pflag = true,
  56. 'i' => iflag = true,
  57. 'e' => eflag = true,
  58. 's' => sflag = true,
  59. 'o' => output = args.eargf().to_string(),
  60. _ => hflag = true,
  61. });
  62. argv = args.parse();
  63. }
  64. if hflag || argv.is_empty() {
  65. usage();
  66. return ExitCode::FAILURE
  67. }
  68. let filename = argv[0].as_str();
  69. let source = match read_to_string(filename) {
  70. Ok(v) => v,
  71. Err(e) => {
  72. eprintln!("Error: Failed reading from \"{}\". {}", filename, e);
  73. return ExitCode::FAILURE
  74. }
  75. };
  76. // Clean up tabs, and convert CRLF to LF.
  77. let source = source.replace('\t', " ").replace("\r\n", "\n");
  78. // ANCHOR: zkas
  79. // The lexer goes over the input file and separates its content into
  80. // tokens that get fed into a parser.
  81. let lexer = Lexer::new(filename, source.chars());
  82. let tokens = match lexer.lex() {
  83. Ok(v) => v,
  84. Err(_) => return ExitCode::FAILURE,
  85. };
  86. // The parser goes over the tokens provided by the lexer and builds
  87. // the initial AST, not caring much about the semantics, just enforcing
  88. // syntax and general structure.
  89. let parser = Parser::new(filename, source.chars(), tokens);
  90. let (namespace, k, constants, witnesses, statements) = match parser.parse() {
  91. Ok(v) => v,
  92. Err(_) => return ExitCode::FAILURE,
  93. };
  94. // The analyzer goes through the initial AST provided by the parser and
  95. // converts return and variable types to their correct forms, and also
  96. // checks that the semantics of the ZK script are correct.
  97. let mut analyzer = Analyzer::new(filename, source.chars(), constants, witnesses, statements);
  98. if analyzer.analyze_types().is_err() {
  99. return ExitCode::FAILURE
  100. }
  101. if iflag && analyzer.analyze_semantic().is_err() {
  102. return ExitCode::FAILURE
  103. }
  104. if pflag {
  105. println!("{:#?}", analyzer.constants);
  106. println!("{:#?}", analyzer.witnesses);
  107. println!("{:#?}", analyzer.statements);
  108. println!("{:#?}", analyzer.heap);
  109. return ExitCode::SUCCESS
  110. }
  111. let compiler = Compiler::new(
  112. filename,
  113. source.chars(),
  114. namespace,
  115. k,
  116. analyzer.constants,
  117. analyzer.witnesses,
  118. analyzer.statements,
  119. analyzer.literals,
  120. !sflag,
  121. );
  122. let bincode = match compiler.compile() {
  123. Ok(v) => v,
  124. Err(_) => return ExitCode::FAILURE,
  125. };
  126. // ANCHOR_END: zkas
  127. let output = if output.is_empty() { format!("{}.bin", filename) } else { output };
  128. let mut file = match File::create(&output) {
  129. Ok(v) => v,
  130. Err(e) => {
  131. eprintln!("Error: Failed to create \"{}\". {}", output, e);
  132. return ExitCode::FAILURE
  133. }
  134. };
  135. if let Err(e) = file.write_all(&bincode) {
  136. eprintln!("Error: Failed to write bincode to \"{}\". {}", output, e);
  137. return ExitCode::FAILURE
  138. };
  139. println!("Wrote output to {}", &output);
  140. if eflag {
  141. let zkbin = ZkBinary::decode(&bincode).unwrap();
  142. println!("{:#?}", zkbin);
  143. }
  144. ExitCode::SUCCESS
  145. }