1use rustyline::config::Configurer;
2use std::fs::File;
3use std::io::Read;
4use std::path::Path;
5use std::process::exit;
6
7use rustyline::error::ReadlineError;
8use rustyline::DefaultEditor;
9
10use crate::chunk::OpCode::OpReturn;
11use crate::chunk::{Instr, ModuleChunk};
12use crate::compiler::{CompilationResult, Compiler};
13use crate::vm::{ExecutionMode, Global, VMState, VM};
14use crate::InterpretResult::InterpretOK;
15
16pub mod chunk;
17pub mod compiler;
18pub mod debug;
19pub mod gc;
20pub mod io;
21pub mod log;
22pub mod native;
23pub mod precedence;
24pub mod resolver;
25pub mod run;
26pub mod scanner;
27pub mod utils;
28pub mod value;
29pub mod vm;
30
31#[derive(Debug, PartialEq)]
32pub enum InterpretResult {
33 InterpretOK(VMState, Vec<ModuleChunk>),
34 InterpretCompileError,
35 InterpretRuntimeError,
36}
37
38pub const VERSION: &str = env!("CARGO_PKG_VERSION");
39
40pub fn fly(file: String, source: String, debug: bool, quiet: bool, wait: bool) -> InterpretResult {
41 let mut compiler = Compiler::new_file(file, source, quiet, 0, DEBUG, wait);
42 let result = compiler.compile(debug);
43 if result.is_none() {
44 return InterpretResult::InterpretCompileError;
45 }
46
47 let result = result.unwrap();
48 let mut vm = if debug {
49 VM::new(ExecutionMode::Trace, result, quiet)
50 } else {
51 VM::new(ExecutionMode::Default, result, quiet)
52 };
53 vm.run()
54}
55
56pub fn repl() -> Result<(), ReadlineError> {
57 let mut rl = DefaultEditor::new()?;
58 if rl.load_history("history.txt").is_err() {
59 println!("No previous history.");
60 }
61 let s = &String::new();
62 let file_name = "script.phx".to_string();
63 let mut vm = VM::new(
64 if DEBUG {
65 ExecutionMode::Trace
66 } else {
67 ExecutionMode::Default
68 },
69 CompilationResult::default(),
70 false,
71 );
72 let mut state: Option<VMState> = None;
73 let mut modules = Vec::new();
74 let mut compiler = Compiler::new_file(file_name, s.clone(), false, 0, DEBUG, false);
75 let mut last_state;
76 let mut last_compiler;
77 let mut current_prompt = ">>";
78 let default_prompt = ">>";
79 let mut current_line = "".to_string();
80 let mut block_offset = 0;
82 loop {
83 let readline = rl.readline_with_initial(current_prompt, (&*" ".repeat(block_offset), ""));
85 match readline {
86 Ok(mut line) => {
87 rl.set_max_history_size(10000)
88 .expect("failed to set max history size");
89 rl.add_history_entry(line.as_str())
90 .expect("failed to add history");
91 line.push('\n');
92 current_line.push_str(&line);
93 let mut open_blocks = 0;
95 let mut open_parens = 0;
96 let mut open_brackets = 0;
97 for c in current_line.chars() {
98 match c {
99 '{' => open_blocks += 1,
100 '}' => open_blocks -= 1,
101 '(' => open_parens += 1,
102 ')' => open_parens -= 1,
103 '[' => open_brackets += 1,
104 ']' => open_brackets -= 1,
105 _ => (),
106 }
107 }
108 if open_blocks > 0 || open_parens > 0 || open_brackets > 0 {
109 block_offset = open_blocks;
110 current_prompt = "..";
111 continue;
112 }
113 current_prompt = default_prompt;
114 last_state = state.clone();
115 last_compiler = compiler.clone();
116 compiler
120 .current_module()
121 .scanner
122 .code
123 .push_str(¤t_line);
124 current_line = "".to_string();
125 block_offset = 0;
126 let result = compiler.compile(DEBUG);
128 if result.is_none() {
129 compiler = last_compiler;
130 continue;
131 }
132
133 let mut result = result.unwrap();
134 result.modules[0].functions[0]
136 .chunk
137 .code
138 .retain(|x| x.op_code != OpReturn);
139 let line_num = result.modules[0].functions[0]
140 .chunk
141 .code
142 .last()
143 .unwrap()
144 .line_num;
145 result.modules[0].functions[0].chunk.code.push(Instr {
146 op_code: OpReturn,
147 line_num,
148 });
149 state = if let Some(mut s) = state {
150 while s.globals[0].len() < result.modules[0].identifiers.len() {
151 s.globals[0].push(Global::Uninit);
152 }
153 Some(s)
154 } else {
155 state
156 };
157 vm.modules_cache = result.modules;
158 let s = vm.run_state(state.clone(), modules.clone());
159 if let InterpretOK(mut s, m) = s {
160 s.current_frame.ip -= 1;
161 state = Some(s);
162 modules = m;
163 if DEBUG {
164 }
166 } else {
167 state = last_state;
168 compiler = last_compiler;
169 }
170 }
171 Err(ReadlineError::Interrupted) => {
172 break;
174 }
175 Err(ReadlineError::Eof) => {
176 break;
178 }
179 Err(err) => {
180 phoenix_error!("Error: {:?}", err);
181 }
182 }
183 }
184 rl.save_history("history.txt")?;
185 println!("Saved history. Goodbye!");
186 Ok(())
187}
188
189pub fn run_file(filename: String, debug: bool, wait: bool) -> InterpretResult {
190 let path = Path::new(&filename);
191 let path_display = path.display();
192
193 let mut file = match File::open(path) {
194 Ok(file) => file,
195 Err(why) => {
196 eprintln!("Failed to open {}: {}", path_display, why);
197 exit(1);
198 }
199 };
200
201 let mut s = String::new();
202 match file.read_to_string(&mut s) {
203 Ok(_) => fly(filename, s, debug, false, wait),
204 Err(why) => {
205 eprintln!("Failed to read {}: {}", path_display, why);
206 exit(1);
207 }
208 }
209}
210
211#[cfg(feature = "debug")]
212pub const DEBUG: bool = true;
213
214#[cfg(not(feature = "debug"))]
215pub const DEBUG: bool = false;