Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 0 additions & 26 deletions .github/ISSUE_TEMPLATE/syntax-change-request.md

This file was deleted.

22 changes: 20 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,27 @@ jobs:
run: cargo install cargo-audit
- name: Build
run: cargo build --verbose
- name: Test
run: cargo test --verbose
- name: Test (excluding codegen)
run: "cargo test --verbose --lib -- --skip codegen::"
- name: Clippy
run: cargo clippy --verbose -- -D warnings
- name: Audit
run: cargo audit

codegen-tests:
name: Codegen tests
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
toolchain:
- nightly
runs-on: ${{ matrix.os }}
steps:
- name: Setup Rust
uses: actions/checkout@v4.1.3
- name: Update Rust
run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- name: Build
run: cargo build --verbose
- name: Test codegen
run: "cargo test --lib --verbose codegen::"
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ Cargo.lock
*_output/

*.v
*.wasm
*.wat
out
test_data/**/*.json
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ members = [
"cli",
"wasm-v-translator",
"wat-codegen",
"wasm-codegen",
"inference",
"tests",
"playground-server",
Expand All @@ -26,13 +27,14 @@ inference-ast = { path = "./ast", version = "0.0.1" }
inference-cli = { path = "./cli", version = "0.0.1" }
inference-wasm-v-translator = { path = "./wasm-v-translator", version = "0.0.1" }
inference-wat-codegen = { path = "./wat-codegen", version = "0.0.1" }
inference-wasm-codegen = { path = "./wasm-codegen", version = "0.0.1" }
inference-tests = { path = "./tests", version = "0.0.1" }
inference-playground-server = { path = "./playground-server", version = "0.0.1" }
inf-wast = "0.0.8"
inf-wasmparser = "0.0.8"
wat-fmt = "0.0.8"
wasm-fmt = { path = "./wasm-fmt", version = "0.0.1" }
tree-sitter = "0.25.3"
tree-sitter = "0.25.10"
tree-sitter-inference = "0.0.36"
anyhow = "1.0.99"
serde = { version = "1.0.217", features = ["derive", "rc"] }
anyhow = "1.0.100"
serde = { version = "1.0.228", features = ["derive", "rc"] }
10 changes: 10 additions & 0 deletions ast/src/nodes_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ impl SourceFile {
}
}
impl SourceFile {
#[must_use]
pub fn specs(&self) -> Vec<Rc<SpecDefinition>> {
self.definitions
.iter()
.filter_map(|def| match def {
Definition::Spec(spec) => Some(spec.clone()),
_ => None,
})
.collect()
}
#[must_use]
pub fn function_definitions(&self) -> Vec<Rc<FunctionDefinition>> {
self.definitions
Expand Down
34 changes: 25 additions & 9 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,24 +91,40 @@ fn main() {
}

if need_codegen {
let wasm = match codegen(t_ast) {
let wasm = match codegen(&t_ast) {
Ok(w) => w,
Err(e) => {
eprintln!("Codegen failed: {e}");
process::exit(1);
}
};
println!("WASM generated");
let source_fname = args
.path
.file_stem()
.unwrap_or_else(|| std::ffi::OsStr::new("module"))
.to_str()
.unwrap();
if args.generate_wasm_output {
let wasm_file_path = output_path.join(format!("{source_fname}.wasm"));
if let Err(e) = fs::create_dir_all(&output_path) {
eprintln!("Failed to create output directory: {e}");
process::exit(1);
}
if let Err(e) = fs::write(&wasm_file_path, &wasm) {
eprintln!("Failed to write WASM file: {e}");
process::exit(1);
}
println!("WASM generated at: {}", wasm_file_path.to_string_lossy());
}
if args.generate_v_output {
let mod_name = args
.path
.file_stem()
.unwrap_or_else(|| std::ffi::OsStr::new("module"))
.to_str()
.unwrap();
match wasm_to_v(mod_name, &wasm) {
match wasm_to_v(source_fname, &wasm) {
Ok(v_output) => {
let v_file_path = output_path.join("out.v");
let v_file_path = output_path.join(format!("{source_fname}.v"));
if let Err(e) = fs::create_dir_all(&output_path) {
eprintln!("Failed to create output directory: {e}");
process::exit(1);
}
if let Err(e) = fs::write(&v_file_path, v_output) {
eprintln!("Failed to write V file: {e}");
process::exit(1);
Expand Down
5 changes: 4 additions & 1 deletion cli/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ pub(crate) struct Cli {
// Runs code generation, exit with 0 if successful
#[clap(long = "codegen", action = clap::ArgAction::SetTrue)]
pub(crate) codegen: bool,
// Generates output .v files
// Generates output .wasm files
#[clap(short = 'o', action = clap::ArgAction::SetTrue)]
pub(crate) generate_wasm_output: bool,
// Generates output .v files
#[clap(short = 'v', action = clap::ArgAction::SetTrue)]
pub(crate) generate_v_output: bool,
}
2 changes: 1 addition & 1 deletion inference/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ anyhow.workspace = true
tree-sitter.workspace = true
tree-sitter-inference.workspace = true
inference-ast.workspace = true
inference-wat-codegen.workspace = true
inference-wasm-codegen.workspace = true
inference-wasm-v-translator.workspace = true
56 changes: 4 additions & 52 deletions inference/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,50 +33,17 @@ pub fn parse(source_code: &str) -> anyhow::Result<TypedAst> {
///
/// This function will return an error if the type analysis fails.
pub fn analyze(_: &TypedAst) -> anyhow::Result<()> {
todo!("Type analysis not yet implemented");
// todo!("Type analysis not yet implemented");
Ok(())
}

/// Generates WebAssembly binary format (WASM) from the given Typed AST.
///
/// # Errors
///
/// This function will return an error if the code generation fails.
pub fn codegen(_: TypedAst) -> anyhow::Result<Vec<u8>> {
todo!("Code generation not yet implemented");
}

/// Compiles the given source code to WebAssembly Text format (WAT).
///
/// # Panics
///
/// This function will panic if there is an error loading the Inference grammar.
/// Compiles the given source code to WebAssembly Text format (WAT).
///
/// # Errors
///
/// This function will return an error if the source code cannot be parsed or if there is an error during the AST building process.
///
/// # Panics
///
/// This function will panic if there is an error loading the Inference grammar.
#[must_use = "This function returns the compiled WebAssembly Text format as a string"]
pub fn compile_to_wat(source_code: &str) -> anyhow::Result<String> {
let inference_language = tree_sitter_inference::language();
let mut parser = tree_sitter::Parser::new();
parser
.set_language(&inference_language)
.expect("Error loading Inference grammar");
let tree = parser.parse(source_code, None).unwrap();
let code = source_code.as_bytes();
let root_node = tree.root_node();
let mut builder = Builder::new();
builder.add_source_code(root_node, code);
let builder = builder.build_ast()?;
let mut wat_generator = inference_wat_codegen::wat_emitter::WatEmitter::default();
for ast in builder.t_ast().source_files {
wat_generator.add_source_file(ast);
}
Ok(wat_generator.emit())
pub fn codegen(t_ast: &TypedAst) -> anyhow::Result<Vec<u8>> {
inference_wasm_codegen::codegen(t_ast)
}

/// Converts WebAssembly Text format (WAT) to WebAssembly binary format (WASM).
Expand All @@ -97,21 +64,6 @@ pub fn wat_to_wasm(wat: &str) -> anyhow::Result<Vec<u8>> {
Ok(wasm)
}

/// Compiles the given source code to WebAssembly binary format (WASM).
///
/// # Errors
///
/// This function will return an error if the WAT string cannot be parsed or if there is an error during the compilation process.
///
/// # Panics
///
/// This function will panic if the WAT string cannot be parsed.
#[must_use = "This function compiles Inference source code to the WebAssembly binary format as a vector of bytes"]
pub fn compile_to_wasm(source_code: &str) -> anyhow::Result<Vec<u8>> {
let wat = compile_to_wat(source_code)?;
wat_to_wasm(&wat)
}

/// Translates WebAssembly binary format (WASM) to Coq format.
///
/// # Errors
Expand Down
86 changes: 41 additions & 45 deletions playground-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
use actix_cors::Cors;
use actix_web::{post, web, App, HttpResponse, HttpServer, Responder};
use inference::{compile_to_wat, wasm_to_v, wat_to_wasm};
use serde::{Deserialize, Serialize};

use wasm_fmt::format as wasm_format;
use wat_fmt::format as wat_format;

#[derive(Deserialize)]
struct CompileRequest {
code: String,
Expand All @@ -20,52 +16,52 @@ struct Response {
errors: Vec<String>,
}

fn parse_inf_file(input: &str) -> Response {
let mut wasm = vec![];
let mut v = String::new();
let mut errors = vec![];
fn parse_inf_file(_: &str) -> Response {
// let mut wasm = vec![];
let v = String::new();
let errors = vec![];

let wat = match compile_to_wat(input) {
Ok(w) => w,
Err(e) => {
errors.push(e.to_string());
return Response {
wat: String::new(),
wasm: vec![],
wasm_str: String::new(),
v: String::new(),
errors,
};
}
};
// let wat = match compile_to_wat(input) {
// Ok(w) => w,
// Err(e) => {
// errors.push(e.to_string());
// return Response {
// wat: String::new(),
// wasm: vec![],
// wasm_str: String::new(),
// v: String::new(),
// errors,
// };
// }
// };

if !wat.is_empty() {
wat_to_wasm(&wat)
.map(|w| wasm = w)
.unwrap_or_else(|e| errors.push(e.to_string()));
// if !wat.is_empty() {
// wat_to_wasm(&wat)
// .map(|w| wasm = w)
// .unwrap_or_else(|e| errors.push(e.to_string()));

wasm_to_v("playground", &wasm)
.map(|v_str| v = v_str)
.unwrap_or_else(|e| errors.push(e.to_string()));
// wasm_to_v("playground", &wasm)
// .map(|v_str| v = v_str)
// .unwrap_or_else(|e| errors.push(e.to_string()));

let wat = wat_format(&wat);
let wasm_str = wasm_format(&wasm);
Response {
wat,
wasm,
wasm_str,
v,
errors,
}
} else {
Response {
wat: String::new(),
wasm: vec![],
wasm_str: String::new(),
v,
errors,
}
// let wat = wat_format(&wat);
// let wasm_str = wasm_format(&wasm);
// Response {
// wat,
// wasm,
// wasm_str,
// v,
// errors,
// }
// } else {
Response {
wat: String::new(),
wasm: vec![],
wasm_str: String::new(),
v,
errors,
}
// }
}

#[post("/compile")]
Expand Down
3 changes: 3 additions & 0 deletions test_data/codegen/wasm/base/trivial.inf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn hello_world() -> i32 {
return 42;
}
Binary file added test_data/codegen/wasm/base/trivial.wasm
Binary file not shown.
2 changes: 2 additions & 0 deletions tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ repository = { workspace = true }

[dependencies]
inference-ast.workspace = true
inference-wasm-codegen.workspace = true
inference.workspace = true
tree-sitter.workspace = true
tree-sitter-inference.workspace = true
anyhow.workspace = true
serde_json = "1.0.99"

1 change: 1 addition & 0 deletions tests/src/codegen/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod wasm;
20 changes: 20 additions & 0 deletions tests/src/codegen/wasm/base.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#[cfg(test)]
mod base_codegen_tests {
use crate::utils::{
assert_wasms_modules_equivalence, get_test_file_path, get_test_wasm_path, wasm_codegen,
};

#[test]
fn trivial_test() {
let test_name = "trivial";
let test_file_path = get_test_file_path(module_path!(), test_name);
let source_code = std::fs::read_to_string(&test_file_path)
.unwrap_or_else(|_| panic!("Failed to read test file: {:?}", test_file_path));
let expected = get_test_wasm_path(module_path!(), test_name);
let expected = std::fs::read(&expected).unwrap_or_else(|_| {
panic!("Failed to read expected wasm file for test: {}", test_name)
});
let actual = wasm_codegen(&source_code);
assert_wasms_modules_equivalence(&expected, &actual);
}
}
Loading