From b3141319b98199d75f1317ef3754c0b6b14b1f78 Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Mon, 25 Oct 2021 00:15:04 -0700 Subject: refactor into lib-style crate, and add some early tests --- src/bin/casual.rs | 65 ++++++++++++++++++++++++++++++++++ src/cexpr.rs | 4 +-- src/lib.rs | 34 ++++++++++++++++++ src/main.rs | 96 --------------------------------------------------- tests/canonicalize.rs | 26 ++++++++++++++ tests/things.txt | 35 +++++++++++++++++++ 6 files changed, 162 insertions(+), 98 deletions(-) create mode 100644 src/bin/casual.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 tests/canonicalize.rs create mode 100644 tests/things.txt diff --git a/src/bin/casual.rs b/src/bin/casual.rs new file mode 100644 index 0000000..828c4b6 --- /dev/null +++ b/src/bin/casual.rs @@ -0,0 +1,65 @@ +use std::env; +use std::path::Path; + +use casual::{repl, sexpr_parse_file}; + +fn usage() { + println!("usage:\tcasual [-h] [-v] [--no-repl] []"); + println!(""); + println!("Files will be loaded in order, then drop to REPL (unless \"--no-repl\" is passed)."); + println!( + "Verbose flag (\"-v\") will result in lexed tokens and parsed AST \ + being dumped to stdout (when on REPL)." + ); +} + +fn main() { + let mut verbose: bool = false; + let mut no_repl: bool = false; + + let mut file_list = Vec::::new(); + + for arg in env::args().skip(1) { + match &*arg { + "-v" | "--verbose" => { + verbose = true; + } + "--no-repl" => { + no_repl = true; + } + "-h" | "--help" => { + usage(); + return; + } + _ if arg.starts_with("-") => { + println!("Unknown option: {}", arg); + println!(""); + usage(); + return; + } + _ => { + file_list.push(arg.clone()); + } + } + } + + for fname in file_list { + let fpath = Path::new(&fname); + if !fpath.is_file() { + println!("File not found (or not file): {}", fname); + return; + } + println!("Loading {}...", fname); + match sexpr_parse_file(&fpath) { + Err(e) => { + println!("Error loading file: {}\n {}", fname, e); + return; + } + Ok(_) => (), + } + } + + if !no_repl { + repl(verbose); + } +} diff --git a/src/cexpr.rs b/src/cexpr.rs index b183c4a..357caff 100644 --- a/src/cexpr.rs +++ b/src/cexpr.rs @@ -37,7 +37,7 @@ impl NumericConstant { } } -#[derive(Clone, PartialEq, PartialOrd)] +#[derive(Clone, PartialEq, PartialOrd, Debug)] pub enum CNumber { // order here is important for sorting, etc Integer(i64), @@ -99,7 +99,7 @@ impl CNumber { } } -#[derive(Clone, PartialEq, PartialOrd)] +#[derive(Clone, PartialEq, PartialOrd, Debug)] pub enum CExpr { // order here is important for sorting, etc Number(CNumber), diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..950329b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,34 @@ +use std::io; +use std::io::Write; + +mod cexpr; +mod sexpr; + +pub use cexpr::{CExpr, CNumber}; +pub use sexpr::{sexpr_parse_file, SExpr}; + +pub fn repl(_verbose: bool) { + let stdin = io::stdin(); + let mut stdout = io::stdout(); + + loop { + let raw_input = &mut String::new(); + stdout.write(b"\ncasual> ").unwrap(); + stdout.flush().unwrap(); + stdin.read_line(raw_input).unwrap(); + let raw_input = raw_input; // mutable to immutable reference + if raw_input.len() == 0 { + // end-of-line, aka Ctrl-D. Blank line will still have newline char + stdout.write(b"\nCiao!\n").unwrap(); + return; + } + let expr = match CExpr::from_str(&raw_input) { + Ok(expr) => expr, + Err(e) => { + println!("error: {}", e); + continue; + } + }; + println!("{}", expr); + } +} diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index f09e0f0..0000000 --- a/src/main.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::env; -use std::io; -use std::io::Write; -use std::path::Path; - -mod cexpr; -mod sexpr; - -use cexpr::CExpr; - -fn repl(_verbose: bool) { - let stdin = io::stdin(); - let mut stdout = io::stdout(); - - loop { - let raw_input = &mut String::new(); - stdout.write(b"\ncasual> ").unwrap(); - stdout.flush().unwrap(); - stdin.read_line(raw_input).unwrap(); - let raw_input = raw_input; // mutable to immutable reference - if raw_input.len() == 0 { - // end-of-line, aka Ctrl-D. Blank line will still have newline char - stdout.write(b"\nCiao!\n").unwrap(); - return; - } - let expr = match CExpr::from_str(&raw_input) { - Ok(expr) => expr, - Err(e) => { - println!("error: {}", e); - continue; - } - }; - println!("{}", expr); - } -} - -fn usage() { - println!("usage:\tcasual [-h] [-v] [--no-repl] []"); - println!(""); - println!("Files will be loaded in order, then drop to REPL (unless \"--no-repl\" is passed)."); - println!( - "Verbose flag (\"-v\") will result in lexed tokens and parsed AST \ - being dumped to stdout (when on REPL)." - ); -} - -fn main() { - let mut verbose: bool = false; - let mut no_repl: bool = false; - - let mut file_list = Vec::::new(); - - for arg in env::args().skip(1) { - match &*arg { - "-v" | "--verbose" => { - verbose = true; - } - "--no-repl" => { - no_repl = true; - } - "-h" | "--help" => { - usage(); - return; - } - _ if arg.starts_with("-") => { - println!("Unknown option: {}", arg); - println!(""); - usage(); - return; - } - _ => { - file_list.push(arg.clone()); - } - } - } - - for fname in file_list { - let fpath = Path::new(&fname); - if !fpath.is_file() { - println!("File not found (or not file): {}", fname); - return; - } - println!("Loading {}...", fname); - match sexpr::sexpr_parse_file(&fpath) { - Err(e) => { - println!("Error loading file: {}\n {}", fname, e); - return; - } - Ok(_) => (), - } - } - - if !no_repl { - repl(verbose); - } -} diff --git a/tests/canonicalize.rs b/tests/canonicalize.rs new file mode 100644 index 0000000..3ef088a --- /dev/null +++ b/tests/canonicalize.rs @@ -0,0 +1,26 @@ +use casual::{CExpr, CNumber}; + +#[test] +fn basics() { + assert_eq!( + CExpr::from_str("(+ 1 2 3)").unwrap(), + CExpr::Number(CNumber::Integer(6)), + ); +} + +#[test] +fn canonicalization() { + let cases = vec![ + ("(+ (+ a 2) (+ b 2))", "(+ 4 a b)"), + ("(+ (+ a 2) (+ b 2))", "(+ 4 a b)"), + ("(+ 1 2 b)", "(+ 3 b)"), + ("(+ 1 2 3)", "6"), + ("(* (/ 2 3) (/ 3 2))", "1"), + ]; + + for (input, output) in cases.iter() { + assert_eq!(CExpr::from_str(input).unwrap().to_string(), *output); + } +} + +// TODO: helper to read an examples file, as pairs of rows diff --git a/tests/things.txt b/tests/things.txt new file mode 100644 index 0000000..f525f6e --- /dev/null +++ b/tests/things.txt @@ -0,0 +1,35 @@ + +(+ (+ a 2) (+ b 2)) +(+ 4 a b) + +(+ 1 2 b) +(+ 3 b) + +(+ 1 2 3) +6 + +(* (/ 2 3) (/ 3 2)) +1 + +# TODO +(* (/ -2 3) (/ 3 2)) +-1 + + +(* (* a b 1) (* c d)) +(* a b c d) + +(+ (+ a b) (+ c d)) +(+ a b c d) + +(+ (+ a b 1) (+ c d 3)) +(+ 4 a b c d) + +(^ 4 -1) +(/ 1 4) + +(^ (/ 2 3) -1) +(/ 3 2) + +(^ (/ 1 3) -1) +3 -- cgit v1.2.3