Implement code generation for records

This commit is contained in:
Shunsuke Shibayama 2022-08-28 03:14:26 +09:00
parent 84f11bf03b
commit bce13ef270
5 changed files with 112 additions and 6 deletions

View file

@ -225,6 +225,14 @@ fn convert_to_python_name(name: Str) -> Str {
"print!" => Str::ever("print"), "print!" => Str::ever("print"),
"py" | "pyimport" => Str::ever("__import__"), "py" | "pyimport" => Str::ever("__import__"),
"quit" | "exit" => Str::ever("quit"), "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, _ => name,
} }
} }
@ -299,6 +307,7 @@ impl_stream_for_wrapper!(CodeGenStack, CodeGenUnit);
pub struct CodeGenerator { pub struct CodeGenerator {
cfg: ErgConfig, cfg: ErgConfig,
str_cache: CacheSet<str>, str_cache: CacheSet<str>,
namedtuple_loaded: bool,
unit_size: usize, unit_size: usize,
units: CodeGenStack, units: CodeGenStack,
pub(crate) errs: CompileErrors, pub(crate) errs: CompileErrors,
@ -309,6 +318,7 @@ impl CodeGenerator {
Self { Self {
cfg, cfg,
str_cache: CacheSet::new(), str_cache: CacheSet::new(),
namedtuple_loaded: false,
unit_size: 0, unit_size: 0,
units: CodeGenStack::empty(), units: CodeGenStack::empty(),
errs: CompileErrors::empty(), errs: CompileErrors::empty(),
@ -545,6 +555,26 @@ impl CodeGenerator {
Ok(()) 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( fn emit_load_attr_instr(
&mut self, &mut self,
class: &str, class: &str,
@ -1184,6 +1214,56 @@ impl CodeGenerator {
} }
other => todo!("{other}"), 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 => { other => {
self.errs.push(CompileError::feature_error( self.errs.push(CompileError::feature_error(
self.cfg.input.clone(), self.cfg.input.clone(),

View file

@ -630,10 +630,18 @@ impl RecordAttrs {
Self(vec![]) Self(vec![])
} }
pub fn len(&self) -> usize {
self.0.len()
}
pub fn iter(&self) -> impl Iterator<Item = &Def> { pub fn iter(&self) -> impl Iterator<Item = &Def> {
self.0.iter() self.0.iter()
} }
pub fn into_iter(self) -> impl Iterator<Item = Def> {
self.0.into_iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Def> { pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Def> {
self.0.iter_mut() self.0.iter_mut()
} }

View file

@ -432,7 +432,9 @@ impl CodeObj {
| Opcode::LOAD_GLOBAL | Opcode::LOAD_GLOBAL
| Opcode::STORE_ATTR | Opcode::STORE_ATTR
| Opcode::LOAD_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()); instrs += &format!("{} ({})", arg, self.names.get(*arg as usize).unwrap());
} }
Opcode::STORE_DEREF | Opcode::LOAD_DEREF => { Opcode::STORE_DEREF | Opcode::LOAD_DEREF => {

View file

@ -17,7 +17,7 @@ use erg_common::{fmt_iter, impl_display_from_debug, switch_lang};
use erg_common::{RcArray, Str}; use erg_common::{RcArray, Str};
use crate::codeobj::CodeObj; 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::free::fresh_varname;
use crate::typaram::TyParam; use crate::typaram::TyParam;
use crate::{ConstSubr, HasType, Predicate, Type}; use crate::{ConstSubr, HasType, Predicate, Type};
@ -33,6 +33,7 @@ pub enum ValueObj {
Bool(bool), Bool(bool),
Array(Rc<[ValueObj]>), Array(Rc<[ValueObj]>),
Dict(Rc<[(ValueObj, ValueObj)]>), Dict(Rc<[(ValueObj, ValueObj)]>),
Tuple(Rc<[ValueObj]>),
Record(Dict<Field, ValueObj>), Record(Dict<Field, ValueObj>),
Code(Box<CodeObj>), Code(Box<CodeObj>),
Subr(ConstSubr), Subr(ConstSubr),
@ -79,6 +80,7 @@ impl fmt::Debug for ValueObj {
} }
write!(f, "}}") write!(f, "}}")
} }
Self::Tuple(tup) => write!(f, "({})", fmt_iter(tup.iter())),
Self::Code(code) => write!(f, "{code}"), Self::Code(code) => write!(f, "{code}"),
Self::Record(rec) => { Self::Record(rec) => {
write!(f, "{{")?; write!(f, "{{")?;
@ -134,6 +136,7 @@ impl Hash for ValueObj {
Self::Bool(b) => b.hash(state), Self::Bool(b) => b.hash(state),
Self::Array(arr) => arr.hash(state), Self::Array(arr) => arr.hash(state),
Self::Dict(dict) => dict.hash(state), Self::Dict(dict) => dict.hash(state),
Self::Tuple(tup) => tup.hash(state),
Self::Code(code) => code.hash(state), Self::Code(code) => code.hash(state),
Self::Record(rec) => rec.hash(state), Self::Record(rec) => rec.hash(state),
Self::Subr(subr) => subr.hash(state), Self::Subr(subr) => subr.hash(state),
@ -327,6 +330,15 @@ impl ValueObj {
} }
bytes 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 => { Self::None => {
vec![DataTypePrefix::None as u8] vec![DataTypePrefix::None as u8]
} }
@ -359,6 +371,7 @@ impl ValueObj {
TyParam::value(arr.len()), TyParam::value(arr.len()),
), ),
Self::Dict(_dict) => todo!(), Self::Dict(_dict) => todo!(),
Self::Tuple(tup) => tuple(tup.iter().map(|v| v.class()).collect()),
Self::Code(_) => Type::Code, Self::Code(_) => Type::Code,
Self::Record(rec) => { Self::Record(rec) => {
Type::Record(rec.iter().map(|(k, v)| (k.clone(), v.class())).collect()) Type::Record(rec.iter().map(|(k, v)| (k.clone(), v.class())).collect())

View file

@ -5,10 +5,12 @@ john = {
.age = !27 .age = !27
} }
print! john.name
print! john.age
assert john.name == "John Smith" assert john.name == "John Smith"
assert john.age == 27 assert john.age == 27
john.age.update! old -> old + 1 # john.age.update! old -> old + 1
assert john.age == 28 # assert john.age == 28
# Record is not Dict, so `john["name"]` is invalid # Record is not Dict, so `john["name"]` is invalid
# A record whose values are all types will also behave as a type # A record whose values are all types will also behave as a type
@ -17,5 +19,6 @@ Person! = {
.age = Nat! .age = Nat!
} }
assert Person!.age == Str print! Person!.name
assert john in Person! assert Person!.name == Str
# assert john in Person!