1use std::io::{self, BufRead, IsTerminal, Write};
16
17use crate::ir_generator::IRGenerator;
18use crate::lexer::{Lexer, LexerError};
19use crate::parser::{ParseError, Parser};
20use crate::runner::AXON_VERSION;
21use crate::type_checker::TypeChecker;
22
23const CYAN: &str = "\x1b[36m";
26const GREEN: &str = "\x1b[32m";
27const RED: &str = "\x1b[31m";
28const YELLOW: &str = "\x1b[33m";
29const BOLD: &str = "\x1b[1m";
30const DIM: &str = "\x1b[2m";
31const RESET: &str = "\x1b[0m";
32
33fn c(text: &str, code: &str, use_color: bool) -> String {
34 if use_color {
35 format!("{code}{text}{RESET}")
36 } else {
37 text.to_string()
38 }
39}
40
41fn print_banner(use_color: bool) {
44 if use_color {
45 println!("{CYAN}\u{2554}{}\u{2557}{RESET}", "\u{2550}".repeat(42));
46 println!("{CYAN}\u{2551}{RESET} {BOLD}{GREEN}AXON REPL{RESET} v{AXON_VERSION} {CYAN}\u{2551}{RESET}");
47 println!("{CYAN}\u{2551}{RESET} A cognitive language for AI {CYAN}\u{2551}{RESET}");
48 println!("{CYAN}\u{255a}{}\u{255d}{RESET}", "\u{2550}".repeat(42));
49 println!(" Type {YELLOW}.help{RESET} for commands, {YELLOW}.quit{RESET} to exit.");
50 } else {
51 println!("+{}+", "=".repeat(42));
52 println!("| AXON REPL v{AXON_VERSION} |");
53 println!("| A cognitive language for AI |");
54 println!("+{}+", "=".repeat(42));
55 println!(" Type .help for commands, .quit to exit.");
56 }
57 println!();
58}
59
60fn handle_dot_command(cmd: &str, use_color: bool) -> Option<bool> {
65 let cmd = cmd.trim().to_lowercase();
66 if !cmd.starts_with('.') {
67 return None;
68 }
69
70 match cmd.as_str() {
71 ".quit" | ".exit" | ".q" => {
72 println!("{}", c("Goodbye.", DIM, use_color));
73 Some(false)
74 }
75 ".help" => {
76 println!();
77 println!(" {} Show this message", c(".help", YELLOW, use_color));
78 println!(" {} Clear screen", c(".clear", YELLOW, use_color));
79 println!(" {} Exit REPL", c(".quit", YELLOW, use_color));
80 println!();
81 Some(true)
82 }
83 ".clear" => {
84 print!("\x1b[2J\x1b[H");
85 let _ = io::stdout().flush();
86 Some(true)
87 }
88 _ => {
89 println!("{}", c(&format!(" Unknown command: {cmd}. Type .help"), RED, use_color));
90 Some(true)
91 }
92 }
93}
94
95fn eval_source(source: &str, use_color: bool) {
98 let tokens = match Lexer::new(source, "<repl>").tokenize() {
100 Ok(t) => t,
101 Err(LexerError { message, .. }) => {
102 eprintln!("{}", c(&format!(" Lexer error: {message}"), RED, use_color));
103 return;
104 }
105 };
106
107 let mut parser = Parser::new(tokens);
109 let program = match parser.parse() {
110 Ok(p) => p,
111 Err(ParseError { message, line, .. }) => {
112 let loc = if line > 0 { format!(" (line {line})") } else { String::new() };
113 eprintln!("{}", c(&format!(" Parse error{loc}: {message}"), RED, use_color));
114 return;
115 }
116 };
117
118 let type_errors = TypeChecker::new(&program).check();
120 for te in &type_errors {
121 let loc = if te.line > 0 { format!(" (line {})", te.line) } else { String::new() };
122 eprintln!("{}", c(&format!(" Type error{loc}: {}", te.message), YELLOW, use_color));
123 }
124
125 let ir_program = IRGenerator::new().generate(&program);
127
128 let ir_value = serde_json::to_value(&ir_program).unwrap_or(serde_json::Value::Null);
130 let formatted = serde_json::to_string_pretty(&ir_value).unwrap_or_default();
131 println!("{}", c(&formatted, GREEN, use_color));
132}
133
134fn read_multiline(first_line: &str, reader: &mut dyn BufRead, use_color: bool) -> String {
137 let mut lines = vec![first_line.to_string()];
138 let mut depth: i32 = first_line.matches('{').count() as i32
139 - first_line.matches('}').count() as i32;
140
141 while depth > 0 {
142 print!("{} ", c(" ...", DIM, use_color));
143 let _ = io::stdout().flush();
144
145 let mut cont = String::new();
146 match reader.read_line(&mut cont) {
147 Ok(0) => return String::new(), Ok(_) => {
149 let trimmed = cont.trim_end_matches('\n').trim_end_matches('\r');
150 depth += trimmed.matches('{').count() as i32
151 - trimmed.matches('}').count() as i32;
152 lines.push(trimmed.to_string());
153 }
154 Err(_) => return String::new(),
155 }
156 }
157
158 lines.join("\n")
159}
160
161pub fn run_repl() -> i32 {
164 let use_color = io::stdout().is_terminal() && io::stdin().is_terminal();
165 let stdin = io::stdin();
166 let mut reader = stdin.lock();
167
168 print_banner(use_color);
169
170 loop {
171 print!("{} ", c("axon>", &format!("{CYAN}{BOLD}"), use_color));
172 let _ = io::stdout().flush();
173
174 let mut line = String::new();
175 match reader.read_line(&mut line) {
176 Ok(0) => {
177 println!("{}", c("\nGoodbye.", DIM, use_color));
179 return 0;
180 }
181 Ok(_) => {}
182 Err(_) => {
183 println!("{}", c("\nGoodbye.", DIM, use_color));
184 return 0;
185 }
186 }
187
188 let stripped = line.trim();
189 if stripped.is_empty() {
190 continue;
191 }
192
193 if let Some(should_continue) = handle_dot_command(stripped, use_color) {
195 if !should_continue {
196 return 0;
197 }
198 continue;
199 }
200
201 let source = if stripped.contains('{')
203 && stripped.matches('{').count() > stripped.matches('}').count()
204 {
205 let s = read_multiline(stripped, &mut reader, use_color);
206 if s.is_empty() {
207 continue;
208 }
209 s
210 } else {
211 stripped.to_string()
212 };
213
214 eval_source(&source, use_color);
215 }
216}
217
218pub fn eval_source_captured(source: &str) -> Result<(String, Vec<String>), String> {
223 let tokens = Lexer::new(source, "<repl>")
225 .tokenize()
226 .map_err(|e| format!("Lexer error: {}", e.message))?;
227
228 let mut parser = Parser::new(tokens);
230 let program = parser
231 .parse()
232 .map_err(|e| format!("Parse error: {}", e.message))?;
233
234 let type_errors: Vec<String> = TypeChecker::new(&program)
236 .check()
237 .iter()
238 .map(|te| te.message.clone())
239 .collect();
240
241 let ir_program = IRGenerator::new().generate(&program);
243 let ir_value = serde_json::to_value(&ir_program).unwrap_or(serde_json::Value::Null);
244 let formatted = serde_json::to_string_pretty(&ir_value).unwrap_or_default();
245
246 Ok((formatted, type_errors))
247}