2025-02-07 09:21:23 +01:00
|
|
|
//! Interpret the Lox language. Either compile (interpret for now though) some source code or run a
|
|
|
|
//! REPL.
|
|
|
|
|
2025-02-06 18:55:54 +01:00
|
|
|
use std::{
|
|
|
|
fs::{self},
|
|
|
|
io::{self, Write},
|
|
|
|
path::Path,
|
|
|
|
};
|
|
|
|
|
2025-05-25 10:52:20 +02:00
|
|
|
use interpreter::{Interpreter, InterpreterError};
|
|
|
|
use parser::ParserError;
|
|
|
|
use resolver::{Resolver, ResolverError};
|
|
|
|
use thiserror::Error;
|
2025-02-12 10:30:51 +01:00
|
|
|
use tracing::error;
|
2025-02-06 11:34:46 +01:00
|
|
|
|
2025-02-13 12:29:31 +01:00
|
|
|
pub mod callable;
|
2025-02-06 11:34:46 +01:00
|
|
|
pub mod cli;
|
2025-02-12 10:30:51 +01:00
|
|
|
pub mod environment;
|
2025-02-10 14:36:55 +01:00
|
|
|
pub mod expression;
|
2025-02-13 12:29:31 +01:00
|
|
|
pub mod function;
|
2025-02-11 11:19:56 +01:00
|
|
|
pub mod interpreter;
|
2025-02-06 18:55:54 +01:00
|
|
|
pub mod keywords;
|
2025-02-13 12:29:31 +01:00
|
|
|
pub mod native_functions;
|
2025-02-10 14:36:55 +01:00
|
|
|
pub mod parser;
|
2025-05-25 10:52:20 +02:00
|
|
|
pub mod resolver;
|
2025-02-06 18:55:54 +01:00
|
|
|
pub mod scanner;
|
2025-02-12 10:30:51 +01:00
|
|
|
pub mod statement;
|
2025-02-06 18:55:54 +01:00
|
|
|
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;
|
|
|
|
}
|
2025-02-11 14:09:52 +01:00
|
|
|
pub mod value;
|
2025-02-06 18:55:54 +01:00
|
|
|
|
2025-05-25 10:52:20 +02:00
|
|
|
#[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),
|
|
|
|
}
|
|
|
|
|
2025-02-07 09:21:23 +01:00
|
|
|
/// Read the source code in a file and scan it to tokens.
|
2025-02-06 18:55:54 +01:00
|
|
|
pub fn compile(source: &Path) -> Result<(), io::Error> {
|
|
|
|
let input = fs::read_to_string(source)?;
|
2025-02-12 10:30:51 +01:00
|
|
|
let mut interpreter = Interpreter::default();
|
|
|
|
|
|
|
|
run(&input, &mut interpreter);
|
2025-02-06 18:55:54 +01:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-02-07 09:21:23 +01:00
|
|
|
/// Run a Lox REPL until SIGINT.
|
2025-02-06 18:55:54 +01:00
|
|
|
pub fn repl() {
|
2025-02-12 10:30:51 +01:00
|
|
|
let mut interpreter = Interpreter::default();
|
2025-02-06 18:55:54 +01:00
|
|
|
loop {
|
|
|
|
print!("> ");
|
|
|
|
let _ = io::stdout().flush();
|
2025-02-06 11:34:46 +01:00
|
|
|
|
2025-02-06 18:55:54 +01:00
|
|
|
let mut input = String::new();
|
|
|
|
match io::stdin().read_line(&mut input) {
|
|
|
|
Ok(_) => {}
|
|
|
|
Err(e) => error!("{}", e),
|
|
|
|
}
|
|
|
|
let input = input.trim().to_string();
|
2025-02-12 10:30:51 +01:00
|
|
|
run(&input, &mut interpreter);
|
2025-02-11 11:19:56 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluate a Lox input string and print errors or output.
|
2025-02-12 10:30:51 +01:00
|
|
|
fn run(input: &str, interpreter: &mut Interpreter) {
|
2025-05-25 10:52:20 +02:00
|
|
|
if let Err(e) = run_rox(input, interpreter) {
|
|
|
|
error!("{e}");
|
2025-02-06 18:55:54 +01:00
|
|
|
}
|
|
|
|
}
|
2025-05-25 10:52:20 +02:00
|
|
|
|
|
|
|
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(())
|
|
|
|
}
|