//! src/main.rs //! -------------------------------------------- //! Build & run: //! cargo run //! -------------------------------------------- use std::env; use std::fs; use std::io::{self, Read, Write}; use std::path::Path; use rottlib::arena::Arena; use rottlib::lexer::TokenizedFile; use rottlib::parser::{ParseError, Parser, pretty::ExprTree}; /* - Convenient array definitions: [1, 3, 5, 2, 4] - Boolean dynamic arrays - Structures in default properties - Auto conversion of arrays into strings - Making 'var' and 'local' unnecessary - Allowing variable creation in 'for' loops - Allowing variable creation at any place inside a function - Default parameters for functions - Function overloading? - repeat/until - The syntax of the default properties block is pretty strict for an arcane reason. Particularly adding spaces before or after the "=" will lead to errors in pre-UT2003 versions. - Scopes - different names for variables and in config file - anonymous pairs (objects?) and value destruction >>> AST > HIR > MIR > byte code */ /// Closest plan: /// - Add top-level declaration parsing /// - Handle pretty.rs shit somehow /// - COMMITS /// --------------------------------------- /// - Add fancy error reporting /// - Make a fancy REPL /// - Add evaluation /// /// WARNINGS: /// - Empty code/switch blocks fn parse_and_print(src: &str) -> Result<(), ParseError> { let tokenized = TokenizedFile::from_str(src); let arena = Arena::new(); let mut parser = Parser::new(&tokenized, &arena); let expr = parser.parse_expression(); // ArenaNode println!("{}", ExprTree(&*expr)); // if ArenaNode // or: println!("{}", ExprTree(expr.as_ref())); // if no Deref Ok(()) } fn repl_once() -> Result<(), ParseError> { print!("Enter an statement > "); io::stdout().flush().unwrap(); let mut input = String::new(); if io::stdin().read_line(&mut input).is_err() { eprintln!("failed to read input"); return Ok(()); } if input.trim().is_empty() { return Ok(()); } parse_and_print(&input) } fn read_stdin_all() -> io::Result { let mut buf = String::new(); io::stdin().read_to_string(&mut buf)?; Ok(buf) } fn read_file_to_string(path: &Path) -> io::Result { fs::read_to_string(path) } fn main() -> Result<(), ParseError> { // Accept a single positional arg as the input path. // "-" means read all of stdin. let mut args = env::args().skip(1); if let Some(arg1) = args.next() { if arg1 == "-h" || arg1 == "--help" { println!("Usage:"); println!( " {} # REPL", env::args().next().unwrap_or_else(|| "prog".into()) ); println!( " {} # parse file", env::args().next().unwrap_or_else(|| "prog".into()) ); println!( " {} - # read source from stdin", env::args().next().unwrap_or_else(|| "prog".into()) ); return Ok(()); } if arg1 == "-" { match read_stdin_all() { Ok(src) => return parse_and_print(&src), Err(e) => { eprintln!("stdin read error: {}", e); return Ok(()); } } } else { let path = Path::new(&arg1); match read_file_to_string(path) { Ok(src) => return parse_and_print(&src), Err(e) => { eprintln!("file read error ({}): {}", path.display(), e); return Ok(()); } } } } // No filename provided -> keep REPL behavior repl_once() }