diff --git a/examples/make.py b/examples/make.py old mode 100644 new mode 100755 diff --git a/src/protocol/inputsource.rs b/src/protocol/inputsource.rs index d32a32a8d4fdfced36e7620ce21089d1cdedaa98..f4e2766fb0b9af8e809460b0194b8be8bbabdf75 100644 --- a/src/protocol/inputsource.rs +++ b/src/protocol/inputsource.rs @@ -8,7 +8,7 @@ use backtrace::Backtrace; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct InputSource { filename: String, - input: Vec, + pub input: Vec, line: usize, column: usize, offset: usize, @@ -41,13 +41,15 @@ primitive merger(in l, in r, out o) { if(fires(l)) put(o, get(l)); else if(fires(r)) put(o, get(r)); } -}"; +} +"; impl InputSource { // Constructors pub fn new(filename: S, reader: &mut R) -> io::Result { - let mut vec = STD_LIB_PDL.to_vec(); + let mut vec = Vec::new(); reader.read_to_end(&mut vec)?; + vec.extend(STD_LIB_PDL.to_vec()); Ok(InputSource { filename: filename.to_string(), input: vec, diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index 1dddc8395a31fd2cfe788c22b4eb2eea2aebd726..392f0fa16177c236bccb99018a2a4f52753dba9c 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -3,7 +3,7 @@ mod ast; mod eval; pub(crate) mod inputsource; mod lexer; -// mod library; +mod library; mod parser; lazy_static::lazy_static! { diff --git a/src/protocol/parser.rs b/src/protocol/parser.rs index 8f4fa8489d1bfbc65775d07ac695adfc49e6428c..8d32cdb9f1b996c9294dd310addac8e90cb23179 100644 --- a/src/protocol/parser.rs +++ b/src/protocol/parser.rs @@ -1,7 +1,7 @@ use crate::protocol::ast::*; use crate::protocol::inputsource::*; use crate::protocol::lexer::*; -// use crate::protocol::library; +use crate::protocol::library; // The following indirection is needed due to a bug in the cbindgen tool. type Unit = (); @@ -796,14 +796,14 @@ impl Visitor for BuildSymbolDeclarations { h[pd].declarations.append(&mut self.declarations); Ok(()) } - fn visit_import(&mut self, _h: &mut Heap, _import: ImportId) -> VisitorResult { - todo!() - // let vec = library::get_declarations(h, import)?; - // // Destructively iterate over the vector - // for decl in vec { - // self.checked_add(h, decl)?; - // } - // Ok(()) + fn visit_import(&mut self, h: &mut Heap, import: ImportId) -> VisitorResult { + println!("DEBUG: Warning (at {}:{}), import actually not yet implemented", file!(), line!()); + let vec = library::get_declarations(h, import)?; + // Destructively iterate over the vector + for decl in vec { + self.checked_add(h, decl)?; + } + Ok(()) } fn visit_symbol_definition(&mut self, h: &mut Heap, definition: DefinitionId) -> VisitorResult { let signature = Signature::from_definition(h, definition); @@ -1821,66 +1821,152 @@ impl<'a> Parser<'a> { } } -// #[cfg(test)] -// mod tests { -// extern crate test_generator; - -// use std::fs::File; -// use std::io::Read; -// use std::path::Path; - -// use test_generator::test_resources; - -// use super::*; - -// #[test_resources("testdata/parser/positive/*.pdl")] -// fn batch1(resource: &str) { -// let path = Path::new(resource); -// let mut heap = Heap::new(); -// let mut source = InputSource::from_file(&path).unwrap(); -// let mut parser = Parser::new(&mut source); -// match parser.parse(&mut heap) { -// Ok(_) => {} -// Err(err) => { -// println!("{}", err.display(&source)); -// println!("{:?}", err); -// assert!(false); -// } -// } -// } - -// #[test_resources("testdata/parser/negative/*.pdl")] -// fn batch2(resource: &str) { -// let path = Path::new(resource); -// let expect = path.with_extension("txt"); -// let mut heap = Heap::new(); -// let mut source = InputSource::from_file(&path).unwrap(); -// let mut parser = Parser::new(&mut source); -// match parser.parse(&mut heap) { -// Ok(pd) => { -// println!("{:?}", heap[pd]); -// println!("Expected parse error:"); - -// let mut cev: Vec = Vec::new(); -// let mut f = File::open(expect).unwrap(); -// f.read_to_end(&mut cev).unwrap(); -// println!("{}", String::from_utf8_lossy(&cev)); -// assert!(false); -// } -// Err(err) => { -// println!("{:?}", err); - -// let mut vec: Vec = Vec::new(); -// err.write(&source, &mut vec).unwrap(); -// println!("{}", String::from_utf8_lossy(&vec)); - -// let mut cev: Vec = Vec::new(); -// let mut f = File::open(expect).unwrap(); -// f.read_to_end(&mut cev).unwrap(); -// println!("{}", String::from_utf8_lossy(&cev)); - -// assert_eq!(vec, cev); -// } -// } -// } -// } +#[cfg(test)] +mod tests { + use std::fs::File; + use std::io::Read; + use std::path::Path; + + use super::*; + + #[test] + fn positive_tests() { + for resource in TestFileIter::new("testdata/parser/positive", "pdl") { + let resource = resource.expect("read testdata filepath"); + // println!(" * running: {}", &resource); + let path = Path::new(&resource); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + // println!("DEBUG -- input:\n{}", String::from_utf8_lossy(&source.input)); + let mut parser = Parser::new(&mut source); + match parser.parse(&mut heap) { + Ok(_) => {} + Err(err) => { + println!(" > file: {}", &resource); + println!("{}", err.display(&source)); + println!("{:?}", err); + assert!(false); + } + } + } + } + + #[test] + fn negative_tests() { + for resource in TestFileIter::new("testdata/parser/negative", "pdl") { + let resource = resource.expect("read testdata filepath"); + let path = Path::new(&resource); + let expect = path.with_extension("txt"); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + let mut parser = Parser::new(&mut source); + match parser.parse(&mut heap) { + Ok(pd) => { + println!("{:?}", heap[pd]); + println!("Expected parse error:"); + + let mut cev: Vec = Vec::new(); + let mut f = File::open(expect).unwrap(); + f.read_to_end(&mut cev).unwrap(); + println!("{}", String::from_utf8_lossy(&cev)); + assert!(false); + } + Err(err) => { + println!("{:?}", err); + + let mut vec: Vec = Vec::new(); + err.write(&source, &mut vec).unwrap(); + println!("{}", String::from_utf8_lossy(&vec)); + + let mut cev: Vec = Vec::new(); + let mut f = File::open(expect).unwrap(); + f.read_to_end(&mut cev).unwrap(); + println!("{}", String::from_utf8_lossy(&cev)); + + assert_eq!(vec, cev); + } + } + } + } + + #[test] + fn counterexample_tests() { + for resource in TestFileIter::new("testdata/parser/counterexamples", "pdl") { + let resource = resource.expect("read testdata filepath"); + let path = Path::new(&resource); + let mut heap = Heap::new(); + let mut source = InputSource::from_file(&path).unwrap(); + let mut parser = Parser::new(&mut source); + + fn print_header(s: &str) { + println!("{}", "=".repeat(80)); + println!(" > File: {}", s); + println!("{}", "=".repeat(80)); + } + + match parser.parse(&mut heap) { + Ok(parsed) => { + print_header(&resource); + println!("\n SUCCESS\n\n --- source:\n{}", String::from_utf8_lossy(&source.input)); + }, + Err(err) => { + print_header(&resource); + + let mut err_buf = Vec::new(); + err.write(&source, &mut err_buf); + println!( + "\n FAILURE\n\n --- error:\n{}\n --- source:\n{}", + String::from_utf8_lossy(&err_buf), + String::from_utf8_lossy(&source.input) + ) + } + } + } + } + + struct TestFileIter { + iter: std::fs::ReadDir, + root: String, + extension: String + } + + impl TestFileIter { + fn new(root_dir: &str, extension: &str) -> Self { + let path = Path::new(root_dir); + assert!(path.is_dir(), "root '{}' is not a directory", root_dir); + + let iter = std::fs::read_dir(path).expect("list dir contents"); + + Self { + iter, + root: root_dir.to_string(), + extension: extension.to_string(), + } + } + } + + impl Iterator for TestFileIter { + type Item = Result; + + fn next(&mut self) -> Option { + while let Some(entry) = self.iter.next() { + if let Err(e) = entry { + return Some(Err(format!("failed to read dir entry, because: {}", e))); + } + let entry = entry.unwrap(); + + let path = entry.path(); + if !path.is_file() { continue; } + + let extension = path.extension(); + if extension.is_none() { continue; } + let extension = extension.unwrap().to_string_lossy(); + if extension != self.extension { continue; } + + return Some(Ok(path.to_string_lossy().to_string())); + } + + None + } + } +} diff --git a/testdata/parser/counterexamples/arity_checking.pdl b/testdata/parser/counterexamples/arity_checking.pdl new file mode 100644 index 0000000000000000000000000000000000000000..7a54b6d63de3491a39758dbc4cd4bb4d23cab59f --- /dev/null +++ b/testdata/parser/counterexamples/arity_checking.pdl @@ -0,0 +1,17 @@ + +int funcy() { + return 5; +} + +int funcadelic(int a) { + return a; +} + +int caller() { + funcy(); + funcy(1); + funcy(1, 2, 3); + funcadelic(); + funcadelic(5); + return funcadelic(1, 2, 3); +} diff --git a/testdata/parser/counterexamples/declaration_after_function_call.pdl b/testdata/parser/counterexamples/declaration_after_function_call.pdl new file mode 100644 index 0000000000000000000000000000000000000000..58608c9734a50692e8170fbedeb2884845ee3953 --- /dev/null +++ b/testdata/parser/counterexamples/declaration_after_function_call.pdl @@ -0,0 +1,14 @@ +#version 1 +// My bad: C-ism of declarations on top + +int func_b() { + return 5; +} + +int func_a() { + int a = 2; int b = 3; + int c = 5; func_b(c); + int e = 3; + + return b; +} diff --git a/testdata/parser/counterexamples/definition_order.pdl b/testdata/parser/counterexamples/definition_order.pdl new file mode 100644 index 0000000000000000000000000000000000000000..927ff6288e1b337eae33e23d51d97d1823853dcf --- /dev/null +++ b/testdata/parser/counterexamples/definition_order.pdl @@ -0,0 +1,18 @@ +#version 1 +// My bad: C-ism of declarations on top + +int call_me(int later) { + return later; +} + +int function() { + int a = 2; + int b = 3; + + int d = call_me(b); // succeeds, because of assignment + call_me(b); // bare function call seems to work, unless we perform assignment afterwards + + int d = 5; + + return 2; +} \ No newline at end of file diff --git a/testdata/parser/counterexamples/empty_file_reporting.pdl b/testdata/parser/counterexamples/empty_file_reporting.pdl new file mode 100644 index 0000000000000000000000000000000000000000..551a12f5c13dddb97d085090fdcebdb6bd0ea92a --- /dev/null +++ b/testdata/parser/counterexamples/empty_file_reporting.pdl @@ -0,0 +1,2 @@ +// This seems silly, but should be more neatly reported +#version 1 \ No newline at end of file diff --git a/testdata/parser/counterexamples/function_type_checks.pdl b/testdata/parser/counterexamples/function_type_checks.pdl new file mode 100644 index 0000000000000000000000000000000000000000..0a74967c96162835f604e14075883c69dbbbc9ad --- /dev/null +++ b/testdata/parser/counterexamples/function_type_checks.pdl @@ -0,0 +1,19 @@ +// Note sure if this is allowed. It seems silly, but could be useful to +// select ports to return from. In any case, we are returning an out port from +// a function returning an in port + +#version 1 + +in do_something(in a, out b) { + return b; +} + +float another_something(in a) { + return a; +} + +whatami yet_another_one(andwhatismypurpose a) { + return a; +} + +composite main() {} diff --git a/testdata/parser/counterexamples/import_bad_reporting.pdl b/testdata/parser/counterexamples/import_bad_reporting.pdl new file mode 100644 index 0000000000000000000000000000000000000000..e5b69eff3b009d8d5cd580c66234a4aba893b4ec --- /dev/null +++ b/testdata/parser/counterexamples/import_bad_reporting.pdl @@ -0,0 +1,10 @@ +// Bad reporting of EOF. Also main parser loop would be fine if pragmas, imports +// and declarations are mixed. Maybe force pragmas at top for readability... + +#version 1 + +composite main() {} + +#version 2 + +composite another() {} \ No newline at end of file diff --git a/testdata/parser/counterexamples/import_stmt.pdl b/testdata/parser/counterexamples/import_stmt.pdl new file mode 100644 index 0000000000000000000000000000000000000000..66df1e201b7e1b007aa50f730142d3cece726fa1 --- /dev/null +++ b/testdata/parser/counterexamples/import_stmt.pdl @@ -0,0 +1,6 @@ +// Import succeeds due to bad parsing (checking for characters, not numbers, +// should expect whitespace, then a single number + +#version123 456 + +composite main() {} diff --git a/testdata/parser/counterexamples/integer_specification.pdl b/testdata/parser/counterexamples/integer_specification.pdl new file mode 100644 index 0000000000000000000000000000000000000000..07c1185d7efddc971274d205eb72edbbc8a28bbf --- /dev/null +++ b/testdata/parser/counterexamples/integer_specification.pdl @@ -0,0 +1,10 @@ +#version 1 + +int check_integer_specs() { + int a = 0xFF; + // no octal support, but thats fine + int b = 0xxxxFF; + int c = 0x1x2X3x4X5x6X7x8X; // Wut + int d = 1FF; + return a; +} \ No newline at end of file diff --git a/testdata/parser/counterexamples/multiple_versions.pdl b/testdata/parser/counterexamples/multiple_versions.pdl new file mode 100644 index 0000000000000000000000000000000000000000..59a7b30d9440295e30f8731eb62725e52411e125 --- /dev/null +++ b/testdata/parser/counterexamples/multiple_versions.pdl @@ -0,0 +1,4 @@ +#version 1 +#version 2 + +composite main() {} diff --git a/testdata/parser/counterexamples/out_of_order_assignment.pdl b/testdata/parser/counterexamples/out_of_order_assignment.pdl new file mode 100644 index 0000000000000000000000000000000000000000..f74fa1d52f71d2269055730b4c3ddb81b62cd6ac --- /dev/null +++ b/testdata/parser/counterexamples/out_of_order_assignment.pdl @@ -0,0 +1,7 @@ +// It fails, so that is nice, but it fails due to the wrong reasons +// My bad: C-ism of declarations on top +bool some_function() { + result_c = false; + bool result_c = true; + return result_c; +} \ No newline at end of file diff --git a/testdata/parser/positive/14.pdl b/testdata/parser/positive/14.pdl index 2d8e9216148e30f4e08f4fdb07c93b8c0fc2e419..622e756f27a3218031b0ed0e475eb22f95d6aac3 100644 --- a/testdata/parser/positive/14.pdl +++ b/testdata/parser/positive/14.pdl @@ -32,4 +32,4 @@ primitive binary_replicator(in b, out a, out c) { } } } -} \ No newline at end of file +}