From 37b7a3bbf0a4a779923e2d1dcf485f90ee4e147d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 2 Mar 2023 12:15:54 +0100 Subject: [PATCH] Commandline access to asm compiler. --- Cargo.toml | 13 +++--- src/bin/compiler.rs | 97 +++++++++++++++++++++++++++++++++++--------- src/compiler.rs | 49 +++++++++++++++++++++- tests/integration.rs | 13 +++--- 4 files changed, 138 insertions(+), 34 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1c5434847..b0fbf7b9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,13 @@ edition = "2021" build = "build.rs" [dependencies] -codespan-reporting = "0.11.1" -json = "0.12.4" -lalrpop-util = {version = "0.19.8", features = ["lexer"]} +clap = { version = "^4.1", features = ["derive"] } +codespan-reporting = "^0.11" +itertools = "^0.10" +json = "^0.12" +lalrpop-util = {version = "^0.19", features = ["lexer"]} mktemp = "0.5.0" -num-bigint = "0.4.3" -regex = "1" +num-bigint = "^0.4" [build-dependencies] -lalrpop = "0.19.8" +lalrpop = "^0.19" diff --git a/src/bin/compiler.rs b/src/bin/compiler.rs index 6c03b1081..be18028aa 100644 --- a/src/bin/compiler.rs +++ b/src/bin/compiler.rs @@ -1,27 +1,84 @@ -use std::{env, fs, path::Path}; - +use clap::{Parser, Subcommand}; use powdr::compiler::no_callback; +use powdr::number::AbstractNumberType; +use std::{fs, path::Path}; + +#[derive(Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Compiles assembly to PIL and generates fixed and witness columns. + Asm { + /// Input file + file: String, + + /// Comma-separated list of free inputs (numbers). + #[arg(short, long)] + inputs: String, + + /// Output directory for PIL file, json file and fixed and witness column data. + #[arg(short, long)] + #[arg(default_value_t = String::from("."))] + output_directory: String, + + /// Force overwriting of PIL output file. + #[arg(short, long)] + #[arg(default_value_t = false)] + force: bool, + }, + + /// Parses and prints the PIL file on stdout. + Reformat { + /// Input file + file: String, + }, + + /// Compiles the PIL file to json and generates fixed and witness columns. + Compile { + /// Input file + file: String, + /// Output directory for json file and fixed and witness column data. + #[arg(short, long)] + #[arg(default_value_t = String::from("."))] + output_directory: String, + }, +} fn main() { - if env::args().nth(1).unwrap() == "--asm" { - let file_name = env::args().nth(2).unwrap(); - let contents = fs::read_to_string(Path::new(&file_name)).unwrap(); - match powdr::asm_compiler::compile(Some(&file_name), &contents) { - Ok(pil) => println!("{pil}"), - Err(err) => err.output_to_stderr(), + match Cli::parse().command { + Commands::Asm { + file, + inputs, + output_directory, + force, + } => { + let inputs = inputs + .split(',') + .map(|x| x.parse().unwrap()) + .collect::>(); + powdr::compiler::compile_asm(&file, inputs, Path::new(&output_directory), force); } - } else if env::args().nth(1).unwrap() == "--reformat" { - let file_name = env::args().nth(2).unwrap(); - let contents = fs::read_to_string(Path::new(&file_name)).unwrap(); - match powdr::parser::parse(Some(&file_name), &contents) { - Ok(ast) => println!("{ast}"), - Err(err) => err.output_to_stderr(), + Commands::Reformat { file } => { + let contents = fs::read_to_string(&file).unwrap(); + match powdr::parser::parse(Some(&file), &contents) { + Ok(ast) => println!("{ast}"), + Err(err) => err.output_to_stderr(), + } + } + Commands::Compile { + file, + output_directory, + } => { + powdr::compiler::compile_pil( + Path::new(&file), + Path::new(&output_directory), + no_callback(), + ); } - } else { - powdr::compiler::compile_pil( - Path::new(&env::args().nth(1).unwrap()), - &env::current_dir().unwrap(), - no_callback(), - ); } } diff --git a/src/compiler.rs b/src/compiler.rs index ee8dc8011..a30c26ae6 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -2,11 +2,12 @@ use std::fs; use std::io::{BufWriter, Write}; use std::path::Path; +use itertools::Itertools; use num_bigint::Sign; use crate::number::{abstract_to_degree, AbstractNumberType, DegreeType}; use crate::parser::ast::PILFile; -use crate::{analyzer, commit_evaluator, constant_evaluator, json_exporter}; +use crate::{analyzer, asm_compiler, commit_evaluator, constant_evaluator, json_exporter}; pub fn no_callback() -> Option Option> { None @@ -45,6 +46,52 @@ pub fn compile_pil_ast( ) } +/// Compiles a .asm file, outputs the PIL on stdout and tries to generate +/// fixed and witness columns. +pub fn compile_asm( + file_name: &str, + inputs: Vec, + output_dir: &Path, + force_overwrite: bool, +) { + let contents = fs::read_to_string(file_name).unwrap(); + let pil = asm_compiler::compile(Some(file_name), &contents).unwrap(); + let pil_file_name = output_dir.join(format!( + "{}.pil", + Path::new(file_name).file_stem().unwrap().to_str().unwrap() + )); + if pil_file_name.exists() && !force_overwrite { + eprint!( + "Target file {} already exists. Not overwriting.", + pil_file_name.to_str().unwrap() + ); + return; + } + fs::write(pil_file_name.clone(), format!("{pil}")).unwrap(); + + let query_callback = |query: &str| -> Option { + let items = query.split(',').map(|s| s.trim()).collect::>(); + let mut it = items.iter(); + let _current_step = it.next().unwrap(); + let current_pc = it.next().unwrap(); + assert!(it.clone().len() % 3 == 0); + for (pc_check, input, index) in it.tuples() { + if pc_check == current_pc { + assert_eq!(*input, "\"input\""); + let index: usize = index.parse().unwrap(); + return inputs.get(index).cloned(); + } + } + None + }; + compile_pil_ast( + &pil, + pil_file_name.to_str().unwrap(), + output_dir, + Some(query_callback), + ); +} + fn compile( analyzed: &analyzer::Analyzed, file_name: &str, diff --git a/tests/integration.rs b/tests/integration.rs index ea356b607..81dcb03bc 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,5 +1,6 @@ use std::{fs, path::Path, process::Command}; +use itertools::Itertools; use powdr::compiler; use powdr::number::AbstractNumberType; @@ -31,14 +32,12 @@ fn verify_asm(file_name: &str, inputs: Vec) { let mut it = items.iter(); let _current_step = it.next().unwrap(); let current_pc = it.next().unwrap(); - while let Some(pc_check) = it.next() { + assert!(it.clone().len() % 3 == 0); + for (pc_check, input, index) in it.tuples() { if pc_check == current_pc { - assert_eq!(*it.next().unwrap(), "\"input\""); - let index: usize = it.next().map(|s| s.parse().unwrap()).unwrap(); - return Some(inputs[index].clone()); - } else { - it.next(); - it.next(); + assert_eq!(*input, "\"input\""); + let index: usize = index.parse().unwrap(); + return inputs.get(index).cloned(); } } None