phoenix_lang/
lib.rs

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    // tracks how many blocks deep we are (and thus how many spaces to indent)
81    let mut block_offset = 0;
82    loop {
83        // let readline = rl.readline(" \x1b[32m>>\x1b[0m");
84        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                // check if there are any open blocks/parens (check for: {, (, [)
94                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                // let _result = fly(file_name.clone(), line, DEBUG, false);
117                // dbg!(&compiler.current_module().scanner.code);
118
119                compiler
120                    .current_module()
121                    .scanner
122                    .code
123                    .push_str(&current_line);
124                current_line = "".to_string();
125                block_offset = 0;
126                // dbg!(&compiler.current_module().scanner.code);
127                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                // pop the return instructions
135                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                        // println!("state: {:#?}, modules: {:#?}", state, modules);
165                    }
166                } else {
167                    state = last_state;
168                    compiler = last_compiler;
169                }
170            }
171            Err(ReadlineError::Interrupted) => {
172                // println!("CTRL-C");
173                break;
174            }
175            Err(ReadlineError::Eof) => {
176                // println!("CTRL-D");
177                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;