Skip to main content

virtual_rust/
lib.rs

1//! # Virtual Rust
2//!
3//! A tree-walking interpreter that runs Rust source code directly
4//! without compilation. Supports variables, functions, closures,
5//! control flow, structs, arrays/Vec, match expressions, and more.
6//!
7//! ## Quick start
8//!
9//! ```rust,no_run
10//! // Run a Rust source file
11//! virtual_rust::run_source(r#"
12//!     fn main() {
13//!         println!("Hello from Virtual Rust!");
14//!     }
15//! "#).unwrap();
16//! ```
17
18pub mod ast;
19pub mod cargo_runner;
20pub mod interpreter;
21pub mod lexer;
22pub mod parser;
23pub mod token;
24
25use interpreter::Interpreter;
26use lexer::Lexer;
27use parser::Parser;
28
29/// Compiles source code through the lexer → parser → AST pipeline.
30fn compile(source: &str) -> Result<Vec<ast::Expr>, String> {
31    let tokens = Lexer::new(source).tokenize().map_err(|e| format!("{e}"))?;
32
33    Parser::new(tokens)
34        .parse_program()
35        .map_err(|e| format!("{e}"))
36}
37
38/// Returns `true` if the program contains a `fn main()` definition.
39fn has_main(program: &[ast::Expr]) -> bool {
40    program
41        .iter()
42        .any(|expr| matches!(expr, ast::Expr::FnDef { name, .. } if name == "main"))
43}
44
45/// Evaluates all top-level statements. If a `fn main()` exists, calls it.
46fn execute(
47    interpreter: &mut Interpreter,
48    program: &[ast::Expr],
49) -> Result<interpreter::Value, String> {
50    let mut result = interpreter::Value::Unit;
51    for expr in program {
52        result = interpreter.eval(expr).map_err(|e| format!("{e}"))?;
53    }
54
55    if has_main(program) {
56        result = interpreter
57            .eval(&ast::Expr::FnCall {
58                name: "main".to_string(),
59                args: vec![],
60            })
61            .map_err(|e| format!("{e}"))?;
62    }
63
64    Ok(result)
65}
66
67/// Runs Rust source code directly without compilation.
68///
69/// If the source contains a `fn main()`, it will be called automatically.
70pub fn run_source(source: &str) -> Result<(), String> {
71    let program = compile(source)?;
72    let mut interpreter = Interpreter::new();
73    execute(&mut interpreter, &program)?;
74    Ok(())
75}
76
77/// Runs Rust source code and returns the final result as a display string.
78///
79/// Useful for REPL-style evaluation where the result should be shown.
80pub fn eval_source(source: &str) -> Result<String, String> {
81    let program = compile(source)?;
82    let mut interpreter = Interpreter::new();
83    let result = execute(&mut interpreter, &program)?;
84    Ok(format!("{result}"))
85}