use std::io; use std::env; use std::io::Write; use std::path::Path; mod sexpr; 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; } // TODO: use debug or something instead of "verbose"? let tokens = match sexpr::sexpr_tokenize(&raw_input) { Ok(tokens) => { if verbose { println!("Tokens: {}", tokens.join(", ")); }; tokens }, Err(e) => { println!("couldn't tokenize: {}", e); continue; } }; let ast = match sexpr::sexpr_parse(&tokens, 0) { Ok((mut ast_list, _)) => { if verbose { for ast in &ast_list { println!("AST: {}", sexpr::sexpr_repr(ast).unwrap()); }; }; // We're a REPL, so only one expression at a time if ast_list.len() > 1 { println!("one expression at a time on the REPL, please!"); continue; } else if ast_list.len() == 0 { sexpr::SExpr::SNull } else { let ast = ast_list.pop().unwrap(); ast } }, Err(e) => { println!("couldn't parse: {}", e); continue; } }; println!("{}", sexpr::sexpr_repr(&ast).unwrap()); } } 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); } }