From 2bb29991dae1dfe30d02ece047eb11f510ea480a Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 14:59:34 +0200 Subject: [PATCH 1/7] Class semantics --- crates/mist-api/src/transpiler.mist | 4 + crates/parser/src/lib.rs | 1 + crates/parser/src/semantics.rs | 157 ++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 crates/parser/src/semantics.rs diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index f2975fe..6ce29aa 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -206,6 +206,10 @@ pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf let parsed = mist_parser::parse(source)?; + for i in &parsed { + let _ = mist_parser::semantics::check_class_semantics(i); + } + let output = gc.generate(parsed); gc.mapping.shift_rust(mod_decl.lines().count() as isize, 0); diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index ea39710..cf905f0 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -5,6 +5,7 @@ pub mod ast; pub mod error; pub mod parser; pub mod rev_mapper; +pub mod semantics; use ast::*; diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs new file mode 100644 index 0000000..6a2c664 --- /dev/null +++ b/crates/parser/src/semantics.rs @@ -0,0 +1,157 @@ +use crate::{ + ast::{ + Block, ClassItem, Expression, FunctionDecl, Identifier, Postfix, Spanned, TopLevel, + TopLevelKind, + }, + error::AstError, +}; + +pub trait GetMutability { + fn get_mutability(&self) -> Vec; +} + +/// Class semantics - Ensuring fields are initialized + +pub fn find_function_call<'a>( + items: &'a Vec, + name: &Identifier, +) -> Option<&'a Spanned> { + 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<(), AstError<'a, ()>> { + if let TopLevelKind::ClassDecl { + fields, + constructor, + items, + .. + } = &top_level.0.item + { + if let Some(constructor) = constructor { + let fields = fields + .iter() + .map(|field| Spanned { + item: field.item.decl.name.clone(), + line: field.line, + column: field.column, + }) + .collect::>(); + + let mut mutability = constructor.item.body.get_mutability(); + + for name in mutability.clone() { + if let Some(method) = find_function_call(items, &name) { + if let Some(body) = &method.item.body { + mutability.append(&mut body.get_mutability()); + } + } + } + + let mut mutability_iter = mutability.into_iter(); + + for field in &fields { + if mutability_iter.find(|v| v == &field.item).is_none() { + panic!("Unable to find set for field {}", field.item.0); + } + } + } + + Ok(()) + } else { + Ok(()) + } +} + +impl GetMutability for Vec { + fn get_mutability(&self) -> Vec { + self.iter().flat_map(T::get_mutability).collect() + } +} + +impl GetMutability for Block { + fn get_mutability(&self) -> Vec { + let mut items: Vec = 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 Expression { + fn get_self_ref(&self) -> Option { + match self { + Self::Fix { + initial, postfixes, .. + } => match &**initial { + Self::Path(v) => { + if v.0[0].ident.0 == "self" { + if let Some(field) = postfixes.get(0) { + match field { + Postfix::FieldAccess(field, _) => return Some(field.clone()), + _ => {} + } + } + } + } + _ => {} + }, + _ => {} + } + + None + } +} + +impl GetMutability for Expression { + fn get_mutability(&self) -> Vec { + 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 + } + } + _ => Vec::new(), + } + } +} + +pub fn mutates_lhs(op: &str) -> bool { + matches!( + op, + "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" + ) +} From bbb94a4f979dad9d55101a1efa637088532ec363 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 15:19:58 +0200 Subject: [PATCH 2/7] Semantic error handling and slight error improvements --- crates/mist-api/src/transpiler.mist | 80 ++++++++++++++++++----------- crates/parser/src/semantics.rs | 26 ++++++---- 2 files changed, 68 insertions(+), 38 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 6ce29aa..992b3f1 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -184,8 +184,13 @@ fn is_source_newer(source *Path, output *Path) std::io::Result { Ok(source_time > output_time) } +pub enum TranspileError<'a> { + Parse[error::ParseError<'a, Vec>], + Semantic[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]>) - Result<[PathBuf, mist_codegen::RustCodegen, String], error::ParseError<'a, Vec>> { + Result<[PathBuf, mist_codegen::RustCodegen, String], TranspileError<'a>> { if let Some[[_, name]] = &curr_mod_decl { if output_file.file_name().map(fn(v) -> v.to_str()).unwrap_or_default() != Some("mod.rs") { @@ -204,10 +209,10 @@ pub fn transpile_code<'a>(path *PathBuf, source *'a str, mut output_file PathBuf 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); + let _ = mist_parser::semantics::check_class_semantics(i).map_err(TranspileError::Semantic)?; } let output = gc.generate(parsed); @@ -221,32 +226,8 @@ 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) { Ok[output] => output; Err[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.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); - } - - error::ParseError::PreAst[e] => { - eprintln!("error: parse failed in {}\n{}", path.display(), e); - - process::exit(1); - } - } + decode_err(&e, path); + process::exit(1); } }; @@ -276,3 +257,44 @@ pub fn transpile_file(path *PathBuf, source *str, output_file PathBuf, mod_decl 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] => { + 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); + } + } + } + } +} diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs index 6a2c664..fa369ae 100644 --- a/crates/parser/src/semantics.rs +++ b/crates/parser/src/semantics.rs @@ -1,15 +1,19 @@ -use crate::{ - ast::{ - Block, ClassItem, Expression, FunctionDecl, Identifier, Postfix, Spanned, TopLevel, - TopLevelKind, - }, - error::AstError, +use crate::ast::{ + Block, ClassItem, Expression, FunctionDecl, Identifier, Postfix, Spanned, TopLevel, + TopLevelKind, }; pub trait GetMutability { fn get_mutability(&self) -> Vec; } +#[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>( @@ -29,7 +33,7 @@ pub fn find_function_call<'a>( }) } -pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), AstError<'a, ()>> { +pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), SemanticError> { if let TopLevelKind::ClassDecl { fields, constructor, @@ -61,7 +65,11 @@ pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), AstError<'a for field in &fields { if mutability_iter.find(|v| v == &field.item).is_none() { - panic!("Unable to find set for field {}", field.item.0); + return Err(SemanticError { + line: field.line, + column: field.column, + error_message: format!("Field `{}` is uninitialized", field.item.0), + }); } } } @@ -152,6 +160,6 @@ impl GetMutability for Expression { pub fn mutates_lhs(op: &str) -> bool { matches!( op, - "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" + "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "->" ) } From ecfff0ac0dd768dc55acd8da2275567dbe3ae231 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 15:37:34 +0200 Subject: [PATCH 3/7] Improved semantic errors --- crates/mist-api/src/transpiler.mist | 20 +++++++++++--------- crates/parser/src/semantics.rs | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 992b3f1..f7001ea 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -186,7 +186,7 @@ fn is_source_newer(source *Path, output *Path) std::io::Result { pub enum TranspileError<'a> { Parse[error::ParseError<'a, Vec>], - Semantic[mist_parser::semantics::SemanticError] + Semantic[Vec] } 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]>) @@ -262,14 +262,16 @@ pub fn decode_err<'a>(err *TranspileError<'a>, path *PathBuf) { let curr_dir = std::env::current_dir().unwrap(); match err { - TranspileError::Semantic[e] => { - 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::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] => { diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs index fa369ae..3cfccbe 100644 --- a/crates/parser/src/semantics.rs +++ b/crates/parser/src/semantics.rs @@ -33,11 +33,12 @@ pub fn find_function_call<'a>( }) } -pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), SemanticError> { +pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec> { if let TopLevelKind::ClassDecl { fields, constructor, items, + name, .. } = &top_level.0.item { @@ -63,15 +64,24 @@ pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), SemanticErr let mut mutability_iter = mutability.into_iter(); + let mut errs = Vec::new(); + for field in &fields { if mutability_iter.find(|v| v == &field.item).is_none() { - return Err(SemanticError { + errs.push(SemanticError { line: field.line, column: field.column, - error_message: format!("Field `{}` is uninitialized", field.item.0), + error_message: format!( + "class field `{}.{}` is uninitialized", + name.0, field.item.0 + ), }); } } + + if errs.len() > 0 { + return Err(errs); + } } Ok(()) From d434ec78e76d017e79138b4c6410110e9a5c9855 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 15:54:21 +0200 Subject: [PATCH 4/7] More advanced class semantics --- crates/parser/src/semantics.rs | 164 ++++++++++++++++++++++++---- mist-test/src/items/class_test.mist | 16 ++- 2 files changed, 152 insertions(+), 28 deletions(-) diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs index 3cfccbe..ac9f95b 100644 --- a/crates/parser/src/semantics.rs +++ b/crates/parser/src/semantics.rs @@ -1,6 +1,6 @@ use crate::ast::{ - Block, ClassItem, Expression, FunctionDecl, Identifier, Postfix, Spanned, TopLevel, - TopLevelKind, + Block, ClassItem, Expression, FunctionDecl, Identifier, MatchItem, Postfix, Prefix, Spanned, + Statement, StatementBranch, TopLevel, TopLevelKind, VarDeclStmt, }; pub trait GetMutability { @@ -112,28 +112,123 @@ impl GetMutability for Block { } } +impl GetMutability for Statement { + fn get_mutability(&self) -> Vec { + match self { + Statement::Block(body) | Statement::UnsafeBlock(body) => body.get_mutability(), + Statement::If { + initial, + else_if, + else_branch, + } => { + let mut all_sets: Vec> = 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> = + 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 { + let mut a = self.condition.get_mutability(); + a.append(&mut self.body.get_mutability()); + a + } +} + +impl GetMutability for MatchItem { + fn get_mutability(&self) -> Vec { + self.1.get_mutability() + } +} + +impl GetMutability for VarDeclStmt { + fn get_mutability(&self) -> Vec { + self.init + .as_ref() + .map(|e| e.get_mutability()) + .unwrap_or_default() + } +} + +fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option { + match initial { + Expression::Path(v) => { + if v.0.first()?.ident.0 == "self" { + if let Some(Postfix::FieldAccess(field, _)) = postfixes.first() { + if !matches!(postfixes.get(1), Some(Postfix::Call(_))) { + return Some(field.clone()); + } + } + } + } + _ => {} + } + None +} + impl Expression { fn get_self_ref(&self) -> Option { match self { Self::Fix { initial, postfixes, .. - } => match &**initial { - Self::Path(v) => { - if v.0[0].ident.0 == "self" { - if let Some(field) = postfixes.get(0) { - match field { - Postfix::FieldAccess(field, _) => return Some(field.clone()), - _ => {} - } - } - } - } - _ => {} - }, - _ => {} + } => get_self_ref(initial, postfixes), + _ => None, } - - None } } @@ -162,14 +257,37 @@ impl GetMutability for Expression { a } } - _ => Vec::new(), + Self::Statement(stmt) => stmt.get_mutability(), + Self::Fix { + initial, + prefixes, + postfixes, + } => { + let mut result = Vec::new(); + + if prefixes.iter().any(|p| matches!(p, Prefix::RefMut)) { + if let Some(self_ref) = get_self_ref(initial, postfixes) { + result.push(self_ref); + } + } + + for postfix in postfixes { + if let Postfix::Assign(_, rhs) = postfix { + if let Some(self_ref) = get_self_ref(initial, postfixes) { + result.push(self_ref); + } + result.append(&mut rhs.get_mutability()); + } + } + + result + } + Self::Closure { body, .. } => body.get_mutability(), + Self::Literal(_) | Self::Path(_) => Vec::new(), } } } pub fn mutates_lhs(op: &str) -> bool { - matches!( - op, - "=" | "+=" | "-=" | "*=" | "/=" | "%=" | "&=" | "|=" | "^=" | "<<=" | ">>=" | "->" - ) + matches!(op, "=" | "->" | "+=" | "-=" | "*=" | "/=") } diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index a95c15a..65310fa 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -1,11 +1,17 @@ pub module class_test; -pub class Animal { +pub class Animal { pub name String; - pub val T; + pub val i32; constructor() { self.name = "Rex".to_string(); + + self.init_v(); + } + + pub fn void init_v(*mut self) { + self.val = 0; } pub fn String speak(*self) { @@ -19,7 +25,7 @@ pub class Animal { pub fn void static_method() {} } -pub class Mammal : Animal { +pub class Mammal : Animal { constructor() { super = Super::new(); } @@ -30,12 +36,12 @@ pub class Mammal : Animal { } } -pub class Dog : Mammal { +pub class Dog : Mammal { constructor() { super = Super::new(); } - pub override(Animal) fn String speak(*self) { + pub override(Animal) fn String speak(*self) { "Woof!".to_string() } From e369c94d213924a088a9e5ec5fc6919a4cd58fbc Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 16:00:52 +0200 Subject: [PATCH 5/7] Method calls --- crates/parser/src/semantics.rs | 66 ++++++++++++++++++++++++----- mist-test/src/items/class_test.mist | 7 --- 2 files changed, 56 insertions(+), 17 deletions(-) diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs index ac9f95b..a90706c 100644 --- a/crates/parser/src/semantics.rs +++ b/crates/parser/src/semantics.rs @@ -54,20 +54,25 @@ pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec Option Option { + match initial { + Expression::Path(v) => { + if v.0.first()?.ident.0 == "self" { + 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 { match self { @@ -265,21 +286,46 @@ impl GetMutability for Expression { } => { let mut result = Vec::new(); + // &mut self.field pattern if prefixes.iter().any(|p| matches!(p, Prefix::RefMut)) { if let Some(self_ref) = get_self_ref(initial, postfixes) { result.push(self_ref); } } + // Recurse into all postfix sub-expressions for postfix in postfixes { - if let Postfix::Assign(_, rhs) = postfix { - if let Some(self_ref) = get_self_ref(initial, postfixes) { - result.push(self_ref); + 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()); } - 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(), @@ -289,5 +335,5 @@ impl GetMutability for Expression { } pub fn mutates_lhs(op: &str) -> bool { - matches!(op, "=" | "->" | "+=" | "-=" | "*=" | "/=") + matches!(op, "=" | "->") } diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index 65310fa..3cb41b9 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -2,16 +2,9 @@ pub module class_test; pub class Animal { pub name String; - pub val i32; constructor() { self.name = "Rex".to_string(); - - self.init_v(); - } - - pub fn void init_v(*mut self) { - self.val = 0; } pub fn String speak(*self) { From 8b954977254a7a4b125d50e8806b0fe7e7439a1f Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 16:14:02 +0200 Subject: [PATCH 6/7] Super semantics --- crates/parser/src/semantics.rs | 44 +++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/parser/src/semantics.rs b/crates/parser/src/semantics.rs index a90706c..0b1a9aa 100644 --- a/crates/parser/src/semantics.rs +++ b/crates/parser/src/semantics.rs @@ -39,11 +39,12 @@ pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec(top_level: &TopLevel) -> Result<(), 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; @@ -148,7 +157,11 @@ impl GetMutability for Statement { Statement::Loop(body) => body.get_mutability(), Statement::While(branch) => branch.body.get_mutability(), Statement::CStyleFor { - body, init, condition, update, .. + body, + init, + condition, + update, + .. } => { let mut a = init.get_mutability(); a.append(&mut condition.get_mutability()); @@ -165,8 +178,10 @@ impl GetMutability for Statement { if items.is_empty() { return Vec::new(); } - let mut all_sets: Vec> = - items.iter().map(|item| item.item.get_mutability()).collect(); + let mut all_sets: Vec> = 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)); @@ -213,7 +228,8 @@ impl GetMutability for VarDeclStmt { fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option { match initial { Expression::Path(v) => { - if v.0.first()?.ident.0 == "self" { + 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()); @@ -229,7 +245,8 @@ fn get_self_ref(initial: &Expression, postfixes: &[Postfix]) -> Option Option { match initial { Expression::Path(v) => { - if v.0.first()?.ident.0 == "self" { + 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()); @@ -286,10 +303,14 @@ impl GetMutability for Expression { } => { let mut result = Vec::new(); - // &mut self.field pattern + // &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())); + } } } @@ -329,7 +350,14 @@ impl GetMutability for Expression { result } Self::Closure { body, .. } => body.get_mutability(), - Self::Literal(_) | Self::Path(_) => Vec::new(), + 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() + } + } } } } From 4def6db820fc766492ebfbab5dcf20f0327c1291 Mon Sep 17 00:00:00 2001 From: Klesti Selimaj Date: Tue, 16 Jun 2026 16:19:28 +0200 Subject: [PATCH 7/7] Moved ovveride to after params --- crates/parser/src/grammar.pest | 2 +- crates/parser/src/parser/items/function.rs | 3 ++- mist-test/src/items/class_test.mist | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/parser/src/grammar.pest b/crates/parser/src/grammar.pest index 5af7c30..561c979 100644 --- a/crates/parser/src/grammar.pest +++ b/crates/parser/src/grammar.pest @@ -333,7 +333,7 @@ trait_decl = { override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? } 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) } // ====================================================== diff --git a/crates/parser/src/parser/items/function.rs b/crates/parser/src/parser/items/function.rs index ef37078..10b519e 100644 --- a/crates/parser/src/parser/items/function.rs +++ b/crates/parser/src/parser/items/function.rs @@ -13,7 +13,6 @@ impl<'a> TryFrom> for FunctionDecl { ast_ensure!(pair, Rule::function_decl => { let mut inner = pair.into_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) .map(TypeExpr::try_from) @@ -64,6 +63,8 @@ impl<'a> TryFrom> for FunctionDecl { }) .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(); ast_expr!(Self { diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index 3cb41b9..3d7ab57 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -34,7 +34,7 @@ pub class Dog : Mammal { super = Super::new(); } - pub override(Animal) fn String speak(*self) { + pub fn String speak(*self) override(Animal) { "Woof!".to_string() }