interpreter/
interpreter.rs

1//! (Very) simple Lua interpreter.
2
3use lunka::prelude::*;
4use std::{
5	env::args,
6	ffi::{
7		CStr, c_int, c_uint,
8	},
9	io::{
10		stderr,
11		Write
12	},
13	process::ExitCode
14};
15
16fn c_eprintln(data: &CStr) {
17	let mut out = stderr();
18	let _ = out.write_all(data.to_bytes());
19	let _ = out.write_all(b"\n");
20}
21
22fn report(lua: &mut LuaThread, status: LuaStatus) -> bool {
23	if !status.is_ok() {
24		if let Some(message) = lua.to_c_str(-1) {
25			c_eprintln(message);
26		}
27		lua.run_managed(|mut mg| unsafe { mg.pop(1) });
28		false
29	} else {
30		true
31	}
32}
33
34unsafe extern "C-unwind" fn l_err_handler(l: *mut LuaState) -> c_int {
35	let lua = unsafe { LuaThread::from_ptr_mut(l) };
36
37	if let Some(msg) = lua.to_c_str(1) {
38		lua.traceback(lua, Some(msg), 1);
39		return 1
40	}
41
42	let ok = lua.run_managed(|mut mg| unsafe {
43		mg.call_metamethod(1, c"__tostring")
44	});
45
46	if ok && lua.type_of(-1) == LuaType::String {
47		return 1
48	}
49
50	unsafe { lua_push_fmt_string!(lua, c"(error object is a %s value)", lua.type_name_of(1)) };
51
52	1
53}
54
55unsafe extern "C-unwind" fn l_main(l: *mut LuaState) -> c_int {
56	let lua = unsafe { LuaThread::from_ptr_mut(l) };
57
58	lua.check_version();
59	lua.run_managed(|mut mg| mg.open_libs());
60
61	lua.push_c_function(l_err_handler);
62	let base = lua.top();
63
64	let mut arguments = args().skip(1);
65	let load_status = if let Some(mut file_name) = arguments.next() {
66		lua.load_file(unsafe {
67			file_name.push('\0');
68			CStr::from_bytes_until_nul(file_name.as_bytes()).unwrap_unchecked()
69		})
70	} else {
71		lua.load_stdin()
72	};
73
74	if !report(lua, load_status) {
75		return 0
76	}
77
78	let mut arg_count: c_uint = 0;
79	for arg in arguments {
80		lua.push_string(arg.as_bytes());
81		arg_count += 1;
82	}
83
84	let run_status = lua.run_managed(|mut mg| {
85		mg.restart_gc();
86		let run_status = unsafe { mg.pcall(arg_count, 0, base) };
87		mg.stop_gc();
88		run_status
89	});
90	if !report(lua, run_status) {
91		return 0
92	}
93
94	lua.push_boolean(true);
95	1
96}
97
98fn main() -> ExitCode {
99	let mut lua = Lua::new();
100
101	lua.push_c_function(l_main);
102	let status = lua.run_managed(|mut mg| unsafe { mg.pcall(0, 1, 0) });
103	let is_ok = lua.to_boolean(-1);
104	report(&mut lua, status);
105
106	if status.is_ok() && is_ok {
107		ExitCode::SUCCESS
108	} else {
109		ExitCode::FAILURE
110	}
111}