From e90e9a1f29075e39aa54bf92e3b6a3d51e13f058 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 03:56:14 +0200 Subject: [PATCH 1/7] Parse with semantics --- parser/src/lib.rs | 2 -- src/main.rs | 18 +++++++++--------- test/src/main.ms | 6 +----- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index d30e056..81cfb67 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -167,8 +167,6 @@ impl Statement { let kind_pair = inner.next().unwrap(); // let/const/var let name_pair = inner.next().unwrap(); // identifier - println!("{:#?}", inner); - let init = inner.next().map(Expression::from_pair); let kind = match kind_pair.as_str() { diff --git a/src/main.rs b/src/main.rs index dec07c7..a9e6d3a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,15 +72,15 @@ fn cmd_check(path: &str) { fn cmd_parse(path: &str) { let source = read_ms_file(path); match parser::parse(&source) { - Ok(output) => { - fs::write( - "output.json", - serde_json::to_string_pretty(&output).unwrap(), - ) - .unwrap_or_else(|e| { - eprintln!("error: could not write output.json: {}", e); - process::exit(1); - }); + Ok(mut ast) => { + walk_ast(semantic::scope::Scope::from_top(&ast), &mut ast); + + fs::write("output.json", serde_json::to_string_pretty(&ast).unwrap()).unwrap_or_else( + |e| { + eprintln!("error: could not write output.json: {}", e); + process::exit(1); + }, + ); } Err(e) => { eprintln!("parse error:\n{}", e); diff --git a/test/src/main.ms b/test/src/main.ms index 5b271de..d53a617 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,9 +1,5 @@ -struct Test { - x: int -} +function test(): int { } function add() { let output = test(); } - -function test(): int { } \ No newline at end of file From 41f47f3241873f01f3840091c4ca8f120bb48b0b Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 03:59:00 +0200 Subject: [PATCH 2/7] Cleaner json output --- parser/src/ast.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 4d9f0c7..2f9aa4f 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -9,7 +9,6 @@ pub struct ParamList(pub Vec<(String, TypeExpr)>); pub struct Block(pub Vec); #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] pub enum TypeExpr { Identifier(String), } @@ -30,7 +29,6 @@ pub enum BinaryOp { } #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] pub enum TopLevel { Import(String), StructDecl { @@ -48,7 +46,6 @@ pub enum TopLevel { } #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] pub enum Postfix { FieldAccess(String), Call(Vec), @@ -57,7 +54,6 @@ pub enum Postfix { } #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] pub enum Statement { // expr; Expression(Expression), @@ -106,7 +102,6 @@ pub enum Statement { } #[derive(Debug, Clone, Serialize)] -#[serde(tag = "type", content = "value")] pub enum Expression { Identifier(String), IntLiteral(i64), From d0b7898ad72f7715aaee2dbb086adeb3ef1df53a Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 04:06:29 +0200 Subject: [PATCH 3/7] Identifier fields --- semantic/src/scope.rs | 19 ++++++++++++++----- test/src/main.ms | 8 ++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index 8eada56..a395701 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -84,8 +84,8 @@ impl LocalScope { pub fn walk_postfixes( self: &Arc, - initial: &Box, - postfixes: &Vec, + initial: &mut Box, + postfixes: &mut Vec, ) -> Option> { let mut current_type = self.get_type_from_expr(initial)?; @@ -93,7 +93,9 @@ impl LocalScope { match postfix { Postfix::FieldAccess(id) => match &*current_type { TypeRef::Struct(s) => { - current_type = s.fields.get(id)?.var_type.clone(); + let field = s.fields.get(id)?; + *id = field.name.clone(); + current_type = field.var_type.clone(); } _ => unimplemented!(), }, @@ -111,7 +113,10 @@ impl LocalScope { Some(current_type) } - pub fn get_type_from_expr(self: &Arc, expr: &ast::Expression) -> Option> { + pub fn get_type_from_expr( + self: &Arc, + expr: &mut ast::Expression, + ) -> Option> { match expr { ast::Expression::IntLiteral(_) => self .parent @@ -133,7 +138,11 @@ impl LocalScope { .get_reference(&"string".to_string()) .map(|r| r.var_type.clone()), - ast::Expression::Identifier(id) => self.get_reference(id).map(|r| r.var_type.clone()), + ast::Expression::Identifier(id) => { + let rf = self.get_reference(id)?; + *id = rf.name.clone(); + Some(rf.var_type.clone()) + } ast::Expression::Postfix { initial, postfixes } => { self.walk_postfixes(initial, postfixes) diff --git a/test/src/main.ms b/test/src/main.ms index d53a617..cf9b82a 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,5 +1,9 @@ -function test(): int { } +struct MyType { + x: int, +} + +function test(): MyType { } function add() { - let output = test(); + let output = test().x; } From 7996a3ffaa5fbf1c6ff5931a20e863cda0a3ebf4 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 12:04:44 +0200 Subject: [PATCH 4/7] Decleration identifier --- semantic/src/hir.rs | 52 +++++++++++++++++++++------------------------ semantic/src/lib.rs | 10 +++++++-- test/src/main.ms | 4 ++-- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/semantic/src/hir.rs b/semantic/src/hir.rs index aa242cf..3e678e8 100644 --- a/semantic/src/hir.rs +++ b/semantic/src/hir.rs @@ -97,33 +97,37 @@ impl TopLevelHirScope { tlss: &TopLevelSymbolScope, symbol: &StructSymbol, ) -> Arc { - let name = self.get_name(symbol.export); + if let Some(rf) = self.variables.get(&symbol.name) { + rf.var_type.clone() + } else { + let name = self.get_name(symbol.export); - let rf = Arc::new(TypeRef::Struct(StructRef { - export: symbol.export, - name: name.clone(), - fields: symbol - .fields - .iter() - .map(|(name, v)| (name.clone(), self.var_ref(tlss, v))) - .collect(), - })); - - self.variables.insert( - symbol.name.clone(), - Arc::new(VarRef { + let rf = Arc::new(TypeRef::Struct(StructRef { + export: symbol.export, name: name.clone(), - var_type: rf.clone(), - }), - ); + fields: symbol + .fields + .iter() + .map(|(name, v)| (name.clone(), self.var_ref(tlss, v))) + .collect(), + })); - rf + self.variables.insert( + symbol.name.clone(), + Arc::new(VarRef { + name: name.clone(), + var_type: rf.clone(), + }), + ); + + rf + } } pub fn var_ref(&mut self, tlss: &TopLevelSymbolScope, symbol: &VarSymbol) -> Arc { Arc::new(VarRef { var_type: self.type_ref(tlss, &symbol.var_type), - name: symbol.name.clone(), + name: self.get_name(true), }) } @@ -132,15 +136,7 @@ impl TopLevelHirScope { var_ref.var_type.clone() } else { if let Some(tlss_rf) = tlss.structs.get(&symbol.0) { - let struct_ref = self.struct_ref(tlss, tlss_rf); - - let var_ref = Arc::new(VarRef { - name: symbol.0.clone(), - var_type: struct_ref, - }); - self.variables.insert(symbol.0.clone(), var_ref.clone()); - - var_ref.var_type.clone() + self.struct_ref(tlss, tlss_rf) } else { match symbol.0.as_str() { "int" => { diff --git a/semantic/src/lib.rs b/semantic/src/lib.rs index 3405a2e..0d300f7 100644 --- a/semantic/src/lib.rs +++ b/semantic/src/lib.rs @@ -11,7 +11,11 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { match tl { parser::ast::TopLevel::Import(_) => unimplemented!(), - parser::ast::TopLevel::FunctionDecl { params, body, .. } => { + parser::ast::TopLevel::FunctionDecl { + params, body, name, .. + } => { + *name = top_scope.get_reference(name).unwrap().name.clone(); + let scope = LocalScope::new(top_scope.clone()); scope.with_params(params); @@ -19,7 +23,9 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { scope.with_block(body); } - parser::ast::TopLevel::StructDecl { .. } => {} + parser::ast::TopLevel::StructDecl { name, .. } => { + *name = top_scope.get_reference(name).unwrap().name.clone(); + } } } } diff --git a/test/src/main.ms b/test/src/main.ms index cf9b82a..fe84848 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,8 +1,8 @@ -struct MyType { +export struct MyType { x: int, } -function test(): MyType { } +export function test(): MyType { } function add() { let output = test().x; From d2f7c0c69b091172c86544783de228033a8f9e8a Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 12:35:20 +0200 Subject: [PATCH 5/7] field naming --- semantic/src/hir.rs | 12 +++++++++++- semantic/src/lib.rs | 38 +++++++++++++++++++++++++++++++++----- test/src/main.ms | 6 +----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/semantic/src/hir.rs b/semantic/src/hir.rs index 3e678e8..b9b879f 100644 --- a/semantic/src/hir.rs +++ b/semantic/src/hir.rs @@ -163,10 +163,20 @@ impl TopLevelHirScope { } pub fn next_var_idx(&self) -> usize { - self.var_idx.fetch_add(1, Ordering::Relaxed) + 1 + self.var_idx.fetch_add(1, Ordering::Relaxed) } pub fn get_name(&self, export: bool) -> String { format!("{}{}", if export { 'V' } else { 'v' }, self.next_var_idx()) } } + +impl TypeRef { + pub fn get_name(&self) -> String { + match self { + TypeRef::Function(f) => f.name.clone(), + TypeRef::Struct(s) => s.name.clone(), + TypeRef::Int => "int".to_string(), + } + } +} diff --git a/semantic/src/lib.rs b/semantic/src/lib.rs index 0d300f7..14851bc 100644 --- a/semantic/src/lib.rs +++ b/semantic/src/lib.rs @@ -1,6 +1,11 @@ -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; -use crate::scope::{LocalScope, Scope}; +use parser::ast::{ParamList, TypeExpr}; + +use crate::{ + hir::VarRef, + scope::{LocalScope, Scope}, +}; pub mod hir; pub mod scope; @@ -14,7 +19,13 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { parser::ast::TopLevel::FunctionDecl { params, body, name, .. } => { - *name = top_scope.get_reference(name).unwrap().name.clone(); + let rf = top_scope.get_reference(name).unwrap(); + *name = rf.name.clone(); + + match &*rf.var_type { + hir::TypeRef::Function(f) => walk_param_list(&f.params, params), + _ => unimplemented!(), + } let scope = LocalScope::new(top_scope.clone()); @@ -23,9 +34,26 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { scope.with_block(body); } - parser::ast::TopLevel::StructDecl { name, .. } => { - *name = top_scope.get_reference(name).unwrap().name.clone(); + parser::ast::TopLevel::StructDecl { name, fields, .. } => { + let rf = top_scope.get_reference(name).unwrap(); + *name = rf.name.clone(); + + match &*rf.var_type { + hir::TypeRef::Struct(s) => walk_param_list(&s.fields, fields), + _ => unimplemented!(), + } } } } } + +pub fn walk_param_list(fields: &HashMap>, param_list: &mut ParamList) { + param_list.0.clear(); + + for (_, param) in fields { + param_list.0.push(( + param.name.clone(), + TypeExpr::Identifier(param.var_type.get_name()), + )); + } +} diff --git a/test/src/main.ms b/test/src/main.ms index fe84848..f84ddbd 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -2,8 +2,4 @@ export struct MyType { x: int, } -export function test(): MyType { } - -function add() { - let output = test().x; -} +export function test(a: MyType) { } From ec30d18e15708f3e121d4114e89c89ef43bcfbe6 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 12:56:08 +0200 Subject: [PATCH 6/7] field exporting --- parser/src/ast.rs | 8 +------- parser/src/grammar.pest | 2 +- parser/src/lib.rs | 19 +++++++++++++----- semantic/src/lib.rs | 12 +++++++---- semantic/src/scope.rs | 2 +- semantic/src/top_level.rs | 42 ++++++++++++++++++++++++++++----------- test/src/main.ms | 2 +- 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 2f9aa4f..a84e4a9 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use serde::Serialize; #[derive(Debug, Clone, Serialize)] -pub struct ParamList(pub Vec<(String, TypeExpr)>); +pub struct ParamList(pub HashMap); #[derive(Debug, Clone, Serialize)] pub struct Block(pub Vec); @@ -120,9 +120,3 @@ pub enum VarKind { Const, Var, } - -impl ParamList { - pub fn to_hashmap(&self, f: impl Fn(&String, &TypeExpr) -> T) -> HashMap { - self.0.iter().map(|(k, v)| (k.clone(), f(k, v))).collect() - } -} diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index ef15bad..ec4c62e 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -20,7 +20,7 @@ export = { "export" } import = { "import" ~ string_lit ~ ";" } param_list = { param ~ ("," ~ param)* ~ ","? } -param = { identifier ~ ":" ~ type_expr } +param = { export? ~ identifier ~ ":" ~ type_expr } struct_decl = { export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 81cfb67..7e48fab 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use pest::Parser; use pest_derive::Parser; @@ -39,14 +41,21 @@ impl TypeExpr { } impl ParamList { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { + pub fn from_pair(export_allowed: bool, pair: pest::iterators::Pair) -> Self { let params = pair .into_inner() .map(|p| { let mut param_inner = p.into_inner(); + let export = + if export_allowed && param_inner.peek().unwrap().as_rule() == Rule::export { + param_inner.next().unwrap(); + true + } else { + false + }; let param_name = param_inner.next().unwrap().as_str().to_string(); let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); - (param_name, param_type) + (param_name, (export, param_type)) }) .collect(); @@ -76,9 +85,9 @@ impl TopLevel { }; let name = inner.next().unwrap().as_str().to_string(); let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from_pair(inner.next().unwrap()) + ParamList::from_pair(false, inner.next().unwrap()) } else { - ParamList(vec![]) + ParamList(HashMap::new()) }; let return_type = if let Some(next) = inner.peek() { if next.as_rule() == Rule::type_expr { @@ -115,7 +124,7 @@ impl TopLevel { }; let name = inner.next().unwrap().as_str().to_string(); let fields_pair = inner.next().unwrap(); - let fields = ParamList::from_pair(fields_pair); + let fields = ParamList::from_pair(true, fields_pair); Some(TopLevel::StructDecl { export, diff --git a/semantic/src/lib.rs b/semantic/src/lib.rs index 14851bc..24057de 100644 --- a/semantic/src/lib.rs +++ b/semantic/src/lib.rs @@ -48,12 +48,16 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { } pub fn walk_param_list(fields: &HashMap>, param_list: &mut ParamList) { + let old_param_list = param_list.clone(); param_list.0.clear(); - for (_, param) in fields { - param_list.0.push(( + for (name, param) in fields { + param_list.0.insert( param.name.clone(), - TypeExpr::Identifier(param.var_type.get_name()), - )); + ( + old_param_list.0.get(name).map(|a| a.0).unwrap_or_default(), + TypeExpr::Identifier(param.var_type.get_name()), + ), + ); } } diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index a395701..f733d0a 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -151,7 +151,7 @@ impl LocalScope { } pub fn with_params(self: &Arc, param_list: &ParamList) { - for (param_name, type_expr) in ¶m_list.0 { + for (param_name, (_, type_expr)) in ¶m_list.0 { match type_expr { parser::ast::TypeExpr::Identifier(id) => { if let Some(var_type) = diff --git a/semantic/src/top_level.rs b/semantic/src/top_level.rs index ffcd52d..8e6e1e8 100644 --- a/semantic/src/top_level.rs +++ b/semantic/src/top_level.rs @@ -47,12 +47,21 @@ impl FunctionSymbol { Self { export: export, name: name.clone(), - params: params.to_hashmap(|name, v| VarSymbol { - name: name.clone(), - var_type: TypeSymbol(match v { - TypeExpr::Identifier(i) => i.to_string(), - }), - }), + params: params + .0 + .iter() + .map(|(name, (_, v))| { + ( + name.clone(), + VarSymbol { + name: name.clone(), + var_type: TypeSymbol(match v { + TypeExpr::Identifier(i) => i.to_string(), + }), + }, + ) + }) + .collect(), return_type: return_type.map(TypeSymbol::from_ast), } } @@ -63,12 +72,21 @@ impl StructSymbol { Self { export, name, - fields: fields.to_hashmap(|name, v| VarSymbol { - name: name.clone(), - var_type: TypeSymbol(match v { - TypeExpr::Identifier(i) => i.to_string(), - }), - }), + fields: fields + .0 + .iter() + .map(|(name, (_, v))| { + ( + name.clone(), + VarSymbol { + name: name.clone(), + var_type: TypeSymbol(match v { + TypeExpr::Identifier(i) => i.to_string(), + }), + }, + ) + }) + .collect(), // TODO - parse struct methods methods: HashMap::new(), } diff --git a/test/src/main.ms b/test/src/main.ms index f84ddbd..59ea0e1 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,5 +1,5 @@ export struct MyType { - x: int, + export x: int, } export function test(a: MyType) { } From 60d668a222e3282cbc4deb6186e16d633f4556d7 Mon Sep 17 00:00:00 2001 From: Selimaj Dev Date: Fri, 17 Apr 2026 13:03:16 +0200 Subject: [PATCH 7/7] Field name exporting --- semantic/src/hir.rs | 7 ++++++- semantic/src/scope.rs | 4 +++- semantic/src/top_level.rs | 7 +++++-- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/semantic/src/hir.rs b/semantic/src/hir.rs index b9b879f..ed5129f 100644 --- a/semantic/src/hir.rs +++ b/semantic/src/hir.rs @@ -17,6 +17,7 @@ pub enum TypeRef { #[derive(Clone, Debug)] pub struct VarRef { + pub export: bool, pub var_type: Arc, pub name: String, } @@ -82,6 +83,7 @@ impl TopLevelHirScope { self.variables.insert( symbol.name.clone(), Arc::new(VarRef { + export: false, name: name.clone(), var_type: Arc::new(TypeRef::Function(rf)), }), @@ -115,6 +117,7 @@ impl TopLevelHirScope { self.variables.insert( symbol.name.clone(), Arc::new(VarRef { + export: symbol.export, name: name.clone(), var_type: rf.clone(), }), @@ -126,8 +129,9 @@ impl TopLevelHirScope { pub fn var_ref(&mut self, tlss: &TopLevelSymbolScope, symbol: &VarSymbol) -> Arc { Arc::new(VarRef { + export: symbol.export, var_type: self.type_ref(tlss, &symbol.var_type), - name: self.get_name(true), + name: self.get_name(symbol.export), }) } @@ -141,6 +145,7 @@ impl TopLevelHirScope { match symbol.0.as_str() { "int" => { let var_ref = Arc::new(VarRef { + export: false, name: symbol.0.clone(), var_type: Arc::new(TypeRef::Int), }); diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index f733d0a..dff29cf 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -71,6 +71,7 @@ impl LocalScope { self.variables.lock().unwrap().insert( name.clone(), Arc::new(VarRef { + export: false, name: name.to_string(), var_type, }), @@ -151,7 +152,7 @@ impl LocalScope { } pub fn with_params(self: &Arc, param_list: &ParamList) { - for (param_name, (_, type_expr)) in ¶m_list.0 { + for (param_name, (export, type_expr)) in ¶m_list.0 { match type_expr { parser::ast::TypeExpr::Identifier(id) => { if let Some(var_type) = @@ -160,6 +161,7 @@ impl LocalScope { self.variables.lock().unwrap().insert( param_name.clone(), Arc::new(VarRef { + export: *export, var_type, name: param_name.clone(), }), diff --git a/semantic/src/top_level.rs b/semantic/src/top_level.rs index 8e6e1e8..f58fa96 100644 --- a/semantic/src/top_level.rs +++ b/semantic/src/top_level.rs @@ -9,6 +9,7 @@ pub struct TypeSymbol(pub String); #[derive(Clone, Debug)] pub struct VarSymbol { + pub export: bool, pub var_type: TypeSymbol, pub name: String, } @@ -50,10 +51,11 @@ impl FunctionSymbol { params: params .0 .iter() - .map(|(name, (_, v))| { + .map(|(name, (export, v))| { ( name.clone(), VarSymbol { + export: *export, name: name.clone(), var_type: TypeSymbol(match v { TypeExpr::Identifier(i) => i.to_string(), @@ -75,10 +77,11 @@ impl StructSymbol { fields: fields .0 .iter() - .map(|(name, (_, v))| { + .map(|(name, (export, v))| { ( name.clone(), VarSymbol { + export: *export, name: name.clone(), var_type: TypeSymbol(match v { TypeExpr::Identifier(i) => i.to_string(),