#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct ExprId(
pub u32,
);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct StatId(
pub u32,
);
#[derive(Clone, Debug)]
pub struct Name {
pub text: Box<str>,
pub line: u32,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Attrib {
Const,
Close,
}
#[derive(Clone, Debug)]
pub struct AttribName {
pub name: Name,
pub attrib: Option<Attrib>,
}
#[derive(Clone, Debug)]
pub struct Block {
pub stats: Vec<StatId>,
}
#[derive(Clone, Debug)]
pub struct FuncName {
pub base: Name,
pub path: Vec<Name>,
pub method: Option<Name>,
}
#[derive(Clone, Debug)]
pub enum Vararg {
None,
Anonymous,
Named(
Name,
),
}
#[derive(Clone, Debug)]
pub struct FuncBody {
pub params: Vec<Name>,
pub vararg: Vararg,
pub block: Block,
pub line: u32,
pub end_line: u32,
}
#[derive(Clone, Debug)]
pub enum Stat {
Do(
Block,
),
While {
cond: ExprId,
body: Block,
},
Repeat {
body: Block,
cond: ExprId,
},
If {
arms: Vec<(ExprId, u32, Block)>,
else_body: Option<Block>,
},
NumericFor {
var: Name,
start: ExprId,
limit: ExprId,
step: Option<ExprId>,
body: Block,
},
GenericFor {
vars: Vec<Name>,
exprs: Vec<ExprId>,
body: Block,
expr_line: u32,
},
Local {
collective: Option<Attrib>,
names: Vec<AttribName>,
exprs: Vec<ExprId>,
},
Global {
collective: Option<Attrib>,
names: Vec<AttribName>,
exprs: Vec<ExprId>,
},
GlobalAll {
attrib: Option<Attrib>,
},
Assign {
targets: Vec<ExprId>,
exprs: Vec<ExprId>,
},
Call(
ExprId,
),
Function {
name: FuncName,
body: FuncBody,
},
LocalFunction {
name: Name,
body: FuncBody,
},
GlobalFunction {
name: Name,
body: FuncBody,
},
Return {
exprs: Vec<ExprId>,
line: u32,
},
Break {
line: u32,
},
Goto(
Name,
),
Label(
Name,
),
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
IDiv,
Mod,
Pow,
Concat,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
BAnd,
BOr,
BXor,
Shl,
Shr,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum UnOp {
Neg,
Not,
Len,
BNot,
}
#[derive(Clone, Debug)]
pub enum TableField {
Item(
ExprId,
),
Named(
Name,
ExprId,
),
Keyed(
ExprId,
ExprId,
),
}
#[derive(Clone, Debug)]
pub enum Expr {
Nil,
True,
False,
Vararg,
Int(
i64,
),
Float(
f64,
),
Str(
Vec<u8>,
),
Name(
Name,
),
Index {
obj: ExprId,
key: ExprId,
},
Call {
func: ExprId,
args: Vec<ExprId>,
line: u32,
},
MethodCall {
obj: ExprId,
method: Name,
args: Vec<ExprId>,
line: u32,
},
Function(
FuncBody,
),
Table {
fields: Vec<TableField>,
line: u32,
},
BinOp {
op: BinOp,
lhs: ExprId,
rhs: ExprId,
line: u32,
},
UnOp {
op: UnOp,
operand: ExprId,
line: u32,
},
Paren(
ExprId,
),
}
#[derive(Clone, Debug)]
pub struct Chunk {
pub exprs: Vec<Expr>,
pub stats: Vec<Stat>,
pub stat_lines: Vec<u32>,
pub block: Block,
pub end_line: u32,
}
impl Chunk {
pub fn expr(&self, id: ExprId) -> &Expr {
&self.exprs[id.0 as usize]
}
pub fn stat(&self, id: StatId) -> &Stat {
&self.stats[id.0 as usize]
}
pub fn stat_line(&self, id: StatId) -> u32 {
self.stat_lines.get(id.0 as usize).copied().unwrap_or(0)
}
}
pub fn block_uses_vararg(chunk: &Chunk, block: &Block) -> bool {
block
.stats
.iter()
.any(|&sid| stat_uses_vararg(chunk, chunk.stat(sid)))
}
fn stat_uses_vararg(chunk: &Chunk, stat: &Stat) -> bool {
use Stat::*;
match stat {
Do(b) => block_uses_vararg(chunk, b),
While { cond, body } => expr_uses_vararg(chunk, *cond) || block_uses_vararg(chunk, body),
Repeat { body, cond } => block_uses_vararg(chunk, body) || expr_uses_vararg(chunk, *cond),
If { arms, else_body } => {
arms.iter()
.any(|(c, _, b)| expr_uses_vararg(chunk, *c) || block_uses_vararg(chunk, b))
|| else_body
.as_ref()
.is_some_and(|b| block_uses_vararg(chunk, b))
}
NumericFor {
start,
limit,
step,
body,
..
} => {
expr_uses_vararg(chunk, *start)
|| expr_uses_vararg(chunk, *limit)
|| step.is_some_and(|s| expr_uses_vararg(chunk, s))
|| block_uses_vararg(chunk, body)
}
GenericFor { exprs, body, .. } => {
exprs.iter().any(|&e| expr_uses_vararg(chunk, e)) || block_uses_vararg(chunk, body)
}
Local { exprs, .. } | Global { exprs, .. } => {
exprs.iter().any(|&e| expr_uses_vararg(chunk, e))
}
GlobalAll { .. } => false,
Assign { targets, exprs } => {
targets.iter().any(|&e| expr_uses_vararg(chunk, e))
|| exprs.iter().any(|&e| expr_uses_vararg(chunk, e))
}
Call(e) => expr_uses_vararg(chunk, *e),
Function { .. } | LocalFunction { .. } | GlobalFunction { .. } => false,
Return { exprs, .. } => exprs.iter().any(|&e| expr_uses_vararg(chunk, e)),
Break { .. } | Goto(_) | Label(_) => false,
}
}
fn expr_uses_vararg(chunk: &Chunk, eid: ExprId) -> bool {
match chunk.expr(eid) {
Expr::Vararg => true,
Expr::Function(_) => false,
Expr::Index { obj, key } => expr_uses_vararg(chunk, *obj) || expr_uses_vararg(chunk, *key),
Expr::Call { func, args, .. } => {
expr_uses_vararg(chunk, *func) || args.iter().any(|&a| expr_uses_vararg(chunk, a))
}
Expr::MethodCall { obj, args, .. } => {
expr_uses_vararg(chunk, *obj) || args.iter().any(|&a| expr_uses_vararg(chunk, a))
}
Expr::Table { fields, .. } => fields.iter().any(|f| table_field_uses_vararg(chunk, f)),
Expr::BinOp { lhs, rhs, .. } => {
expr_uses_vararg(chunk, *lhs) || expr_uses_vararg(chunk, *rhs)
}
Expr::UnOp { operand, .. } => expr_uses_vararg(chunk, *operand),
Expr::Paren(inner) => expr_uses_vararg(chunk, *inner),
Expr::Nil
| Expr::True
| Expr::False
| Expr::Int(_)
| Expr::Float(_)
| Expr::Str(_)
| Expr::Name(_) => false,
}
}
fn table_field_uses_vararg(chunk: &Chunk, f: &TableField) -> bool {
match f {
TableField::Item(e) | TableField::Named(_, e) => expr_uses_vararg(chunk, *e),
TableField::Keyed(k, v) => expr_uses_vararg(chunk, *k) || expr_uses_vararg(chunk, *v),
}
}