diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 0fda8ae8..bcc2bbd5 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -225,6 +225,14 @@ fn convert_to_python_name(name: Str) -> Str { "print!" => Str::ever("print"), "py" | "pyimport" => Str::ever("__import__"), "quit" | "exit" => Str::ever("quit"), + "Nat" | "Nat!" => Str::ever("int"), + "Int" | "Int!" => Str::ever("int"), + "Float" | "Float!" => Str::ever("float"), + "Ratio" | "Ratio!" => Str::ever("float"), + "Complex" => Str::ever("complex"), + "Str" | "Str!" => Str::ever("str"), + "Bool" | "Bool!" => Str::ever("bool"), + "Array" | "Array!" => Str::ever("list"), _ => name, } } @@ -299,6 +307,7 @@ impl_stream_for_wrapper!(CodeGenStack, CodeGenUnit); pub struct CodeGenerator { cfg: ErgConfig, str_cache: CacheSet, + namedtuple_loaded: bool, unit_size: usize, units: CodeGenStack, pub(crate) errs: CompileErrors, @@ -309,6 +318,7 @@ impl CodeGenerator { Self { cfg, str_cache: CacheSet::new(), + namedtuple_loaded: false, unit_size: 0, units: CodeGenStack::empty(), errs: CompileErrors::empty(), @@ -545,6 +555,26 @@ impl CodeGenerator { Ok(()) } + fn emit_import_name_instr(&mut self, ident: Identifier) -> CompileResult<()> { + let name = self + .local_search(ident.inspect(), Name) + .unwrap_or_else(|| self.register_name(ident)); + self.write_instr(IMPORT_NAME); + self.write_arg(name.idx as u8); + self.stack_dec(); // (level + from_list) -> module object + Ok(()) + } + + fn emit_import_from_instr(&mut self, ident: Identifier) -> CompileResult<()> { + let name = self + .local_search(ident.inspect(), Name) + .unwrap_or_else(|| self.register_name(ident)); + self.write_instr(IMPORT_FROM); + self.write_arg(name.idx as u8); + // self.stack_inc(); (module object) -> attribute + Ok(()) + } + fn emit_load_attr_instr( &mut self, class: &str, @@ -1184,6 +1214,56 @@ impl CodeGenerator { } other => todo!("{other}"), }, + Expr::Record(rec) => { + let attrs_len = rec.attrs.len(); + // importing namedtuple + if !self.namedtuple_loaded { + self.emit_load_const(0); + self.emit_load_const(ValueObj::Tuple(std::rc::Rc::from([ValueObj::Str( + Str::ever("namedtuple"), + )]))); + let ident = Identifier::public("collections"); + self.emit_import_name_instr(ident).unwrap(); + let ident = Identifier::public("namedtuple"); + self.emit_import_from_instr(ident).unwrap(); + let ident = Identifier::private(Str::ever("#NamedTuple")); + self.emit_store_instr(ident, Name); + self.namedtuple_loaded = true; + } + // making record type + let ident = Identifier::private(Str::ever("#NamedTuple")); + self.emit_load_name_instr(ident).unwrap(); + // record name, let it be anonymous + self.emit_load_const("Record"); + for field in rec.attrs.iter() { + self.emit_load_const(ValueObj::Str( + field.sig.ident().unwrap().inspect().clone(), + )); + } + self.write_instr(BUILD_LIST); + self.write_arg(attrs_len as u8); + if attrs_len == 0 { + self.stack_inc(); + } else { + self.stack_dec_n(attrs_len - 1); + } + self.write_instr(CALL_FUNCTION); + self.write_arg(2); + // (1 (subroutine) + argc + kwsc) input objects -> 1 return object + self.stack_dec_n((1 + 2 + 0) - 1); + let ident = Identifier::private(Str::ever("#rec")); + self.emit_store_instr(ident, Name); + // making record instance + let ident = Identifier::private(Str::ever("#rec")); + self.emit_load_name_instr(ident).unwrap(); + for field in rec.attrs.into_iter() { + self.codegen_frameless_block(field.body.block, vec![]); + } + self.write_instr(CALL_FUNCTION); + self.write_arg(attrs_len as u8); + // (1 (subroutine) + argc + kwsc) input objects -> 1 return object + self.stack_dec_n((1 + attrs_len + 0) - 1); + } other => { self.errs.push(CompileError::feature_error( self.cfg.input.clone(), diff --git a/compiler/erg_compiler/hir.rs b/compiler/erg_compiler/hir.rs index 6c2c6841..6421c3ae 100644 --- a/compiler/erg_compiler/hir.rs +++ b/compiler/erg_compiler/hir.rs @@ -630,10 +630,18 @@ impl RecordAttrs { Self(vec![]) } + pub fn len(&self) -> usize { + self.0.len() + } + pub fn iter(&self) -> impl Iterator { self.0.iter() } + pub fn into_iter(self) -> impl Iterator { + self.0.into_iter() + } + pub fn iter_mut(&mut self) -> impl Iterator { self.0.iter_mut() } diff --git a/compiler/erg_type/codeobj.rs b/compiler/erg_type/codeobj.rs index 0d8ce5c3..f1e6673b 100644 --- a/compiler/erg_type/codeobj.rs +++ b/compiler/erg_type/codeobj.rs @@ -432,7 +432,9 @@ impl CodeObj { | Opcode::LOAD_GLOBAL | Opcode::STORE_ATTR | Opcode::LOAD_ATTR - | Opcode::LOAD_METHOD => { + | Opcode::LOAD_METHOD + | Opcode::IMPORT_NAME + | Opcode::IMPORT_FROM => { instrs += &format!("{} ({})", arg, self.names.get(*arg as usize).unwrap()); } Opcode::STORE_DEREF | Opcode::LOAD_DEREF => { diff --git a/compiler/erg_type/value.rs b/compiler/erg_type/value.rs index e88b77e2..e9e0720c 100644 --- a/compiler/erg_type/value.rs +++ b/compiler/erg_type/value.rs @@ -17,7 +17,7 @@ use erg_common::{fmt_iter, impl_display_from_debug, switch_lang}; use erg_common::{RcArray, Str}; use crate::codeobj::CodeObj; -use crate::constructors::{array, class, poly_class, refinement}; +use crate::constructors::{array, class, poly_class, refinement, tuple}; use crate::free::fresh_varname; use crate::typaram::TyParam; use crate::{ConstSubr, HasType, Predicate, Type}; @@ -33,6 +33,7 @@ pub enum ValueObj { Bool(bool), Array(Rc<[ValueObj]>), Dict(Rc<[(ValueObj, ValueObj)]>), + Tuple(Rc<[ValueObj]>), Record(Dict), Code(Box), Subr(ConstSubr), @@ -79,6 +80,7 @@ impl fmt::Debug for ValueObj { } write!(f, "}}") } + Self::Tuple(tup) => write!(f, "({})", fmt_iter(tup.iter())), Self::Code(code) => write!(f, "{code}"), Self::Record(rec) => { write!(f, "{{")?; @@ -134,6 +136,7 @@ impl Hash for ValueObj { Self::Bool(b) => b.hash(state), Self::Array(arr) => arr.hash(state), Self::Dict(dict) => dict.hash(state), + Self::Tuple(tup) => tup.hash(state), Self::Code(code) => code.hash(state), Self::Record(rec) => rec.hash(state), Self::Subr(subr) => subr.hash(state), @@ -327,6 +330,15 @@ impl ValueObj { } bytes } + Self::Tuple(tup) => { + let mut bytes = Vec::with_capacity(tup.len()); + bytes.push(DataTypePrefix::Tuple as u8); + bytes.append(&mut (tup.len() as u32).to_le_bytes().to_vec()); + for obj in tup.iter().cloned() { + bytes.append(&mut obj.into_bytes()); + } + bytes + } Self::None => { vec![DataTypePrefix::None as u8] } @@ -359,6 +371,7 @@ impl ValueObj { TyParam::value(arr.len()), ), Self::Dict(_dict) => todo!(), + Self::Tuple(tup) => tuple(tup.iter().map(|v| v.class()).collect()), Self::Code(_) => Type::Code, Self::Record(rec) => { Type::Record(rec.iter().map(|(k, v)| (k.clone(), v.class())).collect()) diff --git a/examples/record.er b/examples/record.er index da23ecaa..7ee2b642 100644 --- a/examples/record.er +++ b/examples/record.er @@ -5,10 +5,12 @@ john = { .age = !27 } +print! john.name +print! john.age assert john.name == "John Smith" assert john.age == 27 -john.age.update! old -> old + 1 -assert john.age == 28 +# john.age.update! old -> old + 1 +# assert john.age == 28 # Record is not Dict, so `john["name"]` is invalid # A record whose values are all types will also behave as a type @@ -17,5 +19,6 @@ Person! = { .age = Nat! } -assert Person!.age == Str -assert john in Person! +print! Person!.name +assert Person!.name == Str +# assert john in Person!