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 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}