use std::collections::HashSet;
use std::fmt;
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use crate::lexer::Pos;
use crate::vm::{BinOp, UnOp, Value};
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Oper {
Local(usize),
Up(usize),
Reg(usize),
#[serde(skip)]
Raw(Value), }
#[derive(Clone, Copy, Debug, Deserialize, Serialize)]
pub enum VarType {
Local(usize),
Up(usize),
}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum Literal {
Nil,
Empty,
EmptyCap(usize),
Bool(bool),
Int(i64),
Float(f64),
String(Rc<Vec<u8>>),
}
#[derive(Clone, Deserialize, Serialize)]
pub struct FuncObject {
pub regs: usize,
pub locals_cap: usize,
pub params: Vec<String>,
pub ups: Vec<VarType>,
pub varargs: bool,
pub code: Rc<Code>,
pub linedefined: i64,
pub lastlinedefined: i64,
}
#[derive(Clone, Deserialize, Serialize)]
pub enum OpCode {
Mov {
src_reg: usize,
dst_reg: usize,
},
MovMult {
src_reg: usize,
ind: usize,
dst_reg: usize,
},
Copy {
src_reg: usize,
dst_reg: usize,
},
Lit {
val: Literal,
dst_reg: usize,
},
GlobalGet {
env: VarType,
name: String,
dst_reg: usize,
},
GlobalSet {
src_reg: usize,
name: String,
env: VarType,
},
LocalAlloc {
dst_loc: usize,
name: String,
to_close: bool,
},
LocalClose {
loc_from: usize,
},
LocalGet {
src_loc: usize,
dst_reg: usize,
},
LocalSet {
src_reg: usize,
dst_loc: usize,
},
UpGet {
src_up: usize,
dst_reg: usize,
},
UpSet {
src_reg: usize,
dst_up: usize,
},
Varargs {
dst_reg: usize,
},
BinOp {
lhs: Oper,
rhs: Oper,
op: BinOp,
dst_reg: usize,
},
UnOp {
lhs: Oper,
op: UnOp,
dst_reg: usize,
},
Single {
reg: usize,
},
Append {
src_reg: usize,
dst_reg: usize,
},
Extend {
src_reg: usize,
dst_reg: usize,
},
TableGet {
tbl_reg: usize,
ind_reg: usize,
dst_reg: usize,
},
TableSet {
src_reg: usize,
tbl_reg: usize,
ind_reg: usize,
},
TableExtend {
src_reg: usize,
tbl_reg: usize,
ind_from: usize,
},
TableEmpty {
dst_reg: usize,
},
Closure {
func: Box<FuncObject>,
dst_reg: usize,
},
Jump {
off: i64,
},
JumpIf {
cmp_reg: usize,
off: i64,
},
JumpIfNot {
cmp_reg: usize,
off: i64,
},
JumpClose {
loc_from: usize,
off: i64,
},
ForNum {
ctrl_reg: usize,
limit_reg: usize,
step_reg: Option<usize>,
off_start: i64,
off_end: i64,
ctrl_loc: usize,
},
Call {
func_reg: usize,
args_reg: usize,
ret_reg: usize,
tail: bool,
},
Return {
ret_reg: usize,
},
}
#[derive(Clone, Deserialize, Serialize)]
pub struct Code {
ops: Vec<OpCode>,
pos: Vec<Pos>,
}
impl Code {
pub fn new() -> Self {
Self {
ops: Vec::new(),
pos: Vec::new(),
}
}
pub fn get_op(&self, pc: usize) -> &OpCode {
&self.ops[pc]
}
pub fn get_pos(&self, pc: usize) -> Pos {
self.pos.get(pc).copied().unwrap_or_default()
}
pub fn get_pos_last(&self) -> Pos {
self.pos.last().copied().unwrap_or_default()
}
pub fn get_pos_highest(&self) -> Pos {
self.pos.iter().max().copied().unwrap_or_default()
}
pub fn set_pos_from(&mut self, start: usize, new: Pos) {
for pos in self.pos.iter_mut().skip(start) {
*pos = new;
}
}
pub fn set_op(&mut self, pc: usize, op: OpCode) {
self.ops[pc] = op;
}
pub fn current_pc(&self) -> usize {
self.ops.len()
}
pub fn emit(&mut self, op: OpCode, pos: Pos) {
self.ops.push(op);
self.pos.push(pos);
}
pub fn emit_all<I: IntoIterator<Item = OpCode>>(&mut self, code: I, pos: Pos) {
for op in code {
self.ops.push(op);
self.pos.push(pos);
}
}
pub fn active_lines(&self) -> HashSet<i64> {
self.pos.iter().map(Pos::line).filter(|l| l > &0).collect()
}
pub(super) fn iter_mut_ops<'a>(&'a mut self) -> std::slice::IterMut<'a, OpCode> {
self.ops.iter_mut()
}
pub(super) fn clear_pos(&mut self) {
self.pos.clear();
}
}
const CODE_WIDTH: usize = 12;
impl fmt::Display for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut pos = self.pos.iter();
let op_digits = (self.ops.len() as f64).log10().ceil() as usize;
for (j, op) in self.ops.iter().enumerate() {
write!(f, "{:01$}: ", j, op_digits)?;
let (name, debug) = match op {
OpCode::Mov { src_reg, dst_reg } => {
("Mov", format!("r[{}] = r[{}]", dst_reg, src_reg))
}
OpCode::MovMult {
src_reg,
ind,
dst_reg,
} => (
"MovMult",
format!("r[{}] = r[{}][{}]", dst_reg, src_reg, ind),
),
OpCode::Copy { src_reg, dst_reg } => {
("Copy", format!("r[{}] = r[{}]", dst_reg, src_reg))
}
OpCode::Lit { val, dst_reg } => ("Lit", format!("r[{}] = {:?}", dst_reg, val)),
OpCode::GlobalGet { name, dst_reg, .. } => {
("GlobalGet", format!("r[{}] = g[\"{}\"]", dst_reg, name))
}
OpCode::GlobalSet { src_reg, name, .. } => {
("GlobalSet", format!("g[\"{}\"] = r[{}]", name, src_reg))
}
OpCode::LocalAlloc {
dst_loc,
name,
to_close,
} => (
"LocalAlloc",
format!("alloc l[{}] = nil, ({}, close={})", dst_loc, name, to_close),
),
OpCode::LocalClose { loc_from } => {
("LocalClose", format!("close l[{}, ..]", loc_from))
}
OpCode::LocalGet { src_loc, dst_reg } => {
("LocalGet", format!("r[{}] = l[{}]", dst_reg, src_loc))
}
OpCode::LocalSet { src_reg, dst_loc } => {
("LocalSet", format!("l[{}] = r[{}]", dst_loc, src_reg))
}
OpCode::UpGet { src_up, dst_reg } => {
("UpGet", format!("r[{}] = u[{}]", dst_reg, src_up))
}
OpCode::UpSet { src_reg, dst_up } => {
("UpSet", format!("u[{}] = r[{}]", dst_up, src_reg))
}
OpCode::Varargs { dst_reg } => ("Varargs", format!("r[{}] = ...", dst_reg)),
OpCode::BinOp {
lhs,
rhs,
op,
dst_reg,
} => (
"BinOp",
format!("r[{}] = {} {:?} {}", dst_reg, lhs, op, rhs),
),
OpCode::UnOp { lhs, op, dst_reg } => {
("UnOp", format!("r[{}] = {:?} {}", dst_reg, op, lhs))
}
OpCode::Single { reg } => ("Single", format!("r[{}] = r[{}][..][0]", reg, reg)),
OpCode::Append { src_reg, dst_reg } => (
"Append",
format!("r[{}] = [r[{}][..], r[{}]]", dst_reg, dst_reg, src_reg),
),
OpCode::Extend { src_reg, dst_reg } => (
"Extend",
format!("r[{}] = [r[{}][..], r[{}][..]]", dst_reg, dst_reg, src_reg),
),
OpCode::TableGet {
tbl_reg,
ind_reg,
dst_reg,
} => (
"TableGet",
format!("r[{}] = r[{}][r[{}]]", dst_reg, tbl_reg, ind_reg),
),
OpCode::TableSet {
src_reg,
tbl_reg,
ind_reg,
} => (
"TableSet",
format!("r[{}][r[{}]] = r[{}]", tbl_reg, ind_reg, src_reg),
),
OpCode::TableExtend {
src_reg,
tbl_reg,
ind_from,
} => (
"TableExtend",
format!("r[{}][{}..] = r[{}][..]", tbl_reg, ind_from, src_reg),
),
OpCode::TableEmpty { dst_reg } => ("TableEmpty", format!("r[{}] = {{}}", dst_reg)),
OpCode::Closure { dst_reg, .. } => {
("Closure", format!("r[{}] = close fn", dst_reg))
}
OpCode::Jump { off } => {
("Jump", format!("pc = {} ({:+})", (j as i64) + 1 + off, off))
}
OpCode::JumpIf { cmp_reg, off } => (
"JumpIf",
format!(
"if r[{}] then pc = {} ({:+})",
cmp_reg,
(j as i64) + 1 + off,
off
),
),
OpCode::JumpIfNot { cmp_reg, off } => (
"JumpIfNot",
format!(
"if not r[{}] then pc = {} ({:+})",
cmp_reg,
(j as i64) + 1 + off,
off
),
),
OpCode::JumpClose { loc_from, off } => (
"JumpClose",
format!(
"close l[{}, ..] then pc = {} ({:+})",
loc_from,
(j as i64) + 1 + off,
off,
),
),
OpCode::ForNum {
ctrl_reg,
limit_reg,
step_reg,
off_start,
off_end,
ctrl_loc,
} => (
"ForNum",
format!(
"for l[{}] = r[{}]..r[{}], r[{:?}] do from {} ({:+}) to {} ({:+})",
ctrl_loc,
ctrl_reg,
limit_reg,
step_reg,
(j as i64) + 1 + off_start,
off_start,
(j as i64) + 1 + off_end,
off_end,
),
),
OpCode::Call {
func_reg,
args_reg,
ret_reg,
tail,
} => (
if *tail { "TailCall" } else { "Call" },
format!("r[{}] = r[{}](r[{}][..])", ret_reg, func_reg, args_reg),
),
OpCode::Return { ret_reg } => ("Return", format!("return r[{}]", ret_reg)),
};
writeln!(
f,
"{:04} {:>width$} | {}",
pos.next().map(Pos::line).unwrap_or_default(),
name,
debug,
width = CODE_WIDTH
)?;
}
Ok(())
}
}
impl fmt::Debug for Code {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:p}", &self as *const _)
}
}
impl fmt::Display for Oper {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Oper::Local(loc) => write!(f, "l[{}]", loc),
Oper::Up(up) => write!(f, "u[{}]", up),
Oper::Reg(reg) => write!(f, "r[{}]", reg),
Oper::Raw(..) => unreachable!(),
}
}
}
impl fmt::Display for FuncObject {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let par_width = (self.params.len() as f64).log10().ceil() as usize;
for (i, name) in self.params.iter().enumerate() {
writeln!(f, "loc {:02$}: {}", i, name, par_width)?;
}
let up_width = (self.ups.len() as f64).log10().ceil() as usize;
for (i, typ) in self.ups.iter().enumerate() {
writeln!(f, "up {:02$}: {:?}", i, typ, up_width)?;
}
writeln!(f, "locals: {}", self.locals_cap)?;
writeln!(f, "registers: {}", self.regs)?;
write!(f, "varargs: {}", self.varargs)?;
write!(f, "{}", self.code)
}
}