use std::fmt;
use std::process;
use crate::ty::codeobj::{CodeObj, CodeObjFlags, MakeFunctionFlags};
use crate::ty::value::GenTypeObj;
use erg_common::cache::CacheSet;
use erg_common::config::ErgConfig;
use erg_common::env::erg_std_path;
use erg_common::error::{ErrorDisplay, Location};
use erg_common::fresh::FreshNameGenerator;
use erg_common::io::Input;
use erg_common::opcode::{CommonOpcode, CompareOp};
use erg_common::opcode308::Opcode308;
use erg_common::opcode310::Opcode310;
use erg_common::opcode311::{BinOpCode, Opcode311};
use erg_common::option_enum_unwrap;
use erg_common::python_util::{env_python_version, PythonVersion};
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{debug_power_assert, fn_name, fn_name_full, impl_stream, log, switch_unreachable};
use erg_parser::ast::VisModifierSpec;
use erg_parser::ast::{DefId, DefKind};
use CommonOpcode::*;
use erg_parser::ast::{ParamPattern, TypeBoundSpecs, VarName};
use erg_parser::token::DOT;
use erg_parser::token::EQUAL;
use erg_parser::token::{Token, TokenKind};
use crate::compile::{AccessKind, Name, StoreLoadKind};
use crate::error::CompileError;
use crate::hir::{
Accessor, Args, Array, BinOp, Block, Call, ClassDef, Def, DefBody, Expr, Identifier, Lambda,
Literal, NonDefaultParamSignature, Params, PatchDef, PosArg, ReDef, Record, Signature,
SubrSignature, Tuple, UnaryOp, VarSignature, HIR,
};
use crate::ty::value::ValueObj;
use crate::ty::{HasType, Type, TypeCode, TypePair, VisibilityModifier};
use crate::varinfo::VarInfo;
use AccessKind::*;
use Type::*;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ControlKind {
If,
While,
For,
Match,
With,
Discard,
Assert,
}
fn debind(ident: &Identifier) -> Option<Str> {
match ident.vi.py_name.as_ref().map(|s| &s[..]) {
Some(name) if name.starts_with("Function::") => {
Some(Str::from(name.replace("Function::", "")))
}
Some(patch_method) if patch_method.contains("::") || patch_method.contains('.') => {
Some(Str::rc(patch_method))
}
_ => None,
}
}
fn escape_name(name: &str, vis: &VisibilityModifier, def_line: u32, def_col: u32) -> Str {
let name = name.replace('!', "__erg_proc__");
let name = name.replace('$', "__erg_shared__");
if vis.is_private() {
let line_mangling = match (def_line, def_col) {
(0, 0) => "".to_string(),
(0, _) => format!("_C{def_col}"),
(_, 0) => format!("_L{def_line}"),
(_, _) => format!("_L{def_line}_C{def_col}"),
};
Str::from(format!("::{name}{line_mangling}"))
} else {
Str::from(name)
}
}
fn escape_ident(ident: Identifier) -> Str {
let vis = ident.vis();
if let Some(py_name) = ident.vi.py_name {
py_name
} else {
escape_name(
ident.inspect(),
vis,
ident.vi.def_loc.loc.ln_begin().unwrap_or(0),
ident.vi.def_loc.loc.col_begin().unwrap_or(0),
)
}
}
#[derive(Debug, Clone)]
pub struct PyCodeGenUnit {
pub(crate) id: usize,
pub(crate) py_version: PythonVersion,
pub(crate) codeobj: CodeObj,
pub(crate) stack_len: u32, pub(crate) prev_lineno: u32,
pub(crate) lasti: usize,
pub(crate) prev_lasti: usize,
pub(crate) _refs: Vec<ValueObj>, }
impl PartialEq for PyCodeGenUnit {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl fmt::Display for PyCodeGenUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"CompilerUnit{{\nid: {}\ncode:\n{}\n}}",
self.id,
self.codeobj.code_info(Some(self.py_version))
)
}
}
impl PyCodeGenUnit {
pub fn new<S: Into<Str>, T: Into<Str>>(
id: usize,
py_version: PythonVersion,
params: Vec<Str>,
filename: S,
name: T,
firstlineno: u32,
flags: u32,
) -> Self {
Self {
id,
py_version,
codeobj: CodeObj::empty(params, filename, name, firstlineno, flags),
stack_len: 0,
prev_lineno: firstlineno,
lasti: 0,
prev_lasti: 0,
_refs: vec![],
}
}
}
#[derive(Debug, Clone)]
pub struct PyCodeGenStack(Vec<PyCodeGenUnit>);
impl_stream!(PyCodeGenStack, PyCodeGenUnit);
#[derive(Debug, Default)]
pub struct PyCodeGenerator {
cfg: ErgConfig,
pub(crate) py_version: PythonVersion,
str_cache: CacheSet<str>,
prelude_loaded: bool,
mutate_op_loaded: bool,
in_op_loaded: bool,
record_type_loaded: bool,
module_type_loaded: bool,
control_loaded: bool,
convertors_loaded: bool,
abc_loaded: bool,
unit_size: usize,
units: PyCodeGenStack,
fresh_gen: FreshNameGenerator,
}
impl PyCodeGenerator {
pub fn new(cfg: ErgConfig) -> Self {
Self {
py_version: cfg.target_version.unwrap_or_else(env_python_version),
cfg,
str_cache: CacheSet::new(),
prelude_loaded: false,
mutate_op_loaded: false,
in_op_loaded: false,
record_type_loaded: false,
module_type_loaded: false,
control_loaded: false,
convertors_loaded: false,
abc_loaded: false,
unit_size: 0,
units: PyCodeGenStack::empty(),
fresh_gen: FreshNameGenerator::new("codegen"),
}
}
pub fn clear(&mut self) {
self.units.clear();
}
pub fn initialize(&mut self) {
self.prelude_loaded = false;
self.mutate_op_loaded = false;
self.in_op_loaded = false;
self.record_type_loaded = false;
self.module_type_loaded = false;
self.control_loaded = false;
self.convertors_loaded = false;
self.abc_loaded = false;
}
#[inline]
fn input(&self) -> &Input {
&self.cfg.input
}
fn get_cached(&self, s: &str) -> Str {
self.str_cache.get(s)
}
#[inline]
fn toplevel_block(&self) -> &PyCodeGenUnit {
self.units.first().unwrap()
}
#[inline]
fn cur_block(&self) -> &PyCodeGenUnit {
self.units.last().unwrap()
}
#[inline]
fn mut_cur_block(&mut self) -> &mut PyCodeGenUnit {
self.units.last_mut().unwrap()
}
#[inline]
fn cur_block_codeobj(&self) -> &CodeObj {
&self.cur_block().codeobj
}
#[inline]
fn mut_cur_block_codeobj(&mut self) -> &mut CodeObj {
&mut self.mut_cur_block().codeobj
}
#[inline]
fn toplevel_block_codeobj(&self) -> &CodeObj {
&self.toplevel_block().codeobj
}
#[inline]
fn stack_len(&self) -> u32 {
self.cur_block().stack_len
}
#[inline]
fn lasti(&self) -> usize {
self.cur_block().lasti
}
#[inline]
#[allow(dead_code)]
fn emit_print_expr(&mut self) {
self.write_instr(Opcode311::PRINT_EXPR);
self.write_arg(0);
self.stack_dec();
}
fn _emit_compare_op(&mut self, op: CompareOp) {
self.write_instr(Opcode311::COMPARE_OP);
self.write_arg(op as usize);
self.stack_dec();
if self.py_version.minor >= Some(11) {
self.write_bytes(&[0; 4]);
}
}
#[allow(dead_code)]
fn terminate(&mut self) {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("exit"));
self.emit_load_const(1);
if self.py_version.minor >= Some(11) {
self.emit_precall_and_call(1);
} else {
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
}
self.stack_dec();
}
fn rot2(&mut self) {
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::SWAP);
self.write_arg(2);
} else {
self.write_instr(Opcode310::ROT_TWO);
self.write_arg(0);
}
}
fn dup_top(&mut self) {
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::COPY);
self.write_arg(1);
} else {
self.write_instr(Opcode310::DUP_TOP);
self.write_arg(0);
}
self.stack_inc();
}
fn copy(&mut self, i: usize) {
debug_power_assert!(i, >, 0);
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::COPY);
self.write_arg(i);
} else if i == 1 {
self.write_instr(Opcode310::DUP_TOP);
} else {
todo!()
}
self.stack_inc();
}
#[allow(dead_code)]
fn peek_stack(&mut self, i: usize) {
self.copy(i + 1);
self.emit_print_expr();
}
#[inline]
fn jump_delta(&self, jump_to: usize) -> usize {
if self.py_version.minor >= Some(10) {
if self.lasti() <= jump_to * 2 {
3
} else {
0
}
} else if self.lasti() <= jump_to {
6
} else {
0
}
}
fn calc_edit_jump(&mut self, idx: usize, jump_to: usize) -> usize {
let arg = if self.py_version.minor >= Some(10) {
jump_to / 2
} else {
jump_to
};
if idx == 0
|| !CommonOpcode::is_jump_op(*self.cur_block_codeobj().code.get(idx - 1).unwrap())
{
self.crash(&format!("calc_edit_jump: not jump op: {idx} {jump_to}"));
}
self.edit_code(idx, arg)
}
#[inline]
fn edit_code(&mut self, idx: usize, arg: usize) -> usize {
log!(err "editing: {idx} {arg}");
match u8::try_from(arg) {
Ok(u8code) => {
*self.mut_cur_block_codeobj().code.get_mut(idx).unwrap() = u8code;
0
}
Err(_e) => {
let delta = self.jump_delta(arg);
let bytes = u32::try_from(arg + delta).unwrap().to_be_bytes();
let before_instr = idx.saturating_sub(1);
*self.mut_cur_block_codeobj().code.get_mut(idx).unwrap() = bytes[3];
self.extend_arg(before_instr, &bytes)
}
}
}
#[inline]
fn extend_arg(&mut self, before_instr: usize, bytes: &[u8]) -> usize {
let mut shift_bytes = 0;
for byte in bytes.iter().rev().skip(1) {
self.mut_cur_block_codeobj()
.code
.insert(before_instr, *byte);
self.mut_cur_block_codeobj()
.code
.insert(before_instr, CommonOpcode::EXTENDED_ARG as u8);
self.mut_cur_block().lasti += 2;
shift_bytes += 2;
}
shift_bytes
}
fn write_instr<C: Into<u8>>(&mut self, code: C) {
self.mut_cur_block_codeobj().code.push(code.into());
self.mut_cur_block().lasti += 1;
}
fn write_arg(&mut self, code: usize) -> usize {
match u8::try_from(code) {
Ok(u8code) => {
self.mut_cur_block_codeobj().code.push(u8code);
self.mut_cur_block().lasti += 1;
1
}
Err(_) => match u16::try_from(code) {
Ok(_) => {
let delta =
if CommonOpcode::is_jump_op(*self.cur_block_codeobj().code.last().unwrap())
{
let shift_bytes = 2;
self.jump_delta(code) + shift_bytes
} else {
0
};
let arg = code + delta;
let bytes = u16::try_from(arg).unwrap().to_be_bytes(); let before_instr = self.lasti().saturating_sub(1);
self.mut_cur_block_codeobj().code.push(bytes[1]);
self.mut_cur_block().lasti += 1;
self.extend_arg(before_instr, &bytes)
}
Err(_) => {
let delta =
if CommonOpcode::is_jump_op(*self.cur_block_codeobj().code.last().unwrap())
{
let shift_bytes = 6;
self.jump_delta(code) + shift_bytes
} else {
0
};
let arg = code + delta;
let bytes = u32::try_from(arg).unwrap().to_be_bytes(); let before_instr = self.lasti().saturating_sub(1);
self.mut_cur_block_codeobj().code.push(bytes[3]);
self.mut_cur_block().lasti += 1;
self.extend_arg(before_instr, &bytes)
}
},
}
}
fn write_bytes(&mut self, bytes: &[u8]) {
self.mut_cur_block_codeobj().code.extend_from_slice(bytes);
self.mut_cur_block().lasti += bytes.len();
}
fn stack_inc(&mut self) {
self.mut_cur_block().stack_len += 1;
if self.stack_len() > self.cur_block_codeobj().stacksize {
self.mut_cur_block_codeobj().stacksize = self.stack_len();
}
}
fn stack_dec(&mut self) {
if self.stack_len() == 0 {
let lasti = self.lasti();
let last = self.cur_block_codeobj().code.last().unwrap();
self.crash(&format!(
"the stack size becomes -1\nlasti: {lasti}\nlast code: {last}"
));
} else {
self.mut_cur_block().stack_len -= 1;
}
}
fn stack_inc_n(&mut self, n: usize) {
self.mut_cur_block().stack_len += n as u32;
if self.stack_len() > self.cur_block_codeobj().stacksize {
self.mut_cur_block_codeobj().stacksize = self.stack_len();
}
}
fn stack_dec_n(&mut self, n: usize) {
if n > 0 && self.stack_len() == 0 {
let lasti = self.lasti();
let last = self.cur_block_codeobj().code.last().unwrap();
self.crash(&format!(
"the stack size becomes -1\nlasti: {lasti}\nlast code: {last}"
));
} else {
self.mut_cur_block().stack_len -= n as u32;
}
}
fn emit_load_const<C: Into<ValueObj>>(&mut self, cons: C) {
let value: ValueObj = cons.into();
let idx = self
.mut_cur_block_codeobj()
.consts
.iter()
.position(|c| c == &value)
.unwrap_or_else(|| {
self.mut_cur_block_codeobj().consts.push(value);
self.mut_cur_block_codeobj().consts.len() - 1
});
self.write_instr(LOAD_CONST);
self.write_arg(idx);
self.stack_inc();
}
fn register_const<C: Into<ValueObj>>(&mut self, cons: C) -> usize {
let value = cons.into();
self.mut_cur_block_codeobj()
.consts
.iter()
.position(|c| c == &value)
.unwrap_or_else(|| {
self.mut_cur_block_codeobj().consts.push(value);
self.mut_cur_block_codeobj().consts.len() - 1
})
}
fn local_search(&self, name: &str, _acc_kind: AccessKind) -> Option<Name> {
let current_is_toplevel = self.cur_block() == self.toplevel_block();
if let Some(idx) = self
.cur_block_codeobj()
.names
.iter()
.position(|n| &**n == name)
{
Some(Name::local(idx))
} else if let Some(idx) = self
.cur_block_codeobj()
.varnames
.iter()
.position(|v| &**v == name)
{
if current_is_toplevel {
Some(Name::local(idx))
} else {
Some(Name::fast(idx))
}
} else {
self.cur_block_codeobj()
.freevars
.iter()
.position(|f| &**f == name)
.map(Name::deref)
}
}
fn rec_search(&mut self, name: &str) -> Option<StoreLoadKind> {
for (nth_from_toplevel, block) in self.units.iter_mut().enumerate().rev().skip(1) {
let block_is_toplevel = nth_from_toplevel == 0;
if block.codeobj.cellvars.iter().any(|c| &**c == name) {
return Some(StoreLoadKind::Deref);
} else if let Some(idx) = block.codeobj.varnames.iter().position(|v| &**v == name) {
if block_is_toplevel {
return Some(StoreLoadKind::Global);
} else {
let cellvar_name = block.codeobj.varnames.get(idx).unwrap().clone();
block.codeobj.cellvars.push(cellvar_name);
return Some(StoreLoadKind::Deref);
}
}
if block_is_toplevel && block.codeobj.names.iter().any(|n| &**n == name) {
return Some(StoreLoadKind::Global);
}
}
Some(StoreLoadKind::Global)
}
fn register_name(&mut self, name: Str) -> Name {
let current_is_toplevel = self.cur_block() == self.toplevel_block();
match self.rec_search(&name) {
Some(st @ (StoreLoadKind::Local | StoreLoadKind::Global)) => {
let st = if current_is_toplevel {
StoreLoadKind::Local
} else {
st
};
self.mut_cur_block_codeobj().names.push(name);
Name::new(st, self.cur_block_codeobj().names.len() - 1)
}
Some(StoreLoadKind::Deref) => {
self.mut_cur_block_codeobj().freevars.push(name.clone());
if self.py_version.minor >= Some(11) {
self.mut_cur_block_codeobj().varnames.push(name);
Name::deref(self.cur_block_codeobj().varnames.len() - 1)
} else {
Name::deref(self.cur_block_codeobj().freevars.len() - 1)
}
}
None => {
if current_is_toplevel {
self.mut_cur_block_codeobj().names.push(name);
Name::local(self.cur_block_codeobj().names.len() - 1)
} else {
self.mut_cur_block_codeobj().varnames.push(name);
Name::fast(self.cur_block_codeobj().varnames.len() - 1)
}
}
Some(_) => {
switch_unreachable!()
}
}
}
fn register_attr(&mut self, name: Str) -> Name {
self.mut_cur_block_codeobj().names.push(name);
Name::local(self.cur_block_codeobj().names.len() - 1)
}
fn register_method(&mut self, name: Str) -> Name {
self.mut_cur_block_codeobj().names.push(name);
Name::local(self.cur_block_codeobj().names.len() - 1)
}
fn select_load_instr(&self, kind: StoreLoadKind, acc_kind: AccessKind) -> u8 {
match kind {
StoreLoadKind::Fast | StoreLoadKind::FastConst => LOAD_FAST as u8,
StoreLoadKind::Global | StoreLoadKind::GlobalConst => LOAD_NAME as u8, StoreLoadKind::Deref | StoreLoadKind::DerefConst => {
if self.py_version.minor >= Some(11) {
Opcode311::LOAD_DEREF as u8
} else {
Opcode310::LOAD_DEREF as u8
}
}
StoreLoadKind::Local | StoreLoadKind::LocalConst => match acc_kind {
Name => LOAD_NAME as u8,
UnboundAttr => LOAD_ATTR as u8,
BoundAttr => LOAD_METHOD as u8,
},
}
}
fn select_store_instr(&self, kind: StoreLoadKind, acc_kind: AccessKind) -> u8 {
match kind {
StoreLoadKind::Fast => STORE_FAST as u8,
StoreLoadKind::FastConst => STORE_FAST as u8, StoreLoadKind::Global | StoreLoadKind::GlobalConst => STORE_NAME as u8,
StoreLoadKind::Deref | StoreLoadKind::DerefConst => {
if self.py_version.minor >= Some(11) {
Opcode311::STORE_DEREF as u8
} else {
Opcode310::STORE_DEREF as u8
}
}
StoreLoadKind::Local | StoreLoadKind::LocalConst => {
match acc_kind {
Name => STORE_NAME as u8,
UnboundAttr => STORE_ATTR as u8,
BoundAttr => STORE_ATTR as u8,
}
}
}
}
fn emit_load_name_instr(&mut self, ident: Identifier) {
log!(info "entered {}({ident})", fn_name!());
let escaped = escape_ident(ident);
match &escaped[..] {
"if__" | "for__" | "while__" | "with__" | "discard__" => {
self.load_control();
}
"int__" | "nat__" | "str__" | "float__" => {
self.load_convertors();
}
"NoneType" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("type"));
self.emit_load_const(ValueObj::None);
self.emit_precall_and_call(1);
self.stack_dec();
return;
}
_ => {}
}
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
let instr = self.select_load_instr(name.kind, Name);
self.write_instr(instr);
self.write_arg(name.idx);
self.stack_inc();
if instr == LOAD_GLOBAL as u8 && self.py_version.minor >= Some(11) {
self.write_bytes(&[0; 2]);
self.write_bytes(&[0; 8]);
}
}
fn emit_load_global_instr(&mut self, ident: Identifier) {
log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
let instr = LOAD_GLOBAL;
self.write_instr(instr);
self.write_arg(name.idx);
self.stack_inc();
}
fn emit_import_name_instr(&mut self, ident: Identifier, items_len: usize) {
log!(info "entered {}({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
self.write_instr(IMPORT_NAME);
self.write_arg(name.idx);
self.stack_inc_n(items_len);
self.stack_dec(); }
fn emit_import_from_instr(&mut self, ident: Identifier) {
log!(info "entered {}", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
self.write_instr(IMPORT_FROM);
self.write_arg(name.idx);
}
fn emit_import_all_instr(&mut self, ident: Identifier) {
log!(info "entered {}", fn_name!());
self.emit_load_const(0i32); self.emit_load_const([Str::ever("*")]);
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
self.write_instr(IMPORT_NAME);
self.write_arg(name.idx);
self.stack_inc();
self.write_instr(IMPORT_STAR);
self.write_arg(0);
self.stack_dec_n(3);
}
fn emit_global_import_items(
&mut self,
module: Identifier,
items: Vec<(Identifier, Option<Identifier>)>,
) {
self.emit_load_const(0);
let item_name_tuple = items
.iter()
.map(|ident| ValueObj::Str(ident.0.inspect().clone()))
.collect::<Vec<_>>();
let items_len = item_name_tuple.len();
self.emit_load_const(item_name_tuple);
self.emit_import_name_instr(module, items_len);
for (item, renamed) in items.into_iter() {
if let Some(renamed) = renamed {
self.emit_import_from_instr(item);
self.emit_store_global_instr(renamed);
} else {
self.emit_import_from_instr(item.clone());
self.emit_store_global_instr(item);
}
}
self.emit_pop_top(); }
fn emit_load_attr_instr(&mut self, ident: Identifier) {
log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, UnboundAttr)
.unwrap_or_else(|| self.register_attr(escaped));
let instr = self.select_load_instr(name.kind, UnboundAttr);
self.write_instr(instr);
self.write_arg(name.idx);
if self.py_version.minor >= Some(11) {
self.write_bytes(&[0; 8]);
}
}
fn emit_load_method_instr(&mut self, ident: Identifier) {
log!(info "entered {} ({ident})", fn_name!());
if &ident.inspect()[..] == "__new__" {
log!("{:?}", ident.vi);
}
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, BoundAttr)
.unwrap_or_else(|| self.register_method(escaped));
let instr = self.select_load_instr(name.kind, BoundAttr);
self.write_instr(instr);
self.write_arg(name.idx);
if self.py_version.minor >= Some(11) {
self.stack_inc(); self.write_bytes(&[0; 20]);
}
}
fn emit_store_instr(&mut self, ident: Identifier, acc_kind: AccessKind) {
log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self.local_search(&escaped, acc_kind).unwrap_or_else(|| {
if acc_kind.is_local() {
self.register_name(escaped)
} else {
self.register_attr(escaped)
}
});
let instr = self.select_store_instr(name.kind, acc_kind);
self.write_instr(instr);
self.write_arg(name.idx);
self.stack_dec();
if instr == STORE_ATTR as u8 {
if self.py_version.minor >= Some(11) {
self.write_bytes(&[0; 8]);
}
self.stack_dec();
}
}
fn emit_store_global_instr(&mut self, ident: Identifier) {
log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
let instr = STORE_GLOBAL;
self.write_instr(instr);
self.write_arg(name.idx);
self.stack_dec();
}
fn store_acc(&mut self, acc: Accessor) {
log!(info "entered {} ({acc})", fn_name!());
match acc {
Accessor::Ident(ident) => {
self.emit_store_instr(ident, Name);
}
Accessor::Attr(attr) => {
self.emit_expr(*attr.obj);
self.emit_store_instr(attr.ident, UnboundAttr);
}
}
}
fn emit_pop_top(&mut self) {
self.write_instr(POP_TOP);
self.write_arg(0);
self.stack_dec();
}
fn cancel_if_pop_top(&mut self) {
if self.cur_block_codeobj().code.len() < 2 {
return;
}
let lasop_t_idx = self.cur_block_codeobj().code.len() - 2;
if self.cur_block_codeobj().code.get(lasop_t_idx) == Some(&(POP_TOP as u8)) {
self.mut_cur_block_codeobj().code.pop();
self.mut_cur_block_codeobj().code.pop();
self.mut_cur_block().lasti -= 2;
self.stack_inc();
}
}
fn crash(&mut self, description: &str) -> ! {
if cfg!(feature = "debug") {
println!("current block: {}", self.cur_block());
panic!("internal error: {description}");
} else {
let err = CompileError::compiler_bug(
0,
self.input().clone(),
Location::Unknown,
fn_name!(),
line!(),
);
err.write_to_stderr();
process::exit(1);
}
}
fn gen_param_names(&self, params: &Params) -> Vec<Str> {
params
.non_defaults
.iter()
.map(|p| (p.inspect().map(|s| &s[..]).unwrap_or("_"), &p.vi))
.chain(if let Some(var_args) = ¶ms.var_params {
vec![(
var_args.inspect().map(|s| &s[..]).unwrap_or("_"),
&var_args.vi,
)]
} else {
vec![]
})
.chain(
params
.defaults
.iter()
.map(|p| (p.inspect().map(|s| &s[..]).unwrap_or("_"), &p.sig.vi)),
)
.enumerate()
.map(|(i, (s, vi))| {
if s == "_" {
format!("_{i}")
} else {
escape_name(
s,
&VisibilityModifier::Private,
vi.def_loc.loc.ln_begin().unwrap_or(0),
vi.def_loc.loc.col_begin().unwrap_or(0),
)
.to_string()
}
})
.map(|s| self.get_cached(&s))
.collect()
}
fn emit_acc(&mut self, acc: Accessor) {
log!(info "entered {} ({acc})", fn_name!());
match acc {
Accessor::Ident(ident) => {
if &ident.inspect()[..] == "#ModuleType" && !self.module_type_loaded {
self.load_module_type();
self.module_type_loaded = true;
}
self.emit_load_name_instr(ident);
}
Accessor::Attr(mut a) => {
let is_record = a.obj.ref_t().is_record();
if is_record {
a.ident.raw.vis = VisModifierSpec::Public(DOT);
}
if let Some(varname) = debind(&a.ident) {
a.ident.raw.vis = VisModifierSpec::Private;
a.ident.raw.name = VarName::from_str(varname);
self.emit_load_name_instr(a.ident);
} else {
self.emit_expr(*a.obj);
self.emit_load_attr_instr(a.ident);
}
}
}
}
fn emit_def(&mut self, def: Def) {
log!(info "entered {} ({})", fn_name!(), def.sig);
if def.def_kind().is_trait() {
return self.emit_trait_def(def);
}
match def.sig {
Signature::Subr(sig) => self.emit_subr_def(None, sig, def.body),
Signature::Var(sig) => self.emit_var_def(sig, def.body),
}
}
fn emit_push_null(&mut self) {
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::PUSH_NULL);
self.write_arg(0);
self.stack_inc();
}
}
fn emit_precall_and_call(&mut self, argc: usize) {
self.write_instr(Opcode311::PRECALL);
self.write_arg(argc);
self.write_arg(0);
self.write_arg(0);
self.write_instr(Opcode311::CALL);
self.write_arg(argc);
self.write_bytes(&[0; 8]);
self.stack_dec();
}
fn emit_call_instr(&mut self, argc: usize, kind: AccessKind) {
if self.py_version.minor >= Some(11) {
self.emit_precall_and_call(argc);
} else {
match kind {
AccessKind::BoundAttr => self.write_instr(Opcode310::CALL_METHOD),
_ => self.write_instr(Opcode310::CALL_FUNCTION),
}
self.write_arg(argc);
}
}
fn emit_call_kw_instr(&mut self, argc: usize, kws: Vec<ValueObj>) {
if self.py_version.minor >= Some(11) {
let idx = self.register_const(kws);
self.write_instr(Opcode311::KW_NAMES);
self.write_arg(idx);
self.emit_precall_and_call(argc);
} else {
self.emit_load_const(kws);
self.write_instr(Opcode310::CALL_FUNCTION_KW);
self.write_arg(argc);
}
}
fn emit_trait_def(&mut self, def: Def) {
if !self.abc_loaded {
self.load_abc();
self.abc_loaded = true;
}
self.emit_push_null();
self.write_instr(LOAD_BUILD_CLASS);
self.write_arg(0);
self.stack_inc();
let code = self.emit_trait_block(def.def_kind(), &def.sig, def.body.block);
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
self.emit_load_const(def.sig.ident().inspect().clone());
} else {
self.stack_inc();
}
self.write_instr(MAKE_FUNCTION);
self.write_arg(0);
self.emit_load_const(def.sig.ident().inspect().clone());
self.emit_load_name_instr(Identifier::private("#ABCMeta"));
let subclasses_len = 1;
self.emit_call_kw_instr(2 + subclasses_len, vec![ValueObj::from("metaclass")]);
let sum = if self.py_version.minor >= Some(11) {
1 + 2 + subclasses_len
} else {
1 + 2 + 1 + subclasses_len
};
self.stack_dec_n(sum - 1);
self.emit_store_instr(def.sig.into_ident(), Name);
self.stack_dec();
}
fn emit_trait_block(&mut self, kind: DefKind, sig: &Signature, mut block: Block) -> CodeObj {
debug_assert_eq!(kind, DefKind::Trait);
let name = sig.ident().inspect().clone();
let Expr::Call(mut trait_call) = block.remove(0) else { unreachable!() };
let req = if let Some(Expr::Record(req)) = trait_call.args.remove_left_or_key("Requirement")
{
req.attrs.into_iter()
} else {
vec![].into_iter()
};
self.unit_size += 1;
let firstlineno = block
.get(0)
.and_then(|def| def.ln_begin())
.unwrap_or_else(|| sig.ln_begin().unwrap());
self.units.push(PyCodeGenUnit::new(
self.unit_size,
self.py_version,
vec![],
Str::rc(self.cfg.input.enclosed_name()),
&name,
firstlineno,
0,
));
let mod_name = self.toplevel_block_codeobj().name.clone();
self.emit_load_const(mod_name);
self.emit_store_instr(Identifier::public("__module__"), Name);
self.emit_load_const(name);
self.emit_store_instr(Identifier::public("__qualname__"), Name);
for def in req {
self.emit_empty_func(
Some(sig.ident().inspect()),
def.sig.into_ident(),
Some(Identifier::private("#abstractmethod")),
);
}
self.emit_load_const(ValueObj::None);
self.write_instr(RETURN_VALUE);
self.write_arg(0);
if self.stack_len() > 1 {
let block_id = self.cur_block().id;
let stack_len = self.stack_len();
CompileError::stack_bug(
self.input().clone(),
Location::Unknown,
stack_len,
block_id,
fn_name_full!(),
)
.write_to_stderr();
self.crash("error in emit_trait_block: invalid stack size");
}
if !self.cur_block_codeobj().varnames.is_empty() {
self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32;
}
let unit = self.units.pop().unwrap();
if !self.units.is_empty() {
let ld = unit.prev_lineno - self.cur_block().prev_lineno;
if ld != 0 {
if let Some(l) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*l += ld as u8;
}
self.mut_cur_block().prev_lineno += ld;
}
}
unit.codeobj
}
fn emit_empty_func(
&mut self,
class_name: Option<&str>,
ident: Identifier,
deco: Option<Identifier>,
) {
log!(info "entered {} ({ident})", fn_name!());
self.emit_push_null();
let deco_is_some = deco.is_some();
if let Some(deco) = deco {
self.emit_load_name_instr(deco);
}
let code = {
self.unit_size += 1;
self.units.push(PyCodeGenUnit::new(
self.unit_size,
self.py_version,
vec![],
Str::rc(self.cfg.input.enclosed_name()),
ident.inspect(),
ident.ln_begin().unwrap_or(0),
0,
));
self.emit_load_const(ValueObj::None);
self.write_instr(RETURN_VALUE);
self.write_arg(0);
let unit = self.units.pop().unwrap();
if !self.units.is_empty() {
let ld = unit
.prev_lineno
.saturating_sub(self.cur_block().prev_lineno);
if ld != 0 {
if let Some(l) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*l += ld as u8;
}
self.mut_cur_block().prev_lineno += ld;
}
}
unit.codeobj
};
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
if let Some(class) = class_name {
self.emit_load_const(Str::from(format!("{class}.{}", ident.inspect())));
} else {
self.emit_load_const(ident.inspect().clone());
}
} else {
self.stack_inc();
}
self.write_instr(MAKE_FUNCTION);
self.write_arg(0);
if deco_is_some {
self.emit_call_instr(1, Name);
self.stack_dec();
}
self.stack_dec();
self.emit_store_instr(ident, Name);
}
fn emit_class_def(&mut self, class_def: ClassDef) {
log!(info "entered {} ({})", fn_name!(), class_def.sig);
self.emit_push_null();
let ident = class_def.sig.ident().clone();
let require_or_sup = class_def.require_or_sup.clone().map(|x| *x);
let obj = class_def.obj.clone();
self.write_instr(LOAD_BUILD_CLASS);
self.write_arg(0);
self.stack_inc();
let code = self.emit_class_block(class_def);
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
self.emit_load_const(ident.inspect().clone());
} else {
self.stack_inc();
}
self.write_instr(MAKE_FUNCTION);
self.write_arg(0);
self.emit_load_const(ident.inspect().clone());
let subclasses_len = self.emit_require_type(obj, require_or_sup);
self.emit_call_instr(2 + subclasses_len, Name);
self.stack_dec_n((1 + 2 + subclasses_len) - 1);
self.emit_store_instr(ident, Name);
self.stack_dec();
}
fn emit_patch_def(&mut self, patch_def: PatchDef) {
log!(info "entered {} ({})", fn_name!(), patch_def.sig);
for def in patch_def.methods {
let Expr::Def(mut def) = def else { todo!() };
let namespace = self.cur_block_codeobj().name.trim_start_matches("::");
let name = format!(
"{}{}{}",
namespace,
patch_def.sig.ident().to_string_notype(),
def.sig.ident().to_string_notype()
);
def.sig.ident_mut().raw.name = VarName::from_str(Str::from(name));
def.sig.ident_mut().raw.vis = VisModifierSpec::Private;
self.emit_def(def);
}
}
fn emit_require_type(&mut self, obj: GenTypeObj, require_or_sup: Option<Expr>) -> usize {
log!(info "entered {} ({obj}, {require_or_sup:?})", fn_name!());
match obj {
GenTypeObj::Class(_) => 0,
GenTypeObj::Subclass(_) => {
self.emit_expr(require_or_sup.unwrap());
1 }
_ => todo!(),
}
}
fn emit_redef(&mut self, redef: ReDef) {
log!(info "entered {} ({redef})", fn_name!());
self.emit_frameless_block(redef.block, vec![]);
self.store_acc(redef.attr);
}
fn emit_var_def(&mut self, sig: VarSignature, mut body: DefBody) {
log!(info "entered {} ({sig} = {})", fn_name!(), body.block);
if body.block.len() == 1 {
self.emit_expr(body.block.remove(0));
} else {
self.emit_frameless_block(body.block, vec![]);
}
self.emit_store_instr(sig.ident, Name);
}
fn emit_subr_def(&mut self, class_name: Option<&str>, sig: SubrSignature, body: DefBody) {
log!(info "entered {} ({sig} = {})", fn_name!(), body.block);
let name = sig.ident.inspect().clone();
let mut make_function_flag = 0;
let params = self.gen_param_names(&sig.params);
if !sig.params.defaults.is_empty() {
let defaults_len = sig.params.defaults.len();
sig.params
.defaults
.into_iter()
.for_each(|default| self.emit_expr(default.default_val));
self.write_instr(BUILD_TUPLE);
self.write_arg(defaults_len);
self.stack_dec_n(defaults_len - 1);
make_function_flag += MakeFunctionFlags::Defaults as usize;
}
let flags = if sig.params.var_params.is_some() {
CodeObjFlags::VarArgs as u32
} else {
0
};
let code = self.emit_block(body.block, Some(name.clone()), params, flags);
self.register_cellvars(&mut make_function_flag);
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
if let Some(class) = class_name {
self.emit_load_const(Str::from(format!("{class}.{name}")));
} else {
self.emit_load_const(name);
}
} else {
self.stack_inc();
}
self.write_instr(MAKE_FUNCTION);
self.write_arg(make_function_flag);
self.stack_dec();
if make_function_flag & MakeFunctionFlags::Defaults as usize != 0 {
self.stack_dec();
}
self.emit_store_instr(sig.ident, Name);
}
fn emit_lambda(&mut self, lambda: Lambda) {
log!(info "entered {} ({lambda})", fn_name!());
let mut make_function_flag = 0;
let params = self.gen_param_names(&lambda.params);
if !lambda.params.defaults.is_empty() {
let defaults_len = lambda.params.defaults.len();
lambda
.params
.defaults
.into_iter()
.for_each(|default| self.emit_expr(default.default_val));
self.write_instr(BUILD_TUPLE);
self.write_arg(defaults_len);
self.stack_dec_n(defaults_len - 1);
make_function_flag += MakeFunctionFlags::Defaults as usize;
}
let flags = if lambda.params.var_params.is_some() {
CodeObjFlags::VarArgs as u32
} else {
0
};
let code = self.emit_block(lambda.body, Some("<lambda>".into()), params, flags);
self.register_cellvars(&mut make_function_flag);
self.emit_load_const(code);
if self.py_version.minor < Some(11) {
self.emit_load_const("<lambda>");
} else {
self.stack_inc();
}
self.write_instr(MAKE_FUNCTION);
self.write_arg(make_function_flag);
self.stack_dec();
if make_function_flag & MakeFunctionFlags::Defaults as usize != 0 {
self.stack_dec();
}
}
fn register_cellvars(&mut self, flag: &mut usize) {
if !self.cur_block_codeobj().cellvars.is_empty() {
let cellvars_len = self.cur_block_codeobj().cellvars.len();
for i in 0..cellvars_len {
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::MAKE_CELL);
self.write_arg(i);
self.write_instr(Opcode311::LOAD_CLOSURE);
} else {
self.write_instr(Opcode310::LOAD_CLOSURE);
}
self.write_arg(i);
}
self.write_instr(BUILD_TUPLE);
self.write_arg(cellvars_len);
*flag += MakeFunctionFlags::Closure as usize;
}
}
fn emit_unaryop(&mut self, unary: UnaryOp) {
log!(info "entered {} ({unary})", fn_name!());
let val_t = unary
.info
.t
.non_default_params()
.and_then(|tys| tys.get(0).map(|pt| pt.typ()))
.unwrap_or(Type::FAILURE);
let tycode = TypeCode::from(val_t);
let instr = match &unary.op.kind {
TokenKind::PrePlus => UNARY_POSITIVE,
TokenKind::PreMinus => UNARY_NEGATIVE,
TokenKind::PreBitNot => UNARY_INVERT,
TokenKind::Mutate => {
if !self.mutate_op_loaded {
self.load_mutate_op();
}
if self.py_version.minor >= Some(11) {
self.emit_push_null();
}
self.emit_load_name_instr(Identifier::private("#mutate_operator"));
NOP }
_ => {
CompileError::feature_error(
self.cfg.input.clone(),
unary.op.loc(),
&unary.op.inspect().clone(),
String::from(unary.op.content),
)
.write_to_stderr();
NOT_IMPLEMENTED
}
};
self.emit_expr(*unary.expr);
if instr != NOP {
self.write_instr(instr);
self.write_arg(tycode as usize);
} else {
if self.py_version.minor >= Some(11) {
self.emit_precall_and_call(1);
} else {
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
}
self.stack_dec();
}
}
fn emit_binop(&mut self, bin: BinOp) {
log!(info "entered {} ({bin})", fn_name!());
match &bin.op.kind {
TokenKind::RightOpen => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("RightOpenRange"));
}
TokenKind::LeftOpen => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("LeftOpenRange"));
}
TokenKind::Closed => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("ClosedRange"));
}
TokenKind::Open => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("OpenRange"));
}
TokenKind::InOp => {
if self.cfg.no_std {
self.emit_load_const(true);
return;
}
if !self.in_op_loaded {
self.load_in_op();
}
self.emit_push_null();
self.emit_load_name_instr(Identifier::private("#in_operator"));
}
_ => {}
}
let lhs_t = bin
.info
.t
.non_default_params()
.and_then(|tys| tys.get(0).map(|pt| pt.typ()))
.unwrap_or(Type::FAILURE);
let rhs_t = bin
.info
.t
.non_default_params()
.and_then(|tys| tys.get(1).map(|pt| pt.typ()))
.unwrap_or(Type::FAILURE);
let type_pair = TypePair::new(lhs_t, rhs_t);
self.emit_expr(*bin.lhs);
self.emit_expr(*bin.rhs);
self.emit_binop_instr(bin.op, type_pair);
}
fn emit_binop_instr(&mut self, binop: Token, type_pair: TypePair) {
if self.py_version.minor >= Some(11) {
self.emit_binop_instr_311(binop, type_pair);
} else {
self.emit_binop_instr_310(binop, type_pair);
}
}
fn emit_binop_instr_310(&mut self, binop: Token, type_pair: TypePair) {
let instr = match &binop.kind {
TokenKind::Plus => Opcode310::BINARY_ADD,
TokenKind::Minus => Opcode310::BINARY_SUBTRACT,
TokenKind::Star => Opcode310::BINARY_MULTIPLY,
TokenKind::Slash => Opcode310::BINARY_TRUE_DIVIDE,
TokenKind::FloorDiv => Opcode310::BINARY_FLOOR_DIVIDE,
TokenKind::Pow => Opcode310::BINARY_POWER,
TokenKind::Mod => Opcode310::BINARY_MODULO,
TokenKind::AndOp | TokenKind::BitAnd => Opcode310::BINARY_AND,
TokenKind::OrOp | TokenKind::BitOr => Opcode310::BINARY_OR,
TokenKind::BitXor => Opcode310::BINARY_XOR,
TokenKind::IsOp | TokenKind::IsNotOp => Opcode310::IS_OP,
TokenKind::Less
| TokenKind::LessEq
| TokenKind::DblEq
| TokenKind::NotEq
| TokenKind::Gre
| TokenKind::GreEq => Opcode310::COMPARE_OP,
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Closed
| TokenKind::Open
| TokenKind::InOp => Opcode310::CALL_FUNCTION, _ => {
CompileError::feature_error(
self.cfg.input.clone(),
binop.loc(),
&binop.inspect().clone(),
String::from(binop.content),
)
.write_to_stderr();
Opcode310::NOT_IMPLEMENTED
}
};
let arg = match &binop.kind {
TokenKind::Less => 0,
TokenKind::LessEq => 1,
TokenKind::DblEq => 2,
TokenKind::NotEq => 3,
TokenKind::Gre => 4,
TokenKind::GreEq => 5,
TokenKind::IsOp => 0,
TokenKind::IsNotOp => 1,
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Closed
| TokenKind::Open
| TokenKind::InOp => 2,
_ => type_pair as usize,
};
self.write_instr(instr);
self.write_arg(arg);
self.stack_dec();
match &binop.kind {
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Open
| TokenKind::Closed
| TokenKind::InOp => {
self.stack_dec();
}
_ => {}
}
}
fn emit_binop_instr_311(&mut self, binop: Token, type_pair: TypePair) {
let instr = match &binop.kind {
TokenKind::Plus
| TokenKind::Minus
| TokenKind::Star
| TokenKind::Slash
| TokenKind::FloorDiv
| TokenKind::Pow
| TokenKind::Mod
| TokenKind::AndOp
| TokenKind::OrOp
| TokenKind::BitAnd
| TokenKind::BitOr
| TokenKind::BitXor => Opcode311::BINARY_OP,
TokenKind::IsOp | TokenKind::IsNotOp => Opcode311::IS_OP,
TokenKind::Less
| TokenKind::LessEq
| TokenKind::DblEq
| TokenKind::NotEq
| TokenKind::Gre
| TokenKind::GreEq => Opcode311::COMPARE_OP,
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Closed
| TokenKind::Open
| TokenKind::InOp => {
self.write_instr(Opcode311::PRECALL);
self.write_arg(2);
self.write_arg(0);
self.write_arg(0);
Opcode311::CALL
}
_ => {
CompileError::feature_error(
self.cfg.input.clone(),
binop.loc(),
&binop.inspect().clone(),
String::from(binop.content),
)
.write_to_stderr();
Opcode311::NOT_IMPLEMENTED
}
};
let arg = match &binop.kind {
TokenKind::Plus => BinOpCode::Add as usize,
TokenKind::Minus => BinOpCode::Subtract as usize,
TokenKind::Star => BinOpCode::Multiply as usize,
TokenKind::Slash => BinOpCode::TrueDivide as usize,
TokenKind::FloorDiv => BinOpCode::FloorDiv as usize,
TokenKind::Pow => BinOpCode::Power as usize,
TokenKind::Mod => BinOpCode::Remainder as usize,
TokenKind::AndOp | TokenKind::BitAnd => BinOpCode::And as usize,
TokenKind::OrOp | TokenKind::BitOr => BinOpCode::Or as usize,
TokenKind::BitXor => BinOpCode::Xor as usize,
TokenKind::Less => 0,
TokenKind::LessEq => 1,
TokenKind::DblEq => 2,
TokenKind::NotEq => 3,
TokenKind::Gre => 4,
TokenKind::GreEq => 5,
TokenKind::IsOp => 0,
TokenKind::IsNotOp => 1,
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Closed
| TokenKind::Open
| TokenKind::InOp => 2,
_ => type_pair as usize,
};
self.write_instr(instr);
self.write_arg(arg);
match instr {
Opcode311::CALL => {
self.write_bytes(&[0; 8]);
}
Opcode311::BINARY_OP => {
self.write_bytes(&[0; 2]);
}
Opcode311::COMPARE_OP => {
self.write_bytes(&[0; 4]);
}
_ => {}
}
self.stack_dec();
match &binop.kind {
TokenKind::LeftOpen
| TokenKind::RightOpen
| TokenKind::Open
| TokenKind::Closed
| TokenKind::InOp => {
self.stack_dec();
if self.py_version.minor >= Some(11) {
self.stack_dec();
}
}
_ => {}
}
}
fn emit_del_instr(&mut self, mut args: Args) {
let Some(Expr::Accessor(Accessor::Ident(ident))) = args.remove_left_or_key("obj") else {
log!(err "del instruction requires an identifier");
return;
};
log!(info "entered {} ({ident})", fn_name!());
let escaped = escape_ident(ident);
let name = self
.local_search(&escaped, Name)
.unwrap_or_else(|| self.register_name(escaped));
self.write_instr(DELETE_NAME);
self.write_arg(name.idx);
self.emit_load_const(ValueObj::None);
}
fn emit_not_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
let expr = args.remove_left_or_key("b").unwrap();
self.emit_expr(expr);
self.write_instr(UNARY_NOT);
self.write_arg(0);
}
fn emit_discard_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
while let Some(arg) = args.try_remove(0) {
self.emit_expr(arg);
self.emit_pop_top();
}
self.emit_load_const(ValueObj::None);
}
fn deopt_instr(&mut self, kind: ControlKind, args: Args) {
if !self.control_loaded {
self.load_control();
}
let local = match kind {
ControlKind::If => Identifier::public("if__"),
ControlKind::For => Identifier::public("for__"),
ControlKind::While => Identifier::public("while__"),
ControlKind::With => Identifier::public("with__"),
ControlKind::Discard => Identifier::public("discard__"),
ControlKind::Assert => Identifier::public("assert__"),
kind => todo!("{kind:?}"),
};
self.emit_call_local(local, args);
}
fn emit_if_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
let init_stack_len = self.stack_len();
let cond = args.remove(0);
self.emit_expr(cond);
let idx_pop_jump_if_false = self.lasti();
self.write_instr(Opcode310::POP_JUMP_IF_FALSE);
self.write_arg(0);
match args.remove(0) {
Expr::Lambda(lambda) => {
self.emit_frameless_block(lambda.body, vec![]);
}
other => {
self.emit_expr(other);
}
}
if args.get(0).is_some() {
let mut idx_jump_forward = self.lasti();
self.write_instr(JUMP_FORWARD); self.write_arg(0);
let idx_else_begin = if self.py_version.minor >= Some(11) {
self.lasti() - idx_pop_jump_if_false - 2
} else {
self.lasti()
};
idx_jump_forward += self.calc_edit_jump(idx_pop_jump_if_false + 1, idx_else_begin);
match args.remove(0) {
Expr::Lambda(lambda) => {
self.emit_frameless_block(lambda.body, vec![]);
}
other => {
self.emit_expr(other);
}
}
let idx_end = self.lasti();
self.calc_edit_jump(idx_jump_forward + 1, idx_end - idx_jump_forward - 2);
while self.stack_len() != init_stack_len + 1 {
self.stack_dec();
}
} else {
self.write_instr(JUMP_FORWARD);
self.write_arg(1);
let idx_end = if self.py_version.minor >= Some(11) {
self.lasti() - idx_pop_jump_if_false - 1
} else {
self.lasti()
};
self.calc_edit_jump(idx_pop_jump_if_false + 1, idx_end);
self.emit_load_const(ValueObj::None);
while self.stack_len() != init_stack_len + 1 {
self.stack_dec();
}
}
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
fn emit_for_instr(&mut self, mut args: Args) {
log!(info "entered {} ({})", fn_name!(), args);
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::For, args);
}
let _init_stack_len = self.stack_len();
let iterable = args.remove(0);
self.emit_expr(iterable);
self.write_instr(GET_ITER);
self.write_arg(0);
let idx_for_iter = self.lasti();
self.write_instr(FOR_ITER);
self.stack_inc();
self.write_arg(0);
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let init_stack_len = self.stack_len();
let params = self.gen_param_names(&lambda.params);
self.emit_frameless_block(lambda.body, params);
if self.stack_len() > init_stack_len - 1 {
self.emit_pop_top();
}
debug_assert_eq!(self.stack_len(), init_stack_len - 1); match self.py_version.minor {
Some(11) => {
self.write_instr(Opcode311::JUMP_BACKWARD);
self.write_arg((self.lasti() - idx_for_iter + 2) / 2);
}
Some(10) => {
self.write_instr(Opcode310::JUMP_ABSOLUTE);
self.write_arg(idx_for_iter / 2);
}
Some(9 | 8 | 7) => {
self.write_instr(Opcode308::JUMP_ABSOLUTE);
self.write_arg(idx_for_iter);
}
_ => todo!("not supported Python version"),
}
let idx_end = self.lasti();
self.calc_edit_jump(idx_for_iter + 1, idx_end - idx_for_iter - 2);
self.stack_dec();
self.emit_load_const(ValueObj::None);
debug_assert_eq!(self.stack_len(), _init_stack_len + 1);
}
fn emit_while_instr(&mut self, mut args: Args) {
log!(info "entered {} ({})", fn_name!(), args);
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::While, args);
}
let _init_stack_len = self.stack_len();
let cond_block = args.remove(0);
let cond = match cond_block {
Expr::Lambda(mut lambda) => lambda.body.remove(0),
Expr::Accessor(acc) => Expr::Accessor(acc).call_expr(Args::empty()),
_ => todo!(),
};
self.emit_expr(cond.clone());
let idx_while = self.lasti();
self.write_instr(Opcode310::POP_JUMP_IF_FALSE);
self.write_arg(0);
self.stack_dec();
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let init_stack_len = self.stack_len();
let params = self.gen_param_names(&lambda.params);
self.emit_frameless_block(lambda.body, params);
if self.stack_len() > init_stack_len {
self.emit_pop_top();
}
self.emit_expr(cond);
let arg = if self.py_version.minor >= Some(11) {
let arg = self.lasti() - (idx_while + 2);
self.write_instr(Opcode311::POP_JUMP_BACKWARD_IF_TRUE);
arg / 2 + 1
} else {
self.write_instr(Opcode310::POP_JUMP_IF_TRUE);
if self.py_version.minor >= Some(10) {
(idx_while + 2) / 2
} else {
idx_while + 2
}
};
self.write_arg(arg);
self.stack_dec();
let idx_end = if self.py_version.minor >= Some(11) {
self.lasti() - idx_while - 1
} else {
self.lasti()
};
self.calc_edit_jump(idx_while + 1, idx_end);
self.emit_load_const(ValueObj::None);
debug_assert_eq!(self.stack_len(), _init_stack_len + 1);
}
fn emit_match_instr(&mut self, mut args: Args, _use_erg_specific: bool) {
log!(info "entered {}", fn_name!());
let init_stack_len = self.stack_len();
let expr = args.remove(0);
self.emit_expr(expr);
let len = args.len();
let mut jump_forward_points = vec![];
while let Some(expr) = args.try_remove(0) {
if len > 1 && !args.is_empty() {
self.dup_top();
}
let Expr::Lambda(mut lambda) = expr else { unreachable!() };
debug_power_assert!(lambda.params.len(), ==, 1);
if !lambda.params.defaults.is_empty() {
todo!("default values in match expression are not supported yet")
}
let param = lambda.params.non_defaults.remove(0);
let pop_jump_points = self.emit_match_pattern(param, args.is_empty());
self.emit_frameless_block(lambda.body, Vec::new());
self.stack_dec();
for pop_jump_point in pop_jump_points.into_iter() {
let idx = if self.py_version.minor >= Some(11) {
self.lasti() - pop_jump_point } else {
self.lasti() + 2
};
self.calc_edit_jump(pop_jump_point + 1, idx); jump_forward_points.push(self.lasti());
self.write_instr(JUMP_FORWARD); self.write_arg(0);
}
}
let lasti = self.lasti();
for jump_point in jump_forward_points.into_iter() {
self.calc_edit_jump(jump_point + 1, lasti - jump_point - 1);
}
self.stack_inc();
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
fn emit_match_pattern(
&mut self,
param: NonDefaultParamSignature,
is_last_arm: bool,
) -> Vec<usize> {
log!(info "entered {}", fn_name!());
let mut pop_jump_points = vec![];
if let Some(t_spec) = param.t_spec_as_expr {
if !is_last_arm {
if self.py_version.minor >= Some(11) {
self.emit_push_null();
self.rot2();
}
if !self.in_op_loaded {
self.load_in_op();
}
self.emit_load_name_instr(Identifier::private("#in_operator"));
self.rot2();
self.emit_expr(t_spec);
if self.py_version.minor >= Some(11) {
self.emit_precall_and_call(2);
} else {
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(2);
}
self.stack_dec();
pop_jump_points.push(self.lasti());
self.write_instr(Opcode310::POP_JUMP_IF_FALSE); self.write_arg(0);
self.stack_dec();
}
}
match param.raw.pat {
ParamPattern::VarName(name) => {
let ident = erg_parser::ast::Identifier::private_from_varname(name);
let ident = Identifier::new(ident, None, param.vi);
self.emit_store_instr(ident, AccessKind::Name);
}
ParamPattern::Discard(_) => {
self.emit_pop_top();
}
_other => unreachable!(),
}
pop_jump_points
}
fn emit_with_instr_311(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::With, args);
}
let expr = args.remove(0);
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr);
self.write_instr(Opcode311::BEFORE_WITH);
self.write_arg(0);
self.stack_inc_n(2);
let lambda_line = lambda.body.last().unwrap().ln_begin().unwrap_or(0);
self.emit_with_block(lambda.body, params);
let stash = Identifier::private_with_line(self.fresh_gen.fresh_varname(), lambda_line);
self.emit_store_instr(stash.clone(), Name);
self.emit_load_const(ValueObj::None);
self.emit_load_const(ValueObj::None);
self.emit_load_const(ValueObj::None);
self.emit_precall_and_call(2);
self.emit_pop_top();
let idx_jump_forward = self.lasti();
self.write_instr(Opcode311::JUMP_FORWARD);
self.write_arg(0);
self.write_instr(Opcode311::PUSH_EXC_INFO);
self.write_arg(0);
self.write_instr(Opcode308::WITH_EXCEPT_START);
self.write_arg(0);
self.write_instr(Opcode311::POP_JUMP_FORWARD_IF_TRUE);
self.write_arg(4);
self.write_instr(Opcode311::RERAISE);
self.write_arg(0);
self.write_instr(Opcode311::COPY);
self.write_arg(3);
self.write_instr(Opcode311::POP_EXCEPT);
self.write_arg(0);
self.write_instr(Opcode311::RERAISE);
self.write_arg(1);
self.emit_pop_top();
self.write_instr(Opcode311::POP_EXCEPT);
self.write_arg(0);
self.emit_pop_top();
self.emit_pop_top();
self.calc_edit_jump(idx_jump_forward + 1, self.lasti() - idx_jump_forward - 2);
self.emit_load_name_instr(stash);
}
fn emit_with_instr_310(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::With, args);
}
let expr = args.remove(0);
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr);
let idx_setup_with = self.lasti();
self.write_instr(Opcode310::SETUP_WITH);
self.write_arg(0);
self.stack_inc_n(2);
let lambda_line = lambda.body.last().unwrap().ln_begin().unwrap_or(0);
self.emit_with_block(lambda.body, params);
let stash = Identifier::private_with_line(self.fresh_gen.fresh_varname(), lambda_line);
self.emit_store_instr(stash.clone(), Name);
self.write_instr(POP_BLOCK);
self.write_arg(0);
self.emit_load_const(ValueObj::None);
self.write_instr(Opcode310::DUP_TOP);
self.write_arg(0);
self.stack_inc();
self.write_instr(Opcode310::DUP_TOP);
self.write_arg(0);
self.stack_inc();
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(3);
self.stack_dec_n((1 + 3) - 1);
self.emit_pop_top();
let idx_jump_forward = self.lasti();
self.write_instr(JUMP_FORWARD);
self.write_arg(0);
self.edit_code(idx_setup_with + 1, (self.lasti() - idx_setup_with - 2) / 2);
self.write_instr(Opcode310::WITH_EXCEPT_START);
self.write_arg(0);
let idx_pop_jump_if_true = self.lasti();
self.write_instr(Opcode310::POP_JUMP_IF_TRUE);
self.write_arg(0);
self.write_instr(Opcode310::RERAISE);
self.write_arg(1);
self.edit_code(idx_pop_jump_if_true + 1, self.lasti() / 2);
self.emit_pop_top();
self.write_instr(Opcode310::POP_EXCEPT);
self.write_arg(0);
let idx_end = self.lasti();
self.edit_code(idx_jump_forward + 1, (idx_end - idx_jump_forward - 2) / 2);
self.emit_load_name_instr(stash);
}
fn emit_with_instr_308(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if !matches!(args.get(1).unwrap(), Expr::Lambda(_)) {
return self.deopt_instr(ControlKind::With, args);
}
let expr = args.remove(0);
let Expr::Lambda(lambda) = args.remove(0) else { unreachable!() };
let params = self.gen_param_names(&lambda.params);
self.emit_expr(expr);
let idx_setup_with = self.lasti();
self.write_instr(Opcode308::SETUP_WITH);
self.write_arg(0);
let lambda_line = lambda.body.last().unwrap().ln_begin().unwrap_or(0);
self.emit_with_block(lambda.body, params);
let stash = Identifier::private_with_line(self.fresh_gen.fresh_varname(), lambda_line);
self.emit_store_instr(stash.clone(), Name);
self.write_instr(POP_BLOCK);
self.write_arg(0);
self.write_instr(Opcode308::BEGIN_FINALLY);
self.write_arg(0);
self.write_instr(Opcode308::WITH_CLEANUP_START);
self.write_arg(0);
self.edit_code(idx_setup_with + 1, (self.lasti() - idx_setup_with - 2) / 2);
self.write_instr(Opcode308::WITH_CLEANUP_FINISH);
self.write_arg(0);
self.write_instr(Opcode308::END_FINALLY);
self.write_arg(0);
self.emit_load_name_instr(stash);
}
fn emit_call(&mut self, call: Call) {
log!(info "entered {} ({call})", fn_name!());
let init_stack_len = self.stack_len();
if let Some(attr_name) = call.attr_name {
self.emit_call_method(*call.obj, attr_name, call.args);
} else {
match *call.obj {
Expr::Accessor(Accessor::Ident(ident)) if ident.vis().is_private() => {
self.emit_call_local(ident, call.args)
}
other => {
let is_py_api = other.is_py_api();
self.emit_push_null();
self.emit_expr(other);
self.emit_args_311(call.args, Name, is_py_api);
}
}
}
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
fn emit_call_local(&mut self, local: Identifier, args: Args) {
log!(info "entered {}", fn_name!());
match &local.inspect()[..] {
"assert" => self.emit_assert_instr(args),
"Del" => self.emit_del_instr(args),
"not" => self.emit_not_instr(args),
"discard" => self.emit_discard_instr(args),
"for" | "for!" => self.emit_for_instr(args),
"while!" => self.emit_while_instr(args),
"if" | "if!" => self.emit_if_instr(args),
"match" | "match!" => self.emit_match_instr(args, true),
"with!" => match self.py_version.minor {
Some(11) => self.emit_with_instr_311(args),
Some(10) => self.emit_with_instr_310(args),
Some(9 | 8 | 7) => self.emit_with_instr_308(args),
_ => todo!("not supported Python version"),
},
_ => {
let is_py_api = local.is_py_api();
self.emit_push_null();
self.emit_load_name_instr(local);
self.emit_args_311(args, Name, is_py_api);
}
}
}
fn emit_call_method(&mut self, obj: Expr, method_name: Identifier, args: Args) {
log!(info "entered {}", fn_name!());
match &method_name.inspect()[..] {
"update!" => {
if self.py_version.minor >= Some(11) {
return self.emit_call_update_311(obj, args);
} else {
return self.emit_call_update_310(obj, args);
}
}
"return" if obj.ref_t().is_callable() => {
return self.emit_return_instr(args);
}
"yield" => {
return self.emit_yield_instr(args);
}
_ => {}
}
if let Some(func_name) = debind(&method_name) {
return self.emit_call_fake_method(obj, func_name, method_name, args);
}
let is_py_api = method_name.is_py_api();
self.emit_expr(obj);
self.emit_load_method_instr(method_name);
self.emit_args_311(args, BoundAttr, is_py_api);
}
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
if pos_len > 0 {
self.write_instr(BUILD_LIST);
self.write_arg(pos_len);
}
self.emit_expr(var_args.expr.clone());
if pos_len > 0 {
self.write_instr(Opcode310::LIST_EXTEND);
self.write_arg(1);
self.write_instr(Opcode310::LIST_TO_TUPLE);
self.write_arg(0);
}
}
fn emit_var_args_38(&mut self, pos_len: usize, var_args: &PosArg) {
if pos_len > 0 {
self.write_instr(BUILD_TUPLE);
self.write_arg(pos_len);
}
self.emit_expr(var_args.expr.clone());
if pos_len > 0 {
self.write_instr(Opcode308::BUILD_TUPLE_UNPACK_WITH_CALL);
self.write_arg(2);
}
}
fn emit_args_311(&mut self, mut args: Args, kind: AccessKind, is_py_api: bool) {
let argc = args.len();
let pos_len = args.pos_args.len();
let mut kws = Vec::with_capacity(args.kw_len());
while let Some(arg) = args.try_remove_pos(0) {
self.emit_expr(arg.expr);
}
if let Some(var_args) = &args.var_args {
if self.py_version.minor >= Some(10) {
self.emit_var_args_311(pos_len, var_args);
} else {
self.emit_var_args_38(pos_len, var_args);
}
}
while let Some(arg) = args.try_remove_kw(0) {
let kw = if is_py_api {
arg.keyword.content
} else {
escape_name(&arg.keyword.content, &VisibilityModifier::Private, 0, 0)
};
kws.push(ValueObj::Str(kw));
self.emit_expr(arg.expr);
}
let kwsc = if !kws.is_empty() {
self.emit_call_kw_instr(argc, kws);
#[allow(clippy::bool_to_int_with_if)]
if self.py_version.minor >= Some(11) {
0
} else {
1
}
} else {
if args.var_args.is_some() {
self.write_instr(CALL_FUNCTION_EX);
if kws.is_empty() {
self.write_arg(0);
} else {
self.write_arg(1);
}
} else {
self.emit_call_instr(argc, kind);
}
0
};
self.stack_dec_n((1 + argc + kwsc) - 1);
}
fn emit_call_update_311(&mut self, obj: Expr, mut args: Args) {
log!(info "entered {}", fn_name!());
let Expr::Accessor(acc) = obj else { unreachable!() };
let func = args.remove_left_or_key("f").unwrap();
if !self.mutate_op_loaded {
self.load_mutate_op();
}
self.emit_push_null();
self.emit_load_name_instr(Identifier::private("#mutate_operator"));
self.emit_push_null();
self.emit_expr(func);
self.emit_acc(acc.clone());
self.emit_precall_and_call(1);
self.stack_dec();
self.emit_precall_and_call(1);
self.stack_dec();
self.store_acc(acc);
self.emit_load_const(ValueObj::None);
}
fn emit_call_update_310(&mut self, obj: Expr, mut args: Args) {
log!(info "entered {}", fn_name!());
let Expr::Accessor(acc) = obj else { unreachable!() };
let func = args.remove_left_or_key("f").unwrap();
if !self.mutate_op_loaded {
self.load_mutate_op();
}
self.emit_load_name_instr(Identifier::private("#mutate_operator"));
self.emit_expr(func);
self.emit_acc(acc.clone());
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
self.stack_dec_n((1 + 1) - 1);
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
self.stack_dec();
self.store_acc(acc);
self.emit_load_const(ValueObj::None);
}
fn emit_return_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if args.is_empty() {
self.emit_load_const(ValueObj::None);
} else {
self.emit_expr(args.remove(0));
}
self.write_instr(RETURN_VALUE);
self.write_arg(0);
}
fn emit_yield_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
if args.is_empty() {
self.emit_load_const(ValueObj::None);
} else {
self.emit_expr(args.remove(0));
}
self.write_instr(YIELD_VALUE);
self.write_arg(0);
}
fn emit_call_fake_method(
&mut self,
obj: Expr,
func_name: Str,
mut method_name: Identifier,
mut args: Args,
) {
log!(info "entered {}", fn_name!());
method_name.raw.vis = VisModifierSpec::Private;
method_name.vi.py_name = Some(func_name);
self.emit_push_null();
self.emit_load_name_instr(method_name);
args.insert_pos(0, PosArg::new(obj));
self.emit_args_311(args, Name, true);
}
fn emit_assert_instr(&mut self, mut args: Args) {
log!(info "entered {}", fn_name!());
let init_stack_len = self.stack_len();
self.emit_expr(args.remove(0));
let pop_jump_point = self.lasti();
self.write_instr(Opcode310::POP_JUMP_IF_TRUE);
self.write_arg(0);
self.stack_dec();
if self.py_version.minor >= Some(10) {
self.write_instr(Opcode310::LOAD_ASSERTION_ERROR);
self.write_arg(0);
self.stack_inc();
} else {
self.emit_load_global_instr(Identifier::public("AssertionError"));
}
if let Some(expr) = args.try_remove(0) {
self.emit_expr(expr);
if self.py_version.minor >= Some(11) {
self.emit_precall_and_call(0);
} else {
self.write_instr(Opcode310::CALL_FUNCTION);
self.write_arg(1);
}
}
self.write_instr(RAISE_VARARGS);
self.write_arg(1);
self.stack_dec();
let idx = match self.py_version.minor {
Some(11) => (self.lasti() - pop_jump_point - 2) / 2,
Some(10) => self.lasti() / 2,
Some(_) => self.lasti(),
_ => todo!(),
};
self.edit_code(pop_jump_point + 1, idx);
self.emit_load_const(ValueObj::None);
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
fn emit_array(&mut self, array: Array) {
let init_stack_len = self.stack_len();
if !self.cfg.no_std {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Array"));
}
match array {
Array::Normal(mut arr) => {
let len = arr.elems.len();
while let Some(arg) = arr.elems.try_remove_pos(0) {
self.emit_expr(arg.expr);
}
self.write_instr(BUILD_LIST);
self.write_arg(len);
if len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(len - 1);
}
}
Array::WithLength(arr) => {
self.emit_expr(*arr.elem);
self.write_instr(BUILD_LIST);
self.write_arg(1);
self.emit_call_instr(1, Name);
self.stack_dec();
self.emit_expr(*arr.len);
self.emit_binop_instr(Token::dummy(TokenKind::Star, "*"), TypePair::ArrayNat);
return;
}
other => todo!("{other}"),
}
if !self.cfg.no_std {
self.emit_call_instr(1, Name);
self.stack_dec();
}
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
fn emit_tuple(&mut self, tuple: Tuple) {
match tuple {
Tuple::Normal(mut tup) => {
let len = tup.elems.len();
while let Some(arg) = tup.elems.try_remove_pos(0) {
self.emit_expr(arg.expr);
}
self.write_instr(BUILD_TUPLE);
self.write_arg(len);
if len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(len - 1);
}
}
}
}
fn emit_set(&mut self, set: crate::hir::Set) {
match set {
crate::hir::Set::Normal(mut set) => {
let len = set.elems.len();
while let Some(arg) = set.elems.try_remove_pos(0) {
self.emit_expr(arg.expr);
}
self.write_instr(BUILD_SET);
self.write_arg(len);
if len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(len - 1);
}
}
crate::hir::Set::WithLength(st) => {
self.emit_expr(*st.elem);
self.write_instr(BUILD_SET);
self.write_arg(1);
}
}
}
fn emit_dict(&mut self, dict: crate::hir::Dict) {
match dict {
crate::hir::Dict::Normal(dic) => {
let len = dic.kvs.len();
for kv in dic.kvs.into_iter() {
self.emit_expr(kv.key);
self.emit_expr(kv.value);
}
self.write_instr(BUILD_MAP);
self.write_arg(len);
if len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(2 * len - 1);
}
}
other => todo!("{other}"),
}
}
#[allow(clippy::identity_op)]
fn emit_record(&mut self, rec: Record) {
log!(info "entered {} ({rec})", fn_name!());
let init_stack_len = self.stack_len();
let attrs_len = rec.attrs.len();
self.emit_push_null();
let ident = Identifier::private("#NamedTuple");
self.emit_load_name_instr(ident);
self.emit_load_const("Record");
for field in rec.attrs.iter() {
self.emit_load_const(ValueObj::Str(field.sig.ident().inspect().clone()));
}
self.write_instr(BUILD_LIST);
self.write_arg(attrs_len);
if attrs_len == 0 {
self.stack_inc();
} else {
self.stack_dec_n(attrs_len - 1);
}
self.emit_call_instr(2, Name);
self.stack_dec_n((1 + 2 + 0) - 1);
let ident = Identifier::private("#rec");
self.emit_store_instr(ident, Name);
let ident = Identifier::private("#rec");
self.emit_push_null();
self.emit_load_name_instr(ident);
for field in rec.attrs.into_iter() {
self.emit_frameless_block(field.body.block, vec![]);
}
self.emit_call_instr(attrs_len, Name);
self.stack_dec_n((1 + attrs_len + 0) - 1);
debug_assert_eq!(self.stack_len(), init_stack_len + 1);
}
pub(crate) fn get_root(acc: &Accessor) -> Identifier {
match acc {
Accessor::Ident(ident) => ident.clone(),
Accessor::Attr(attr) => {
if let Expr::Accessor(acc) = attr.obj.as_ref() {
Self::get_root(acc)
} else {
todo!("{:?}", attr.obj)
}
}
}
}
fn emit_import(&mut self, acc: Accessor) {
self.emit_load_const(0i32);
self.emit_load_const(ValueObj::None);
let full_name = Str::from(acc.show());
let name = self
.local_search(&full_name, Name)
.unwrap_or_else(|| self.register_name(full_name));
self.write_instr(IMPORT_NAME);
self.write_arg(name.idx);
let root = Self::get_root(&acc);
self.emit_store_instr(root, Name);
self.stack_dec();
}
fn emit_compound(&mut self, chunks: Block) {
let is_module_loading_chunks = chunks
.get(2)
.map(|chunk| {
option_enum_unwrap!(chunk, Expr::Call)
.map(|call| call.obj.show_acc().as_ref().map(|s| &s[..]) == Some("exec"))
.unwrap_or(false)
})
.unwrap_or(false);
if !self.module_type_loaded && is_module_loading_chunks {
self.load_module_type();
self.module_type_loaded = true;
}
let init_stack_len = self.stack_len();
for chunk in chunks.into_iter() {
self.emit_chunk(chunk);
if self.stack_len() == init_stack_len + 1 {
self.emit_pop_top();
}
}
self.cancel_if_pop_top();
}
fn push_lnotab(&mut self, expr: &Expr) {
let ln_begin = expr.ln_begin().unwrap_or(0);
if ln_begin > self.cur_block().prev_lineno {
let sd = self.lasti() - self.cur_block().prev_lasti;
let ld = ln_begin - self.cur_block().prev_lineno;
if ld != 0 {
if sd != 0 {
self.mut_cur_block_codeobj().lnotab.push(sd as u8);
self.mut_cur_block_codeobj().lnotab.push(ld as u8);
} else {
if let Some(last_ld) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*last_ld += ld as u8;
} else {
self.mut_cur_block_codeobj().lnotab.push(0);
self.mut_cur_block_codeobj().lnotab.push(ld as u8);
}
}
self.mut_cur_block().prev_lineno += ld;
self.mut_cur_block().prev_lasti = self.lasti();
} else {
CompileError::compiler_bug(
0,
self.cfg.input.clone(),
expr.loc(),
fn_name_full!(),
line!(),
)
.write_to_stderr();
self.crash("codegen failed: invalid bytecode format");
}
}
}
fn emit_chunk(&mut self, chunk: Expr) {
log!(info "entered {} ({chunk})", fn_name!());
self.push_lnotab(&chunk);
match chunk {
Expr::Lit(lit) => self.emit_load_const(lit.value),
Expr::Accessor(acc) => self.emit_acc(acc),
Expr::Def(def) => self.emit_def(def),
Expr::ClassDef(class) => self.emit_class_def(class),
Expr::PatchDef(patch) => self.emit_patch_def(patch),
Expr::ReDef(attr) => self.emit_redef(attr),
Expr::Lambda(lambda) => self.emit_lambda(lambda),
Expr::UnaryOp(unary) => self.emit_unaryop(unary),
Expr::BinOp(bin) => self.emit_binop(bin),
Expr::Call(call) => self.emit_call(call),
Expr::Array(arr) => self.emit_array(arr),
Expr::Tuple(tup) => self.emit_tuple(tup),
Expr::Set(set) => self.emit_set(set),
Expr::Dict(dict) => self.emit_dict(dict),
Expr::Record(rec) => self.emit_record(rec),
Expr::Code(code) => {
let code = self.emit_block(code, None, vec![], 0);
self.emit_load_const(code);
}
Expr::Compound(chunks) => self.emit_compound(chunks),
Expr::Import(acc) => self.emit_import(acc),
Expr::Dummy(_) | Expr::TypeAsc(_) => {}
}
}
fn emit_expr(&mut self, expr: Expr) {
log!(info "entered {} ({expr})", fn_name!());
self.push_lnotab(&expr);
let mut wrapped = true;
if !self.cfg.no_std {
match expr.ref_t().derefine() {
Bool => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Bool"));
}
Nat => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Nat"));
}
Int => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Int"));
}
Float => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Float"));
}
Str => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Str"));
}
other => match &other.qual_name()[..] {
"Array" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Array"));
}
"Dict" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Dict"));
}
"Set" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("Set"));
}
"Tuple" => {
self.emit_push_null();
self.emit_load_name_instr(Identifier::public("tuple"));
}
_ => {
wrapped = false;
}
},
}
}
match expr {
Expr::Lit(lit) => self.emit_load_const(lit.value),
Expr::Accessor(acc) => self.emit_acc(acc),
Expr::Def(def) => self.emit_def(def),
Expr::ClassDef(class) => self.emit_class_def(class),
Expr::PatchDef(patch) => self.emit_patch_def(patch),
Expr::ReDef(attr) => self.emit_redef(attr),
Expr::Lambda(lambda) => self.emit_lambda(lambda),
Expr::UnaryOp(unary) => self.emit_unaryop(unary),
Expr::BinOp(bin) => self.emit_binop(bin),
Expr::Call(call) => self.emit_call(call),
Expr::Array(arr) => self.emit_array(arr),
Expr::Tuple(tup) => self.emit_tuple(tup),
Expr::Set(set) => self.emit_set(set),
Expr::Dict(dict) => self.emit_dict(dict),
Expr::Record(rec) => self.emit_record(rec),
Expr::Code(code) => {
let code = self.emit_block(code, None, vec![], 0);
self.emit_load_const(code);
}
Expr::Compound(chunks) => self.emit_compound(chunks),
Expr::TypeAsc(tasc) => self.emit_expr(*tasc.expr),
Expr::Import(acc) => self.emit_import(acc),
Expr::Dummy(_) => {}
}
if !self.cfg.no_std && wrapped {
self.emit_call_instr(1, Name);
self.stack_dec();
}
}
fn emit_frameless_block(&mut self, block: Block, params: Vec<Str>) {
log!(info "entered {}", fn_name!());
let line = block.ln_begin().unwrap_or(0);
for param in params {
self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name);
}
let init_stack_len = self.stack_len();
for chunk in block.into_iter() {
self.emit_chunk(chunk);
if self.stack_len() > init_stack_len {
self.emit_pop_top();
}
}
self.cancel_if_pop_top();
}
fn emit_with_block(&mut self, block: Block, params: Vec<Str>) {
log!(info "entered {}", fn_name!());
let line = block.ln_begin().unwrap_or(0);
for param in params {
self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name);
}
let init_stack_len = self.stack_len();
for chunk in block.into_iter() {
self.emit_chunk(chunk);
if self.stack_len() > init_stack_len {
self.emit_pop_top();
}
}
self.cancel_if_pop_top();
}
fn emit_class_block(&mut self, class: ClassDef) -> CodeObj {
log!(info "entered {}", fn_name!());
let name = class.sig.ident().inspect().clone();
self.unit_size += 1;
let firstlineno = match class.methods.get(0).and_then(|def| def.ln_begin()) {
Some(l) => l,
None => class.sig.ln_begin().unwrap_or(0),
};
self.units.push(PyCodeGenUnit::new(
self.unit_size,
self.py_version,
vec![],
Str::rc(self.cfg.input.enclosed_name()),
&name,
firstlineno,
0,
));
let init_stack_len = self.stack_len();
let mod_name = self.toplevel_block_codeobj().name.clone();
self.emit_load_const(mod_name);
self.emit_store_instr(Identifier::public("__module__"), Name);
self.emit_load_const(name);
self.emit_store_instr(Identifier::public("__qualname__"), Name);
self.emit_init_method(&class.sig, class.__new__.clone());
if class.need_to_gen_new {
self.emit_new_func(&class.sig, class.__new__);
}
if !class.methods.is_empty() {
self.emit_frameless_block(class.methods, vec![]);
}
if self.stack_len() == init_stack_len {
self.emit_load_const(ValueObj::None);
}
self.write_instr(RETURN_VALUE);
self.write_arg(0);
if self.stack_len() > 1 {
let block_id = self.cur_block().id;
let stack_len = self.stack_len();
CompileError::stack_bug(
self.input().clone(),
Location::Unknown,
stack_len,
block_id,
fn_name_full!(),
)
.write_to_stderr();
self.crash("error in emit_class_block: invalid stack size");
}
if !self.cur_block_codeobj().varnames.is_empty() {
self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32;
}
let unit = self.units.pop().unwrap();
if !self.units.is_empty() {
let ld = unit.prev_lineno - self.cur_block().prev_lineno;
if ld != 0 {
if let Some(l) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*l += ld as u8;
}
self.mut_cur_block().prev_lineno += ld;
}
}
unit.codeobj
}
fn emit_init_method(&mut self, sig: &Signature, __new__: Type) {
log!(info "entered {}", fn_name!());
let new_first_param = __new__.non_default_params().unwrap().first();
let line = sig.ln_begin().unwrap_or(0);
let class_name = sig.ident().inspect();
let mut ident = Identifier::public_with_line(DOT, Str::ever("__init__"), line);
ident.vi.t = __new__.clone();
let self_param = VarName::from_str_and_line(Str::ever("self"), line);
let vi = VarInfo::nd_parameter(
__new__.return_t().unwrap().clone(),
ident.vi.def_loc.clone(),
"?".into(),
);
let raw =
erg_parser::ast::NonDefaultParamSignature::new(ParamPattern::VarName(self_param), None);
let self_param = NonDefaultParamSignature::new(raw, vi, None);
let (param_name, params) = if let Some(new_first_param) = new_first_param {
let param_name = new_first_param
.name()
.cloned()
.unwrap_or_else(|| self.fresh_gen.fresh_varname());
let param = VarName::from_str_and_line(param_name.clone(), line);
let raw =
erg_parser::ast::NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let vi = VarInfo::nd_parameter(
new_first_param.typ().clone(),
ident.vi.def_loc.clone(),
"?".into(),
);
let param = NonDefaultParamSignature::new(raw, vi, None);
let params = Params::new(vec![self_param, param], None, vec![], None);
(param_name, params)
} else {
("_".into(), Params::single(self_param))
};
let bounds = TypeBoundSpecs::empty();
let subr_sig = SubrSignature::new(ident, bounds, params, sig.t_spec_with_op().cloned());
let mut attrs = vec![];
match new_first_param.map(|pt| pt.typ()) {
Some(Type::Record(rec)) => {
for field in rec.keys() {
let obj =
Expr::Accessor(Accessor::private_with_line(Str::from(¶m_name), line));
let ident = erg_parser::ast::Identifier::public(field.symbol.clone());
let expr = obj.attr_expr(Identifier::bare(ident));
let obj = Expr::Accessor(Accessor::private_with_line(Str::ever("self"), line));
let dot = if field.vis.is_private() {
VisModifierSpec::Private
} else {
VisModifierSpec::Public(DOT)
};
let attr = erg_parser::ast::Identifier::new(
dot,
VarName::from_str(field.symbol.clone()),
);
let attr = obj.attr(Identifier::bare(attr));
let redef = ReDef::new(attr, Block::new(vec![expr]));
attrs.push(Expr::ReDef(redef));
}
}
Some(_) => {
let expr =
Expr::Accessor(Accessor::private_with_line(Str::from(¶m_name), line));
let obj = Expr::Accessor(Accessor::private_with_line(Str::ever("self"), line));
let attr = obj.attr(Identifier::private_with_line(Str::ever("base"), line));
let redef = ReDef::new(attr, Block::new(vec![expr]));
attrs.push(Expr::ReDef(redef));
}
None => {}
}
let none = Token::new(TokenKind::NoneLit, "None", line, 0);
attrs.push(Expr::Lit(Literal::new(ValueObj::None, none)));
let block = Block::new(attrs);
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_name), subr_sig, body);
}
fn emit_new_func(&mut self, sig: &Signature, __new__: Type) {
log!(info "entered {}", fn_name!());
let class_ident = sig.ident();
let line = sig.ln_begin().unwrap_or(0);
let mut ident = Identifier::public_with_line(DOT, Str::ever("new"), line);
let class = Expr::Accessor(Accessor::Ident(class_ident.clone()));
let mut new_ident = Identifier::private_with_line(Str::ever("__new__"), line);
new_ident.vi.py_name = Some(Str::ever("__call__"));
let class_new = class.attr_expr(new_ident);
ident.vi.t = __new__;
if let Some(new_first_param) = ident.vi.t.non_default_params().unwrap().first() {
let param_name = new_first_param
.name()
.cloned()
.unwrap_or_else(|| self.fresh_gen.fresh_varname());
let param = VarName::from_str_and_line(param_name.clone(), line);
let vi = VarInfo::nd_parameter(
new_first_param.typ().clone(),
ident.vi.def_loc.clone(),
"?".into(),
);
let raw =
erg_parser::ast::NonDefaultParamSignature::new(ParamPattern::VarName(param), None);
let param = NonDefaultParamSignature::new(raw, vi, None);
let params = Params::single(param);
let bounds = TypeBoundSpecs::empty();
let sig = SubrSignature::new(ident, bounds, params, sig.t_spec_with_op().cloned());
let arg = PosArg::new(Expr::Accessor(Accessor::private_with_line(
param_name, line,
)));
let call = class_new.call_expr(Args::single(arg));
let block = Block::new(vec![call]);
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_ident.inspect()), sig, body);
} else {
let params = Params::empty();
let bounds = TypeBoundSpecs::empty();
let sig = SubrSignature::new(ident, bounds, params, sig.t_spec_with_op().cloned());
let call = class_new.call_expr(Args::empty());
let block = Block::new(vec![call]);
let body = DefBody::new(EQUAL, block, DefId(0));
self.emit_subr_def(Some(class_ident.inspect()), sig, body);
}
}
fn emit_block(
&mut self,
block: Block,
opt_name: Option<Str>,
params: Vec<Str>,
flags: u32,
) -> CodeObj {
log!(info "entered {}", fn_name!());
self.unit_size += 1;
let name = if let Some(name) = opt_name {
name
} else {
"<block>".into()
};
let firstlineno = block
.first()
.and_then(|first| first.ln_begin())
.unwrap_or(0);
self.units.push(PyCodeGenUnit::new(
self.unit_size,
self.py_version,
params,
Str::rc(self.cfg.input.enclosed_name()),
name,
firstlineno,
flags,
));
let idx_copy_free_vars = if self.py_version.minor >= Some(11) {
let idx_copy_free_vars = self.lasti();
self.write_instr(Opcode311::COPY_FREE_VARS);
self.write_arg(0);
self.write_instr(Opcode311::RESUME);
self.write_arg(0);
idx_copy_free_vars
} else {
0
};
let init_stack_len = self.stack_len();
for chunk in block.into_iter() {
self.emit_chunk(chunk);
if self.stack_len() > init_stack_len {
self.emit_pop_top();
}
}
self.cancel_if_pop_top(); if self.stack_len() == init_stack_len {
self.emit_load_const(ValueObj::None);
} else if self.stack_len() > init_stack_len + 1 {
let block_id = self.cur_block().id;
let stack_len = self.stack_len();
CompileError::stack_bug(
self.input().clone(),
Location::Unknown,
stack_len,
block_id,
fn_name_full!(),
)
.write_to_stderr();
self.crash("error in emit_block: invalid stack size");
}
self.write_instr(RETURN_VALUE);
self.write_arg(0);
if !self.cur_block_codeobj().varnames.is_empty() {
self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32;
}
let freevars_len = self.cur_block_codeobj().freevars.len();
if freevars_len > 0 {
self.mut_cur_block_codeobj().flags += CodeObjFlags::Nested as u32;
if self.py_version.minor >= Some(11) {
self.edit_code(idx_copy_free_vars + 1, freevars_len);
}
} else if self.py_version.minor >= Some(11) {
let code = self.cur_block_codeobj().code.get(idx_copy_free_vars);
debug_assert_eq!(code, Some(&(Opcode311::COPY_FREE_VARS as u8)));
self.edit_code(idx_copy_free_vars, CommonOpcode::NOP as usize);
}
let unit = self.units.pop().unwrap();
if !self.units.is_empty() {
let ld = unit
.prev_lineno
.saturating_sub(self.cur_block().prev_lineno);
if ld != 0 {
if let Some(l) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*l += ld as u8;
}
self.mut_cur_block().prev_lineno += ld;
}
}
unit.codeobj
}
fn load_prelude(&mut self) {
let no_std = self.cfg.no_std;
self.cfg.no_std = true;
self.load_record_type();
self.load_prelude_py();
self.prelude_loaded = true;
self.record_type_loaded = true;
self.cfg.no_std = no_std;
}
fn load_in_op(&mut self) {
let mod_name = Identifier::public("_erg_std_prelude");
self.emit_global_import_items(
mod_name,
vec![(
Identifier::public("in_operator"),
Some(Identifier::private("#in_operator")),
)],
);
self.in_op_loaded = true;
}
fn load_mutate_op(&mut self) {
let mod_name = Identifier::public("_erg_std_prelude");
self.emit_global_import_items(
mod_name,
vec![(
Identifier::public("mutate_operator"),
Some(Identifier::private("#mutate_operator")),
)],
);
self.mutate_op_loaded = true;
}
fn load_control(&mut self) {
let mod_name = Identifier::public("_erg_control");
self.emit_import_all_instr(mod_name);
self.control_loaded = true;
}
fn load_convertors(&mut self) {
let mod_name = Identifier::public("_erg_convertors");
self.emit_import_all_instr(mod_name);
self.convertors_loaded = true;
}
fn load_prelude_py(&mut self) {
self.emit_global_import_items(
Identifier::public("sys"),
vec![(
Identifier::public("path"),
Some(Identifier::private("#path")),
)],
);
self.emit_load_name_instr(Identifier::private("#path"));
self.emit_load_method_instr(Identifier::public("append"));
self.emit_load_const(erg_std_path().to_str().unwrap());
self.emit_call_instr(1, BoundAttr);
self.stack_dec();
self.emit_pop_top();
let erg_std_mod = Identifier::public("_erg_std_prelude");
self.emit_global_import_items(
erg_std_mod.clone(),
vec![(
Identifier::public("in_operator"),
Some(Identifier::private("#in_operator")),
)],
);
self.emit_import_all_instr(erg_std_mod);
}
fn load_record_type(&mut self) {
self.emit_global_import_items(
Identifier::public("collections"),
vec![(
Identifier::public("namedtuple"),
Some(Identifier::private("#NamedTuple")),
)],
);
}
fn load_abc(&mut self) {
self.emit_global_import_items(
Identifier::public("abc"),
vec![
(
Identifier::public("ABCMeta"),
Some(Identifier::private("#ABCMeta")),
),
(
Identifier::public("abstractmethod"),
Some(Identifier::private("#abstractmethod")),
),
],
);
}
fn load_module_type(&mut self) {
self.emit_global_import_items(
Identifier::public("types"),
vec![(
Identifier::public("ModuleType"),
Some(Identifier::private("#ModuleType")),
)],
);
}
pub fn emit(&mut self, hir: HIR) -> CodeObj {
log!(info "the code-generating process has started.{RESET}");
self.unit_size += 1;
self.units.push(PyCodeGenUnit::new(
self.unit_size,
self.py_version,
vec![],
Str::rc(self.cfg.input.enclosed_name()),
"<module>",
1,
0,
));
if self.py_version.minor >= Some(11) {
self.write_instr(Opcode311::RESUME);
self.write_arg(0);
}
if !self.cfg.no_std && !self.prelude_loaded {
self.load_prelude();
}
for chunk in hir.module.into_iter() {
self.emit_chunk(chunk);
if self.stack_len() == 1 {
self.emit_pop_top();
}
}
self.cancel_if_pop_top(); if self.input().is_repl() {
if self.stack_len() == 1 {
self.emit_print_expr();
}
self.stack_dec_n(self.stack_len() as usize);
}
if self.stack_len() == 0 {
self.emit_load_const(ValueObj::None);
} else if self.stack_len() > 1 {
let block_id = self.cur_block().id;
let stack_len = self.stack_len();
CompileError::stack_bug(
self.input().clone(),
Location::Unknown,
stack_len,
block_id,
fn_name_full!(),
)
.write_to_stderr();
self.crash("error in emit: invalid stack size");
}
self.write_instr(RETURN_VALUE);
self.write_arg(0);
if !self.cur_block_codeobj().varnames.is_empty() {
self.mut_cur_block_codeobj().flags += CodeObjFlags::NewLocals as u32;
}
let unit = self.units.pop().unwrap();
if !self.units.is_empty() {
let ld = unit.prev_lineno - self.cur_block().prev_lineno;
if ld != 0 {
if let Some(l) = self.mut_cur_block_codeobj().lnotab.last_mut() {
*l += ld as u8;
}
self.mut_cur_block().prev_lineno += ld;
}
}
log!(info "the code-generating process has completed.{RESET}");
unit.codeobj
}
}