1#[cfg(feature = "compiler")]
13use crate::compiler::compile;
14#[cfg(feature = "compiler")]
15use crate::errors::BOLD;
16#[cfg(feature = "compiler")]
17use crate::errors::ErrorCtx;
18#[cfg(feature = "compiler")]
19use crate::errors::RED;
20#[cfg(feature = "compiler")]
21use crate::errors::RESET;
22#[cfg(feature = "compiler")]
23use crate::repl::repl;
24use crate::vm::RegisterFile;
25#[cfg(all(feature = "embed", feature = "compiler"))]
26use std::ffi::{CStr, CString, c_char};
27use std::fs;
28#[cfg(feature = "compiler")]
29use std::hint::cold_path;
30#[cfg(all(feature = "embed", feature = "compiler"))]
31use std::panic::catch_unwind;
32#[cfg(target_arch = "wasm32")]
33use wasm_bindgen::prelude::*;
34
35pub(crate) use candela_vm::data;
39pub(crate) use candela_vm::errors;
40pub(crate) use candela_vm::instr;
41pub(crate) use candela_vm::rt;
42pub(crate) use candela_vm::vm;
43
44#[cfg(feature = "compiler")]
48#[path = "./compiler/compiler.rs"]
49pub mod compiler;
50#[cfg(feature = "compiler")]
53mod engine;
54#[cfg(feature = "compiler")]
56mod build;
57#[cfg(feature = "compiler")]
60#[path = "./parser/parser.rs"]
61pub mod parser;
62#[cfg(feature = "compiler")]
63mod repl;
64#[path = "./tests.rs"]
65#[cfg(all(test, feature = "compiler"))]
66mod tests;
67#[cfg(feature = "compiler")]
70mod update;
71#[path = "./util/util.rs"]
72mod util;
73
74pub use candela_vm::Diagnostic;
75pub use candela_vm::collect_diagnostic;
76
77pub use candela_vm::FromHostValue;
78pub use candela_vm::HostType;
79pub use candela_vm::IntoHostFn;
80pub use candela_vm::IntoHostValue;
81pub use candela_vm::Value;
82#[cfg(feature = "compiler")]
83pub use engine::Engine;
84#[cfg(feature = "compiler")]
85pub use engine::Program;
86
87pub use candela_vm::LoadError;
89pub use candela_vm::RuntimeProgram;
90pub use candela_vm::load_program;
91#[cfg(feature = "compiler")]
94pub use build::build_bytecode;
95
96#[cfg(feature = "compiler")]
100fn execute_compiled(out: compiler::CompileOutput) {
101 let compiler::CompileOutput {
102 instructions,
103 registers,
104 mut pools,
105 instr_src,
106 fn_registers,
107 dyn_lib_fns,
108 structs,
109 enums,
110 allocated_arg_count,
111 allocated_call_depth,
112 sources,
113 ..
114 } = out;
115 vm::execute(
116 &instructions,
117 &mut RegisterFile(registers),
118 &mut pools,
119 &ErrorCtx { instr_src, sources },
120 &fn_registers,
121 &dyn_lib_fns,
122 &structs,
123 &enums,
124 allocated_arg_count,
125 allocated_call_depth,
126 &[],
127 &[],
128 0,
129 );
130}
131
132#[cfg(target_arch = "wasm32")]
133#[wasm_bindgen]
134pub fn get_output() -> String {
135 candela_vm::captured_output::CAPTURED_OUTPUT.with(|o| o.take())
136}
137
138#[cfg(all(target_arch = "wasm32", feature = "compiler"))]
139#[wasm_bindgen]
140pub fn run(code: String) {
141 candela_vm::captured_output::CAPTURED_OUTPUT.with(|o| o.borrow_mut().clear());
142 execute_compiled(compile(code, "playground.cdl", false));
143}
144
145#[cfg(all(feature = "embed", feature = "compiler"))]
146#[unsafe(no_mangle)]
147#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn candela_run(code: *const c_char) -> *mut c_char {
149 std::panic::set_hook(Box::new(|_| {}));
150 let code = unsafe { CStr::from_ptr(code) }
151 .to_string_lossy()
152 .to_string();
153 candela_vm::captured_output::CAPTURED_OUTPUT.with(|o| o.borrow_mut().clear());
154 let was_capturing = candela_vm::captured_output::set_capturing(true);
157 let _ = catch_unwind(|| {
158 execute_compiled(compile(code, "embedded.cdl", false));
159 });
160 candela_vm::captured_output::set_capturing(was_capturing);
161 let output = candela_vm::captured_output::CAPTURED_OUTPUT.with(|o| o.take());
162 CString::new(output).unwrap_or_default().into_raw()
163}
164
165#[cfg(all(feature = "embed", feature = "compiler"))]
166#[unsafe(no_mangle)]
167#[allow(clippy::missing_safety_doc)] pub unsafe extern "C" fn candela_free_output(output: *mut c_char) {
169 if !output.is_null() {
170 #[allow(unused_must_use)]
171 unsafe {
172 CString::from_raw(output)
173 };
174 }
175}
176
177#[cfg(feature = "compiler")]
182fn build_subcommand(args: &mut impl Iterator<Item = String>) {
183 let Some(input) = args.next() else {
184 eprintln!("{RED}CANDELA ERROR{RESET}\nUsage:\n candela build <file.cdl> [-o out.cdlb]");
185 std::process::exit(1);
186 };
187
188 let mut output: Option<String> = None;
193 while let Some(a) = args.next() {
194 if a == "-o" || a == "--output" {
195 let Some(path) = args.next() else {
196 eprintln!(
197 "{RED}CANDELA ERROR{RESET}\n{a} needs an output path\nUsage:\n candela build <file.cdl> [-o out.cdlb]"
198 );
199 std::process::exit(1);
200 };
201 output = Some(path);
202 } else {
203 eprintln!(
204 "{RED}CANDELA ERROR{RESET}\nUnexpected argument {RED}{BOLD}{a}{RESET}\nName the output file with -o or --output\nUsage:\n candela build <file.cdl> [-o out.cdlb]"
205 );
206 std::process::exit(1);
207 }
208 }
209 let output = output.unwrap_or_else(|| {
215 let stem = input.strip_suffix(".cdl").unwrap_or(&input);
216 format!("{stem}.cdlb")
217 });
218
219 let contents = fs::read_to_string(&input).unwrap_or_else(|_| {
220 cold_path();
221 eprintln!(
222 "--------------\n{RED}CANDELA RUNTIME ERROR:{RESET}\nCannot read {RED}{BOLD}{input}{RESET}\n--------------",
223 );
224 std::process::exit(1);
225 });
226
227 let bytes = match build::build_bytecode(contents, &input) {
228 Ok(b) => b,
229 Err(e) => {
230 eprintln!("{RED}CANDELA ERROR{RESET}\nCannot build bytecode: {e}");
231 std::process::exit(1);
232 }
233 };
234
235 if let Err(e) = fs::write(&output, &bytes) {
236 eprintln!("{RED}CANDELA ERROR{RESET}\nCannot write {output}: {e}");
237 std::process::exit(1);
238 }
239 println!("Wrote {} ({} bytes)", output, bytes.len());
240}
241
242#[cfg(feature = "compiler")]
248fn reject_extra_args(args: &mut impl Iterator<Item = String>, flag: &str) {
249 if let Some(extra) = args.next() {
250 cold_path();
251 eprintln!(
252 "{RED}CANDELA ERROR{RESET}\n{flag} takes no other arguments, got {RED}{BOLD}{extra}{RESET}\nUsage:\n candela myfile.cdl\n candela build <file.cdl> [-o out.cdlb]\n candela [-h | --help]\n candela [-v | --version]"
253 );
254 std::process::exit(1);
255 }
256}
257
258#[cfg(feature = "compiler")]
259pub fn main() {
260 #[cfg(not(debug_assertions))]
261 std::panic::set_hook(Box::new(|info| {
262 eprintln!("{RED}CANDELA ERROR{RESET}\n{info}");
263 }));
264
265 let mut args = std::env::args().skip(1);
266
267 if args.len() == 0 {
268 cold_path();
269 repl();
270 return;
271 }
272
273 let next_arg = unsafe { args.next().unwrap_unchecked() };
274
275 if next_arg == "build" || next_arg == "compile" {
276 cold_path();
277 build_subcommand(&mut args);
278 return;
279 }
280
281 if next_arg == "--help" || next_arg == "-h" {
282 cold_path();
283 reject_extra_args(&mut args, &next_arg);
284 let update = update::start();
285 println!(
286 "{}\nCandela is a fast, statically-typed interpreted language that aims to combine Rust-like syntax with Python's ease-of-use.\n\nUsage:\n candela myfile.cdl\n candela build <file.cdl> [-o out.cdlb] (compile to bytecode; run with candela-vm)\n candela [-v | --version]",
287 util::CANDELA_LOGO
288 );
289 update::finish(update);
290 return;
291 }
292
293 if next_arg == "--version" || next_arg == "-v" {
294 cold_path();
295 reject_extra_args(&mut args, &next_arg);
296 println!("Candela {}", env!("CARGO_PKG_VERSION"));
297 return;
298 }
299
300 let filename = &next_arg;
301
302 let contents = fs::read_to_string(filename).unwrap_or_else(|_| {
303 cold_path();
304 eprintln!(
305 "--------------\n{RED}CANDELA RUNTIME ERROR:{RESET}\nCannot read {RED}{BOLD}{filename}{RESET}\n--------------",
306 );
307 std::process::exit(1);
308 });
309
310 #[cfg(debug_assertions)]
311 {
312 let next = args.next();
313 if next == Some(String::from("--debug")) {
314 let now = std::time::Instant::now();
315 let out = compile(contents, filename, true);
316 println!("COMPILATION TIME: {:.2?}", now.elapsed());
317 let now = std::time::Instant::now();
318 execute_compiled(out);
319 println!(
320 "EXECUTION TIME: {:.3}ms",
321 now.elapsed().as_nanos() / 1_000_000
322 );
323 return;
324 } else if next == Some(String::from("--debug-parser")) {
325 let _ = compile(contents, filename, false);
326 return;
327 }
328 }
329
330 execute_compiled(compile(contents, filename, false));
331}