diff --git a/.github/workflows/acl2.yml b/.github/workflows/acl2.yml new file mode 100644 index 0000000000..d9fc9c4af4 --- /dev/null +++ b/.github/workflows/acl2.yml @@ -0,0 +1,95 @@ +name: Leo-ACL2 +on: + pull_request: + push: + branches: + - master + - staging + - trying + paths-ignore: + - 'docs/**' + - 'documentation/**' +env: + RUST_BACKTRACE: 1 + +# This job can only be run on linux (Ubuntu) +jobs: + acl2: + name: leo-acl2 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - name: Generate asts + run: | + cargo -q run -p leo-test-framework --bin tgc + + # Pull the latest release from the leo-acl2-bin repo, and put it into the + # repo/acl2 directory. After it's done, unpack the tgz file locally. + - name: Pull tgc executable + run: | + mkdir acl2 && cd acl2; + wget $(curl -s https://api.github.com/repos/AleoHQ/leo-acl2-bin/releases/latest \ + | grep "browser_download_url.*.tgz" \ + | cut -d : -f 2,3 \ + | tr -d \" \ + | xargs) + + tar -xvzf $(ls) + + # Using the prepared ASTs and the pulled and unzipped tgc run theorem generation. + - name: Run tgc over ASTs + run: | + canonicalization_errors=(); + type_inference_errors=(); + for dir in `ls tmp/tgc`; + do + cd tmp/tgc/$dir; # enter the directory + ./../../../acl2/tgc canonicalization initial_ast.json canonicalization_ast.json canonicalization-theorem.lisp > canonicalization_result.out || canonicalization_errors+=("$dir"); + # Disabling Type inference for now + # ./../../../acl2/tgc type-inference canonicalization_ast.json type_inferenced_ast.json type-inference-theorem.lisp > type_inference_result.out || type_inference_errors+=("$dir"); + cd ../../.. + done; + + if [ ${#canonicalization_errors[@]} -eq 0 ]; then + echo "Canonicalization - Success!" + else + echo "Canonicalization Failures:"; + for dir in ${canonicalization_errors[@]}; + do + echo $dir; + done; + + echo "Attaching logs:" + for dir in ${canonicalization_errors[@]}; + do + cat tmp/tgc/$dir/canonicalization_result.out + done; + exit 1 + fi + + if [ ${#type_inference_errors[@]} -eq 0 ]; then + echo "Type Inference - Success!" + else + echo "Type Inference Failures:"; + for dir in ${type_inference_errors[@]}; + do + echo $dir; + done; + + echo "Attaching logs:" + for dir in ${type_inference_errors[@]}; + do + cat tmp/tgc/$dir/type_inference_result.out + done; + + exit 1 + fi diff --git a/Cargo.lock b/Cargo.lock index a67fd805ed..d76817cbf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1425,9 +1425,15 @@ dependencies = [ name = "leo-test-framework" version = "1.5.3" dependencies = [ + "leo-asg", + "leo-ast", + "leo-compiler", + "leo-imports", + "leo-parser", "serde", "serde_json", "serde_yaml", + "structopt", ] [[package]] diff --git a/FORMAT_ABNF_GRAMMER.md b/FORMAT_ABNF_GRAMMER.md deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/ast/src/reducer/canonicalization.rs b/ast/src/reducer/canonicalization.rs index 63bf895b59..1f2ec23b74 100644 --- a/ast/src/reducer/canonicalization.rs +++ b/ast/src/reducer/canonicalization.rs @@ -291,7 +291,7 @@ impl Canonicalizer { return Expression::Identifier(self.circuit_name.as_ref().unwrap().clone()); } } - _ => {} + _ => (), } expression.clone() diff --git a/test-framework/.gitignore b/test-framework/.gitignore new file mode 100644 index 0000000000..ef4cd56d9d --- /dev/null +++ b/test-framework/.gitignore @@ -0,0 +1 @@ +!src/bin diff --git a/test-framework/Cargo.toml b/test-framework/Cargo.toml index fa5440225b..b471480d24 100644 --- a/test-framework/Cargo.toml +++ b/test-framework/Cargo.toml @@ -26,3 +26,28 @@ version = "1.0" [dependencies.serde_yaml] version = "0.8" + +# List of dependencies for tgc binary; + +[dependencies.leo-ast] +path = "../ast" +version = "1.5.2" + +[dependencies.leo-parser] +path = "../parser" +version = "1.5.2" + +[dependencies.leo-imports] +path = "../imports" +version = "1.5.2" + +[dependencies.leo-asg] +path = "../asg" +version = "1.5.2" + +[dependencies.leo-compiler] +path = "../compiler" +version = "1.5.2" + +[dependencies.structopt] +version = "0.3" diff --git a/test-framework/src/bin/tgc.rs b/test-framework/src/bin/tgc.rs new file mode 100644 index 0000000000..fc62bcb413 --- /dev/null +++ b/test-framework/src/bin/tgc.rs @@ -0,0 +1,161 @@ +// Copyright (C) 2019-2021 Aleo Systems Inc. +// This file is part of the Leo library. + +// The Leo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// The Leo library 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with the Leo library. If not, see . + +use leo_asg::Asg; +use leo_compiler::{compiler::thread_leaked_context, TypeInferencePhase}; +use leo_imports::ImportParser; +use leo_test_framework::{ + fetch::find_tests, + test::{extract_test_config, TestExpectationMode as Expectation}, +}; + +use std::{error::Error, fs, path::PathBuf}; +use structopt::{clap::AppSettings, StructOpt}; + +#[derive(StructOpt)] +#[structopt(name = "ast-stages-generator", author = "The Aleo Team ", setting = AppSettings::ColoredHelp)] +struct Opt { + #[structopt( + short, + long, + help = "Path to the output folder (auto generated)", + default_value = "tmp/tgc" + )] + path: PathBuf, + + #[structopt(short, long, help = "Run only for test that match pattern")] + filter: Option, + + #[structopt(short, long, help = "Skip tests matching pattern")] + skip: Option>, +} + +fn main() { + handle_error(run_with_args(Opt::from_args())); +} + +fn run_with_args(opt: Opt) -> Result<(), Box> { + // Variable that stores all the tests. + let mut tests = Vec::new(); + let mut test_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + test_dir.push("../tests/"); + find_tests(&test_dir, &mut tests); + + if !opt.path.exists() { + fs::create_dir_all(&opt.path)?; + } + + // Prepare directory for placing results. + 'main_loop: for (index, (path, text)) in tests.iter().enumerate() { + if let Some(config) = extract_test_config(text) { + // Skip namespaces that we don't need; also skip failure tests. + if config.namespace != "Compile" || config.expectation == Expectation::Fail { + continue; + } + + let mut test_name = path + .split("tests/") + .last() + .unwrap() + .replace(std::path::MAIN_SEPARATOR, "_"); + + // Filter out the tests that do not match pattern, if pattern is set. + if let Some(filter) = &opt.filter { + if !test_name.contains(filter) { + continue; + } + } + + // If skip flag is used, don't run tests matching the pattern. + if let Some(skips) = &opt.skip { + for skip_pattern in skips { + if test_name.contains(skip_pattern) { + println!("Skipping: {} because of {}", test_name, skip_pattern); + continue 'main_loop; + } + } + } + + test_name.push_str(&format!("_{}", index)); + + // Create directory for this file. + let mut target = PathBuf::from("tmp/tgc"); + target.push(test_name); + + if !target.exists() { + fs::create_dir_all(target.clone())?; + } + + let cwd = config + .extra + .get("cwd") + .map(|val| { + let mut cwd = PathBuf::from(path); + cwd.pop(); + cwd.join(&val.as_str().unwrap()) + }) + .unwrap_or(PathBuf::from(path)); + + // Write all files into the directory. + let (initial, canonicalized, type_inferenced) = generate_asts(cwd, text)?; + + target.push("initial_ast.json"); + fs::write(target.clone(), initial)?; + target.pop(); + + target.push("canonicalization_ast.json"); + fs::write(target.clone(), canonicalized)?; + target.pop(); + + target.push("type_inferenced_ast.json"); + fs::write(target.clone(), type_inferenced)?; + } + } + + Ok(()) +} + +/// Do what Compiler does - prepare 3 stages of AST: initial, canonicalized and type_inferenced +fn generate_asts(path: PathBuf, text: &String) -> Result<(String, String, String), Box> { + let mut ast = leo_parser::parse_ast(path.clone().into_os_string().into_string().unwrap(), text)?; + let initial = ast.to_json_string()?; + + ast.canonicalize()?; + let canonicalized = ast.to_json_string()?; + + let asg = Asg::new( + thread_leaked_context(), + &ast, + &mut ImportParser::new(path, Default::default()), + )?; + + let type_inferenced = TypeInferencePhase::default() + .phase_ast(&ast.into_repr(), &asg.clone().into_repr()) + .expect("Failed to produce type inference ast.") + .to_json_string()?; + + Ok((initial, canonicalized, type_inferenced)) +} + +fn handle_error(res: Result<(), Box>) { + match res { + Ok(_) => (), + Err(err) => { + eprintln!("Error: {}", err); + std::process::exit(1); + } + } +} diff --git a/test-framework/src/test.rs b/test-framework/src/test.rs index ba129c39ab..dc3d0871f5 100644 --- a/test-framework/src/test.rs +++ b/test-framework/src/test.rs @@ -22,7 +22,7 @@ pub enum TestExpectationMode { Fail, } -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize)] pub struct TestConfig { pub namespace: String, pub expectation: TestExpectationMode,