1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! (Very) simple Lua interpreter.

use lunka::prelude::*;
use std::env::args;
use std::ffi::{
	c_int,
	c_uint,
	CStr
};
use std::io::{
	stderr,
	Write
};
use std::mem::transmute;
use std::process::ExitCode;

macro_rules! cstr {
	($data:literal) => {
		CStr::from_bytes_with_nul_unchecked(concat!($data, "\0").as_bytes())
	};
}

fn c_eprintln(data: &CStr) {
	let mut out = stderr();
	let _ = out.write_all(unsafe { transmute(data.to_bytes()) });
	let _ = out.write_all(b"\n");
}

fn report(lua: &mut LuaThread, status: LuaStatus) -> bool {
	if !status.is_ok() {
		if let Some(message) = unsafe { lua.to_string(-1) } {
			c_eprintln(message);
		}
		lua.run_managed(|mut mg| unsafe { mg.pop(1) });
		false
	} else {
		true
	}
}

unsafe extern "C" fn l_err_handler(l: *mut LuaState) -> c_int {
	let mut lua: LuaThread = LuaThread::from_ptr(l);

	if let Some(msg) = lua.to_string(1) {
		lua.traceback(&lua, Some(msg), 1);
		return 1
	}

	let ok = lua.run_managed(|mut mg| {
		mg.call_metamethod(1, cstr!("__tostring"))
	});

	if ok && lua.type_of(-1) == LuaType::String {
		return 1
	}

	lua_push_fmt_string!(lua, "(error object is a %s value)", lua.type_name_of(1));

	1
}

unsafe extern "C" fn l_main(lua: *mut LuaState) -> c_int {
	let mut lua: LuaThread = LuaThread::from_ptr(lua);

	unsafe { lua.check_version() };
	lua.run_managed(|mut mg| unsafe { mg.open_libs() });

	lua.push_c_function(l_err_handler);
	let base = lua.top();

	let mut arguments = args().skip(1);
	let load_status = if let Some(mut file_name) = arguments.nth(0) {
		lua.load_file(unsafe {
			file_name.push('\0');
			CStr::from_bytes_until_nul(file_name.as_bytes()).unwrap_unchecked()
		})
	} else {
		lua.load_stdin()
	};

	if !report(&mut lua, load_status) {
		return 0
	}

	let mut arg_count: c_uint = 0;
	for arg in arguments {
		unsafe { lua.push_byte_str(arg.as_bytes()) };
		arg_count += 1;
	}

	let run_status = lua.run_managed(|mut mg| {
		mg.restart_gc();
		let run_status = mg.pcall(arg_count, 0, base);
		mg.stop_gc();
		run_status
	});
	if !report(&mut lua, run_status) {
		return 0
	}

	lua.push_boolean(true);
	1
}

fn main() -> ExitCode {
	let Some(mut lua) = Lua::new() else {
		eprintln!("cannot create state: not enough memory");
		return ExitCode::FAILURE
	};

	lua.push_c_function(l_main);
	let status = lua.run_managed(|mut mg| mg.pcall(0, 1, 0));
	let is_ok = lua.to_boolean(-1);
	report(&mut lua, status);

	if status.is_ok() && is_ok {
		ExitCode::SUCCESS
	} else {
		ExitCode::FAILURE
	}
}