Simplify load / exec / eval

load is the fundamental operation here
This commit is contained in:
kyren 2017-06-21 19:38:58 -05:00
parent efd77fb862
commit 9dc0a3e098
2 changed files with 59 additions and 108 deletions

View file

@ -951,106 +951,10 @@ impl Lua {
}
}
/// Execute a chunk of Lua code.
///
/// The source can be named by setting the `name` parameter. This is generally recommended as it
/// results in better error traces.
///
/// Returns the values returned by the chunk.
pub fn exec<'lua, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> LuaResult<R> {
unsafe {
stack_guard(self.state, 0, || {
let stack_start = ffi::lua_gettop(self.state);
handle_error(
self.state,
if let Some(name) = name {
let name = CString::new(name.to_owned())?;
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
name.as_ptr(),
)
} else {
ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
)
},
)?;
check_stack(self.state, 2)?;
handle_error(
self.state,
pcall_with_traceback(self.state, 0, ffi::LUA_MULTRET),
)?;
let nresults = ffi::lua_gettop(self.state) - stack_start;
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(self.pop_value(self.state));
}
R::from_lua_multi(results, self)
})
}
}
/// Evaluate the given expression or chunk inside this Lua state.
///
/// If `source` is an expression, returns the value it evaluates to. Otherwise, returns the
/// values returned by the chunk (if any).
pub fn eval<'lua, R: FromLuaMulti<'lua>>(&'lua self, source: &str) -> LuaResult<R> {
unsafe {
stack_guard(self.state, 0, || {
let stack_start = ffi::lua_gettop(self.state);
// First, try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
let return_source = "return ".to_owned() + source;
let mut res = ffi::luaL_loadbuffer(
self.state,
return_source.as_ptr() as *const c_char,
return_source.len(),
ptr::null(),
);
if res == ffi::LUA_ERRSYNTAX {
ffi::lua_pop(self.state, 1);
res = ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
);
}
handle_error(self.state, res)?;
check_stack(self.state, 2)?;
handle_error(
self.state,
pcall_with_traceback(self.state, 0, ffi::LUA_MULTRET),
)?;
let nresults = ffi::lua_gettop(self.state) - stack_start;
let mut results = LuaMultiValue::new();
for _ in 0..nresults {
results.push_front(self.pop_value(self.state));
}
R::from_lua_multi(results, self)
})
}
}
/// Loads a chunk of Lua code and returns it as a function.
///
/// Unlike `exec`, this will not execute the chunk, but precompile it into a callable
/// `LuaFunction`.
/// The source can be named by setting the `name` parameter. This is
/// generally recommended as it results in better error traces.
///
/// Equivalent to Lua's `load` function.
pub fn load(&self, source: &str, name: Option<&str>) -> LuaResult<LuaFunction> {
@ -1081,6 +985,53 @@ impl Lua {
}
}
/// Execute a chunk of Lua code.
///
/// This is equivalent to simply loading the source with `load` and then
/// calling the resulting function with no arguments.
///
/// Returns the values returned by the chunk.
pub fn exec<'lua, R: FromLuaMulti<'lua>>(
&'lua self,
source: &str,
name: Option<&str>,
) -> LuaResult<R> {
self.load(source, name)?.call(())
}
/// Evaluate the given expression or chunk inside this Lua state.
///
/// If `source` is an expression, returns the value it evaluates
/// to. Otherwise, returns the values returned by the chunk (if any).
pub fn eval<'lua, R: FromLuaMulti<'lua>>(&'lua self, source: &str) -> LuaResult<R> {
unsafe {
stack_guard(self.state, 0, || {
// First, try interpreting the lua as an expression by adding
// "return", then as a statement. This is the same thing the
// actual lua repl does.
let return_source = "return ".to_owned() + source;
let mut res = ffi::luaL_loadbuffer(
self.state,
return_source.as_ptr() as *const c_char,
return_source.len(),
ptr::null(),
);
if res == ffi::LUA_ERRSYNTAX {
ffi::lua_pop(self.state, 1);
res = ffi::luaL_loadbuffer(
self.state,
source.as_ptr() as *const c_char,
source.len(),
ptr::null(),
);
}
handle_error(self.state, res)?;
LuaFunction(self.pop_ref(self.state)).call(())
})
}
}
/// Pass a `&str` slice to Lua, creating and returning a interned Lua string.
pub fn create_string(&self, s: &str) -> LuaResult<LuaString> {
unsafe {

View file

@ -16,6 +16,16 @@ fn test_set_get() {
assert_eq!(globals.get::<_, String>("baz").unwrap(), "baf");
}
#[test]
fn test_load() {
let lua = Lua::new();
let func = lua.load("return 1+2", None).unwrap();
let result: i32 = func.call(()).unwrap();
assert_eq!(result, 3);
assert!(lua.load("§$%§&$%&", None).is_err());
}
#[test]
fn test_exec() {
let lua = Lua::new();
@ -730,13 +740,3 @@ fn test_num_conversion() {
assert_eq!(globals.get::<_, f64>("a").unwrap(), 1.5);
assert_eq!(globals.get::<_, String>("a").unwrap(), "1.5");
}
#[test]
fn test_load() {
let lua = Lua::new();
let func = lua.load("return 1+2", None).unwrap();
let result: i32 = func.call(()).unwrap();
assert_eq!(result, 3);
assert!(lua.load("§$%§&$%&", None).is_err());
}