Add back luau builtin

This commit is contained in:
Filip Tibell 2023-08-19 19:18:36 -05:00
parent 73361d5a52
commit d6dbe5c861
3 changed files with 160 additions and 0 deletions

View file

@ -0,0 +1,39 @@
use mlua::prelude::*;
use crate::lune::util::TableBuilder;
mod options;
use options::{LuauCompileOptions, LuauLoadOptions};
const BYTECODE_ERROR_BYTE: u8 = 0;
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
TableBuilder::new(lua)?
.with_function("compile", compile_source)?
.with_function("load", load_source)?
.build_readonly()
}
fn compile_source<'lua>(
lua: &'lua Lua,
(source, options): (LuaString<'lua>, LuauCompileOptions),
) -> LuaResult<LuaString<'lua>> {
let bytecode = options.into_compiler().compile(source);
match bytecode.first() {
Some(&BYTECODE_ERROR_BYTE) => Err(LuaError::RuntimeError(
String::from_utf8_lossy(&bytecode).into_owned(),
)),
Some(_) => lua.create_string(bytecode),
None => panic!("Compiling resulted in empty bytecode"),
}
}
fn load_source<'lua>(
lua: &'lua Lua,
(source, options): (LuaString<'lua>, LuauLoadOptions),
) -> LuaResult<LuaFunction<'lua>> {
lua.load(source.as_bytes())
.set_name(options.debug_name)
.into_function()
}

View file

@ -0,0 +1,116 @@
use mlua::prelude::*;
use mlua::Compiler as LuaCompiler;
const DEFAULT_DEBUG_NAME: &str = "luau.load(...)";
pub struct LuauCompileOptions {
pub(crate) optimization_level: u8,
pub(crate) coverage_level: u8,
pub(crate) debug_level: u8,
}
impl LuauCompileOptions {
pub fn into_compiler(self) -> LuaCompiler {
LuaCompiler::default()
.set_optimization_level(self.optimization_level)
.set_coverage_level(self.coverage_level)
.set_debug_level(self.debug_level)
}
}
impl Default for LuauCompileOptions {
fn default() -> Self {
// NOTE: This is the same as LuaCompiler::default() values, but they are
// not accessible from outside of mlua so we need to recreate them here.
Self {
optimization_level: 1,
coverage_level: 0,
debug_level: 1,
}
}
}
impl<'lua> FromLua<'lua> for LuauCompileOptions {
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
Ok(match value {
LuaValue::Nil => Self::default(),
LuaValue::Table(t) => {
let mut options = Self::default();
let get_and_check = |name: &'static str| -> LuaResult<Option<u8>> {
match t.get(name)? {
Some(n @ (0 | 1 | 2)) => Ok(Some(n)),
Some(n) => Err(LuaError::runtime(format!(
"'{name}' must be one of: 0, 1, or 2 - got {n}"
))),
None => Ok(None),
}
};
if let Some(optimization_level) = get_and_check("optimizationLevel")? {
options.optimization_level = optimization_level;
}
if let Some(coverage_level) = get_and_check("coverageLevel")? {
options.coverage_level = coverage_level;
}
if let Some(debug_level) = get_and_check("debugLevel")? {
options.debug_level = debug_level;
}
options
}
_ => {
return Err(LuaError::FromLuaConversionError {
from: value.type_name(),
to: "CompileOptions",
message: Some(format!(
"Invalid compile options - expected table, got {}",
value.type_name()
)),
})
}
})
}
}
pub struct LuauLoadOptions {
pub(crate) debug_name: String,
}
impl Default for LuauLoadOptions {
fn default() -> Self {
Self {
debug_name: DEFAULT_DEBUG_NAME.to_string(),
}
}
}
impl<'lua> FromLua<'lua> for LuauLoadOptions {
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
Ok(match value {
LuaValue::Nil => Self::default(),
LuaValue::Table(t) => {
let mut options = Self::default();
if let Some(debug_name) = t.get("debugName")? {
options.debug_name = debug_name;
}
options
}
LuaValue::String(s) => Self {
debug_name: s.to_string_lossy().to_string(),
},
_ => {
return Err(LuaError::FromLuaConversionError {
from: value.type_name(),
to: "LoadOptions",
message: Some(format!(
"Invalid load options - expected string or table, got {}",
value.type_name()
)),
})
}
})
}
}

View file

@ -2,11 +2,13 @@ use std::str::FromStr;
use mlua::prelude::*; use mlua::prelude::*;
mod luau;
mod stdio; mod stdio;
mod task; mod task;
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum LuneBuiltin { pub enum LuneBuiltin {
Luau,
Task, Task,
Stdio, Stdio,
} }
@ -17,6 +19,7 @@ where
{ {
pub fn name(&self) -> &'static str { pub fn name(&self) -> &'static str {
match self { match self {
Self::Luau => "luau",
Self::Task => "task", Self::Task => "task",
Self::Stdio => "stdio", Self::Stdio => "stdio",
} }
@ -24,6 +27,7 @@ where
pub fn create(&self, lua: &'lua Lua) -> LuaResult<LuaMultiValue<'lua>> { pub fn create(&self, lua: &'lua Lua) -> LuaResult<LuaMultiValue<'lua>> {
let res = match self { let res = match self {
Self::Luau => luau::create(lua),
Self::Task => task::create(lua), Self::Task => task::create(lua),
Self::Stdio => stdio::create(lua), Self::Stdio => stdio::create(lua),
}; };
@ -41,6 +45,7 @@ impl FromStr for LuneBuiltin {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.trim().to_ascii_lowercase().as_str() { match s.trim().to_ascii_lowercase().as_str() {
"luau" => Ok(Self::Luau),
"task" => Ok(Self::Task), "task" => Ok(Self::Task),
"stdio" => Ok(Self::Stdio), "stdio" => Ok(Self::Stdio),
_ => Err(format!("Unknown builtin library '{s}'")), _ => Err(format!("Unknown builtin library '{s}'")),