@@ -0,0 +1,462 @@
|
|||||||
|
use mist_parser::ast::*;
|
||||||
|
|
||||||
|
use crate::Context;
|
||||||
|
|
||||||
|
use crate::{GenRust, GetRust, RustCodegen};
|
||||||
|
|
||||||
|
pub fn class_decl(
|
||||||
|
ctx: &mut Context,
|
||||||
|
cg: &mut RustCodegen,
|
||||||
|
|
||||||
|
visibility: &Visibility,
|
||||||
|
name: &Identifier,
|
||||||
|
generics: &GenericsDecl,
|
||||||
|
inherits: &Option<TypeExpr>,
|
||||||
|
fields: &Vec<Spanned<FieldDeclStmt>>,
|
||||||
|
constructor: &Spanned<ClassConstructor>,
|
||||||
|
items: &Vec<ClassItem>,
|
||||||
|
) {
|
||||||
|
let inherits = if let Some(inherits) = inherits {
|
||||||
|
let path = get_type_path(inherits);
|
||||||
|
ctx.expr_super = Some(path.clone());
|
||||||
|
Some((inherits, path))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
// Struct decl
|
||||||
|
cg.addln(&format!(
|
||||||
|
"{}struct {}{} {{",
|
||||||
|
visibility.get_rust(),
|
||||||
|
name.clone().get_rust(),
|
||||||
|
generics.clone().get_rust()
|
||||||
|
));
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indentedln(
|
||||||
|
"pub _m_oop: (&'static [*const std::ffi::c_void; Self::__V_COUNT], *mut std::ffi::c_void),",
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((inherits, _)) = inherits {
|
||||||
|
cg.add_indented("pub _super: Box<");
|
||||||
|
cg.add(&inherits.get_rust());
|
||||||
|
cg.addln(">,");
|
||||||
|
}
|
||||||
|
|
||||||
|
for field in fields.clone() {
|
||||||
|
cg.add_indentedln(&field.get_comment());
|
||||||
|
cg.add_indentedln(&field.item.decl.get_rust());
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.addln("}\n");
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
cg.addln(&format!(
|
||||||
|
"impl{} {}{} {{",
|
||||||
|
generics.clone().get_rust(),
|
||||||
|
name.clone().get_rust(),
|
||||||
|
format!(
|
||||||
|
"<{}>",
|
||||||
|
generics
|
||||||
|
.clone()
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| Generic::from(v).get_rust())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
));
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
let methods = items
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|item| match item {
|
||||||
|
ClassItem::ImplDecl(_) => None,
|
||||||
|
ClassItem::Method(method) => Some(method),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let v_table = methods
|
||||||
|
.iter()
|
||||||
|
.filter_map(|method| match method.item.visibility {
|
||||||
|
Visibility::Public => Some((method.item.name.clone(), method.item.is_override)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// V TABLE
|
||||||
|
{
|
||||||
|
cg.add_indentedln(&format!("pub const __V_COUNT: usize = {};", v_table.len()));
|
||||||
|
|
||||||
|
for (i, method_name) in v_table.iter().enumerate() {
|
||||||
|
cg.add_indentedln(&format!(
|
||||||
|
"pub const __FN_{}: usize = {i};",
|
||||||
|
method_name.0.0.to_uppercase()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.add_indentedln("pub const __V_TABLE: [*const std::ffi::c_void; Self::__V_COUNT] = [");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
for (method_name, _) in &v_table {
|
||||||
|
cg.add_indented("Self::__m_");
|
||||||
|
cg.add(&method_name.get_rust());
|
||||||
|
cg.add(" as *const std::ffi::c_void");
|
||||||
|
cg.addln(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("];");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Super V Table
|
||||||
|
if let Some((inherits, inherit_path)) = &inherits {
|
||||||
|
cg.add_indented(&format!(
|
||||||
|
"pub const __SUPER_V_TABLE: [*const std::ffi::c_void; {}",
|
||||||
|
inherits.get_rust()
|
||||||
|
));
|
||||||
|
|
||||||
|
cg.addln("::__V_COUNT] = {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indented("let mut table = ");
|
||||||
|
cg.add(&inherits.get_rust());
|
||||||
|
cg.addln("::__V_TABLE;");
|
||||||
|
|
||||||
|
for (name, is_override) in v_table {
|
||||||
|
if is_override {
|
||||||
|
cg.add_indentedln(&format!(
|
||||||
|
"table[{}::__FN_{}] = Self::__m_{} as *const std::ffi::c_void;",
|
||||||
|
inherits.get_rust(),
|
||||||
|
name.0.to_uppercase(),
|
||||||
|
name.get_rust()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.add_indentedln("table");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("};");
|
||||||
|
|
||||||
|
cg.add_indentedln("const fn __test_vt() {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
for i in &methods {
|
||||||
|
if i.item.is_override {
|
||||||
|
let mut params = i
|
||||||
|
.item
|
||||||
|
.params
|
||||||
|
.clone()
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|v| v.type_)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if params.len() == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
match params.remove(0) {
|
||||||
|
TypeExpr::Ref { mutable, .. } => {
|
||||||
|
cg.add_indented(&inherit_path.get_rust());
|
||||||
|
cg.add("::__m_");
|
||||||
|
cg.add(&i.item.name.get_rust());
|
||||||
|
cg.add(" as ");
|
||||||
|
|
||||||
|
params.insert(
|
||||||
|
0,
|
||||||
|
TypeExpr::Ref {
|
||||||
|
lifetime: None,
|
||||||
|
mutable: mutable,
|
||||||
|
ty: Box::new((*inherits).clone()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
cg.add(
|
||||||
|
&TypeExpr::StaticFn(params, i.item.return_type.clone().map(Box::new))
|
||||||
|
.get_rust(),
|
||||||
|
);
|
||||||
|
cg.addln(";");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let constructor_comment = constructor.get_comment();
|
||||||
|
|
||||||
|
cg.add_indentedln("#[allow(invalid_value)]");
|
||||||
|
cg.add_indentedln(&constructor_comment);
|
||||||
|
|
||||||
|
(fields, constructor, inherits.is_some()).gen_rust(ctx, cg);
|
||||||
|
|
||||||
|
for mut method in methods {
|
||||||
|
match method.item.visibility {
|
||||||
|
Visibility::Public => {
|
||||||
|
if !method.item.is_override {
|
||||||
|
gen_method_point(&method.item, ctx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
method.item.name.0.insert_str(0, "__m_");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
method.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.addln("}\n");
|
||||||
|
|
||||||
|
for item in items {
|
||||||
|
match item {
|
||||||
|
ClassItem::ImplDecl(impl_) => {
|
||||||
|
let mut impl_ = impl_.clone();
|
||||||
|
|
||||||
|
impl_.item.trait_ = Some(impl_.item.target);
|
||||||
|
impl_.item.target = TypeExpr::Path(Path(vec![name.clone()]), None);
|
||||||
|
|
||||||
|
impl_.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
ClassItem::Method(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((inherits, _)) = inherits {
|
||||||
|
cg.add("impl std::ops::Deref for ");
|
||||||
|
cg.add(&name.get_rust());
|
||||||
|
|
||||||
|
cg.addln(" {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indented("type Target = ");
|
||||||
|
cg.add(&inherits.get_rust());
|
||||||
|
cg.addln(";");
|
||||||
|
|
||||||
|
cg.add_indentedln("fn deref(&self) -> &Self::Target {&self._super}");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.addln("}");
|
||||||
|
|
||||||
|
// Mut
|
||||||
|
|
||||||
|
cg.add("impl std::ops::DerefMut for ");
|
||||||
|
cg.add(&name.get_rust());
|
||||||
|
|
||||||
|
cg.addln(" {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indentedln("fn deref_mut(&mut self) -> &mut Self::Target {&mut self._super}");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.addln("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.expr_super = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenRust
|
||||||
|
for (
|
||||||
|
&Vec<Spanned<FieldDeclStmt>>,
|
||||||
|
&Spanned<ClassConstructor>,
|
||||||
|
bool,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
||||||
|
cg.add_indented(&format!(
|
||||||
|
"{}fn new{}(",
|
||||||
|
self.1.item.visibility.get_rust(),
|
||||||
|
self.1.item.generics.get_rust()
|
||||||
|
));
|
||||||
|
|
||||||
|
let params = self
|
||||||
|
.1
|
||||||
|
.item
|
||||||
|
.params
|
||||||
|
.0
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, mut v)| {
|
||||||
|
v.name = construct_pattern(&v.name, idx);
|
||||||
|
(idx, v)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for (i, param) in ¶ms {
|
||||||
|
if *i > 0 {
|
||||||
|
cg.add(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
param.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.addln(") -> Box<Self> {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indentedln("let mut this = Box::new(unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() });");
|
||||||
|
cg.add_indentedln("let this_ptr = &mut *this as *mut Self as *mut std::ffi::c_void;");
|
||||||
|
cg.add_indentedln("this._m_oop = (&Self::__V_TABLE, this_ptr);");
|
||||||
|
|
||||||
|
for field in self.0 {
|
||||||
|
let comment = field.get_comment();
|
||||||
|
|
||||||
|
if let Some(init) = &field.item.init {
|
||||||
|
cg.add_indentedln(&comment);
|
||||||
|
|
||||||
|
cg.add_indentedln(&format!("this.{} = ", field.item.decl.name.get_rust()));
|
||||||
|
|
||||||
|
init.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.add_indented(&format!("this.constructor("));
|
||||||
|
|
||||||
|
for (i, param) in params {
|
||||||
|
if i > 0 {
|
||||||
|
cg.add(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.expr_ensure_semicolon = false;
|
||||||
|
param.name.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.addln(");");
|
||||||
|
|
||||||
|
if self.2 {
|
||||||
|
cg.add_indentedln("this._super._m_oop.0 = &Self::__SUPER_V_TABLE;");
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.add_indentedln("this");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("}\n");
|
||||||
|
|
||||||
|
let mut constructor_params = vec![VarDecl {
|
||||||
|
name: Pattern::Path(false, Path(vec![Identifier(String::from("self"))])),
|
||||||
|
type_: Some(TypeExpr::Ref {
|
||||||
|
lifetime: None,
|
||||||
|
mutable: true,
|
||||||
|
ty: Box::new(TypeExpr::Path(
|
||||||
|
Path(vec![Identifier(String::from("Self"))]),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
}),
|
||||||
|
}];
|
||||||
|
|
||||||
|
constructor_params.append(&mut self.1.item.params.0.clone());
|
||||||
|
|
||||||
|
Spanned {
|
||||||
|
line: self.1.line,
|
||||||
|
column: self.1.column,
|
||||||
|
item: FunctionDecl {
|
||||||
|
visibility: self.1.item.visibility.clone(),
|
||||||
|
is_override: false,
|
||||||
|
name: Identifier(String::from("constructor")),
|
||||||
|
generics: self.1.item.generics.clone(),
|
||||||
|
params: ParamList(constructor_params),
|
||||||
|
return_type: Some(TypeExpr::Tuple(Vec::new())),
|
||||||
|
body: Some(self.1.item.body.clone()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn construct_pattern(pat: &Pattern, idx: usize) -> Pattern {
|
||||||
|
match pat {
|
||||||
|
Pattern::Literal(v) => Pattern::Literal(v.clone()),
|
||||||
|
Pattern::Path(is_mut, v) => Pattern::Path(*is_mut, v.clone().into()),
|
||||||
|
_ => Pattern::Path(false, Path(vec![Identifier(format!("_{idx}"))])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn gen_method_point(method: &FunctionDecl, ctx: &mut Context, cg: &mut RustCodegen) {
|
||||||
|
cg.add(&format!(
|
||||||
|
"{}fn {}{}(",
|
||||||
|
method.visibility.get_rust(),
|
||||||
|
method.name.get_rust(),
|
||||||
|
method.generics.get_rust(),
|
||||||
|
));
|
||||||
|
|
||||||
|
for (i, param) in method.params.0.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
cg.add(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
param.gen_rust(ctx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.add(") ");
|
||||||
|
if let Some(return_type) = &method.return_type {
|
||||||
|
cg.add("-> ");
|
||||||
|
cg.add(&return_type.get_rust());
|
||||||
|
}
|
||||||
|
|
||||||
|
cg.addln("{");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indentedln("unsafe {");
|
||||||
|
cg.indent += 1;
|
||||||
|
|
||||||
|
cg.add_indentedln(&format!(
|
||||||
|
"let func_ptr = self._m_oop.0[Self::__FN_{}];",
|
||||||
|
method.name.0.to_uppercase()
|
||||||
|
));
|
||||||
|
|
||||||
|
cg.add_indented("let func: ");
|
||||||
|
|
||||||
|
let mut param_types: Vec<TypeExpr> = method
|
||||||
|
.params
|
||||||
|
.clone()
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|v| v.type_)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
param_types.remove(0);
|
||||||
|
|
||||||
|
param_types.insert(
|
||||||
|
0,
|
||||||
|
TypeExpr::UnsafePtr {
|
||||||
|
mutable: true,
|
||||||
|
ty: Box::new(TypeExpr::Path(
|
||||||
|
Path(vec![
|
||||||
|
Identifier(String::from("std")),
|
||||||
|
Identifier(String::from("ffi")),
|
||||||
|
Identifier(String::from("c_void")),
|
||||||
|
]),
|
||||||
|
None,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
cg.add(&TypeExpr::StaticFn(param_types, method.return_type.clone().map(Box::new)).get_rust());
|
||||||
|
|
||||||
|
cg.addln(" = std::mem::transmute(func_ptr);");
|
||||||
|
|
||||||
|
cg.add_indentedln("func(self._m_oop.1)");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("}");
|
||||||
|
|
||||||
|
cg.indent -= 1;
|
||||||
|
cg.add_indentedln("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_type_path(ty: &TypeExpr) -> Path {
|
||||||
|
match ty {
|
||||||
|
TypeExpr::Path(p, _) => p.clone(),
|
||||||
|
TypeExpr::Dyn(v) => get_type_path(v),
|
||||||
|
TypeExpr::Ref { ty, .. } => get_type_path(ty),
|
||||||
|
TypeExpr::UnsafePtr { ty, .. } => get_type_path(ty),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,30 @@ impl GetRust for ExprPath {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("::")
|
.join("::")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_rust_ctx(&self, cx: &mut Context) -> String {
|
||||||
|
let mut a = self.clone();
|
||||||
|
|
||||||
|
if let Some(path) = &cx.expr_super {
|
||||||
|
if self.0[0].ident.0 == "super" && self.0.len() == 1 {
|
||||||
|
a.0[0].ident.0 = String::from("self._super");
|
||||||
|
} else if self.0[0].ident.0 == "Super" {
|
||||||
|
a.0[0].ident = path.0[0].clone();
|
||||||
|
|
||||||
|
if path.0.len() > 1 {
|
||||||
|
a.0.splice(
|
||||||
|
1..1,
|
||||||
|
path.0.clone().into_iter().skip(1).map(|v| ExprPathSegment {
|
||||||
|
ident: v,
|
||||||
|
generics: None,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.get_rust()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GetRust for ExprPathSegment {
|
impl GetRust for ExprPathSegment {
|
||||||
@@ -63,7 +87,7 @@ impl GenRust for Expression {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Expression::Path(path) => cg.add(&path.get_rust()),
|
Expression::Path(path) => cg.add(&path.get_rust_ctx(ctx)),
|
||||||
Expression::Literal(literal) => literal.gen_rust(ctx, cg),
|
Expression::Literal(literal) => literal.gen_rust(ctx, cg),
|
||||||
Expression::Statement(stmt) => stmt.gen_rust(ctx, cg),
|
Expression::Statement(stmt) => stmt.gen_rust(ctx, cg),
|
||||||
Expression::Array(values) => {
|
Expression::Array(values) => {
|
||||||
|
|||||||
+36
-17
@@ -1,3 +1,4 @@
|
|||||||
|
pub mod class_decl;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
pub mod top_level;
|
pub mod top_level;
|
||||||
@@ -6,6 +7,7 @@ use mist_parser::ast::*;
|
|||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub expr_ensure_semicolon: bool,
|
pub expr_ensure_semicolon: bool,
|
||||||
|
pub expr_super: Option<Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait GenRust {
|
pub trait GenRust {
|
||||||
@@ -14,6 +16,10 @@ pub trait GenRust {
|
|||||||
|
|
||||||
pub trait GetRust {
|
pub trait GetRust {
|
||||||
fn get_rust(&self) -> String;
|
fn get_rust(&self) -> String;
|
||||||
|
fn get_rust_ctx(&self, cx: &mut Context) -> String {
|
||||||
|
let _ = cx;
|
||||||
|
self.get_rust()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@@ -56,6 +62,7 @@ impl RustCodegen {
|
|||||||
pub fn generate(&mut self, toplevels: Vec<TopLevel>) -> String {
|
pub fn generate(&mut self, toplevels: Vec<TopLevel>) -> String {
|
||||||
let mut ctx = Context {
|
let mut ctx = Context {
|
||||||
expr_ensure_semicolon: true,
|
expr_ensure_semicolon: true,
|
||||||
|
expr_super: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for tl in toplevels {
|
for tl in toplevels {
|
||||||
@@ -175,16 +182,11 @@ impl GetRust for Identifier {
|
|||||||
impl GetRust for TypeExpr {
|
impl GetRust for TypeExpr {
|
||||||
fn get_rust(&self) -> String {
|
fn get_rust(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
// TypePostfix::Ref => format!("&"),
|
|
||||||
// TypePostfix::RefMut => format!("&mut "),
|
|
||||||
// TypePostfix::RefLifetime(lifetime) => format!("&'{} ", lifetime.get_rust()),
|
|
||||||
// TypePostfix::RefMutLifetime(lifetime) => format!("&'{} mut ", lifetime.get_rust()),
|
|
||||||
// TypePostfix::Dyn => format!("dyn "),
|
|
||||||
Self::Path(path, generics) => {
|
Self::Path(path, generics) => {
|
||||||
if let Some(generics) = generics {
|
if let Some(generics) = generics {
|
||||||
format!("{}{}", get_static_type_path(path), generics.get_rust())
|
format!("{}{}", path.get_rust(), generics.get_rust())
|
||||||
} else {
|
} else {
|
||||||
get_static_type_path(path)
|
path.get_rust()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Lifetime(name) => format!("'{}", name.get_rust()),
|
Self::Lifetime(name) => format!("'{}", name.get_rust()),
|
||||||
@@ -196,6 +198,33 @@ impl GetRust for TypeExpr {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
|
Self::StaticFn(types, return_type) => {
|
||||||
|
if let Some(return_type) = return_type {
|
||||||
|
format!(
|
||||||
|
"fn({}) -> {}",
|
||||||
|
types
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| t.get_rust())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
return_type.get_rust()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!(
|
||||||
|
"fn({})",
|
||||||
|
types
|
||||||
|
.into_iter()
|
||||||
|
.map(|t| t.get_rust())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", "),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::UnsafePtr { mutable, ty } => {
|
||||||
|
let mutable = if *mutable { "mut " } else { "const " };
|
||||||
|
format!("*{mutable}{}", ty.get_rust())
|
||||||
|
}
|
||||||
|
|
||||||
Self::Ref {
|
Self::Ref {
|
||||||
lifetime,
|
lifetime,
|
||||||
@@ -218,16 +247,6 @@ impl GetRust for TypeExpr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_static_type_path(path: &Path) -> String {
|
|
||||||
let rust_path = path.get_rust();
|
|
||||||
|
|
||||||
if rust_path == "void" {
|
|
||||||
format!("()")
|
|
||||||
} else {
|
|
||||||
rust_path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenRust for Pattern {
|
impl GenRust for Pattern {
|
||||||
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
||||||
match self {
|
match self {
|
||||||
|
|||||||
@@ -6,16 +6,20 @@ use crate::{GenRust, GetRust, RustCodegen};
|
|||||||
|
|
||||||
impl GenRust for Block {
|
impl GenRust for Block {
|
||||||
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
||||||
|
if self.is_unsafe {
|
||||||
|
cg.add("unsafe ");
|
||||||
|
}
|
||||||
|
|
||||||
cg.addln("{");
|
cg.addln("{");
|
||||||
cg.indent += 1;
|
cg.indent += 1;
|
||||||
|
|
||||||
for stmt in &self.0 {
|
for stmt in &self.statements {
|
||||||
ctx.expr_ensure_semicolon = true;
|
ctx.expr_ensure_semicolon = true;
|
||||||
stmt.gen_rust(ctx, cg);
|
stmt.gen_rust(ctx, cg);
|
||||||
cg.addln("");
|
cg.addln("");
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(soft_return) = &self.1 {
|
if let Some(soft_return) = &self.soft_return {
|
||||||
ctx.expr_ensure_semicolon = false;
|
ctx.expr_ensure_semicolon = false;
|
||||||
soft_return.gen_rust(ctx, cg);
|
soft_return.gen_rust(ctx, cg);
|
||||||
cg.addln("");
|
cg.addln("");
|
||||||
|
|||||||
+12
-209
@@ -1,6 +1,6 @@
|
|||||||
use mist_parser::ast::*;
|
use mist_parser::ast::*;
|
||||||
|
|
||||||
use crate::Context;
|
use crate::{Context, class_decl};
|
||||||
|
|
||||||
use crate::{GenRust, GetRust, RustCodegen};
|
use crate::{GenRust, GetRust, RustCodegen};
|
||||||
|
|
||||||
@@ -170,109 +170,6 @@ impl GenRust for TopLevel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenRust for (&Vec<Spanned<FieldDeclStmt>>, &Spanned<ClassConstructor>) {
|
|
||||||
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
|
||||||
cg.add_indented(&format!(
|
|
||||||
"{}fn new{}(",
|
|
||||||
self.1.item.visibility.get_rust(),
|
|
||||||
self.1.item.generics.get_rust()
|
|
||||||
));
|
|
||||||
|
|
||||||
let params = self
|
|
||||||
.1
|
|
||||||
.item
|
|
||||||
.params
|
|
||||||
.0
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, mut v)| {
|
|
||||||
v.name = construct_pattern(&v.name, idx);
|
|
||||||
(idx, v)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
for (i, param) in ¶ms {
|
|
||||||
if *i > 0 {
|
|
||||||
cg.add(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
param.gen_rust(ctx, cg);
|
|
||||||
}
|
|
||||||
|
|
||||||
cg.addln(") -> Self {");
|
|
||||||
cg.indent += 1;
|
|
||||||
|
|
||||||
cg.add_indentedln("let mut this: Self = unsafe { std::mem::MaybeUninit::<Self>::zeroed().assume_init() };");
|
|
||||||
|
|
||||||
for field in self.0 {
|
|
||||||
let comment = field.get_comment();
|
|
||||||
|
|
||||||
if let Some(init) = &field.item.init {
|
|
||||||
cg.add_indentedln(&comment);
|
|
||||||
|
|
||||||
cg.add_indentedln(&format!("this.{} = ", field.item.decl.name.get_rust()));
|
|
||||||
|
|
||||||
init.gen_rust(ctx, cg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cg.add_indented(&format!("this.constructor("));
|
|
||||||
|
|
||||||
for (i, param) in params {
|
|
||||||
if i > 0 {
|
|
||||||
cg.add(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.expr_ensure_semicolon = false;
|
|
||||||
param.name.gen_rust(ctx, cg);
|
|
||||||
}
|
|
||||||
|
|
||||||
cg.addln(");");
|
|
||||||
|
|
||||||
cg.add_indentedln("this");
|
|
||||||
|
|
||||||
cg.indent -= 1;
|
|
||||||
cg.add_indentedln("}\n");
|
|
||||||
|
|
||||||
let mut constructor_params = vec![VarDecl {
|
|
||||||
name: Pattern::Path(false, Path(vec![Identifier(String::from("self"))])),
|
|
||||||
type_: Some(TypeExpr::Ref {
|
|
||||||
lifetime: None,
|
|
||||||
mutable: true,
|
|
||||||
ty: Box::new(TypeExpr::Path(
|
|
||||||
Path(vec![Identifier(String::from("Self"))]),
|
|
||||||
None,
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
}];
|
|
||||||
|
|
||||||
constructor_params.append(&mut self.1.item.params.0.clone());
|
|
||||||
|
|
||||||
Spanned {
|
|
||||||
line: self.1.line,
|
|
||||||
column: self.1.column,
|
|
||||||
item: FunctionDecl {
|
|
||||||
visibility: self.1.item.visibility.clone(),
|
|
||||||
name: Identifier(String::from("constructor")),
|
|
||||||
generics: self.1.item.generics.clone(),
|
|
||||||
params: ParamList(constructor_params),
|
|
||||||
return_type: Some(TypeExpr::Tuple(Vec::new())),
|
|
||||||
body: Some(self.1.item.body.clone()),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.gen_rust(ctx, cg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn construct_pattern(pat: &Pattern, idx: usize) -> Pattern {
|
|
||||||
match pat {
|
|
||||||
Pattern::Literal(v) => Pattern::Literal(v.clone()),
|
|
||||||
Pattern::Path(is_mut, v) => Pattern::Path(*is_mut, v.clone().into()),
|
|
||||||
_ => Pattern::Path(false, Path(vec![Identifier(format!("_{idx}"))])),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GenRust for TopLevelKind {
|
impl GenRust for TopLevelKind {
|
||||||
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
fn gen_rust(&self, ctx: &mut Context, cg: &mut RustCodegen) {
|
||||||
match self {
|
match self {
|
||||||
@@ -365,111 +262,17 @@ impl GenRust for TopLevelKind {
|
|||||||
fields,
|
fields,
|
||||||
constructor,
|
constructor,
|
||||||
items,
|
items,
|
||||||
} => {
|
} => class_decl::class_decl(
|
||||||
// Struct decl
|
ctx,
|
||||||
cg.addln(&format!(
|
cg,
|
||||||
"{}struct {}{} {{",
|
visibility,
|
||||||
visibility.get_rust(),
|
name,
|
||||||
name.clone().get_rust(),
|
generics,
|
||||||
generics.clone().get_rust()
|
inherits,
|
||||||
));
|
fields,
|
||||||
cg.indent += 1;
|
constructor,
|
||||||
|
items,
|
||||||
if let Some(inherits) = inherits {
|
),
|
||||||
cg.add_indented("pub _super: ");
|
|
||||||
cg.add(&inherits.get_rust());
|
|
||||||
cg.addln(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
for field in fields.clone() {
|
|
||||||
cg.add_indentedln(&field.get_comment());
|
|
||||||
cg.add_indentedln(&field.item.decl.get_rust());
|
|
||||||
}
|
|
||||||
|
|
||||||
cg.indent -= 1;
|
|
||||||
cg.addln("}\n");
|
|
||||||
|
|
||||||
// Constructor
|
|
||||||
cg.addln(&format!(
|
|
||||||
"impl{} {}{} {{",
|
|
||||||
generics.clone().get_rust(),
|
|
||||||
name.clone().get_rust(),
|
|
||||||
format!(
|
|
||||||
"<{}>",
|
|
||||||
generics
|
|
||||||
.clone()
|
|
||||||
.0
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| Generic::from(v).get_rust())
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(", ")
|
|
||||||
)
|
|
||||||
));
|
|
||||||
cg.indent += 1;
|
|
||||||
|
|
||||||
let constructor_comment = constructor.get_comment();
|
|
||||||
|
|
||||||
cg.add_indentedln("#[allow(invalid_value)]");
|
|
||||||
cg.add_indentedln(&constructor_comment);
|
|
||||||
|
|
||||||
(fields, constructor).gen_rust(ctx, cg);
|
|
||||||
|
|
||||||
for item in items.clone() {
|
|
||||||
match item {
|
|
||||||
ClassItem::ImplDecl(_) => {}
|
|
||||||
ClassItem::Method(method) => method.gen_rust(ctx, cg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cg.indent -= 1;
|
|
||||||
cg.addln("}\n");
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
match item {
|
|
||||||
ClassItem::ImplDecl(impl_) => {
|
|
||||||
let mut impl_ = impl_.clone();
|
|
||||||
|
|
||||||
impl_.item.trait_ = Some(impl_.item.target);
|
|
||||||
impl_.item.target = TypeExpr::Path(Path(vec![name.clone()]), None);
|
|
||||||
|
|
||||||
impl_.gen_rust(ctx, cg);
|
|
||||||
}
|
|
||||||
ClassItem::Method(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(inherits) = inherits {
|
|
||||||
cg.add("impl std::ops::Deref for ");
|
|
||||||
cg.add(&name.get_rust());
|
|
||||||
|
|
||||||
cg.addln(" {");
|
|
||||||
cg.indent += 1;
|
|
||||||
|
|
||||||
cg.add_indented("type Target = ");
|
|
||||||
cg.add(&inherits.get_rust());
|
|
||||||
cg.addln(";");
|
|
||||||
|
|
||||||
cg.add_indentedln("fn deref(&self) -> &Self::Target {&self._super}");
|
|
||||||
|
|
||||||
cg.indent -= 1;
|
|
||||||
cg.addln("}");
|
|
||||||
|
|
||||||
// Mut
|
|
||||||
|
|
||||||
cg.add("impl std::ops::DerefMut for ");
|
|
||||||
cg.add(&name.get_rust());
|
|
||||||
|
|
||||||
cg.addln(" {");
|
|
||||||
cg.indent += 1;
|
|
||||||
|
|
||||||
cg.add_indentedln(
|
|
||||||
"fn deref_mut(&mut self) -> &mut Self::Target {&mut self._super}",
|
|
||||||
);
|
|
||||||
|
|
||||||
cg.indent -= 1;
|
|
||||||
cg.addln("}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,13 @@ pub enum TypeExpr {
|
|||||||
mutable: bool,
|
mutable: bool,
|
||||||
ty: Box<TypeExpr>,
|
ty: Box<TypeExpr>,
|
||||||
},
|
},
|
||||||
|
UnsafePtr {
|
||||||
|
mutable: bool,
|
||||||
|
ty: Box<TypeExpr>,
|
||||||
|
},
|
||||||
Dyn(Box<TypeExpr>),
|
Dyn(Box<TypeExpr>),
|
||||||
Path(Path, Option<Generics>),
|
Path(Path, Option<Generics>),
|
||||||
|
StaticFn(Vec<TypeExpr>, Option<Box<TypeExpr>>),
|
||||||
Tuple(Vec<TypeExpr>),
|
Tuple(Vec<TypeExpr>),
|
||||||
Lifetime(Identifier),
|
Lifetime(Identifier),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ use serde::Serialize;
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Default)]
|
#[derive(Debug, Clone, Serialize, Default)]
|
||||||
pub struct Block(
|
pub struct Block {
|
||||||
pub Vec<Spanned<Expression>>,
|
pub is_unsafe: bool,
|
||||||
pub Option<Spanned<Expression>>,
|
|
||||||
);
|
pub statements: Vec<Spanned<Expression>>,
|
||||||
|
pub soft_return: Option<Spanned<Expression>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub enum StatementBody {
|
pub enum StatementBody {
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ pub struct ClassConstructor {
|
|||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct FunctionDecl {
|
pub struct FunctionDecl {
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
pub is_override: bool,
|
||||||
pub name: Identifier,
|
pub name: Identifier,
|
||||||
pub generics: GenericsDecl,
|
pub generics: GenericsDecl,
|
||||||
pub params: ParamList,
|
pub params: ParamList,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ keyword = {
|
|||||||
| "loop"
|
| "loop"
|
||||||
| "fn"
|
| "fn"
|
||||||
| "unsafe"
|
| "unsafe"
|
||||||
|
| "override"
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier = @{
|
identifier = @{
|
||||||
@@ -63,6 +64,8 @@ static_path = {
|
|||||||
identifier ~ ("::" ~ identifier)*
|
identifier ~ ("::" ~ identifier)*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe_kw = { "unsafe" }
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
// PROGRAM
|
// PROGRAM
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@@ -322,8 +325,10 @@ trait_decl = {
|
|||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
// ======================================================
|
// ======================================================
|
||||||
|
|
||||||
|
override_kw = { "override" }
|
||||||
|
|
||||||
function_decl = {
|
function_decl = {
|
||||||
visibility? ~ "fn" ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ type_expr? ~ (block | semicolon)
|
visibility? ~ override_kw? ~ "fn" ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ type_expr? ~ (block | semicolon)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@@ -340,7 +345,7 @@ top_level = {
|
|||||||
// ======================================================
|
// ======================================================
|
||||||
|
|
||||||
block = {
|
block = {
|
||||||
"{" ~ statement_list ~ expr? ~ "}"
|
unsafe_kw? ~ "{" ~ statement_list ~ expr? ~ "}"
|
||||||
}
|
}
|
||||||
|
|
||||||
statement_list = {
|
statement_list = {
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ use crate::{
|
|||||||
Rule,
|
Rule,
|
||||||
ast::*,
|
ast::*,
|
||||||
ast_ensure, ast_expr,
|
ast_ensure, ast_expr,
|
||||||
error::{AstError, IntoErr, collect_recovered},
|
error::{AstError, AstResult, IntoErr, collect_recovered},
|
||||||
|
parser::listen_rule,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for Block {
|
impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for Block {
|
||||||
@@ -12,7 +13,11 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for Block {
|
|||||||
let mut inner = pair.clone().into_inner();
|
let mut inner = pair.clone().into_inner();
|
||||||
|
|
||||||
ast_ensure!(pair, Rule::block => {
|
ast_ensure!(pair, Rule::block => {
|
||||||
ast_expr!(Block(collect_recovered(inner.next().unwrap().into_inner()), inner.next().map(Spanned::try_from).transpose()))
|
ast_expr!(Block {
|
||||||
|
is_unsafe: Ok(listen_rule(&mut inner, Rule::unsafe_kw)) as AstResult<bool>,
|
||||||
|
statements: collect_recovered(inner.next().unwrap().into_inner()),
|
||||||
|
soft_return: inner.next().map(Spanned::try_from).transpose(),
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
|
|||||||
ast_ensure!(pair, Rule::function_decl => {
|
ast_ensure!(pair, Rule::function_decl => {
|
||||||
let mut inner = pair.into_inner();
|
let mut inner = pair.into_inner();
|
||||||
let visibility = Visibility::try_from(&mut inner);
|
let visibility = Visibility::try_from(&mut inner);
|
||||||
|
let is_override: AstResult<bool> = Ok(listen_rule(&mut inner, Rule::override_kw));
|
||||||
let name = Identifier::try_from(inner.next().unwrap());
|
let name = Identifier::try_from(inner.next().unwrap());
|
||||||
|
|
||||||
let generics = consume_rule(&mut inner, Rule::generics_decl)
|
let generics = consume_rule(&mut inner, Rule::generics_decl)
|
||||||
@@ -66,6 +67,7 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
|
|||||||
|
|
||||||
ast_expr!(Self {
|
ast_expr!(Self {
|
||||||
visibility: visibility,
|
visibility: visibility,
|
||||||
|
is_override: is_override,
|
||||||
return_type: return_type,
|
return_type: return_type,
|
||||||
name: name,
|
name: name,
|
||||||
generics: generics,
|
generics: generics,
|
||||||
|
|||||||
@@ -1,58 +1,33 @@
|
|||||||
pub class Counter {
|
pub class Animal {
|
||||||
pub count i32;
|
|
||||||
|
|
||||||
pub constructor(start i32) {
|
|
||||||
self.count = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn increment(*mut self) {
|
|
||||||
self.count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(*self) i32 {
|
|
||||||
self.count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_class_create() {
|
|
||||||
let c = Counter::new(0);
|
|
||||||
assert_eq!(c.get(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_class_method() {
|
|
||||||
let mut c = Counter::new(5);
|
|
||||||
c.increment();
|
|
||||||
assert_eq!(c.get(), 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_class_field_access() {
|
|
||||||
let c = Counter::new(42);
|
|
||||||
assert_eq!(c.count, 42);
|
|
||||||
}
|
|
||||||
|
|
||||||
class NamedCounter : Counter {
|
|
||||||
pub name String;
|
pub name String;
|
||||||
|
|
||||||
pub constructor(start i32, name String) {
|
constructor() {
|
||||||
self._super = Counter::new(start);
|
self.name = "Rex".to_string();
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_name(*self) String {
|
pub fn speak(*self) {
|
||||||
self.name.clone()
|
println!("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_speak(*self) {
|
||||||
|
self.speak();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_class_inherit() {
|
pub class Dog : Animal {
|
||||||
let nc = NamedCounter::new(10, "test".to_string());
|
constructor() {
|
||||||
assert_eq!(nc.get_name(), "test");
|
unsafe {
|
||||||
assert_eq!(nc.count, 10);
|
std::ptr::write(&mut super, Super::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub override fn speak(*self) {
|
||||||
|
println!("Woof!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_tests() {
|
pub fn run_tests() {
|
||||||
test_class_create();
|
let dawg = Dog::new();
|
||||||
test_class_method();
|
|
||||||
test_class_field_access();
|
dawg.test_speak();
|
||||||
test_class_inherit();
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user