mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Initial scaffolding to get custom globals and require working
This commit is contained in:
parent
7fe43a969f
commit
7d73601a58
13 changed files with 193 additions and 11 deletions
|
@ -32,7 +32,7 @@ pub async fn show_interface() -> Result<ExitCode> {
|
|||
let mut prompt_state = PromptState::Regular;
|
||||
let mut source_code = String::new();
|
||||
|
||||
let lune_instance = Lune::new();
|
||||
let mut lune_instance = Lune::new();
|
||||
|
||||
loop {
|
||||
let prompt = match prompt_state {
|
||||
|
|
18
src/lune/globals/mod.rs
Normal file
18
src/lune/globals/mod.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::util::TableBuilder;
|
||||
|
||||
mod require;
|
||||
|
||||
pub fn inject_all(lua: &'static Lua) -> LuaResult<()> {
|
||||
let all = TableBuilder::new(lua)?
|
||||
.with_value("require", require::create(lua)?)?
|
||||
.build_readonly()?;
|
||||
|
||||
for res in all.pairs() {
|
||||
let (key, value): (LuaValue, LuaValue) = res?;
|
||||
lua.globals().set(key, value)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
13
src/lune/globals/require/absolute.rs
Normal file
13
src/lune/globals/require/absolute.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::context::*;
|
||||
|
||||
pub(super) async fn require<'lua>(
|
||||
_lua: &'lua Lua,
|
||||
_ctx: RequireContext,
|
||||
path: &str,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
Err(LuaError::runtime(format!(
|
||||
"TODO: Support require for absolute paths (tried to require '{path}')"
|
||||
)))
|
||||
}
|
14
src/lune/globals/require/alias.rs
Normal file
14
src/lune/globals/require/alias.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::context::*;
|
||||
|
||||
pub(super) async fn require<'lua>(
|
||||
_lua: &'lua Lua,
|
||||
_ctx: RequireContext,
|
||||
alias: &str,
|
||||
name: &str,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
Err(LuaError::runtime(format!(
|
||||
"TODO: Support require for built-in libraries (tried to require '{name}' with alias '{alias}')"
|
||||
)))
|
||||
}
|
13
src/lune/globals/require/builtin.rs
Normal file
13
src/lune/globals/require/builtin.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::context::*;
|
||||
|
||||
pub(super) async fn require<'lua>(
|
||||
_lua: &'lua Lua,
|
||||
_ctx: RequireContext,
|
||||
name: &str,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
Err(LuaError::runtime(format!(
|
||||
"TODO: Support require for built-in libraries (tried to require '{name}')"
|
||||
)))
|
||||
}
|
43
src/lune/globals/require/context.rs
Normal file
43
src/lune/globals/require/context.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
const REGISTRY_KEY: &str = "RequireContext";
|
||||
|
||||
// TODO: Store current file path for each thread in
|
||||
// this context somehow, as well as built-in libraries
|
||||
#[derive(Clone)]
|
||||
pub(super) struct RequireContext {
|
||||
pub(super) use_absolute_paths: bool,
|
||||
}
|
||||
|
||||
impl RequireContext {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
// TODO: Set to false by default, load some kind of config
|
||||
// or env var to check if we should be using absolute paths
|
||||
use_absolute_paths: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_registry(lua: &Lua) -> Self {
|
||||
lua.named_registry_value(REGISTRY_KEY)
|
||||
.expect("Missing require context in lua registry")
|
||||
}
|
||||
|
||||
pub fn insert_into_registry(self, lua: &Lua) {
|
||||
lua.set_named_registry_value(REGISTRY_KEY, self)
|
||||
.expect("Failed to insert RequireContext into registry");
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for RequireContext {}
|
||||
|
||||
impl<'lua> FromLua<'lua> for RequireContext {
|
||||
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
|
||||
if let LuaValue::UserData(ud) = value {
|
||||
if let Ok(ctx) = ud.borrow::<RequireContext>() {
|
||||
return Ok(ctx.clone());
|
||||
}
|
||||
}
|
||||
unreachable!("RequireContext should only be used from registry")
|
||||
}
|
||||
}
|
41
src/lune/globals/require/mod.rs
Normal file
41
src/lune/globals/require/mod.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use crate::lune::scheduler::LuaSchedulerExt;
|
||||
|
||||
mod context;
|
||||
use context::RequireContext;
|
||||
|
||||
mod absolute;
|
||||
mod alias;
|
||||
mod builtin;
|
||||
mod relative;
|
||||
|
||||
pub fn create(lua: &'static Lua) -> LuaResult<impl IntoLua<'_>> {
|
||||
RequireContext::new().insert_into_registry(lua);
|
||||
|
||||
lua.create_async_function(|lua, path: LuaString| async move {
|
||||
let context = RequireContext::from_registry(lua);
|
||||
|
||||
let path = path
|
||||
.to_str()
|
||||
.into_lua_err()
|
||||
.context("Failed to parse require path as string")?
|
||||
.to_string();
|
||||
|
||||
if let Some(builtin_name) = path
|
||||
.strip_prefix("@lune/")
|
||||
.map(|name| name.to_ascii_lowercase())
|
||||
{
|
||||
builtin::require(lua, context, &builtin_name).await
|
||||
} else if let Some(aliased_path) = path.strip_prefix('@') {
|
||||
let (alias, name) = aliased_path.split_once('/').ok_or(LuaError::runtime(
|
||||
"Require with custom alias must contain '/' delimiter",
|
||||
))?;
|
||||
alias::require(lua, context, alias, name).await
|
||||
} else if context.use_absolute_paths {
|
||||
absolute::require(lua, context, &path).await
|
||||
} else {
|
||||
relative::require(lua, context, &path).await
|
||||
}
|
||||
})
|
||||
}
|
13
src/lune/globals/require/relative.rs
Normal file
13
src/lune/globals/require/relative.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use mlua::prelude::*;
|
||||
|
||||
use super::context::*;
|
||||
|
||||
pub(super) async fn require<'lua>(
|
||||
_lua: &'lua Lua,
|
||||
_ctx: RequireContext,
|
||||
path: &str,
|
||||
) -> LuaResult<LuaValue<'lua>> {
|
||||
Err(LuaError::runtime(format!(
|
||||
"TODO: Support require for absolute paths (tried to require '{path}')"
|
||||
)))
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use std::process::ExitCode;
|
||||
|
||||
mod error;
|
||||
mod globals;
|
||||
mod scheduler;
|
||||
mod util;
|
||||
|
||||
|
@ -12,6 +13,7 @@ use mlua::Lua;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Lune {
|
||||
lua: &'static Lua,
|
||||
scheduler: &'static Scheduler<'static, 'static>,
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
|
@ -21,8 +23,19 @@ impl Lune {
|
|||
*/
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
// FIXME: Leaking these and using a manual drop implementation
|
||||
// does not feel great... is there any way for us to create a
|
||||
// scheduler, store it in app data, and guarantee it has
|
||||
// the same lifetime as Lua without using any unsafe?
|
||||
let lua = Lua::new().into_static();
|
||||
let scheduler = Scheduler::new(lua).into_static();
|
||||
|
||||
lua.set_app_data(scheduler);
|
||||
globals::inject_all(lua).expect("Failed to inject lua globals");
|
||||
|
||||
Self {
|
||||
lua: Lua::new().into_static(),
|
||||
lua,
|
||||
scheduler,
|
||||
args: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
@ -42,29 +55,29 @@ impl Lune {
|
|||
Runs a Lune script inside of a new Luau VM.
|
||||
*/
|
||||
pub async fn run(
|
||||
&self,
|
||||
&mut self,
|
||||
script_name: impl AsRef<str>,
|
||||
script_contents: impl AsRef<[u8]>,
|
||||
) -> Result<ExitCode, LuneError> {
|
||||
let scheduler = Scheduler::new(self.lua);
|
||||
self.lua.set_app_data(scheduler.clone());
|
||||
|
||||
let main = self
|
||||
.lua
|
||||
.load(script_contents.as_ref())
|
||||
.set_name(script_name.as_ref());
|
||||
|
||||
scheduler.push_back(main, ())?;
|
||||
Ok(scheduler.run_to_completion().await)
|
||||
self.scheduler.push_back(main, ())?;
|
||||
Ok(self.scheduler.run_to_completion().await)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Lune {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: The scheduler needs the static lifetime reference to lua,
|
||||
// when dropped nothing outside of here has access to the scheduler
|
||||
// SAFETY: When the Lune struct is dropped, it is guaranteed
|
||||
// that the Lua and Scheduler structs are no longer being used,
|
||||
// since all the methods that reference them (eg. `run`)
|
||||
// take an exclusive / mutable reference
|
||||
unsafe {
|
||||
Lua::from_static(self.lua);
|
||||
Scheduler::from_static(self.scheduler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ where
|
|||
|
||||
/**
|
||||
Schedules the given `thread` to run when the given `fut` completes.
|
||||
|
||||
If the given future returns a [`LuaError`], that error will be passed to the given `thread`.
|
||||
*/
|
||||
pub fn schedule_future_thread<F, FR>(
|
||||
&'fut self,
|
||||
|
@ -35,6 +37,7 @@ where
|
|||
{
|
||||
let thread = thread.into_owned_lua_thread(self.lua)?;
|
||||
self.schedule_future(async move {
|
||||
// TODO: Throw any error back to lua instead of panicking here
|
||||
let rets = fut.await.expect("Failed to receive result");
|
||||
let rets = rets
|
||||
.into_lua_multi(self.lua)
|
||||
|
|
|
@ -51,4 +51,14 @@ impl<'lua, 'fut> Scheduler<'lua, 'fut> {
|
|||
futures: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn into_static(self) -> &'static Self {
|
||||
Box::leak(Box::new(self))
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub unsafe fn from_static(lua: &'static Scheduler) -> Self {
|
||||
*Box::from_raw(lua as *const Scheduler as *mut Scheduler)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,7 @@ where
|
|||
let async_func = self
|
||||
.load(ASYNC_IMPL_LUA)
|
||||
.set_name("async")
|
||||
.set_environment(async_env)
|
||||
.into_function()?;
|
||||
Ok(async_func)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ macro_rules! create_tests {
|
|||
// The rest of the test logic can continue as normal
|
||||
let full_name = format!("tests/{}.luau", $value);
|
||||
let script = read_to_string(&full_name).await?;
|
||||
let lune = Lune::new().with_args(
|
||||
let mut lune = Lune::new().with_args(
|
||||
ARGS
|
||||
.clone()
|
||||
.iter()
|
||||
|
|
Loading…
Reference in a new issue