130 lines
3.7 KiB
Rust
130 lines
3.7 KiB
Rust
//! 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<Expression>
|
|
println!("{}", ExprTree(&*expr)); // if ArenaNode<Deref>
|
|
// 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<String> {
|
|
let mut buf = String::new();
|
|
io::stdin().read_to_string(&mut buf)?;
|
|
Ok(buf)
|
|
}
|
|
|
|
fn read_file_to_string(path: &Path) -> io::Result<String> {
|
|
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!(
|
|
" {} <file> # 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()
|
|
}
|