rune/
main.rs

1#![allow(unsafe_op_in_unsafe_fn)]
2#[macro_use]
3mod macros;
4#[macro_use]
5mod core;
6#[macro_use]
7mod debug;
8mod alloc;
9mod arith;
10mod buffer;
11mod bytecode;
12mod casefiddle;
13mod character;
14mod chartab;
15mod data;
16mod dired;
17mod editfns;
18mod emacs;
19mod eval;
20mod fileio;
21mod filelock;
22mod floatfns;
23mod fns;
24mod interpreter;
25mod intervals;
26mod keymap;
27mod library;
28mod lisp;
29mod lread;
30mod print;
31mod reader;
32mod search;
33mod textprops;
34mod threads;
35mod timefns;
36
37use crate::core::{
38    env::{Env, intern, sym},
39    gc::{Context, RootSet, Rt},
40    object::{Gc, LispString, NIL},
41};
42use crate::eval::EvalError;
43use clap::Parser;
44use rune_core::macros::root;
45use std::io::{self, Write};
46
47#[derive(Parser, Debug)]
48#[command(author, version, about, long_about = None)]
49struct Args {
50    #[arg(short, long, value_name = "FILE")]
51    load: Vec<String>,
52    #[arg(short, long)]
53    repl: bool,
54    #[arg(short, long)]
55    no_bootstrap: bool,
56    #[arg(long)]
57    eval_stdin: bool,
58}
59
60fn main() -> Result<(), ()> {
61    let args = Args::parse();
62
63    let roots = &RootSet::default();
64    let cx = &mut Context::new(roots);
65    root!(env, new(Env), cx);
66
67    sym::init_symbols();
68    crate::core::env::init_variables(cx, env);
69    crate::data::defalias(intern("not", cx), (sym::NULL).into(), None)
70        .expect("null should be defined");
71
72    if args.eval_stdin {
73        return eval_stdin(cx, env);
74    }
75
76    if !args.no_bootstrap {
77        bootstrap(env, cx)?;
78    }
79
80    for file in args.load {
81        load(&file, cx, env)?;
82    }
83
84    if args.repl {
85        repl(env, cx);
86    }
87    Ok(())
88}
89
90fn parens_closed(buffer: &str) -> bool {
91    let open = buffer.chars().filter(|&x| x == '(').count();
92    let close = buffer.chars().filter(|&x| x == ')').count();
93    open <= close
94}
95
96fn repl(env: &mut Rt<Env>, cx: &mut Context) {
97    let mut buffer = String::new();
98    let stdin = io::stdin();
99    loop {
100        print!("> ");
101        io::stdout().flush().unwrap();
102        stdin.read_line(&mut buffer).unwrap();
103        if buffer.trim() == "exit" {
104            return;
105        }
106        if buffer.trim().is_empty() {
107            continue;
108        }
109        if !parens_closed(&buffer) {
110            continue;
111        }
112        let (obj, _) = match reader::read(&buffer, cx) {
113            Ok(obj) => obj,
114            Err(e) => {
115                eprintln!("Error: {e}");
116                buffer.clear();
117                continue;
118            }
119        };
120
121        root!(obj, cx);
122        match interpreter::eval(obj, None, env, cx) {
123            Ok(val) => println!("{val}"),
124            Err(e) => {
125                eprintln!("Error: {e}");
126                if let Ok(e) = e.downcast::<EvalError>() {
127                    e.print_backtrace();
128                }
129            }
130        }
131        buffer.clear();
132    }
133}
134
135fn load(file: &str, cx: &mut Context, env: &mut Rt<Env>) -> Result<(), ()> {
136    let file: Gc<&LispString> = cx.add_as(file);
137    root!(file, cx);
138    match crate::lread::load(file, None, None, cx, env) {
139        Ok(val) => {
140            println!("{val}");
141            Ok(())
142        }
143        Err(e) => {
144            eprintln!("Error: {e}");
145            if let Ok(e) = e.downcast::<EvalError>() {
146                e.print_backtrace();
147            }
148            Err(())
149        }
150    }
151}
152
153fn eval_stdin(cx: &mut Context, env: &mut Rt<Env>) -> Result<(), ()> {
154    let mut buffer = String::new();
155    let mut point = 0;
156    let mut count = 0;
157    loop {
158        io::stdin().read_line(&mut buffer).unwrap();
159        let obj = match reader::read(&buffer[point..], cx) {
160            Ok((obj, offset)) => {
161                point += offset;
162                obj
163            }
164            Err(reader::Error::EmptyStream) => continue,
165            Err(e) => {
166                eprintln!("Error: {e}");
167                break;
168            }
169        };
170
171        root!(obj, cx);
172        match interpreter::eval(obj, None, env, cx) {
173            Ok(val) => println!(";; ELPROP_START:{count}\n{val}\n;; ELPROP_END\n"),
174            Err(e) => println!(";; ELPROP_START:{count}\nError: {e}\n;; ELPROP_END\n"),
175        }
176        count += 1;
177        std::thread::sleep(std::time::Duration::from_millis(10));
178        // timeout after ~1 minute
179        if count > 6000 {
180            break;
181        }
182    }
183    Err(())
184}
185
186fn bootstrap(env: &mut Rt<Env>, cx: &mut Context) -> Result<(), ()> {
187    buffer::get_buffer_create(cx.add("*scratch*"), Some(NIL), cx).unwrap();
188    load("bootstrap.el", cx, env)
189}
190
191#[test]
192fn verify_cli() {
193    use clap::CommandFactory;
194    Args::command().debug_assert()
195}