mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-03 05:54:33 +00:00
Implement code generation for records
This commit is contained in:
parent
84f11bf03b
commit
bce13ef270
5 changed files with 112 additions and 6 deletions
|
@ -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(),
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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!
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue