diff --git a/lib/edlang_ast/src/lib.rs b/lib/edlang_ast/src/lib.rs index a533fc121..56dd3c4b3 100644 --- a/lib/edlang_ast/src/lib.rs +++ b/lib/edlang_ast/src/lib.rs @@ -16,6 +16,7 @@ pub enum ModuleStatement { Function(Function), Constant(Constant), Struct(Struct), + StructImpl(StructImpl), Module(Module), } @@ -169,6 +170,14 @@ pub struct Struct { pub span: Span, } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct StructImpl { + pub name: Ident, + pub generics: Vec, + pub methods: Vec, + pub span: Span, +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum Expression { Value(ValueExpr), diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 24203445c..f0a85c300 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -16,6 +16,7 @@ pub struct SymbolTable { pub symbols: BTreeMap, pub modules: BTreeMap, pub functions: BTreeMap, + pub methods: BTreeMap>, pub constants: BTreeMap, pub structs: BTreeMap, pub types: BTreeMap, diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index bed9ffd4e..45b424ff6 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -60,35 +60,104 @@ fn lower_module( // fill fn sigs for content in &module.contents { - if let ModuleStatement::Function(fn_def) = content { - let body = ctx.body.modules.get(&id).unwrap(); - let fn_id = *body.symbols.functions.get(&fn_def.name.name).unwrap(); + match content { + ModuleStatement::Function(fn_def) => { + let body = ctx.body.modules.get(&id).unwrap(); + let fn_id = *body.symbols.functions.get(&fn_def.name.name).unwrap(); - let mut args = Vec::new(); - let ret_type; + let mut args = Vec::new(); + let ret_type; - for arg in &fn_def.params { - let ty = lower_type(&ctx, &arg.arg_type, id)?; - args.push(ty); + for arg in &fn_def.params { + let ty = lower_type(&ctx, &arg.arg_type, id)?; + args.push(ty); + } + + if let Some(ty) = &fn_def.return_type { + ret_type = lower_type(&ctx, ty, id)?; + } else { + ret_type = TypeInfo { + span: None, + kind: ir::TypeKind::Unit, + }; + } + + ctx.body.function_signatures.insert(fn_id, (args, ret_type)); } + ModuleStatement::StructImpl(info) => { + // todo: handle generics + assert!(info.generics.is_empty(), "generics not yet implemented"); - if let Some(ty) = &fn_def.return_type { - ret_type = lower_type(&ctx, ty, id)?; - } else { - ret_type = TypeInfo { - span: None, - kind: ir::TypeKind::Unit, + let struct_id = { + let body = ctx.body.modules.get(&id).unwrap(); + *body.symbols.structs.get(&info.name.name).unwrap() }; - } - ctx.body.function_signatures.insert(fn_id, (args, ret_type)); + for fn_def in &info.methods { + let body = ctx.body.modules.get(&id).unwrap(); + + let fn_id = *body + .symbols + .methods + .get(&struct_id) + .expect("struct id not found") + .get(&fn_def.name.name) + .expect("struct method not found"); + + let mut args = Vec::new(); + let ret_type; + + for arg in &fn_def.params { + let ty = lower_type(&ctx, &arg.arg_type, id)?; + args.push(ty); + } + + if let Some(ty) = &fn_def.return_type { + ret_type = lower_type(&ctx, ty, id)?; + } else { + ret_type = TypeInfo { + span: None, + kind: ir::TypeKind::Unit, + }; + } + + ctx.body.function_signatures.insert(fn_id, (args, ret_type)); + } + } + _ => {} } } for content in &module.contents { match content { ModuleStatement::Function(fn_def) => { - ctx = lower_function(ctx, fn_def, id)?; + let fn_id = { + let body = ctx.body.modules.get(&id).unwrap(); + *body.symbols.functions.get(&fn_def.name.name).unwrap() + }; + ctx = lower_function(ctx, fn_def, id, fn_id)?; + } + ModuleStatement::StructImpl(info) => { + let struct_id = { + let body = ctx.body.modules.get(&id).unwrap(); + *body.symbols.structs.get(&info.name.name).unwrap() + }; + + for fn_def in &info.methods { + // todo: handle generics + assert!(info.generics.is_empty(), "generics not yet implemented"); + let fn_id = { + let body = ctx.body.modules.get(&id).unwrap(); + *body + .symbols + .methods + .get(&struct_id) + .unwrap() + .get(&fn_def.name.name) + .unwrap() + }; + ctx = lower_function(ctx, fn_def, id, fn_id)?; + } } // ModuleStatement::Type(_) => todo!(), ModuleStatement::Module(mod_def) => { @@ -139,16 +208,14 @@ fn lower_function( ctx: BuildCtx, func: &ast::Function, module_id: DefId, + fn_id: DefId, ) -> Result { let mut builder = BodyBuilder { body: Body { blocks: Default::default(), locals: Default::default(), name: func.name.name.clone(), - def_id: { - let body = ctx.body.modules.get(&module_id).unwrap(); - *body.symbols.functions.get(&func.name.name).unwrap() - }, + def_id: fn_id, is_pub: func.is_public, is_extern: func.is_extern, is_exported: func.is_exported || func.name.name == "main", diff --git a/lib/edlang_lowering/src/prepass.rs b/lib/edlang_lowering/src/prepass.rs index 4b8b347ef..968e0cab5 100644 --- a/lib/edlang_lowering/src/prepass.rs +++ b/lib/edlang_lowering/src/prepass.rs @@ -71,12 +71,19 @@ pub fn prepass_module( ); } ast::ModuleStatement::Struct(info) => { - let next_id = gen.next_defid(); - current_module + if current_module .symbols .structs - .insert(info.name.name.clone(), next_id); - current_module.structs.insert(next_id); + .get(&info.name.name) + .is_none() + { + let next_id = gen.next_defid(); + current_module + .symbols + .structs + .insert(info.name.name.clone(), next_id); + current_module.structs.insert(next_id); + } } /* ast::ModuleStatement::Type(info) => { @@ -96,6 +103,39 @@ pub fn prepass_module( .insert(info.name.name.clone(), next_id); current_module.modules.insert(next_id); } + ast::ModuleStatement::StructImpl(info) => { + if current_module + .symbols + .structs + .get(&info.name.name) + .is_none() + { + let next_id = gen.next_defid(); + current_module + .symbols + .structs + .insert(info.name.name.clone(), next_id); + current_module.structs.insert(next_id); + } + + let struct_id = *current_module.symbols.structs.get(&info.name.name).unwrap(); + + for method in &info.methods { + let next_id = gen.next_defid(); + let struct_methods = + current_module.symbols.methods.entry(struct_id).or_default(); + current_module.functions.insert(next_id); + struct_methods.insert(method.name.name.clone(), next_id); + + ctx.unresolved_function_signatures.insert( + next_id, + ( + method.params.iter().map(|x| &x.arg_type).cloned().collect(), + method.return_type.clone(), + ), + ); + } + } } } @@ -172,12 +212,14 @@ pub fn prepass_sub_module( ); } ast::ModuleStatement::Struct(info) => { - let next_id = gen.next_defid(); - submodule - .symbols - .structs - .insert(info.name.name.clone(), next_id); - submodule.structs.insert(next_id); + if submodule.symbols.structs.get(&info.name.name).is_none() { + let next_id = gen.next_defid(); + submodule + .symbols + .structs + .insert(info.name.name.clone(), next_id); + submodule.structs.insert(next_id); + } } /* ast::ModuleStatement::Type(info) => { @@ -197,6 +239,34 @@ pub fn prepass_sub_module( .insert(info.name.name.clone(), next_id); submodule.modules.insert(next_id); } + ast::ModuleStatement::StructImpl(info) => { + if submodule.symbols.structs.get(&info.name.name).is_none() { + let next_id = gen.next_defid(); + submodule + .symbols + .structs + .insert(info.name.name.clone(), next_id); + submodule.structs.insert(next_id); + } + + let struct_id = *submodule.symbols.structs.get(&info.name.name).unwrap(); + + for method in &info.methods { + let next_id = gen.next_defid(); + let struct_methods = + submodule.symbols.methods.entry(struct_id).or_default(); + submodule.functions.insert(next_id); + struct_methods.insert(method.name.name.clone(), next_id); + + ctx.unresolved_function_signatures.insert( + next_id, + ( + method.params.iter().map(|x| &x.arg_type).cloned().collect(), + method.return_type.clone(), + ), + ); + } + } } } diff --git a/lib/edlang_parser/src/grammar.lalrpop b/lib/edlang_parser/src/grammar.lalrpop index 0d07b1578..29ee8e344 100644 --- a/lib/edlang_parser/src/grammar.lalrpop +++ b/lib/edlang_parser/src/grammar.lalrpop @@ -29,6 +29,7 @@ extern { "extern" => Token::KeywordExtern, "as" => Token::KeywordAs, "exported" => Token::KeywordExported, + "impl" => Token::KeywordImpl, // literals "identifier" => Token::Identifier(), @@ -473,6 +474,15 @@ pub(crate) Struct: ast::Struct = { } } +pub StructImpl: ast::StructImpl = { + "impl" > ">")?> "{" ?> "}" => ast::StructImpl { + name, + methods: methods.unwrap_or(vec![]), + generics: generics.unwrap_or(vec![]), + span: ast::Span::new(lo, hi), + } +} + pub(crate) Import: ast::Import = { "use" > > "}")?> ";" => ast::Import { module, @@ -512,5 +522,6 @@ pub(crate) ModuleStatement: ast::ModuleStatement = { => ast::ModuleStatement::Function(<>), => ast::ModuleStatement::Constant(<>), => ast::ModuleStatement::Struct(<>), + => ast::ModuleStatement::StructImpl(<>), => ast::ModuleStatement::Module(<>), } diff --git a/lib/edlang_parser/src/tokens.rs b/lib/edlang_parser/src/tokens.rs index 880e8b160..fe810d04b 100644 --- a/lib/edlang_parser/src/tokens.rs +++ b/lib/edlang_parser/src/tokens.rs @@ -59,6 +59,8 @@ pub enum Token { KeywordAs, #[token("exported")] KeywordExported, + #[token("impl")] + KeywordImpl, // Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/ #[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())]