diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index 2584acb..3e84e25 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -230,6 +230,7 @@ dependencies = [ "pipec-arena", "pipec-ast", "pipec-file-loader", + "pipec-gst", ] [[package]] diff --git a/compiler/pipec-ast/src/ast/asttree.rs b/compiler/pipec-ast/src/ast/asttree.rs index 0a3d585..d0c8636 100644 --- a/compiler/pipec-ast/src/ast/asttree.rs +++ b/compiler/pipec-ast/src/ast/asttree.rs @@ -3,33 +3,12 @@ use pipec_file_loader::FileId; #[derive(Debug, Clone)] pub struct ASTTree { - stream: Vec, - pos: usize, + pub stream: Vec, pub id: FileId, } impl ASTTree { pub fn new(stream: Vec, id: FileId) -> Self { - Self { stream, pos: 0, id } - } - pub fn current_node(&mut self) -> Option<&ASTNode> { - self.stream.get(self.pos) - } - pub fn next_node(&mut self) -> Option { - self.pos += 1; - self.stream.get(self.pos).cloned() - } - pub fn peek(&mut self) -> Option<&ASTNode> { - self.stream.get(self.pos) - } - // pub fn from_vec(vec: Vec) -> Self { - // Self { - // stream: vec, - // pos: 0, - // } - // } - - pub fn reset(&mut self) { - self.pos = 0; + Self { stream, id } } } diff --git a/compiler/pipec-ast/src/ast/mod.rs b/compiler/pipec-ast/src/ast/mod.rs index e2c4dd9..49f9804 100644 --- a/compiler/pipec-ast/src/ast/mod.rs +++ b/compiler/pipec-ast/src/ast/mod.rs @@ -59,7 +59,6 @@ impl<'this> ASTGenerator<'this> { #[inline] pub(crate) fn advance_stream(&mut self) -> Option { - println!("{:#?}", self.peek_stream()); self.tokens.next_token() } #[inline] @@ -82,7 +81,6 @@ impl<'this> ASTGenerator<'this> { Token::ImplementKeyword => self.consume_implement_keyword(), Token::AtSign => self.consume_attributes(), _v => { - println!("{_v:#?}"); todo!(); } }, @@ -450,15 +448,11 @@ impl<'this> ASTGenerator<'this> { self.consume_whitespace(); let params = self.consume_function_parameters(); self.consume_whitespace(); - let mut out_type = None; - if self.next_is(Token::FatArrow) { - self.advance_stream(); - self.consume_whitespace(); - out_type = Some(self.consume_a_path()); - } + self.must(Token::FatArrow); + self.consume_whitespace(); + let out_type = self.consume_a_path(); self.consume_whitespace(); let block = self.consume_function_block(); - ASTNode::FunctionDeclaration { name, params, @@ -538,7 +532,6 @@ impl<'this> ASTGenerator<'this> { #[inline] pub(crate) fn must(&mut self, val: Token) { - println!("should have {:#?}", self.peek_stream()); if self.advance_stream() != Some(val) { // TODO : compiler error unreachable!() @@ -652,7 +645,6 @@ impl<'this> ASTGenerator<'this> { }; } - println!("{path1:#?},{path2:#?}"); unreachable!() } @@ -1020,7 +1012,6 @@ impl<'this> ASTGenerator<'this> { Some(Token::SwitchKeyword) => self.consume_switch_expression(), _v => { - println!("{_v:#?}"); //TODO : compiler error unreachable!(); } @@ -1091,7 +1082,6 @@ impl<'this> ASTGenerator<'this> { #[inline] pub(crate) fn consume_switch_arm(&mut self) -> SwitchArm { let expr = self.consume_an_expression(); - println!("arm lhs = {expr:#?}"); let lhs = Box::new(expr); self.consume_whitespace(); self.must(Token::ThinArrow); @@ -1298,7 +1288,7 @@ pub enum ASTNode { generics: Generics, params: FunctionDeclarationParameters, block: Block, - out_type: Option, + out_type: Path, }, ViewportDeclaration { name: Span, @@ -1399,7 +1389,7 @@ pub struct FragmentsBlock { block: Block, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] pub enum FunctionBlockStatements { MutableVariableDeclaration { variablename: Span, @@ -1425,7 +1415,7 @@ pub enum FunctionBlockStatements { }, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, Hash)] pub enum Exported { ColorBuiltin, PositionBuiltin, @@ -1495,7 +1485,7 @@ pub enum VariableType { Final, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Hash)] #[allow(unused)] pub struct Block(Vec); diff --git a/compiler/pipec-gst/src/lib.rs b/compiler/pipec-gst/src/lib.rs index beeaa46..28db443 100644 --- a/compiler/pipec-gst/src/lib.rs +++ b/compiler/pipec-gst/src/lib.rs @@ -1,42 +1,48 @@ use pipec_arena::AStr; use pipec_arena::{ASlice, Arena}; -use pipec_ast::ast::ASTNode; use pipec_ast::ast::FunctionDeclarationParameters; use pipec_ast::ast::Path; use pipec_ast::ast::PathNode; use pipec_ast::ast::asttree::ASTTree; +use pipec_ast::ast::{ASTNode, Block, Generics}; use pipec_file_loader::FileLoader; -use pipec_span::Span; use std::collections::HashMap; +use std::collections::HashSet; pub struct GlobalSymbolTree<'this> { ast: ASTTree, loader: &'this mut FileLoader, arena: &'this mut Arena, src: ASlice, + #[allow(unused)] + attribute_cache: HashSet, } #[derive(Default, Debug)] pub struct ModuleScope<'a> { - symbols: HashMap<&'a str, Symbol>, - submodules: HashMap<&'a str, Self>, + pub symbols: HashMap<&'a str, Symbol<'a>>, + pub submodules: HashMap<&'a str, Self>, } impl<'this> GlobalSymbolTree<'this> { pub fn new(arena: &'this mut Arena, loader: &'this mut FileLoader, ast: ASTTree) -> Self { let src = loader.load(ast.id); + let attribute_cache = HashSet::new(); Self { ast, arena, loader, src, + attribute_cache, } } pub fn generate<'a>(&mut self) -> ModuleScope<'a> { let mut out = ModuleScope::default(); + let stream = self.ast.stream.clone(); + let mut iter = stream.iter(); loop { - let next = self.ast.next_node(); + let next = iter.next(); match next { Some(v) => match v { ASTNode::EOF => { @@ -47,6 +53,8 @@ impl<'this> GlobalSymbolTree<'this> { None => break, } } + println!("{:#?}", &out); + self.import_using(&mut out); out } @@ -55,151 +63,155 @@ impl<'this> GlobalSymbolTree<'this> { ASTNode::FunctionDeclaration { name, params, - block: _, - generics: _, + block, + generics, out_type, - } => self.parse_function_declaration(name, params, out_type, scope), + } => { + let parsed_name = name.parse_arena(self.src, self.arena); + println!("found function {parsed_name}"); + scope.symbols.insert( + parsed_name, + Symbol::Function { + out_type, + params, + block, + generics, + }, + ); + } ASTNode::ViewportDeclaration { name, params, - block: _, - } => self.parse_viewport_declaration(name, params, scope), + block, + } => { + let parsed_name = name.parse_arena(self.src, self.arena); + scope + .symbols + .insert(parsed_name, Symbol::Viewport { params, block }); + } ASTNode::ModStatement { name, tree } => { - self.parse_mod_statement(name, tree, scope); + println!("consuming mod"); + let old = self.src; + self.src = self.loader.load(tree.id); + let mod_name = name.parse_arena(old, self.arena); + let mut mod_scope = ModuleScope::default(); + let stream = tree.stream.clone(); + let mut iter = stream.iter(); + loop { + let next = iter.next(); + match next { + Some(v) => match v { + ASTNode::EOF => { + break; + } + _ => self.check_node(v.clone(), &mut mod_scope), + }, + None => break, + } + } + scope.submodules.insert(mod_name, mod_scope); + self.src = old; } _ => {} } } - pub(crate) fn parse_function_declaration( - &mut self, - name: Span, - params: FunctionDeclarationParameters, - out_type: Option, - scope: &mut ModuleScope, - ) { - let return_type = match out_type { - None => Type::Nothing, - Some(v) => self.type_from_path(&v), - }; - let symbol = Symbol::Function { - params, - return_type, - }; - let name = name.parse_arena(self.src, self.arena); - scope.symbols.insert(name, symbol); - } - - pub(crate) fn parse_viewport_declaration( - &mut self, - name: Span, - params: FunctionDeclarationParameters, - scope: &mut ModuleScope, - ) { - let symbol = Symbol::Viewport { params }; - let name = name.parse_arena(self.src, self.arena); - scope.symbols.insert(name, symbol); - } - - pub(crate) fn parse_mod_statement( - &mut self, - name: Span, - mut tree: ASTTree, - parent: &mut ModuleScope, - ) { - let old = self.src; - self.src = self.loader.load(tree.id); - let mod_name = name.parse_arena(old, self.arena); - let mut mod_scope = ModuleScope::default(); - loop { - let next = tree.next_node(); + pub(crate) fn import_using(&mut self, scope: &mut ModuleScope) { + let stream = self.ast.stream.clone(); + let iter = stream.iter(); + for next in iter { match next { - None => break, - Some(v) => { - self.check_node(v, &mut mod_scope); - } - } - } - parent.submodules.insert(mod_name, mod_scope); - self.src = old; - } - - pub(crate) fn type_from_path(&mut self, input: &Path) -> Type { - let vec = input.0.clone(); - use Type::*; - - if vec.len() == 1 { - let first = vec.first(); - match first { - None => {} - Some(PathNode::Singly { name, generics: _ }) => { - let name = name.parse_arena(self.src, self.arena); - match name { - "integer8" => return Integer8, - "unsigned8" => return Unsigned8, - "float8" => return Float8, - "integer16" => return Integer16, - "unsigned16" => return Unsigned16, - "float16" => return Float16, - "integer32" => return Integer32, - "unsigned32" => return Unsigned32, - "float32" => return Float32, - "integer64" => return Integer64, - "unsigned64" => return Unsigned64, - "float64" => return Float64, - "floatport" => return FloatPort, - "nothing" => return Nothing, - _ => {} + ASTNode::EOF => break, + ASTNode::UsingStatement { using } => self.use_path(using, scope), + ASTNode::ModStatement { name, tree } => { + println!("importing module appearently"); + let old = self.src; + self.src = self.loader.load(tree.id); + let mod_name = name.parse_arena(old, self.arena); + let mod_scope = scope.submodules.get_mut(mod_name).unwrap(); + let stream = tree.stream.clone(); + for item in stream { + match item { + ASTNode::EOF => break, + ASTNode::UsingStatement { using } => self.use_path(&using, mod_scope), + _ => {} + } } + self.src = old; } _ => {} } } - Link(self.path_to_symbol_name(input)) } - pub(crate) fn path_to_symbol_name(&mut self, input: &Path) -> SymbolName { - let vec = input.0.clone(); - let mut out = SymbolName::new(); - let mut iter = vec.iter(); + #[inline] + pub(crate) fn use_path(&mut self, input: &Path, target: &mut ModuleScope) { + println!("using {input:#?}"); + let mut iter = input.0.iter().peekable(); + let mut current: &mut ModuleScope = target; loop { let next = iter.next(); - if next.is_none() { + if iter.peek().is_none() { + match next { + Some(PathNode::Singly { name, generics }) => { + if !generics.0.is_empty() { + // TODO : compiler error + unreachable!(); + } + let parsed_name = name.parse_arena(self.src, self.arena); + let current_ptr: *const ModuleScope = current; + target + .symbols + .insert(parsed_name, Symbol::Alias(current_ptr)); + } + Some(PathNode::Multi(paths)) => { + for path in paths { + let inner = path.0.clone(); + let mut new_path_inner = input.0.clone(); + new_path_inner.extend_from_slice(&inner); + + let new_path = Path(new_path_inner); + self.use_path(&new_path, target); + } + } + None => unreachable!("path is empty"), + } break; - } - if let Some(PathNode::Singly { name, generics: _ }) = next { - let parsed = name.parse_arena(self.src, self.arena).to_string(); - out.path.push(parsed); + } else if let Some(PathNode::Singly { name, generics }) = next { + if !generics.0.is_empty() { + // TODO : compiler error + unreachable!(); + } + let module_name = name.parse_arena(self.src, self.arena); + println!("{module_name} is the modules name"); + current = current.submodules.get_mut(module_name).unwrap_or_else(|| { + // TODO : compiler error + unreachable!(); + }); } } - out - } -} - -#[derive(Hash, Clone, PartialEq, Eq, Debug)] -pub struct SymbolName { - pub(crate) path: Vec, -} - -impl SymbolName { - fn new() -> Self { - Self { path: vec![] } } } #[derive(Hash, Clone, Debug)] -pub enum Symbol { +pub enum Symbol<'a> { Function { - return_type: Type, + out_type: Path, params: FunctionDeclarationParameters, + block: Block, + generics: Generics, }, Viewport { params: FunctionDeclarationParameters, + block: Block, }, + + Builtin(LanguageAttribute), + Alias(*const ModuleScope<'a>), } #[derive(Hash, Clone, PartialEq, Eq, Debug)] -pub enum Type { +pub enum LanguageAttribute { Integer8, Unsigned8, Float8, @@ -214,5 +226,4 @@ pub enum Type { Float64, FloatPort, Nothing, - Link(SymbolName), } diff --git a/compiler/pipec-tests/Cargo.toml b/compiler/pipec-tests/Cargo.toml index 80e3191..184d43a 100644 --- a/compiler/pipec-tests/Cargo.toml +++ b/compiler/pipec-tests/Cargo.toml @@ -7,3 +7,4 @@ edition = "2024" pipec-arena = { version = "0.1.0", path = "../pipec-arena" } pipec-ast = { version = "0.1.0", path = "../pipec-ast" } pipec-file-loader = { version = "0.1.0", path = "../pipec-file-loader" } +pipec-gst = { version = "0.1.0", path = "../pipec-gst" } diff --git a/compiler/pipec-tests/src/ast/functiondeclaration/test.pipec b/compiler/pipec-tests/src/ast/functiondeclaration/test.pipec index df61824..954e878 100644 --- a/compiler/pipec-tests/src/ast/functiondeclaration/test.pipec +++ b/compiler/pipec-tests/src/ast/functiondeclaration/test.pipec @@ -1,3 +1,3 @@ -function random_function_name(arg1 : this , arg2 : that) {} -function random_function_name(arg1 : this , arg2 : that) => u32 {} +function random_function_name(arg1 : this , arg2 : that) => nothing {} +function random_function_name(arg1 : this , arg2 : that) => nothing {} diff --git a/compiler/pipec-tests/src/ast/generics/functiongenerics.pipec b/compiler/pipec-tests/src/ast/generics/functiongenerics.pipec index deb9cc6..0c3f6e4 100644 --- a/compiler/pipec-tests/src/ast/generics/functiongenerics.pipec +++ b/compiler/pipec-tests/src/ast/generics/functiongenerics.pipec @@ -1,15 +1,15 @@ -function basic[T](input : T) {} +function basic[T](input : T) => nothing {} -function multiple[T,U](input1 : T , input2 : U) {} +function multiple[T,U](input1 : T , input2 : U) => nothing {} -function with_traits[T : Trait](input : T) {} +function with_traits[T : Trait](input : T) => nothing {} -function multiple_traits[T : Trait1 + Trait2 + Trait3](input : T) => T {} +function multiple_traits[T : Trait1 + Trait2 + Trait3](input : T) => nothing {} -function multiple_with_multiple_traits[T : Trait1 + Trait2 + Trait3 , U : Trait1 + Trait2 + Trait3](input1 : T , input2 : U) {} +function multiple_with_multiple_traits[T : Trait1 + Trait2 + Trait3 , U : Trait1 + Trait2 + Trait3](input1 : T , input2 : U) => nothing {} -function generic_trait[T: Trait1[T]](input : T) {} +function generic_trait[T: Trait1[T]](input : T) => nothing {} -function multiple_generic_trait[T : Trait1[T] + Trait2[T] + Trait3[T]](input : T) => T {} +function multiple_generic_trait[T : Trait1[T] + Trait2[T] + Trait3[T]](input : T) => nothing {} -function multiple_generic_with_generic_trait[T : Trait1[T] + Trait2[T] + Trait3[T] , U : Trait1[T] + Trait2[T] + Trait3[T]](input1 : T, input3 : T) => T {} +function multiple_generic_with_generic_trait[T : Trait1[T] + Trait2[T] + Trait3[T] , U : Trait1[T] + Trait2[T] + Trait3[T]](input1 : T, input3 : T) => nothing {} diff --git a/compiler/pipec-tests/src/ast/mod.rs b/compiler/pipec-tests/src/ast/mod.rs index 5b6b87a..4e35aa6 100644 --- a/compiler/pipec-tests/src/ast/mod.rs +++ b/compiler/pipec-tests/src/ast/mod.rs @@ -1,5 +1,6 @@ mod functiondeclaration; mod generics; mod traits; +mod usingstatements; mod variablemutability; mod viewportdeclaration; diff --git a/compiler/pipec-tests/src/ast/usingstatements/mod.rs b/compiler/pipec-tests/src/ast/usingstatements/mod.rs new file mode 100644 index 0000000..99f7dc3 --- /dev/null +++ b/compiler/pipec-tests/src/ast/usingstatements/mod.rs @@ -0,0 +1,29 @@ +#[test] +fn test_using_statements() { + { + crate::test_file_generation!("test.pipec",scope scope); + println!("{scope:#?}"); + assert!(scope.symbols.contains_key("func1")); + assert!(scope.symbols.contains_key("func2")); + assert!(scope.symbols.contains_key("func3")); + assert!(scope.symbols.contains_key("func4")); + assert!(scope.symbols.contains_key("func5")); + assert!(scope.symbols.contains_key("func6")); + assert!( + scope + .submodules + .get("mod5") + .unwrap() + .symbols + .contains_key("func7") + ); + assert!( + scope + .submodules + .get("mod5") + .unwrap() + .symbols + .contains_key("func8") + ); + } +} diff --git a/compiler/pipec-tests/src/ast/usingstatements/test.pipec b/compiler/pipec-tests/src/ast/usingstatements/test.pipec new file mode 100644 index 0000000..00eddf6 --- /dev/null +++ b/compiler/pipec-tests/src/ast/usingstatements/test.pipec @@ -0,0 +1,30 @@ +using mod1\(func1,func2); +using mod2\( +mod3\(func3,func4), +mod4\(func5,func6) +); + +module mod1 { + function func1() => nothing {} + function func2() => nothing {} +} + +module mod2 { + module mod3 { + function func3() => nothing {} + function func4() => nothing {} + } + + module mod4 { + function func5() => nothing {} + function func6() => nothing {} + } +} + +module mod5 { + using mod6\(func7,func8); + module mod6 { + function func7() => nothing {} + function func8() => nothing {} + } +} diff --git a/compiler/pipec-tests/src/ast/variablemutability/test.pipec b/compiler/pipec-tests/src/ast/variablemutability/test.pipec index bb63369..0686977 100644 --- a/compiler/pipec-tests/src/ast/variablemutability/test.pipec +++ b/compiler/pipec-tests/src/ast/variablemutability/test.pipec @@ -1,4 +1,4 @@ -function main() { +function main() => nothing { immutable x = 0; mutable x = 0; } diff --git a/compiler/pipec-tests/src/lib.rs b/compiler/pipec-tests/src/lib.rs index 4187a1b..f902c22 100644 --- a/compiler/pipec-tests/src/lib.rs +++ b/compiler/pipec-tests/src/lib.rs @@ -15,7 +15,6 @@ macro_rules! test_file_generation { .parent() .unwrap() .join($filename); - let c = file_dir.clone(); let mut arena = Arena::new(Size::Megs(10)); let mut loader = FileLoader::default(); @@ -25,21 +24,51 @@ macro_rules! test_file_generation { let mut tokentree = Tokenizer::new(&file_contents).tree(); let mut guard = RecursiveGuard::default(); - let mut ast_generator = ASTGenerator::new( + #[allow(unused_variables)] + let ast_tree = ASTGenerator::new( file_id, &mut tokentree, file_dir, &mut arena, &mut guard, &mut loader, - ); - - loop { - let next = ast_generator.parse_value(); - if matches!(next, pipec_ast::ast::ASTNode::EOF) { - break; - } - } - println!("{c:#?}"); + ) + .tree(); + }; + + ($filename : literal,scope $scope:ident) => { + use pipec_arena::{Arena, Size}; + use pipec_ast::{RecursiveGuard, ast::ASTGenerator, tokenizer::Tokenizer}; + use pipec_file_loader::FileLoader; + use pipec_gst::GlobalSymbolTree; + + let file_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .join(file!()) + .parent() + .unwrap() + .join($filename); + + let mut arena = Arena::new(Size::Megs(10)); + let mut loader = FileLoader::default(); + let file_id = loader.open(&file_dir, &mut arena).unwrap(); + + let file_contents = include_str!($filename); + let mut tokentree = Tokenizer::new(&file_contents).tree(); + let mut guard = RecursiveGuard::default(); + + let ast_tree = ASTGenerator::new( + file_id, + &mut tokentree, + file_dir, + &mut arena, + &mut guard, + &mut loader, + ) + .tree(); + + let mut gst = GlobalSymbolTree::new(&mut arena, &mut loader, ast_tree); + let $scope = gst.generate(); }; }