@@ -184,8 +184,13 @@ fn is_source_newer(source *Path, output *Path) std::io::Result<bool> {
|
|||||||
Ok(source_time > output_time)
|
Ok(source_time > output_time)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum TranspileError<'a> {
|
||||||
|
Parse[error::ParseError<'a, Vec<mist_parser::ast::TopLevel>>],
|
||||||
|
Semantic[Vec<mist_parser::semantics::SemanticError>]
|
||||||
|
}
|
||||||
|
|
||||||
pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf, mod_decl *str, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>)
|
pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf, mod_decl *str, curr_mod_decl *Option<[mist_parser::ast::Visibility, mist_parser::ast::Identifier]>)
|
||||||
Result<[PathBuf, mist_codegen::RustCodegen, String], error::ParseError<'a, Vec<mist_parser::ast::TopLevel>>> {
|
Result<[PathBuf, mist_codegen::RustCodegen, String], TranspileError<'a>> {
|
||||||
|
|
||||||
if let Some[[_, name]] = &curr_mod_decl {
|
if let Some[[_, name]] = &curr_mod_decl {
|
||||||
if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") {
|
if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") {
|
||||||
@@ -204,7 +209,11 @@ pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf
|
|||||||
|
|
||||||
let mut gc = mist_codegen::RustCodegen::new(path.clone());
|
let mut gc = mist_codegen::RustCodegen::new(path.clone());
|
||||||
|
|
||||||
let parsed = mist_parser::parse(source)?;
|
let parsed = mist_parser::parse(source).map_err(TranspileError::Parse)?;
|
||||||
|
|
||||||
|
for i in &parsed {
|
||||||
|
let _ = mist_parser::semantics::check_class_semantics(i).map_err(TranspileError::Semantic)?;
|
||||||
|
}
|
||||||
|
|
||||||
let output = gc.generate(parsed);
|
let output = gc.generate(parsed);
|
||||||
|
|
||||||
@@ -217,33 +226,9 @@ pub fn transpile_file(path *PathBuf, source *str, output_file PathBuf, mod_decl
|
|||||||
let [output_file, gc, output] = match transpile_code(path, source, output_file, mod_decl, curr_mod_decl) {
|
let [output_file, gc, output] = match transpile_code(path, source, output_file, mod_decl, curr_mod_decl) {
|
||||||
Ok[output] => output;
|
Ok[output] => output;
|
||||||
Err[e] => {
|
Err[e] => {
|
||||||
match e {
|
decode_err(&e, path);
|
||||||
error::ParseError::Ast[e] => {
|
|
||||||
let start_pos = e.span.start_pos().line_col();
|
|
||||||
|
|
||||||
let span = e.span.as_str();
|
|
||||||
|
|
||||||
eprintln!(
|
|
||||||
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}",
|
|
||||||
path.as_os_str().display(),
|
|
||||||
start_pos.0,
|
|
||||||
start_pos.1,
|
|
||||||
e.error_message,
|
|
||||||
span,
|
|
||||||
if span.ends_with("\n") { "" } else { "\n" },
|
|
||||||
"^".repeat(span.trim().len()),
|
|
||||||
);
|
|
||||||
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
error::ParseError::PreAst[e] => {
|
|
||||||
eprintln!("error: parse failed in {}\n{}", path.display(), e);
|
|
||||||
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
let res = fs::write(&output_file, format!("{mod_decl}{output}"));
|
||||||
@@ -272,3 +257,46 @@ pub fn transpile_file(path *PathBuf, source *str, output_file PathBuf, mod_decl
|
|||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decode_err<'a>(err *TranspileError<'a>, path *PathBuf) {
|
||||||
|
let curr_dir = std::env::current_dir().unwrap();
|
||||||
|
|
||||||
|
match err {
|
||||||
|
TranspileError::Semantic[e_list] => {
|
||||||
|
for e in e_list {
|
||||||
|
eprintln!(
|
||||||
|
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}",
|
||||||
|
path.strip_prefix(&curr_dir).unwrap_or_else(|_| path).as_os_str().display(),
|
||||||
|
e.line,
|
||||||
|
e.column,
|
||||||
|
e.error_message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TranspileError::Parse[e] => {
|
||||||
|
match e {
|
||||||
|
error::ParseError::Ast[e] => {
|
||||||
|
let start_pos = e.span.start_pos().line_col();
|
||||||
|
|
||||||
|
let span = e.span.as_str();
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"\n{}:{}:{}\n \x1b[31mError\x1b[0m: {}\n\t{}{}\t{}",
|
||||||
|
path.strip_prefix(curr_dir).unwrap_or_else(|_| path).as_os_str().display(),
|
||||||
|
start_pos.0,
|
||||||
|
start_pos.1,
|
||||||
|
e.error_message,
|
||||||
|
span,
|
||||||
|
if span.ends_with("\n") { "" } else { "\n" },
|
||||||
|
"^".repeat(span.trim().len()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
error::ParseError::PreAst[e] => {
|
||||||
|
eprintln!("error: parse failed in {}\n{}", path.strip_prefix(curr_dir).unwrap_or_else(|_| path).display(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -333,7 +333,7 @@ trait_decl = {
|
|||||||
override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? }
|
override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? }
|
||||||
|
|
||||||
function_decl = {
|
function_decl = {
|
||||||
visibility? ~ override_kw? ~ "fn" ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ (block | semicolon)
|
visibility? ~ "fn" ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ override_kw? ~ (block | semicolon)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ pub mod ast;
|
|||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod rev_mapper;
|
pub mod rev_mapper;
|
||||||
|
pub mod semantics;
|
||||||
|
|
||||||
use ast::*;
|
use ast::*;
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ 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 = consume_rule(&mut inner, Rule::override_kw).map(Override::try_from).transpose();
|
|
||||||
|
|
||||||
let return_type = consume_rule(&mut inner, Rule::type_expr)
|
let return_type = consume_rule(&mut inner, Rule::type_expr)
|
||||||
.map(TypeExpr::try_from)
|
.map(TypeExpr::try_from)
|
||||||
@@ -64,6 +63,8 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| Ok(ParamList(self_param.into_iter().collect())));
|
.unwrap_or_else(|| Ok(ParamList(self_param.into_iter().collect())));
|
||||||
|
|
||||||
|
let is_override = consume_rule(&mut inner, Rule::override_kw).map(Override::try_from).transpose();
|
||||||
|
|
||||||
let body = inner.next().map(Block::try_from).transpose();
|
let body = inner.next().map(Block::try_from).transpose();
|
||||||
|
|
||||||
ast_expr!(Self {
|
ast_expr!(Self {
|
||||||
|
|||||||
@@ -0,0 +1,367 @@
|
|||||||
|
use crate::ast::{
|
||||||
|
Block, ClassItem, Expression, FunctionDecl, Identifier, MatchItem, Postfix, Prefix, Spanned,
|
||||||
|
Statement, StatementBranch, TopLevel, TopLevelKind, VarDeclStmt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait GetMutability {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SemanticError {
|
||||||
|
pub line: usize,
|
||||||
|
pub column: usize,
|
||||||
|
pub error_message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Class semantics - Ensuring fields are initialized
|
||||||
|
|
||||||
|
pub fn find_function_call<'a>(
|
||||||
|
items: &'a Vec<ClassItem>,
|
||||||
|
name: &Identifier,
|
||||||
|
) -> Option<&'a Spanned<FunctionDecl>> {
|
||||||
|
items.iter().find_map(|v| {
|
||||||
|
if let ClassItem::Method(m) = v {
|
||||||
|
if m.item.is_using_self() && &m.item.name == name {
|
||||||
|
Some(m)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec<SemanticError>> {
|
||||||
|
if let TopLevelKind::ClassDecl {
|
||||||
|
fields,
|
||||||
|
constructor,
|
||||||
|
items,
|
||||||
|
name,
|
||||||
|
inherits,
|
||||||
|
..
|
||||||
|
} = &top_level.0.item
|
||||||
|
{
|
||||||
|
if let Some(constructor) = constructor {
|
||||||
|
let mut fields = fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| Spanned {
|
||||||
|
item: field.item.decl.name.clone(),
|
||||||
|
line: field.line,
|
||||||
|
column: field.column,
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if inherits.is_some() {
|
||||||
|
fields.push(Spanned {
|
||||||
|
line: top_level.0.line,
|
||||||
|
column: top_level.0.column,
|
||||||
|
item: Identifier("_super".to_string()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut mutability = constructor.item.body.get_mutability();
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < mutability.len() {
|
||||||
|
let name = mutability[i].clone();
|
||||||
|
if let Some(method) = find_function_call(items, &name) {
|
||||||
|
if let Some(body) = &method.item.body {
|
||||||
|
for entry in body.get_mutability() {
|
||||||
|
if !mutability.contains(&entry) {
|
||||||
|
mutability.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut errs = Vec::new();
|
||||||
|
|
||||||
|
for field in &fields {
|
||||||
|
if !mutability.contains(&field.item) {
|
||||||
|
errs.push(SemanticError {
|
||||||
|
line: field.line,
|
||||||
|
column: field.column,
|
||||||
|
error_message: format!(
|
||||||
|
"class field `{}.{}` is uninitialized",
|
||||||
|
name.0, field.item.0
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs.len() > 0 {
|
||||||
|
return Err(errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: GetMutability> GetMutability for Vec<T> {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
self.iter().flat_map(T::get_mutability).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for Block {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
let mut items: Vec<Identifier> = self
|
||||||
|
.statements
|
||||||
|
.iter()
|
||||||
|
.flat_map(|v| v.item.get_mutability())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(v) = &self.soft_return {
|
||||||
|
items.append(&mut v.item.get_mutability());
|
||||||
|
}
|
||||||
|
|
||||||
|
items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for Statement {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
match self {
|
||||||
|
Statement::Block(body) | Statement::UnsafeBlock(body) => body.get_mutability(),
|
||||||
|
Statement::If {
|
||||||
|
initial,
|
||||||
|
else_if,
|
||||||
|
else_branch,
|
||||||
|
} => {
|
||||||
|
let mut all_sets: Vec<Vec<Identifier>> = Vec::new();
|
||||||
|
all_sets.push(initial.body.get_mutability());
|
||||||
|
|
||||||
|
for branch in else_if {
|
||||||
|
all_sets.push(branch.body.get_mutability());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(else_block) = else_branch {
|
||||||
|
all_sets.push(else_block.get_mutability());
|
||||||
|
} else {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = all_sets.remove(0);
|
||||||
|
for set in all_sets {
|
||||||
|
result.retain(|id| set.contains(id));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
Statement::Loop(body) => body.get_mutability(),
|
||||||
|
Statement::While(branch) => branch.body.get_mutability(),
|
||||||
|
Statement::CStyleFor {
|
||||||
|
body,
|
||||||
|
init,
|
||||||
|
condition,
|
||||||
|
update,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let mut a = init.get_mutability();
|
||||||
|
a.append(&mut condition.get_mutability());
|
||||||
|
a.append(&mut update.get_mutability());
|
||||||
|
a.append(&mut body.get_mutability());
|
||||||
|
a
|
||||||
|
}
|
||||||
|
Statement::For { body, iterator, .. } => {
|
||||||
|
let mut a = iterator.get_mutability();
|
||||||
|
a.append(&mut body.get_mutability());
|
||||||
|
a
|
||||||
|
}
|
||||||
|
Statement::Match(_, items) => {
|
||||||
|
if items.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
let mut all_sets: Vec<Vec<Identifier>> = items
|
||||||
|
.iter()
|
||||||
|
.map(|item| item.item.get_mutability())
|
||||||
|
.collect();
|
||||||
|
let mut result = all_sets.remove(0);
|
||||||
|
for set in all_sets {
|
||||||
|
result.retain(|id| set.contains(id));
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
Statement::VarDecl(stmt) => stmt
|
||||||
|
.init
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.get_mutability())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Statement::Return(expr) => expr
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.get_mutability())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
Statement::Break | Statement::Continue => Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for StatementBranch {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
let mut a = self.condition.get_mutability();
|
||||||
|
a.append(&mut self.body.get_mutability());
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for MatchItem {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
self.1.get_mutability()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for VarDeclStmt {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
self.init
|
||||||
|
.as_ref()
|
||||||
|
.map(|e| e.get_mutability())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
||||||
|
match initial {
|
||||||
|
Expression::Path(v) => {
|
||||||
|
let ident = v.0.first()?.ident.0.as_str();
|
||||||
|
if ident == "self" || ident == "super" {
|
||||||
|
if let Some(Postfix::FieldAccess(field, _)) = postfixes.first() {
|
||||||
|
if !matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
||||||
|
return Some(field.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_self_method_call(initial: &Expression, postfixes: &[Postfix]) -> Option<Identifier> {
|
||||||
|
match initial {
|
||||||
|
Expression::Path(v) => {
|
||||||
|
let ident = v.0.first()?.ident.0.as_str();
|
||||||
|
if ident == "self" || ident == "super" {
|
||||||
|
if let Some(Postfix::FieldAccess(method, _)) = postfixes.first() {
|
||||||
|
if matches!(postfixes.get(1), Some(Postfix::Call(_))) {
|
||||||
|
return Some(method.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expression {
|
||||||
|
fn get_self_ref(&self) -> Option<Identifier> {
|
||||||
|
match self {
|
||||||
|
Self::Fix {
|
||||||
|
initial, postfixes, ..
|
||||||
|
} => get_self_ref(initial, postfixes),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GetMutability for Expression {
|
||||||
|
fn get_mutability(&self) -> Vec<Identifier> {
|
||||||
|
match self {
|
||||||
|
Self::Array(v) => v.get_mutability(),
|
||||||
|
Self::ArrayRepeat(v, i) => {
|
||||||
|
let mut a = v.get_mutability();
|
||||||
|
a.append(&mut i.get_mutability());
|
||||||
|
a
|
||||||
|
}
|
||||||
|
Self::Binary { lhs, op, rhs } => {
|
||||||
|
if mutates_lhs(op) {
|
||||||
|
let mut a = if let Some(self_ref) = lhs.get_self_ref() {
|
||||||
|
vec![self_ref]
|
||||||
|
} else {
|
||||||
|
lhs.get_mutability()
|
||||||
|
};
|
||||||
|
|
||||||
|
a.append(&mut rhs.get_mutability());
|
||||||
|
a
|
||||||
|
} else {
|
||||||
|
let mut a = lhs.get_mutability();
|
||||||
|
a.append(&mut rhs.get_mutability());
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::Statement(stmt) => stmt.get_mutability(),
|
||||||
|
Self::Fix {
|
||||||
|
initial,
|
||||||
|
prefixes,
|
||||||
|
postfixes,
|
||||||
|
} => {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
// &mut self.field / &mut super.field / &mut super pattern
|
||||||
|
if prefixes.iter().any(|p| matches!(p, Prefix::RefMut)) {
|
||||||
|
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
||||||
|
result.push(self_ref);
|
||||||
|
} else if let Expression::Path(v) = &**initial {
|
||||||
|
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
||||||
|
result.push(Identifier("_super".to_string()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recurse into all postfix sub-expressions
|
||||||
|
for postfix in postfixes {
|
||||||
|
match postfix {
|
||||||
|
Postfix::Assign(_, rhs) => {
|
||||||
|
if let Some(self_ref) = get_self_ref(initial, postfixes) {
|
||||||
|
result.push(self_ref);
|
||||||
|
}
|
||||||
|
result.append(&mut rhs.get_mutability());
|
||||||
|
}
|
||||||
|
Postfix::Call(args) => {
|
||||||
|
for arg in args {
|
||||||
|
result.append(&mut arg.get_mutability());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Postfix::StructCall(fields) => {
|
||||||
|
for (_, expr) in fields {
|
||||||
|
if let Some(expr) = expr {
|
||||||
|
result.append(&mut expr.get_mutability());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Postfix::Index(expr) => {
|
||||||
|
result.append(&mut expr.get_mutability());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method calls on self: self.method_name(args)
|
||||||
|
if let Some(method) = get_self_method_call(initial, postfixes) {
|
||||||
|
result.push(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
Self::Closure { body, .. } => body.get_mutability(),
|
||||||
|
Self::Literal(_) => Vec::new(),
|
||||||
|
Self::Path(v) => {
|
||||||
|
if v.0.first().map_or(false, |s| s.ident.0 == "super") {
|
||||||
|
vec![Identifier("_super".to_string())]
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mutates_lhs(op: &str) -> bool {
|
||||||
|
matches!(op, "=" | "->")
|
||||||
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
pub module class_test;
|
pub module class_test;
|
||||||
|
|
||||||
pub class Animal<T> {
|
pub class Animal {
|
||||||
pub name String;
|
pub name String;
|
||||||
pub val T;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
self.name = "Rex".to_string();
|
self.name = "Rex".to_string();
|
||||||
@@ -19,7 +18,7 @@ pub class Animal<T> {
|
|||||||
pub fn void static_method() {}
|
pub fn void static_method() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub class Mammal<T> : Animal<T> {
|
pub class Mammal : Animal {
|
||||||
constructor() {
|
constructor() {
|
||||||
super = Super::new();
|
super = Super::new();
|
||||||
}
|
}
|
||||||
@@ -30,12 +29,12 @@ pub class Mammal<T> : Animal<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub class Dog : Mammal<i32> {
|
pub class Dog : Mammal {
|
||||||
constructor() {
|
constructor() {
|
||||||
super = Super::new();
|
super = Super::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub override(Animal<i32>) fn String speak(*self) {
|
pub fn String speak(*self) override(Animal) {
|
||||||
"Woof!".to_string()
|
"Woof!".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user