Merge pull request #112 from mist-go/virtual-methods

Virtual methods
This commit is contained in:
2026-07-09 07:38:25 +02:00
committed by GitHub
7 changed files with 87 additions and 27 deletions
+23 -14
View File
@@ -26,7 +26,7 @@ use crate::transpiler::{
static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0); static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0);
const KEYWORDS: [&'static str; 22] = [ const KEYWORDS: [&'static str; 23] = [
"if", "if",
"else", "else",
"for", "for",
@@ -49,6 +49,7 @@ const KEYWORDS: [&'static str; 22] = [
"override", "override",
"module ", "module ",
"void ", "void ",
"virtual ",
]; ];
fn keyword_completion_items() -> impl Iterator<Item = CompletionItem> { fn keyword_completion_items() -> impl Iterator<Item = CompletionItem> {
@@ -1630,11 +1631,11 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
// Struct/Class // Struct/Class
// ========================= // =========================
if matches!(curr_scope, Scope::Struct) { if matches!(curr_scope, Scope::Class) {
items.push(CompletionItem { items.push(CompletionItem {
label: "field".into(), label: "virtual".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("$1 $2,".into()), insert_text: Some("pub virtual $1 $2()\n{\n\t$3\n}".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()), sort_text: Some("0000".into()),
preselect: Some(true), preselect: Some(true),
@@ -1642,9 +1643,19 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
}); });
items.push(CompletionItem { items.push(CompletionItem {
label: "pub field".into(), label: "override".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("pub $1 $2,".into()), insert_text: Some("$1 $2() override$3\n{\n\t$4\n}".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()),
preselect: Some(true),
..Default::default()
});
items.push(CompletionItem {
label: "virtual override".into(),
kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("pub virtual $1 $2() override$3\n{\n\t$4\n}".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()), sort_text: Some("0000".into()),
preselect: Some(true), preselect: Some(true),
@@ -1652,7 +1663,8 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
}); });
} }
if matches!(curr_scope, Scope::Class) {
if matches!(curr_scope, Scope::Class | Scope::Struct) {
items.push(CompletionItem { items.push(CompletionItem {
label: "field".into(), label: "field".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
@@ -1682,7 +1694,7 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
items.push(CompletionItem { items.push(CompletionItem {
label: "variant".into(), label: "variant".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("$1,".into()), insert_text: Some("$1;".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()), sort_text: Some("0000".into()),
preselect: Some(true), preselect: Some(true),
@@ -1692,7 +1704,7 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
items.push(CompletionItem { items.push(CompletionItem {
label: "tuple variant".into(), label: "tuple variant".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("$1($2),".into()), insert_text: Some("$1($2);".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()), sort_text: Some("0000".into()),
preselect: Some(true), preselect: Some(true),
@@ -1702,7 +1714,7 @@ fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec<Completion
items.push(CompletionItem { items.push(CompletionItem {
label: "struct variant".into(), label: "struct variant".into(),
kind: Some(CompletionItemKind::SNIPPET), kind: Some(CompletionItemKind::SNIPPET),
insert_text: Some("$1\n{\n\t$0\n},".into()), insert_text: Some("$1\n{\n\t$0\n};".into()),
insert_text_format: Some(InsertTextFormat::SNIPPET), insert_text_format: Some(InsertTextFormat::SNIPPET),
sort_text: Some("0000".into()), sort_text: Some("0000".into()),
preselect: Some(true), preselect: Some(true),
@@ -2024,10 +2036,7 @@ fn compute_mod_decls(
let source = rope.to_string(); let source = rope.to_string();
parse_module(&source).ok().flatten().map(|(_, name)| name.0) parse_module(&source).ok().flatten().map(|(_, name)| name.0)
}) })
.or_else(|| { .or_else(|| file.file_stem().and_then(|s| s.to_str().map(String::from)));
file.file_stem()
.and_then(|s| s.to_str().map(String::from))
});
if let Some(name) = mod_name { if let Some(name) = mod_name {
mod_decl.push_str(&format!("pub mod {};\n", name)); mod_decl.push_str(&format!("pub mod {};\n", name));
} }
+16 -7
View File
@@ -16,7 +16,7 @@ pub struct ClassProcessedData {
items: Vec<ClassItem>, items: Vec<ClassItem>,
methods: Vec<Spanned<FunctionDecl>>, methods: Vec<Spanned<FunctionDecl>>,
v_table: Vec<Identifier>, v_table: Vec<Identifier>,
override_v_table: HashMap<Override, Spanned<Vec<Identifier>>>, override_v_table: HashMap<Override, Spanned<Vec<(Identifier, bool)>>>,
} }
impl ClassProcessedData { impl ClassProcessedData {
@@ -51,7 +51,7 @@ impl ClassProcessedData {
if matches!(method.item.visibility, Visibility::Public) { if matches!(method.item.visibility, Visibility::Public) {
match &method.item.is_override { match &method.item.is_override {
None => { None => {
if method.item.self_param.is_some() { if method.item.is_virtual && method.item.self_param.is_some() {
v_table.push(method.item.name.clone()); v_table.push(method.item.name.clone());
} }
} }
@@ -64,7 +64,7 @@ impl ClassProcessedData {
item: Vec::new(), item: Vec::new(),
}) })
.item .item
.push(method.item.name.clone()); .push((method.item.name.clone(), method.item.is_virtual));
} }
} }
} }
@@ -191,13 +191,13 @@ impl ClassProcessedData {
.as_ref() .as_ref()
.unwrap_or(self.inherits.as_ref().unwrap()) .unwrap_or(self.inherits.as_ref().unwrap())
.get_rust(); .get_rust();
for method_ident in &overriden_method_idents.item { for (method_ident, is_virtual) in &overriden_method_idents.item {
cg.add_indentedln(&format!( cg.add_indentedln(&format!(
"table[{}::__FN_{}] = {}::__m_{} as *const std::ffi::c_void;", "table[{}::__FN_{}] = {}::{} as *const std::ffi::c_void;",
base_class_path, base_class_path,
method_ident.0.to_uppercase(), method_ident.0.to_uppercase(),
self.self_path.get_rust(), self.self_path.get_rust(),
method_ident.get_rust() get_virtual_name(method_ident, *is_virtual)
)); ));
} }
} }
@@ -380,6 +380,7 @@ impl ClassProcessedData {
column: constructor.column, column: constructor.column,
item: FunctionDecl { item: FunctionDecl {
visibility: constructor.item.visibility.clone(), visibility: constructor.item.visibility.clone(),
is_virtual: false,
is_override: None, is_override: None,
name: Identifier(String::from("constructor")), name: Identifier(String::from("constructor")),
generics: constructor.item.generics.clone(), generics: constructor.item.generics.clone(),
@@ -396,7 +397,7 @@ impl ClassProcessedData {
for method in &self.methods { for method in &self.methods {
match method.item.visibility { match method.item.visibility {
Visibility::Public => { Visibility::Public => {
if method.item.self_param.is_some() { if method.item.is_virtual && method.item.self_param.is_some() {
if method.item.is_override.is_none() { if method.item.is_override.is_none() {
gen_method_point(&method.item, ctx, cg); gen_method_point(&method.item, ctx, cg);
} }
@@ -617,3 +618,11 @@ pub fn get_type_from_path(path: &ExprPath) -> TypeExpr {
path.0.last().unwrap().generics.clone(), path.0.last().unwrap().generics.clone(),
) )
} }
pub fn get_virtual_name(name: &Identifier, is_virtual: bool) -> String {
if is_virtual {
format!("__m_{}", name.get_rust())
} else {
name.get_rust()
}
}
+3 -3
View File
@@ -107,12 +107,13 @@ pub struct Override(pub Option<ExprPath>);
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct FunctionDecl { pub struct FunctionDecl {
pub visibility: Visibility, pub visibility: Visibility,
pub is_override: Option<Override>, pub is_virtual: bool,
pub return_type: Option<TypeExpr>,
pub name: Identifier, pub name: Identifier,
pub generics: GenericsDecl, pub generics: GenericsDecl,
pub self_param: Option<(bool, Option<Identifier>, bool)>, pub self_param: Option<(bool, Option<Identifier>, bool)>,
pub params: ParamList, pub params: ParamList,
pub return_type: Option<TypeExpr>, pub is_override: Option<Override>,
pub body: Option<Block>, pub body: Option<Block>,
} }
@@ -129,7 +130,6 @@ pub struct FieldDecl {
pub visibility: Visibility, pub visibility: Visibility,
pub type_: TypeExpr, pub type_: TypeExpr,
pub name: Identifier, pub name: Identifier,
// pub tuple_names: Vec<Identifier>,
} }
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
+4 -1
View File
@@ -36,6 +36,7 @@ keyword = {
| "override" | "override"
| "const" | "const"
| "type" | "type"
| "virtual"
} }
identifier = @{ identifier = @{
@@ -373,10 +374,12 @@ trait_decl = {
// FUNCTIONS // FUNCTIONS
// ====================================================== // ======================================================
virtual_kw = { "virtual" }
override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? } override_kw = { "override" ~ ("(" ~ expr_path ~ ")")? }
function_decl = { function_decl = {
visibility? ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ override_kw? ~ (block | semicolon) visibility? ~ virtual_kw? ~ ("void" | type_expr) ~ identifier ~ generics_decl? ~ "(" ~ method_args ~ ")" ~ override_kw? ~ (block | semicolon)
} }
// ====================================================== // ======================================================
+4 -1
View File
@@ -14,6 +14,8 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
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_virtual = listen_rule(&mut inner, Rule::virtual_kw);
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)
.transpose()?; .transpose()?;
@@ -45,12 +47,13 @@ impl<'a> TryFrom<pest::iterators::Pair<'a, Rule>> for FunctionDecl {
Ok(Self { Ok(Self {
visibility: visibility, visibility: visibility,
is_override: is_override, is_virtual: is_virtual,
return_type: return_type, return_type: return_type,
name: name, name: name,
generics: generics, generics: generics,
self_param: self_param, self_param: self_param,
params: params, params: params,
is_override: is_override,
body: body, body: body,
}) })
}) })
+22
View File
@@ -102,6 +102,28 @@ pub fn check_class_semantics<'a>(top_level: &TopLevel) -> Result<(), Vec<Semanti
} }
Ok(()) Ok(())
} else if let TopLevelKind::FunctionDecl(decl) = &top_level.0.item {
if decl.is_virtual && decl.is_override.is_some() {
Err(vec![SemanticError {
line: top_level.0.line,
column: top_level.0.column,
error_message: "virtual and override are only used in classes".to_string(),
}])
} else if decl.is_virtual {
Err(vec![SemanticError {
line: top_level.0.line,
column: top_level.0.column,
error_message: "virtual is only used in classes".to_string(),
}])
} else if decl.is_override.is_some() {
Err(vec![SemanticError {
line: top_level.0.line,
column: top_level.0.column,
error_message: "override is only used in classes".to_string(),
}])
} else {
Ok(())
}
} else { } else {
Ok(()) Ok(())
} }
+15 -1
View File
@@ -1,5 +1,9 @@
pub module class_test; pub module class_test;
pub enum AnimalType {
Mammal;
}
pub class Animal pub class Animal
{ {
pub String name; pub String name;
@@ -9,7 +13,11 @@ pub class Animal
self.name = "Rex".to_string(); self.name = "Rex".to_string();
} }
pub String speak(&self) pub virtual AnimalType get_animal_type(&self) {
panic!()
}
pub virtual String speak(&self)
{ {
"Unknown".to_string() "Unknown".to_string()
} }
@@ -31,6 +39,11 @@ pub class Mammal : Animal
super = Super::new(); super = Super::new();
} }
// override without a target assumes current super
pub AnimalType get_animal_type(&self) override {
AnimalType::Mammal
}
pub void args_test(&self, i32 x, str& y) pub void args_test(&self, i32 x, str& y)
{ {
assert_eq!(x, 10); assert_eq!(x, 10);
@@ -124,6 +137,7 @@ pub void run_tests()
let dawg = Dog::new(); let dawg = Dog::new();
dawg.args_test(10, "hello"); dawg.args_test(10, "hello");
dawg.test_speak(); dawg.test_speak();
dawg.get_animal_type();
assert_eq!(format!("{dawg}"), "🐾 Rex"); assert_eq!(format!("{dawg}"), "🐾 Rex");
test_class_no_inheritance(); test_class_no_inheritance();
test_class_field_default(); test_class_field_default();