//! Interpret the Lox language. Either compile (interpret for now though) some source code or run a //! REPL. use std::{ fs::{self}, io::{self, Write}, path::Path, }; use interpreter::{Interpreter, InterpreterError}; use parser::ParserError; use resolver::{Resolver, ResolverError}; use thiserror::Error; use tracing::error; pub mod callable; pub mod cli; pub mod environment; pub mod expression; pub mod function; pub mod interpreter; pub mod keywords; pub mod native_functions; pub mod parser; pub mod resolver; pub mod scanner; pub mod statement; pub mod token; pub mod tokenizer { pub mod comment; pub mod identifier; pub mod interface; pub mod lookahead; pub mod newline; pub mod number; pub mod single_char; pub mod string; pub mod whitespace; } pub mod value; #[derive(Error, Debug)] pub enum RoxError { #[error("parser failed: {0}")] ParseError(#[from] ParserError), #[error("resolver failed: {0}")] ResolverError(#[from] ResolverError), #[error("interpreter failed: {0}")] InterpreterError(#[from] InterpreterError), } /// Read the source code in a file and scan it to tokens. pub fn compile(source: &Path) -> Result<(), io::Error> { let input = fs::read_to_string(source)?; let mut interpreter = Interpreter::default(); run(&input, &mut interpreter); Ok(()) } /// Run a Lox REPL until SIGINT. pub fn repl() { let mut interpreter = Interpreter::default(); loop { print!("> "); let _ = io::stdout().flush(); let mut input = String::new(); match io::stdin().read_line(&mut input) { Ok(_) => {} Err(e) => error!("{}", e), } let input = input.trim().to_string(); run(&input, &mut interpreter); } } /// Evaluate a Lox input string and print errors or output. fn run(input: &str, interpreter: &mut Interpreter) { if let Err(e) = run_rox(input, interpreter) { error!("{e}"); } } fn run_rox(input: &str, interpreter: &mut Interpreter) -> Result<(), RoxError> { let tokens = scanner::tokenize(input); let mut resolver = Resolver::new(interpreter); let ast = parser::ast(tokens)?; resolver.resolve(&ast)?; interpreter.run(ast)?; Ok(()) }