#[cfg(feature = "decompile-debug")]
mod debug;
#[cfg(not(feature = "decompile-debug"))]
mod debug {
crate::debug::define_unavailable_stage_dump!(dump_parser);
}
mod dialect;
mod error;
mod family;
mod options;
mod raw;
mod reader;
pub(crate) mod strings;
pub use debug::dump_parser;
pub use dialect::lua51::*;
pub use dialect::lua52::*;
pub use dialect::lua53::*;
pub use dialect::lua54::*;
pub use dialect::lua55::*;
pub use dialect::luajit::*;
pub use dialect::luau::*;
pub use error::ParseError;
pub use options::{ParseMode, ParseOptions, StringDecodeMode, StringEncoding};
pub use raw::*;
use dialect::lua51::Lua51Parser;
use dialect::lua52::Lua52Parser;
use dialect::lua53::Lua53Parser;
use dialect::lua54::Lua54Parser;
use dialect::lua55::Lua55Parser;
use dialect::luajit::LuaJitParser;
use dialect::luau::LuauParser;
use crate::decompile::{DecompileContext, DecompileDialect, DecompileError, DecompileState};
pub(crate) fn parse_input(
state: &mut DecompileState,
context: &DecompileContext<'_>,
) -> Result<(), DecompileError> {
state.raw_chunk = Some(parse_chunk_with_dialect(
context.options.dialect,
context.bytes,
context.options.parse,
)?);
Ok(())
}
pub fn detect_dialect(bytes: &[u8]) -> Result<DecompileDialect, ParseError> {
if bytes.starts_with(b"\x1bLua") {
return match bytes.get(4).copied() {
Some(0x51) => Ok(DecompileDialect::Lua51),
Some(0x52) => Ok(DecompileDialect::Lua52),
Some(0x53) => Ok(DecompileDialect::Lua53),
Some(0x54) => Ok(DecompileDialect::Lua54),
Some(0x55) => Ok(DecompileDialect::Lua55),
Some(found) => Err(ParseError::UnsupportedVersion { found }),
None => Err(ParseError::UnexpectedEof {
offset: bytes.len(),
requested: 1,
remaining: 0,
}),
};
}
if bytes.starts_with(b"\x1bLJ") {
return Ok(DecompileDialect::Luajit);
}
if looks_like_luau(bytes) {
return Ok(DecompileDialect::Luau);
}
Err(ParseError::InvalidSignature { offset: 0 })
}
fn looks_like_luau(bytes: &[u8]) -> bool {
let Some((&version, rest)) = bytes.split_first() else {
return false;
};
if version == 0 || version > 32 {
return false;
}
version < 4 || rest.first().is_some_and(|type_version| *type_version <= 3)
}
pub fn parse_chunk_with_dialect(
dialect: DecompileDialect,
bytes: &[u8],
options: ParseOptions,
) -> Result<RawChunk, ParseError> {
let dialect = match dialect {
DecompileDialect::Auto => detect_dialect(bytes)?,
resolved => resolved,
};
match dialect {
DecompileDialect::Auto => unreachable!("auto dialect must be resolved before parsing"),
DecompileDialect::Lua51 => Lua51Parser::new(options).parse(bytes),
DecompileDialect::Lua52 => Lua52Parser::new(options).parse(bytes),
DecompileDialect::Lua53 => Lua53Parser::new(options).parse(bytes),
DecompileDialect::Lua54 => Lua54Parser::new(options).parse(bytes),
DecompileDialect::Lua55 => Lua55Parser::new(options).parse(bytes),
DecompileDialect::Luajit => LuaJitParser::new(options).parse(bytes),
DecompileDialect::Luau => LuauParser::new(options).parse(bytes),
}
}