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
use super::*;

pub fn lua_string<'a>(header: &LuaHeader) -> impl Parser<&'a [u8], &'a [u8], ErrorTree<&'a [u8]>> {
    length_data(lua_size_t(header).map(|x| x as usize))
        .map(|v| if v.is_empty() { v } else { &v[..v.len() - 1] })
        .context("string")
}

pub fn lua_local<'a>(header: &LuaHeader) -> impl Parser<&'a [u8], LuaLocal, ErrorTree<&'a [u8]>> {
    tuple((lua_string(header), lua_int(header), lua_int(header)))
        .map(|(name, start_pc, end_pc)| LuaLocal {
            name: String::from_utf8_lossy(name).into(),
            start_pc,
            end_pc,
            ..Default::default()
        })
        .context("local")
}

pub fn lua_chunk<'h, 'a: 'h>(
    header: &'h LuaHeader,
) -> impl Parser<&'a [u8], LuaChunk, ErrorTree<&'a [u8]>> + 'h {
    |input| {
        let (input, name) = lua_string(header).parse(input)?;
        let (
            input,
            (line_defined, last_line_defined, num_upvalues, num_params, is_vararg, max_stack),
        ) = tuple((lua_int(header), lua_int(header), be_u8, be_u8, be_u8, be_u8))(input)?;
        log::trace!(
            "chunk: {}, line: {line_defined}-{last_line_defined}",
            String::from_utf8_lossy(name)
        );

        map(
            tuple((
                length_count(lua_int(header).map(|x| x as usize), |input| {
                    alt((must(
                        header.instruction_size == 4,
                        complete::u32(header.endian()),
                    ),))(input)
                })
                .context("count instruction"),
                length_count(lua_int(header).map(|x| x as usize), |input| {
                    let (input, b) = be_u8(input)?;
                    let result = match b {
                        0 => success(LuaConstant::Null)(input),
                        1 => map(be_u8, |v| LuaConstant::Bool(v != 0))(input),
                        3 => map(lua_number(header), |v| LuaConstant::Number(v))(input),
                        4 => map(lua_string(header), |v| LuaConstant::from(v.to_vec()))(input),
                        _ => Err(nom::Err::Error(ErrorTree::from_char(
                            input,
                            char::from_digit(b as _, 10).unwrap_or('x'),
                        ))),
                    };
                    result
                })
                .context("count constants"),
                |i| {
                    length_count(lua_int(header).map(|x| x as usize), lua_chunk(header))
                        .context("count prototypes")
                        .parse(i)
                },
                length_count(
                    lua_int(header).map(|x| x as usize),
                    lua_int(header).map(|n| (n as u32, 0u32)),
                )
                .context("count source lines"),
                length_count(lua_int(header).map(|x| x as usize), lua_local(header))
                    .context("count locals"),
                length_count(
                    lua_int(header).map(|x| x as usize),
                    lua_string(header).map(|v| v.to_vec()),
                )
                .context("count upval names"),
            )),
            move |(instructions, constants, prototypes, source_lines, locals, upvalue_names)| {
                LuaChunk {
                    name: name.to_vec(),
                    line_defined,
                    last_line_defined,
                    num_upvalues,
                    num_params,
                    flags: 0,
                    is_vararg: if (is_vararg & 2) != 0 {
                        Some(LuaVarArgInfo {
                            has_arg: (is_vararg & 1) != 0,
                            needs_arg: (is_vararg & 4) != 0,
                        })
                    } else {
                        None
                    },
                    max_stack,
                    instructions,
                    constants,
                    num_constants: vec![],
                    prototypes,
                    source_lines,
                    locals,
                    upvalue_names,
                    upvalue_infos: vec![],
                }
            },
        )
        .context("chunk")
        .parse(input)
    }
}