├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── COPYING ├── Cargo.toml ├── README.md ├── build.rs ├── codegen ├── Cargo.toml └── src │ ├── AST.rs │ ├── ClassRepresentation.rs │ ├── GenASTNodes.rs │ └── lib.rs ├── deriveMacros ├── Cargo.toml └── src │ ├── DeriveCommonAst.rs │ ├── RustycppInheritance.rs │ └── lib.rs ├── gencodecov.sh ├── rebuildallprofiles.sh ├── src ├── Ast.rs ├── Ast │ ├── Attribute.rs │ ├── Attribute │ │ ├── RustyCppCheckSymbolMatchTag.rs │ │ ├── RustyCppTagDecl.rs │ │ └── RustyCppUnused.rs │ ├── Common.rs │ ├── Decl.rs │ ├── Decl │ │ ├── Asm.rs │ │ ├── Empty.rs │ │ ├── Enum.rs │ │ ├── Namespace.rs │ │ └── UsingNamespace.rs │ ├── NestedNameSpecifier.rs │ ├── Tu.rs │ ├── Type.rs │ └── Type │ │ ├── Builtin.rs │ │ ├── Pointer.rs │ │ └── Reference.rs ├── Compiler.rs ├── Grammars.rs ├── Grammars │ ├── .antlr │ │ ├── macrointconstantexpressionast.interp │ │ ├── macrointconstantexpressionast.java │ │ └── macrointconstantexpressionast.tokens │ ├── DefineAst.rs │ ├── DefineParser.rs │ ├── MacroIntConstantExpressionAst.rs │ └── MacroIntConstantExpressionParser.rs ├── Lex.rs ├── Lex │ ├── Lexer.rs │ └── Token.rs ├── ModuleTree.rs ├── ModuleTree │ ├── DependencyAnnotate.rs │ ├── DependencyDfs.rs │ ├── DependencyInterpreter.rs │ ├── DependencyIterator.rs │ ├── DependencyParser.rs │ ├── Generate.rs │ └── Structs.rs ├── Parse.rs ├── Parse │ ├── BufferedLexer.rs │ ├── Parser.rs │ └── Parser │ │ ├── ParserParse.rs │ │ ├── ParserParse │ │ ├── ParseAttribute.rs │ │ ├── ParseAttribute │ │ │ ├── RustyCppCheckSymbolMatchTag.rs │ │ │ ├── RustyCppTagDecl.rs │ │ │ └── RustyCppUnused.rs │ │ ├── ParseDeclaration.rs │ │ ├── ParseDeclaration │ │ │ ├── ParseAsmDeclaration.rs │ │ │ ├── ParseCustomRustycppDeclaration.rs │ │ │ ├── ParseNamespaceDeclaration.rs │ │ │ └── ParseUsingNamespaceDeclaration.rs │ │ ├── ParseMiscUtils.rs │ │ ├── ParseNestedNameSpecifier.rs │ │ └── ParseTu.rs │ │ ├── ParserSema.rs │ │ └── ParserSema │ │ ├── SemaAttribute.rs │ │ ├── SemaDeclaration.rs │ │ ├── SemaLookups.rs │ │ ├── SemaNestedNameSpecifier.rs │ │ └── SemaTu.rs ├── Preprocessor.rs ├── Preprocessor │ ├── Driver.rs │ ├── Driver │ │ ├── CustomMacros.rs │ │ ├── DefineParse.rs │ │ ├── Includer.rs │ │ ├── MacroExpand.rs │ │ └── MacroExpression.rs │ ├── Multilexer.rs │ ├── Prelexer.rs │ ├── Pretoken.rs │ └── Structs.rs ├── Sema.rs ├── Sema │ ├── AstContext.rs │ ├── Scope.rs │ └── TypeDict.rs ├── Test.rs ├── Test │ ├── TestIncluder.rs │ ├── TestLexer.rs │ ├── TestPreprocessorDefine.rs │ ├── TestPreprocessorIf.rs │ ├── TestProject.rs │ ├── TestSingleFile.rs │ ├── include │ │ └── header.h │ ├── testProject │ │ ├── headerModuleErr1 │ │ │ ├── bar.hpp │ │ │ ├── compile_list.json │ │ │ ├── foo.hpp │ │ │ └── main.cpp │ │ └── simpleModule │ │ │ ├── bar.cpp │ │ │ ├── compile_list.json │ │ │ └── main.cpp │ └── testSingleFile │ │ ├── parsesAsmDecl.cpp │ │ ├── parsesAsmDeclError1.cpp │ │ ├── parsesAttrDecl.cpp │ │ ├── parsesAttrDeclError1.cpp │ │ ├── parsesAttrDeclError2.cpp │ │ ├── parsesAttrError1.cpp │ │ ├── parsesAttrError2.cpp │ │ ├── parsesEmpty.cpp │ │ ├── parsesModule.cpp │ │ ├── parsesModuleError1.cpp │ │ ├── parsesModuleError2.cpp │ │ ├── parsesModuleError3.cpp │ │ ├── parsesModuleError4.cpp │ │ ├── parsesModuleError5.cpp │ │ ├── parsesNamedNamespace.cpp │ │ ├── parsesNamedNamespaceError1.cpp │ │ ├── parsesUsingNamespaceDirective.cpp │ │ ├── parsesUsingNamespaceDirectiveError1.cpp │ │ ├── parses__rustycpp__enum.cpp │ │ ├── parses__rustycpp__enumError1.cpp │ │ ├── qualifiedNameResolution.cpp │ │ ├── unqualifiedNameResolutionGlobal.cpp │ │ ├── unqualifiedNameResolutionGlobalErr1.cpp │ │ └── unqualifiedNameResolutionNamespace.cpp ├── Utils.rs ├── Utils │ ├── CompilerState.rs │ ├── DebugNode.rs │ ├── FileMap.rs │ ├── FoldingContainer.rs │ ├── Funcs.rs │ ├── ModuleHeaderAtomicLexingList.rs │ ├── NomLike.rs │ ├── Parameters.rs │ ├── StateCompileUnit.rs │ ├── StringRef.rs │ ├── Structs.rs │ └── UnsafeAllocator.rs └── main.rs └── test_samples ├── check_lexer_string_lit_concat ├── compile_list.json └── main.cpp ├── module_tree_showcase ├── bar.cpp ├── classic_normal.cpp ├── compile_list.json ├── foo.cpp ├── foo_interface.cpp ├── foo_partition.cpp ├── foo_partition_2.cpp ├── foo_partition_interface.cpp ├── foo_partition_interface_2.cpp ├── hello.h ├── importable_header.hpp ├── main.cpp └── main2.cpp ├── parser_validator ├── compile_list.json ├── main.cpp └── out.lsp └── tokenizer_mix ├── compile_list.json ├── hello.h └── main.cpp /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 5 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Install latest nightly 19 | uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: nightly 22 | override: true 23 | - name: Build 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: build 27 | - name: Run tests 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | deriveMacros/target 3 | *.pdf 4 | Cargo.lock 5 | .vscode/* 6 | src/grammars/generated/* 7 | src/grammars/.antlr/* 8 | codegen/target -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Destroyerrrocket/rustycpp/b5756acca3ffd830de3c3586b731bb8334823565/.gitmodules -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ### 0.1.6 4 | - Make the project easy to compile using git submodules 5 | 6 | ### 0.1.5 7 | - Module dependency tree generation has been completed, automatically adds TU for imported headers 8 | - A bit of lexing was done, but is not tested 9 | 10 | ### 0.1.2 11 | Features: 12 | - Finish all critical features of the preprocessor 13 | - Implement conditional code inclussion 14 | - Implement header inclussion 15 | 16 | ### 0.1.1 17 | I took on the project again. The "compiler" is now able to perform macro expansions, but no proper conditional inclusion of code, and certainly not inclusion. For the time being, I do not plan to include any pragmas or useful extensions that other implementations provide :) 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustycpp" 3 | version = "0.1.6" 4 | edition = "2021" 5 | repository = "https://github.com/Destroyerrrocket/rustycpp" 6 | readme = "README.md" 7 | license = "GPL-3.0-or-later" 8 | keywords = ["compiler", "C++"] 9 | categories = ["compilers"] 10 | description = "An attempt to implement the C++20 standard. This is mostly to have fun & learn rust" 11 | 12 | [dependencies] 13 | clap = { version = "4.0.32", features = ["derive"] } 14 | lazy-regex = "2.5.0" 15 | lazy_static = "1.4.0" 16 | logos = "0.12.1" 17 | colored = "2.0.0" 18 | log = "0.4.17" 19 | env_logger = "0.10.0" 20 | test-log = "0.2.11" 21 | multiset = "0.0.5" 22 | chrono = "0.4.24" 23 | json = "0.12.4" 24 | f128 = "0.2.9" 25 | priority-queue = "1.3.1" 26 | threadpool = "1.8.1" 27 | bumpalo = { version="3.12.0", features = ["collections", "allocator_api"] } 28 | deriveMacros = { path = "./deriveMacros" } 29 | function_name = "0.3.0" 30 | enum_dispatch = "0.3.11" 31 | bitflags = "1.3.2" 32 | atomic_enum = "0.2.0" 33 | strum = "0.24.1" 34 | strum_macros = "0.24.3" 35 | 36 | [build-dependencies] 37 | codegen = {path = "./codegen"} 38 | 39 | 40 | 41 | [profile.release] 42 | lto = true 43 | codegen-units = 1 44 | 45 | [profile.codecoverage] 46 | inherits = "test" 47 | incremental = false 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++20 preprocessor of C++ written in Rust 2 | 3 | ### Description 4 | Module dependency tree generation is done! 5 | 6 | This is a very simple and most certainly wrong preprocessor for C++. This was not done with any major intents, it was simply to test Rust and its capabilities (I find learning by doing a lot more useful than just following tutorials). 7 | 8 | As a rust novice, I do not claim this to be of any quality. 9 | 10 | So far, the most rellevant missing things are: 11 | - the #line directive is not supported (and probably won't for some time, I do not intend to support generated code for now :) ) 12 | - QOL Features of preprocessors, like any pragma directive (none mandated by standard, but #pragma once is expected from any sensible implementation), or the `__FUNCTION__` macro (which requires the step 7 parser to be implemented in order to know such information) 13 | - Most test macros are kinda useless right now. `__has_cpp_attribute` is literally just hardcoded to 0 14 | 15 | I'd say that the first 4 steps of the compilation process of C++ are done-ish! Time for lexing. 16 | 17 | If you want more logs on what's going on, you can use the environment varaible `RUST_LOG`, like so: `RUST_LOG=debug` 18 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | clippy::all, 3 | clippy::pedantic, 4 | clippy::nursery, 5 | clippy::cargo, 6 | clippy::verbose_file_reads, 7 | clippy::unneeded_field_pattern, 8 | clippy::unnecessary_self_imports, 9 | clippy::string_to_string, 10 | clippy::if_then_some_else_none, 11 | clippy::empty_structs_with_brackets, 12 | //clippy::missing_docs_in_private_items 13 | )] 14 | #![allow( 15 | clippy::multiple_crate_versions, 16 | non_snake_case, 17 | clippy::missing_panics_doc 18 | )] 19 | 20 | use std::env; 21 | use std::fs; 22 | use std::path::Path; 23 | 24 | fn main() { 25 | let out_dir = env::var_os("OUT_DIR").unwrap(); 26 | 27 | let dest_path = Path::new(&out_dir).join("hello.rs"); 28 | 29 | fs::write( 30 | dest_path, 31 | codegen::GenASTNodes::generateFile(&codegen::AST::getAST()), 32 | ) 33 | .unwrap(); 34 | println!("cargo:rerun-if-changed=build.rs"); 35 | println!("cargo:rerun-if-changed=codegen/mod.rs"); 36 | println!("cargo:rerun-if-changed=codegen/ClassRepresentation.rs"); 37 | println!("cargo:rerun-if-changed=codegen/AST.rs"); 38 | println!("cargo:rerun-if-changed=codegen/ASTNodes.rs"); 39 | } 40 | -------------------------------------------------------------------------------- /codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codegen" 3 | description = "A tool to generate tons of boilerplate code" 4 | version = "0.1.0" 5 | edition = "2021" 6 | readme = "README.md" 7 | license = "GPL-3.0-or-later" 8 | keywords = [] 9 | categories = [] 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | -------------------------------------------------------------------------------- /codegen/src/AST.rs: -------------------------------------------------------------------------------- 1 | use std::vec; 2 | 3 | use crate::ClassRepresentation::{Class, FuncCustomCodegen}; 4 | 5 | const fn fin(name: &'static str) -> Class { 6 | Class { 7 | name, 8 | dependedBy: vec![], 9 | traits: vec![], 10 | customCodegen: vec![], 11 | } 12 | } 13 | 14 | const fn abs(name: &'static str, childs: Vec, traits: Vec<&'static str>) -> Class { 15 | Class { 16 | name, 17 | dependedBy: childs, 18 | traits, 19 | customCodegen: vec![], 20 | } 21 | } 22 | 23 | const fn absCustom( 24 | name: &'static str, 25 | childs: Vec, 26 | traits: Vec<&'static str>, 27 | f: Vec, 28 | ) -> Class { 29 | Class { 30 | name, 31 | dependedBy: childs, 32 | traits, 33 | customCodegen: f, 34 | } 35 | } 36 | 37 | fn generatorType(_: &Vec<&Class>, class: &Class) -> String { 38 | format!( 39 | " 40 | impl Display for {} {{ 41 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ 42 | TypeAst::fmt(self, f) 43 | }} 44 | }} 45 | ", 46 | class.name 47 | ) + if class.dependedBy.is_empty() { 48 | format!( 49 | " 50 | impl Display for {}StructNode {{ 51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ 52 | TypeAst::fmt(self, f) 53 | }} 54 | }} 55 | ", 56 | class.name 57 | ) + format!( 58 | " 59 | impl TypeAst for {}StructNode {{ 60 | fn getBaseType(&self) -> BaseType {{ 61 | TypeAst::getBaseType(&self) 62 | }} 63 | 64 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {{ 65 | TypeAst::fmt(&self, f) 66 | }} 67 | }} 68 | ", 69 | class.name 70 | ) 71 | .as_str() 72 | } else { 73 | String::new() 74 | } 75 | .as_str() 76 | } 77 | 78 | fn generatorCommon(parents: &Vec<&Class>, class: &Class) -> String { 79 | let gen = |dec: &str| { 80 | format!("impl CommonAst for {dec}{}StructNode {{\n", class.name) 81 | + " #[allow(clippy::let_and_return)]" 82 | + format!(" fn getDebugNode(&self) -> DebugNode {{\n").as_str() 83 | + format!(" let nodes = self.base.getDebugNode();\n").as_str() 84 | + if parents.is_empty() { 85 | " nodes\n".to_string() 86 | } else { 87 | format!( 88 | " nodes.add_children(self.parent.getDebugNode().getChilds().clone())\n" 89 | ) 90 | } 91 | .as_str() 92 | + format!(" }}\n").as_str() 93 | + format!("}}\n").as_str() 94 | }; 95 | gen("") + gen("&").as_str() 96 | } 97 | 98 | #[must_use] 99 | #[allow(clippy::module_name_repetitions)] 100 | pub fn getAST() -> Class { 101 | absCustom( 102 | "AstNode", 103 | vec![ 104 | abs( 105 | "AstDecl", 106 | vec![ 107 | fin("AstDeclEmpty"), 108 | fin("AstDeclAsm"), 109 | fin("AstDeclNamespace"), 110 | fin("AstDeclCustomRustyCppEnum"), 111 | fin("AstDeclUsingNamespace"), 112 | ], 113 | vec![], 114 | ), 115 | fin("AstAttribute"), 116 | abs( 117 | "AstAttributeCXX", 118 | vec![ 119 | fin("AstAttributeCXXRustyCppUnused"), 120 | fin("AstAttributeCXXRustyCppCheckSymbolMatchTag"), 121 | fin("AstAttributeCXXRustyCppTagDecl"), 122 | ], 123 | vec!["CXXAttribute"], 124 | ), 125 | absCustom( 126 | "AstType", 127 | vec![ 128 | fin("AstTypeBuiltin"), 129 | fin("AstTypePointer"), 130 | fin("AstTypeReference"), 131 | ], 132 | vec!["TypeAst", "Foldable"], 133 | vec![&generatorType], 134 | ), 135 | fin("AstTu"), 136 | ], 137 | vec!["CommonAst"], 138 | vec![&generatorCommon], 139 | ) 140 | } 141 | -------------------------------------------------------------------------------- /codegen/src/ClassRepresentation.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct Trait { 3 | pub name: &'static str, 4 | pub func: &'static dyn Fn(&str) -> String, 5 | } 6 | 7 | impl Trait { 8 | pub const fn new(name: &'static str, func: &'static dyn Fn(&str) -> String) -> Self { 9 | Self { name, func } 10 | } 11 | } 12 | 13 | pub type FuncCustomCodegen = &'static dyn Fn(&Vec<&Class>, &Class) -> String; 14 | 15 | pub struct Class { 16 | pub name: &'static str, 17 | pub dependedBy: Vec, 18 | pub traits: Vec<&'static str>, 19 | pub customCodegen: Vec, 20 | } 21 | -------------------------------------------------------------------------------- /codegen/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn( 2 | clippy::all, 3 | clippy::pedantic, 4 | clippy::nursery, 5 | clippy::cargo, 6 | clippy::verbose_file_reads, 7 | clippy::unneeded_field_pattern, 8 | clippy::unnecessary_self_imports, 9 | clippy::string_to_string, 10 | clippy::if_then_some_else_none, 11 | clippy::empty_structs_with_brackets, 12 | //clippy::missing_docs_in_private_items 13 | )] 14 | #![allow( 15 | dead_code, 16 | non_snake_case, 17 | clippy::missing_docs_in_private_items, 18 | clippy::cargo_common_metadata 19 | )] 20 | 21 | pub mod AST; 22 | pub mod ClassRepresentation; 23 | pub mod GenASTNodes; 24 | -------------------------------------------------------------------------------- /deriveMacros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deriveMacros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | syn = { version = "1.0", features = ["full"] } 13 | quote = "1.0" 14 | proc-macro2 = "1.0" 15 | codegen = { path = "../codegen" } 16 | -------------------------------------------------------------------------------- /deriveMacros/src/DeriveCommonAst.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{quote, ToTokens}; 3 | use syn::{DeriveInput, Field}; 4 | 5 | macro_rules! hasAttribute { 6 | ($field:ident, $attr:ident) => { 7 | $field 8 | .attrs 9 | .iter() 10 | .find(|at| at.path.is_ident(stringify!($attr))) 11 | .is_some() 12 | }; 13 | } 14 | 15 | fn impl_AstToString(_: &Field, fieldName: TokenStream) -> TokenStream { 16 | quote! { 17 | add_child(crate::Utils::DebugNode::DebugNode::new(concat!(stringify!(#fieldName), ": ").to_string() + &self.#fieldName.to_string())) 18 | } 19 | } 20 | 21 | fn impl_AstChild(_: &Field, fieldName: TokenStream) -> TokenStream { 22 | quote! { 23 | add_child(self.#fieldName.getDebugNode()) 24 | } 25 | } 26 | 27 | fn impl_AstChildSlice(_: &Field, fieldName: TokenStream) -> TokenStream { 28 | quote! { 29 | add_children(self.#fieldName.iter().map(|x| x.getDebugNode()).collect::<_>()) 30 | } 31 | } 32 | 33 | fn impl_AstChildSliceCell(_: &Field, fieldName: TokenStream) -> TokenStream { 34 | quote! { 35 | add_children(self.#fieldName.borrow().iter().map(|x| x.getDebugNode()).collect::<_>()) 36 | } 37 | } 38 | 39 | pub fn impl_CommonAst(ast: &DeriveInput) -> TokenStream { 40 | let name = &ast.ident; 41 | let generics = &ast.generics.to_token_stream(); 42 | match &ast.data { 43 | syn::Data::Struct(structData) => { 44 | let fields = &structData.fields; 45 | match fields { 46 | syn::Fields::Named(fields) => { 47 | let mut vecTypes = vec![]; 48 | for field in fields.named.iter() { 49 | let fieldName = field.ident.as_ref().unwrap().to_token_stream(); 50 | if hasAttribute!(field, AstToString) { 51 | vecTypes.push(impl_AstToString(field, fieldName)); 52 | } else if hasAttribute!(field, AstChild) { 53 | vecTypes.push(impl_AstChild(field, fieldName)); 54 | } else if hasAttribute!(field, AstChildSlice) { 55 | vecTypes.push(impl_AstChildSlice(field, fieldName)); 56 | } else if hasAttribute!(field, AstChildSliceCell) { 57 | vecTypes.push(impl_AstChildSliceCell(field, fieldName)); 58 | } 59 | } 60 | quote! { 61 | impl #generics crate::Ast::Common::CommonAst for #name #generics { 62 | fn getDebugNode(&self) -> crate::Utils::DebugNode::DebugNode { 63 | crate::Utils::DebugNode::DebugNode::new(stringify!(#name).to_string()) 64 | #(. #vecTypes)* 65 | } 66 | } 67 | } 68 | } 69 | syn::Fields::Unnamed(_) => { 70 | quote!(compile_error!("Can't derive CommonAst for tuple struct")) 71 | } 72 | syn::Fields::Unit => quote! { 73 | impl crate::Ast::Common::CommonAst for #name #generics { 74 | fn getDebugNode(&self) -> crate::Utils::DebugNode::DebugNode { 75 | crate::Utils::DebugNode::DebugNode::new(stringify!(#name).to_string()) 76 | } 77 | } 78 | }, 79 | } 80 | } 81 | syn::Data::Enum(enumy) => { 82 | let arms = enumy.variants.iter().map(|variant| { 83 | let vident = &variant.ident; 84 | quote!(#name::#vident(v) => v.getDebugNode()) 85 | }); 86 | quote!( 87 | impl crate::Ast::Common::CommonAst for #name { 88 | fn getDebugNode(&self) -> crate::Utils::DebugNode::DebugNode { 89 | match self { 90 | #(#arms),* 91 | } 92 | } 93 | } 94 | ) 95 | } 96 | syn::Data::Union(_) => quote!(compile_error!("Can't derive CommonAst for union")), 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /deriveMacros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(proc_macro_span)] 2 | #![allow( 3 | non_snake_case, 4 | clippy::missing_const_for_fn // Bugged 5 | )] 6 | 7 | #[allow(unused_imports)] 8 | use quote::{quote, ToTokens}; 9 | 10 | mod RustycppInheritance; 11 | 12 | mod DeriveCommonAst; 13 | 14 | use crate::DeriveCommonAst::impl_CommonAst; 15 | use crate::RustycppInheritance::impl_RustycppInheritanceConstructors; 16 | 17 | macro_rules! finalResult { 18 | (debugerr $genTs:expr) => {{ 19 | let res = $genTs.to_string().to_token_stream(); 20 | quote!(compile_error!(#res)).into() 21 | }}; 22 | (release $genTs:expr) => {{ 23 | $genTs.into() 24 | }}; 25 | } 26 | 27 | #[proc_macro_derive( 28 | CommonAst, 29 | attributes(AstChild, AstChildSlice, AstChildSliceCell, AstToString) 30 | )] 31 | pub fn CommonAst_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 32 | // Construct a representation of Rust code as a syntax tree 33 | // that we can manipulate 34 | let ast = syn::parse(input).unwrap(); 35 | // Build the trait implementation 36 | finalResult!(release impl_CommonAst(&ast)) 37 | } 38 | 39 | #[proc_macro_attribute] 40 | pub fn RustycppInheritanceConstructors( 41 | _attr: proc_macro::TokenStream, 42 | item: proc_macro::TokenStream, 43 | ) -> proc_macro::TokenStream { 44 | // Construct a representation of Rust code as a syntax tree 45 | // that we can manipulate 46 | let ast: syn::ItemImpl = syn::parse(item).unwrap(); 47 | // Build the trait implementation 48 | finalResult!(release impl_RustycppInheritanceConstructors(&ast)) 49 | } 50 | -------------------------------------------------------------------------------- /gencodecov.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo test --profile codecoverage 3 | mkdir -p target/coverage 4 | grcov . --binary-path ./target/codecoverage/deps/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/tests.lcov 5 | grcov . --binary-path ./target/codecoverage/deps/ -s . -t html --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/ 6 | 7 | rm cargo-test-*.profraw -------------------------------------------------------------------------------- /rebuildallprofiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cargo clean 3 | RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='cargo-test-%p-%m.profraw' cargo build --tests --profile codecoverage 4 | cargo build 5 | cargo build --release 6 | cargo build --profile test 7 | cargo build --profile bench 8 | -------------------------------------------------------------------------------- /src/Ast.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::module_name_repetitions)] 2 | pub mod Attribute; 3 | pub mod Decl; 4 | pub mod NestedNameSpecifier; 5 | pub mod Tu; 6 | pub mod Type; 7 | #[macro_use] 8 | pub mod Common; 9 | -------------------------------------------------------------------------------- /src/Ast/Attribute.rs: -------------------------------------------------------------------------------- 1 | use crate::{Ast::Common::AstAttribute, Ast::Common::AstAttributeCXXStructNode, Base, Parent}; 2 | use std::collections::HashMap; 3 | 4 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 5 | use enum_dispatch::enum_dispatch; 6 | use lazy_static::lazy_static; 7 | 8 | use crate::{ 9 | Ast::Common::AstAttributeCXX, 10 | Ast::Common::{AstAttributeStructNode, CommonAst}, 11 | Lex::Token::Token, 12 | Utils::Structs::{FileTokPos, SourceRange}, 13 | }; 14 | use crate::{Parse::BufferedLexer::StateBufferedLexer, Utils::StringRef::StringRef}; 15 | 16 | pub mod RustyCppUnused; 17 | use RustyCppUnused::AstAttributeCXXRustyCppUnusedStruct; 18 | pub mod RustyCppTagDecl; 19 | use RustyCppTagDecl::AstAttributeCXXRustyCppTagDeclStruct; 20 | pub mod RustyCppCheckSymbolMatchTag; 21 | use RustyCppCheckSymbolMatchTag::AstAttributeCXXRustyCppCheckSymbolMatchTagStruct; 22 | 23 | #[derive(Clone, Copy)] 24 | pub enum Kind { 25 | AlignAs, 26 | Cxx(&'static [AstAttributeCXX]), 27 | } 28 | 29 | impl ToString for Kind { 30 | fn to_string(&self) -> String { 31 | match self { 32 | Self::AlignAs => "alignas".to_string(), 33 | Self::Cxx(_) => "CXX".to_string(), 34 | } 35 | } 36 | } 37 | 38 | #[derive(Clone, Copy)] 39 | pub struct AstAttributeStruct { 40 | /// CXX11, alignas, etc. 41 | pub kind: Kind, 42 | /// The range of the attribute in the source code. Includes the brackets/alignas/etc. 43 | pub sourceRange: SourceRange, 44 | } 45 | 46 | impl super::Common::CommonAst for AstAttributeStruct { 47 | fn getDebugNode(&self) -> crate::Utils::DebugNode::DebugNode { 48 | match self.kind { 49 | Kind::AlignAs => crate::Utils::DebugNode::DebugNode::new("AstAttribute".to_string()) 50 | .add_child(crate::Utils::DebugNode::DebugNode::new( 51 | "Kind: Alignas".to_string(), 52 | )), 53 | Kind::Cxx(attrs) => crate::Utils::DebugNode::DebugNode::new("AstAttribute".to_string()) 54 | .add_children(attrs.iter().map(CommonAst::getDebugNode).collect()), 55 | } 56 | } 57 | } 58 | 59 | impl AstAttributeStruct { 60 | pub const fn new(kind: Kind, sourceRange: SourceRange) -> Self { 61 | Self { kind, sourceRange } 62 | } 63 | } 64 | 65 | #[RustycppInheritanceConstructors] 66 | impl AstAttributeStructNode { 67 | pub const fn getKind(&self) -> Kind { 68 | self.base.kind 69 | } 70 | 71 | pub const fn getSourceRange(&self) -> SourceRange { 72 | self.base.sourceRange 73 | } 74 | 75 | pub fn new(kind: Kind, sourceRange: SourceRange) -> Self { 76 | Self { 77 | parent: ::new(), 78 | base: ::new(kind, sourceRange), 79 | } 80 | } 81 | } 82 | 83 | #[derive(Clone, Copy)] 84 | pub struct AtrributeKindInfo { 85 | pub namespace: Option, 86 | pub name: StringRef, 87 | pub requiresParameters: bool, 88 | pub parser: fn( 89 | &mut crate::Parse::Parser::Parser, 90 | &FileTokPos, 91 | Option, 92 | ) -> Option, 93 | } 94 | 95 | pub struct AttributeDispatcher { 96 | pub attributeKinds: HashMap, HashMap>, 97 | } 98 | 99 | impl AttributeDispatcher { 100 | pub fn getAtrributeKindInfo( 101 | &self, 102 | namespace: Option, 103 | name: StringRef, 104 | ) -> Option<&AtrributeKindInfo> { 105 | self.attributeKinds 106 | .get(&namespace) 107 | .and_then(|namespace| namespace.get(&name)) 108 | } 109 | } 110 | 111 | #[derive(Clone, Copy, CommonAst, Default)] 112 | pub struct AstAttributeCXXStruct; 113 | 114 | pub trait CXXAttributeKindInfo { 115 | fn getAtrributeKindInfo() -> AtrributeKindInfo; 116 | } 117 | 118 | impl AstAttributeCXXStructNode { 119 | pub fn new() -> Self { 120 | Self { 121 | parent: ::new(), 122 | base: ::default(), 123 | } 124 | } 125 | 126 | // I'm leaving this here until I have a real case for using getDyn() 127 | pub fn actOnAttributeDeclTestRemoveMePlease( 128 | &'static self, 129 | parser: &mut crate::Parse::Parser::Parser, 130 | ) { 131 | self.getDyn().actOnAttributeDecl(parser); 132 | } 133 | } 134 | 135 | #[enum_dispatch] 136 | pub trait CXXAttribute { 137 | fn actOnAttributeDecl(&self, _parser: &mut crate::Parse::Parser::Parser) {} 138 | } 139 | 140 | macro_rules! register_attributes { 141 | ($($o:ident),*) => { 142 | register_attributes!(@ dispatcher $($o),*); 143 | }; 144 | (@ dispatcher $($o:ident),*) => { 145 | lazy_static! { 146 | pub static ref ATTRIBUTE_DISPATCHER: AttributeDispatcher = { 147 | let mut attributeKinds: HashMap, HashMap> = HashMap::new(); 148 | $({ 149 | let attrInfo = $o::getAtrributeKindInfo(); 150 | if let Some(namespace) = attributeKinds.get_mut(&attrInfo.namespace) { 151 | namespace.insert(attrInfo.name, attrInfo); 152 | } else { 153 | let mut namespace = HashMap::new(); 154 | namespace.insert(attrInfo.name, attrInfo); 155 | attributeKinds.insert(attrInfo.namespace, namespace); 156 | } 157 | })* 158 | AttributeDispatcher { attributeKinds } 159 | }; 160 | } 161 | }; 162 | } 163 | 164 | register_attributes! { 165 | AstAttributeCXXRustyCppUnusedStruct, 166 | AstAttributeCXXRustyCppTagDeclStruct, 167 | AstAttributeCXXRustyCppCheckSymbolMatchTagStruct 168 | } 169 | -------------------------------------------------------------------------------- /src/Ast/Attribute/RustyCppTagDecl.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttributeCXXRustyCppTagDecl; 2 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 3 | 4 | use crate::{ 5 | Ast::{ 6 | Attribute::{AtrributeKindInfo, CXXAttribute, CXXAttributeKindInfo}, 7 | Common::AstAttributeCXXRustyCppTagDeclStructNode, 8 | }, 9 | Base, 10 | Lex::Token::Token, 11 | Parent, 12 | Utils::{StringRef::ToStringRef, Structs::FileTokPos}, 13 | }; 14 | 15 | #[derive(Clone, Copy, CommonAst)] 16 | pub struct AstAttributeCXXRustyCppTagDeclStruct { 17 | pub number: FileTokPos, 18 | } 19 | 20 | impl AstAttributeCXXRustyCppTagDeclStruct { 21 | pub const fn new(number: FileTokPos) -> Self { 22 | Self { number } 23 | } 24 | } 25 | 26 | impl CXXAttributeKindInfo for AstAttributeCXXRustyCppTagDeclStruct { 27 | fn getAtrributeKindInfo() -> AtrributeKindInfo { 28 | AtrributeKindInfo { 29 | name: "tagDecl".to_StringRef(), 30 | namespace: Some("rustycpp".to_StringRef()), 31 | requiresParameters: true, 32 | parser: crate::Parse::Parser::Parser::parseRustyCppTagDecl, 33 | } 34 | } 35 | } 36 | 37 | impl CXXAttribute for &AstAttributeCXXRustyCppTagDeclStructNode {} 38 | 39 | #[RustycppInheritanceConstructors] 40 | impl AstAttributeCXXRustyCppTagDeclStructNode { 41 | pub const fn getNumber(&self) -> FileTokPos { 42 | self.base.number 43 | } 44 | 45 | pub fn new(number: FileTokPos) -> Self { 46 | Self { 47 | parent: ::new(), 48 | base: ::new(number), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Ast/Attribute/RustyCppUnused.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttributeCXXRustyCppUnused; 2 | use crate::{ 3 | Ast::{ 4 | Attribute::{AtrributeKindInfo, CXXAttribute, CXXAttributeKindInfo}, 5 | Common::AstAttributeCXXRustyCppUnusedStructNode, 6 | }, 7 | Base, Parent, 8 | Utils::StringRef::ToStringRef, 9 | }; 10 | use deriveMacros::CommonAst; 11 | use deriveMacros::RustycppInheritanceConstructors; 12 | 13 | #[derive(Clone, Copy, CommonAst)] 14 | pub struct AstAttributeCXXRustyCppUnusedStruct; 15 | 16 | impl AstAttributeCXXRustyCppUnusedStruct { 17 | pub const fn new() -> Self { 18 | Self {} 19 | } 20 | } 21 | 22 | impl CXXAttributeKindInfo for AstAttributeCXXRustyCppUnusedStruct { 23 | fn getAtrributeKindInfo() -> AtrributeKindInfo { 24 | AtrributeKindInfo { 25 | name: "unused".to_StringRef(), 26 | namespace: Some("rustycpp".to_StringRef()), 27 | requiresParameters: false, 28 | parser: crate::Parse::Parser::Parser::parseRustyCppUnused, 29 | } 30 | } 31 | } 32 | 33 | impl CXXAttribute for &AstAttributeCXXRustyCppUnusedStructNode {} 34 | 35 | #[RustycppInheritanceConstructors] 36 | impl AstAttributeCXXRustyCppUnusedStructNode { 37 | pub fn new() -> Self { 38 | Self { 39 | parent: ::new(), 40 | base: ::new(), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Ast/Common.rs: -------------------------------------------------------------------------------- 1 | use deriveMacros::CommonAst; 2 | 3 | use crate::Ast::Attribute::AstAttributeCXXStruct; 4 | use crate::Ast::Attribute::AstAttributeStruct; 5 | use crate::Ast::Attribute::CXXAttribute; 6 | use crate::Ast::Attribute::RustyCppCheckSymbolMatchTag::AstAttributeCXXRustyCppCheckSymbolMatchTagStruct; 7 | use crate::Ast::Attribute::RustyCppTagDecl::AstAttributeCXXRustyCppTagDeclStruct; 8 | use crate::Ast::Attribute::RustyCppUnused::AstAttributeCXXRustyCppUnusedStruct; 9 | use crate::Ast::Decl::Asm::AstDeclAsmStruct; 10 | use crate::Ast::Decl::AstDeclStruct; 11 | use crate::Ast::Decl::Empty::AstDeclEmptyStruct; 12 | use crate::Ast::Decl::Enum::AstDeclCustomRustyCppEnumStruct; 13 | use crate::Ast::Decl::Namespace::AstDeclNamespaceStruct; 14 | use crate::Ast::Decl::UsingNamespace::AstDeclUsingNamespaceStruct; 15 | use crate::Ast::Tu::AstTuStruct; 16 | use crate::Ast::Type::AstTypeStruct; 17 | use crate::Ast::Type::BaseType; 18 | use crate::Ast::Type::Builtin::AstTypeBuiltinStruct; 19 | use crate::Ast::Type::Pointer::AstTypePointerStruct; 20 | use crate::Ast::Type::Reference::AstTypeReferenceStruct; 21 | use crate::Ast::Type::TypeAst; 22 | use crate::Utils::DebugNode::DebugNode; 23 | use crate::Utils::FoldingContainer::Foldable; 24 | 25 | use crate::Utils::FoldingContainer::FoldingNode; 26 | use std::fmt::Display; 27 | 28 | include!(concat!(env!("OUT_DIR"), "/hello.rs")); 29 | 30 | fn foo(d: AstDecl) { 31 | d.getDebugNode(); 32 | } 33 | 34 | #[derive(Clone, Copy, CommonAst)] 35 | pub struct AstNodeStruct; 36 | 37 | impl AstNodeStructNode { 38 | pub fn new() -> Self { 39 | Self { 40 | finalType: AstNodeFinalTypes::default(), 41 | base: AstNodeStruct, 42 | } 43 | } 44 | } 45 | 46 | #[enum_dispatch] 47 | pub trait CommonAst { 48 | fn getDebugNode(&self) -> DebugNode; 49 | } 50 | 51 | #[allow(clippy::non_send_fields_in_send_ty)] 52 | unsafe impl Send for AstTu {} 53 | 54 | /// Access the Base of this node. This is only useful from the *`StructNode` 55 | /// family of classes 56 | #[macro_export] 57 | macro_rules! Base { 58 | () => { 59 | ::Base 60 | }; 61 | } 62 | 63 | /// Access the Parent of this node. This is only useful from the *`StructNode` 64 | /// family of classes. If there is no parent, this will return `()` 65 | #[macro_export] 66 | macro_rules! Parent { 67 | () => { 68 | ::Parent 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /src/Ast/Decl.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use crate::{ 4 | Ast::Common::{AstAttribute, AstDeclStructNode}, 5 | Parent, 6 | }; 7 | use bitflags::bitflags; 8 | use deriveMacros::CommonAst; 9 | 10 | use crate::Sema::Scope::ScopeRef; 11 | use crate::Utils::Structs::SourceRange; 12 | 13 | pub mod Asm; 14 | pub mod Empty; 15 | pub mod Enum; 16 | pub mod Namespace; 17 | pub mod UsingNamespace; 18 | 19 | bitflags! { 20 | pub struct MyFlags: u8 { 21 | const INVALID_DECL = 0b1; 22 | const USED = 0b10; 23 | const REFERENCED = 0b100; 24 | } 25 | } 26 | 27 | impl Display for MyFlags { 28 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 29 | let written = if self.contains(Self::INVALID_DECL) { 30 | write!(f, "INVALID_DECL")?; 31 | true 32 | } else { 33 | false 34 | }; 35 | 36 | let written = if self.contains(Self::USED) { 37 | if written { 38 | write!(f, " | ")?; 39 | } 40 | write!(f, "USED")?; 41 | true 42 | } else { 43 | written 44 | }; 45 | if self.contains(Self::REFERENCED) { 46 | if written { 47 | write!(f, " | ")?; 48 | } 49 | write!(f, "REFERENCED")?; 50 | } 51 | Ok(()) 52 | } 53 | } 54 | 55 | #[derive(CommonAst)] 56 | pub struct AstDeclStruct { 57 | pub sourceRange: SourceRange, 58 | pub scope: ScopeRef, 59 | #[AstToString] 60 | pub flags: MyFlags, 61 | #[AstChildSlice] 62 | pub attrs: &'static [AstAttribute], 63 | } 64 | 65 | impl AstDeclStruct { 66 | pub fn new(sourceRange: SourceRange, scope: ScopeRef, attrs: &'static [AstAttribute]) -> Self { 67 | Self { 68 | sourceRange, 69 | scope, 70 | flags: MyFlags::empty(), 71 | attrs, 72 | } 73 | } 74 | } 75 | 76 | impl AstDeclStructNode { 77 | pub fn new(sourceRange: SourceRange, scope: ScopeRef, attrs: &'static [AstAttribute]) -> Self { 78 | Self { 79 | parent: ::new(), 80 | base: AstDeclStruct::new(sourceRange, scope, attrs), 81 | } 82 | } 83 | 84 | pub const fn getAttributes(&self) -> &'static [AstAttribute] { 85 | self.base.attrs 86 | } 87 | 88 | pub fn getScope(&self) -> ScopeRef { 89 | self.base.scope.clone() 90 | } 91 | 92 | pub const fn getSourceRange(&self) -> SourceRange { 93 | self.base.sourceRange 94 | } 95 | 96 | pub const fn getFlags(&self) -> MyFlags { 97 | self.base.flags 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Ast/Decl/Asm.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttribute; 2 | use crate::Ast::Common::AstDeclAsm; 3 | use crate::Parent; 4 | use crate::{Ast::Common::AstDeclAsmStructNode, Utils::Structs::SourceRange}; 5 | use crate::{Base, Sema::Scope::ScopeRef}; 6 | use deriveMacros::CommonAst; 7 | use deriveMacros::RustycppInheritanceConstructors; 8 | 9 | use crate::Utils::StringRef::StringRef; 10 | 11 | #[derive(CommonAst)] 12 | pub struct AstDeclAsmStruct { 13 | #[AstToString] 14 | asm: StringRef, 15 | } 16 | 17 | impl AstDeclAsmStruct { 18 | pub const fn new(asm: StringRef) -> Self { 19 | Self { asm } 20 | } 21 | } 22 | 23 | #[RustycppInheritanceConstructors] 24 | impl AstDeclAsmStructNode { 25 | pub fn new( 26 | sourceRange: SourceRange, 27 | scope: ScopeRef, 28 | attrs: &'static [AstAttribute], 29 | asm: StringRef, 30 | ) -> Self { 31 | Self { 32 | parent: ::new(sourceRange, scope, attrs), 33 | base: ::new(asm), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Ast/Decl/Empty.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttribute; 2 | use crate::Ast::Common::AstDeclEmpty; 3 | use crate::Ast::Common::AstDeclEmptyStructNode; 4 | use crate::Base; 5 | use crate::Parent; 6 | use crate::Sema::Scope::ScopeRef; 7 | use crate::Utils::Structs::SourceRange; 8 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 9 | 10 | #[derive(CommonAst)] 11 | pub struct AstDeclEmptyStruct; 12 | 13 | impl AstDeclEmptyStruct { 14 | pub const fn new() -> Self { 15 | Self {} 16 | } 17 | } 18 | 19 | #[RustycppInheritanceConstructors] 20 | impl AstDeclEmptyStructNode { 21 | pub fn new(sourceRange: SourceRange, scope: ScopeRef, attrs: &'static [AstAttribute]) -> Self { 22 | Self { 23 | parent: ::new(sourceRange, scope, attrs), 24 | base: ::new(), 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Ast/Decl/Enum.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttribute; 2 | use crate::Ast::Common::AstDeclCustomRustyCppEnum; 3 | use crate::Ast::Common::AstDeclCustomRustyCppEnumStructNode; 4 | use crate::Base; 5 | use crate::Parent; 6 | use crate::Sema::Scope::ScopeRef; 7 | use crate::Utils::StringRef::StringRef; 8 | use crate::Utils::Structs::SourceRange; 9 | use deriveMacros::CommonAst; 10 | use deriveMacros::RustycppInheritanceConstructors; 11 | 12 | #[derive(CommonAst)] 13 | pub struct AstDeclCustomRustyCppEnumStruct { 14 | #[AstToString] 15 | name: StringRef, 16 | } 17 | 18 | impl AstDeclCustomRustyCppEnumStruct { 19 | pub const fn new(name: StringRef) -> Self { 20 | Self { name } 21 | } 22 | } 23 | 24 | #[RustycppInheritanceConstructors] 25 | impl AstDeclCustomRustyCppEnumStructNode { 26 | pub fn new( 27 | sourceRange: SourceRange, 28 | scope: ScopeRef, 29 | attrs: &'static [AstAttribute], 30 | name: StringRef, 31 | ) -> Self { 32 | Self { 33 | parent: ::new(sourceRange, scope, attrs), 34 | base: ::new(name), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Ast/Decl/Namespace.rs: -------------------------------------------------------------------------------- 1 | use crate::Base; 2 | use crate::Parent; 3 | use deriveMacros::CommonAst; 4 | use deriveMacros::RustycppInheritanceConstructors; 5 | use std::cell::RefCell; 6 | 7 | use crate::{ 8 | Ast::Common::{AstAttribute, AstDecl, AstDeclNamespace, AstDeclNamespaceStructNode}, 9 | Sema::Scope::ScopeRef, 10 | Utils::{StringRef::StringRef, Structs::SourceRange}, 11 | }; 12 | 13 | #[derive(CommonAst)] 14 | pub struct AstDeclNamespaceStruct { 15 | #[AstToString] 16 | name: StringRef, 17 | #[AstToString] 18 | isInline: bool, 19 | nextExtension: RefCell>, 20 | #[AstChildSliceCell] 21 | contents: RefCell<&'static [AstDecl]>, 22 | /** 23 | * This is always the parent scope of where this was declared. This is 24 | * specially useful when dealing with namespaces that extend other 25 | * namespaces. 26 | * For example: 27 | * inline namespace Special { 28 | * namespace ExtendMe {/*Stuff*/} 29 | * } 30 | * namespace ExtendMe {/*Stuff*/} 31 | * In the second declaration, parent scope will be the root scope, 32 | * despite the fact that this namespace semantically has the "Special" 33 | * namespace as its parent. 34 | */ 35 | parentScope: ScopeRef, 36 | } 37 | 38 | impl AstDeclNamespaceStruct { 39 | pub fn new(name: StringRef, isInline: bool, parentScope: ScopeRef) -> Self { 40 | Self { 41 | name, 42 | isInline, 43 | nextExtension: RefCell::new(None), 44 | contents: RefCell::default(), 45 | parentScope, 46 | } 47 | } 48 | 49 | pub fn addExtension(&self, extension: &'static AstDeclNamespaceStructNode) { 50 | // This is like a single-linked-list, basically. 51 | let mut next = self.nextExtension.borrow_mut(); 52 | while next.is_some() { 53 | next = next.unwrap().base.nextExtension.borrow_mut(); 54 | } 55 | 56 | *next = Some(extension); 57 | } 58 | 59 | pub fn setContents(&self, newContents: &'static [AstDecl]) { 60 | let mut contents = self.contents.borrow_mut(); 61 | *contents = newContents; 62 | } 63 | 64 | pub const fn isInline(&self) -> bool { 65 | self.isInline 66 | } 67 | 68 | pub const fn parentScope(&self) -> &ScopeRef { 69 | &self.parentScope 70 | } 71 | } 72 | 73 | #[RustycppInheritanceConstructors] 74 | impl AstDeclNamespaceStructNode { 75 | pub fn new( 76 | sourceRange: SourceRange, 77 | scope: ScopeRef, 78 | attrs: &'static [AstAttribute], 79 | name: StringRef, 80 | isInline: bool, 81 | parentScope: ScopeRef, 82 | ) -> Self { 83 | Self { 84 | parent: ::new(sourceRange, scope, attrs), 85 | base: ::new(name, isInline, parentScope), 86 | } 87 | } 88 | 89 | pub fn addExtension(&self, extension: AstDeclNamespace) { 90 | self.base.addExtension(extension.getStatic()); 91 | } 92 | 93 | pub fn setContents(&self, newContents: &'static [AstDecl]) { 94 | self.base.setContents(newContents); 95 | } 96 | 97 | pub const fn isInline(&self) -> bool { 98 | self.base.isInline() 99 | } 100 | 101 | pub const fn parentScope(&self) -> &ScopeRef { 102 | self.base.parentScope() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Ast/Decl/UsingNamespace.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttribute; 2 | use crate::Ast::Common::AstDeclUsingNamespace; 3 | use crate::Ast::Common::AstDeclUsingNamespaceStructNode; 4 | use crate::Base; 5 | use crate::Parent; 6 | use crate::Sema::Scope::ScopeRef; 7 | use crate::Utils::Structs::SourceRange; 8 | use crate::{Ast::NestedNameSpecifier::AstNestedNameSpecifier, Utils::StringRef::StringRef}; 9 | use deriveMacros::CommonAst; 10 | use deriveMacros::RustycppInheritanceConstructors; 11 | 12 | #[derive(CommonAst)] 13 | pub struct AstDeclUsingNamespaceStruct { 14 | #[AstToString] 15 | name: StringRef, 16 | #[AstChildSlice] 17 | nestedNameSpecifier: &'static [AstNestedNameSpecifier], 18 | } 19 | 20 | impl AstDeclUsingNamespaceStruct { 21 | pub const fn new( 22 | name: StringRef, 23 | nestedNameSpecifier: &'static [AstNestedNameSpecifier], 24 | ) -> Self { 25 | Self { 26 | name, 27 | nestedNameSpecifier, 28 | } 29 | } 30 | } 31 | 32 | #[RustycppInheritanceConstructors] 33 | impl AstDeclUsingNamespaceStructNode { 34 | pub fn new( 35 | sourceRange: SourceRange, 36 | scope: ScopeRef, 37 | attrs: &'static [AstAttribute], 38 | name: StringRef, 39 | nestedNameSpecifier: &'static [AstNestedNameSpecifier], 40 | ) -> Self { 41 | Self { 42 | parent: ::new(sourceRange, scope, attrs), 43 | base: ::new(name, nestedNameSpecifier), 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Ast/NestedNameSpecifier.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use deriveMacros::CommonAst; 4 | 5 | use crate::{ 6 | Sema::Scope::ScopeRef, 7 | Utils::{StringRef::StringRef, Structs::SourceRange}, 8 | }; 9 | 10 | #[derive(Clone, Copy)] 11 | pub enum Kind { 12 | Global, 13 | Type(StringRef), 14 | Namespace(StringRef), 15 | 16 | // DeclType not yet implemented 17 | //DeclType, 18 | 19 | // Secondaries 20 | Identifier(StringRef), 21 | // Simple Template Id not yet implemented 22 | // SimpleTemplateId, 23 | } 24 | 25 | impl ToString for Kind { 26 | fn to_string(&self) -> String { 27 | match self { 28 | Self::Global => "".to_string(), 29 | Self::Type(name) | Self::Namespace(name) | Self::Identifier(name) => name.to_string(), 30 | } 31 | } 32 | } 33 | 34 | /** 35 | * Beware! `AstNestedNameSpecifier` usually goes in a vector, as our 36 | * implementation, instead of being recursive as it is in the standard, we just 37 | * parse the nodes individually. 38 | */ 39 | #[derive(Clone, CommonAst)] 40 | pub struct AstNestedNameSpecifier { 41 | #[AstToString] 42 | pub kind: Kind, 43 | /// The range of the nestedNameSpecifier. Does not include the :: 44 | pub sourceRange: SourceRange, 45 | /// Evaluated scope of the nestedNameSpecifier. 46 | pub scope: RefCell>, 47 | } 48 | 49 | impl AstNestedNameSpecifier { 50 | pub const fn new(kind: Kind, sourceRange: SourceRange) -> Self { 51 | Self { 52 | kind, 53 | sourceRange, 54 | scope: RefCell::new(None), 55 | } 56 | } 57 | pub fn new_scoped(kind: Kind, sourceRange: SourceRange, scope: ScopeRef) -> Self { 58 | Self { 59 | kind, 60 | sourceRange, 61 | scope: RefCell::new(Some(scope)), 62 | } 63 | } 64 | 65 | pub fn getName(&self) -> StringRef { 66 | match self.kind { 67 | Kind::Type(name) | Kind::Namespace(name) | Kind::Identifier(name) => name, 68 | Kind::Global => panic!("NestedNameSpecifier::getName called on non-identifier"), 69 | } 70 | } 71 | 72 | pub fn setScope(&self, scope: ScopeRef) { 73 | self.scope.borrow_mut().replace(scope); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Ast/Tu.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstTu; 2 | use crate::Ast::Common::AstTuStructNode; 3 | use crate::Sema::AstContext::AstContext; 4 | 5 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 6 | 7 | use crate::{Ast::Common, Base, Parent}; 8 | 9 | #[derive(CommonAst)] 10 | pub struct AstTuStruct { 11 | #[AstChildSlice] 12 | globalDecl: &'static [Common::AstDecl], 13 | astContext: AstContext, 14 | } 15 | 16 | #[allow(clippy::non_send_fields_in_send_ty)] 17 | unsafe impl Send for AstTuStruct {} 18 | 19 | impl AstTuStruct { 20 | pub fn new(astContext: AstContext, global: &[Common::AstDecl]) -> Self { 21 | let globalDecl = astContext.alloc.alloc().alloc_slice_clone(global); 22 | Self { 23 | globalDecl, 24 | astContext, 25 | } 26 | } 27 | } 28 | 29 | #[RustycppInheritanceConstructors] 30 | impl AstTuStructNode { 31 | pub fn new(astContext: AstContext, global: &[Common::AstDecl]) -> Self { 32 | Self { 33 | parent: ::new(), 34 | base: ::new(astContext, global), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Ast/Type.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::Common::{AstType, AstTypeStructNode}, 3 | Parent, 4 | Utils::FoldingContainer::{FoldingNode, PushFoldingNode}, 5 | }; 6 | use std::fmt::Display; 7 | 8 | use bitflags::bitflags; 9 | use deriveMacros::CommonAst; 10 | use enum_dispatch::enum_dispatch; 11 | 12 | pub mod Builtin; 13 | pub mod Pointer; 14 | pub mod Reference; 15 | 16 | #[derive(Clone, Copy, CommonAst)] 17 | pub struct AstTypeStruct; 18 | 19 | impl AstTypeStructNode { 20 | pub fn new() -> Self { 21 | Self { 22 | parent: ::new(), 23 | base: AstTypeStruct, 24 | } 25 | } 26 | } 27 | 28 | pub struct BaseType { 29 | pub size: u64, 30 | pub align: u64, 31 | } 32 | 33 | impl BaseType { 34 | pub const fn new(size: u64, align: u64) -> Self { 35 | Self { size, align } 36 | } 37 | } 38 | 39 | #[enum_dispatch] 40 | pub trait TypeAst { 41 | fn getBaseType(&self) -> BaseType; 42 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result; 43 | } 44 | 45 | bitflags! { 46 | pub struct QualTypeFlags: u8 { 47 | const CONST = 1; 48 | const VOLATILE = 2; 49 | const RESTRICT = 4; 50 | } 51 | } 52 | 53 | impl Display for QualTypeFlags { 54 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 55 | if self.contains(Self::CONST) { 56 | write!(f, "const ")?; 57 | } 58 | if self.contains(Self::VOLATILE) { 59 | write!(f, "volatile ")?; 60 | } 61 | if self.contains(Self::RESTRICT) { 62 | write!(f, "restrict ")?; 63 | } 64 | Ok(()) 65 | } 66 | } 67 | 68 | #[derive(CommonAst)] 69 | pub struct QualType { 70 | #[AstChild] 71 | unqualType: AstType, 72 | #[AstToString] 73 | flags: QualTypeFlags, 74 | } 75 | 76 | impl TypeAst for QualType { 77 | fn getBaseType(&self) -> BaseType { 78 | self.unqualType.getBaseType() 79 | } 80 | 81 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 82 | write!(f, "{} {}", self.flags, self.unqualType) 83 | } 84 | } 85 | 86 | impl Display for QualType { 87 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 88 | write!(f, "{}{}", self.flags, self.unqualType) 89 | } 90 | } 91 | 92 | impl QualType { 93 | pub const fn new(unqualType: AstType, flags: QualTypeFlags) -> Self { 94 | Self { unqualType, flags } 95 | } 96 | 97 | pub const fn getUnqualType(&self) -> AstType { 98 | self.unqualType 99 | } 100 | 101 | pub const fn getFlags(&self) -> QualTypeFlags { 102 | self.flags 103 | } 104 | 105 | pub fn setFlags(&mut self, flags: QualTypeFlags) { 106 | self.flags = flags; 107 | } 108 | 109 | pub fn addFlags(&mut self, flags: QualTypeFlags) { 110 | self.flags |= flags; 111 | } 112 | 113 | pub fn removeFlags(&mut self, flags: QualTypeFlags) { 114 | self.flags &= !flags; 115 | } 116 | 117 | pub const fn isConst(&self) -> bool { 118 | self.flags.contains(QualTypeFlags::CONST) 119 | } 120 | 121 | pub const fn isVolatile(&self) -> bool { 122 | self.flags.contains(QualTypeFlags::VOLATILE) 123 | } 124 | 125 | pub const fn isRestrict(&self) -> bool { 126 | self.flags.contains(QualTypeFlags::RESTRICT) 127 | } 128 | 129 | pub fn isConstVolatile(&self) -> bool { 130 | self.flags 131 | .contains(QualTypeFlags::CONST | QualTypeFlags::VOLATILE) 132 | } 133 | 134 | pub fn isConstRestrict(&self) -> bool { 135 | self.flags 136 | .contains(QualTypeFlags::CONST | QualTypeFlags::RESTRICT) 137 | } 138 | 139 | pub fn isVolatileRestrict(&self) -> bool { 140 | self.flags 141 | .contains(QualTypeFlags::VOLATILE | QualTypeFlags::RESTRICT) 142 | } 143 | 144 | pub fn isConstVolatileRestrict(&self) -> bool { 145 | self.flags 146 | .contains(QualTypeFlags::CONST | QualTypeFlags::VOLATILE | QualTypeFlags::RESTRICT) 147 | } 148 | 149 | pub fn setConst(&mut self) { 150 | self.flags |= QualTypeFlags::CONST; 151 | } 152 | 153 | pub fn setVolatile(&mut self) { 154 | self.flags |= QualTypeFlags::VOLATILE; 155 | } 156 | 157 | pub fn setRestrict(&mut self) { 158 | self.flags |= QualTypeFlags::RESTRICT; 159 | } 160 | } 161 | 162 | impl crate::Utils::FoldingContainer::Foldable for QualType { 163 | fn foldNode(&self, node: &mut FoldingNode) { 164 | node.push(&self.flags.bits); 165 | self.unqualType.foldNode(node); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/Ast/Type/Builtin.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 4 | use strum_macros::EnumIter; 5 | 6 | use crate::{ 7 | Ast::Common::AstTypeBuiltin, 8 | Base, Parent, 9 | Utils::FoldingContainer::{FoldingNode, PushFoldingNode}, 10 | }; 11 | use crate::{ 12 | Ast::{ 13 | Common::AstTypeBuiltinStructNode, 14 | Type::{BaseType, TypeAst}, 15 | }, 16 | Utils::FoldingContainer::Foldable, 17 | }; 18 | 19 | #[derive(EnumIter, Copy, Clone)] 20 | pub enum BuiltinTypeKind { 21 | Void, 22 | Bool, 23 | Char, 24 | SChar, 25 | UChar, 26 | Short, 27 | UShort, 28 | Int, 29 | UInt, 30 | Long, 31 | ULong, 32 | LongLong, 33 | ULongLong, 34 | Float, 35 | Double, 36 | LongDouble, 37 | WChar, 38 | Char16, 39 | Char32, 40 | Int8, 41 | UInt8, 42 | Int16, 43 | UInt16, 44 | Int32, 45 | UInt32, 46 | Int64, 47 | UInt64, 48 | Int128, 49 | UInt128, 50 | NullPtr, 51 | Auto, 52 | DecltypeAuto, 53 | Unknown, 54 | } 55 | 56 | impl Display for BuiltinTypeKind { 57 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 58 | match self { 59 | Self::Void => write!(f, "void"), 60 | Self::Bool => write!(f, "bool"), 61 | Self::Char => write!(f, "char"), 62 | Self::SChar => write!(f, "signed char"), 63 | Self::UChar => write!(f, "unsigned char"), 64 | Self::Short => write!(f, "short"), 65 | Self::UShort => write!(f, "unsigned short"), 66 | Self::Int => write!(f, "int"), 67 | Self::UInt => write!(f, "unsigned int"), 68 | Self::Long => write!(f, "long"), 69 | Self::ULong => write!(f, "unsigned long"), 70 | Self::LongLong => write!(f, "long long"), 71 | Self::ULongLong => write!(f, "unsigned long long"), 72 | Self::Float => write!(f, "float"), 73 | Self::Double => write!(f, "double"), 74 | Self::LongDouble => write!(f, "long double"), 75 | Self::WChar => write!(f, "wchar_t"), 76 | Self::Char16 => write!(f, "char16_t"), 77 | Self::Char32 => write!(f, "char32_t"), 78 | Self::Int8 => write!(f, "int8_t"), 79 | Self::UInt8 => write!(f, "uint8_t"), 80 | Self::Int16 => write!(f, "int16_t"), 81 | Self::UInt16 => write!(f, "uint16_t"), 82 | Self::Int32 => write!(f, "int32_t"), 83 | Self::UInt32 => write!(f, "uint32_t"), 84 | Self::Int64 => write!(f, "int64_t"), 85 | Self::UInt64 => write!(f, "uint64_t"), 86 | Self::Int128 => write!(f, "int128_t"), 87 | Self::UInt128 => write!(f, "uint128_t"), 88 | Self::NullPtr => write!(f, "nullptr_t"), 89 | Self::Auto => write!(f, "auto"), 90 | Self::DecltypeAuto => write!(f, "decltype(auto)"), 91 | Self::Unknown => write!(f, "__unknown_type"), 92 | } 93 | } 94 | } 95 | 96 | #[derive(CommonAst)] 97 | pub struct AstTypeBuiltinStruct { 98 | #[AstToString] 99 | kindType: BuiltinTypeKind, 100 | } 101 | 102 | impl Display for AstTypeBuiltinStruct { 103 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 104 | write!(f, "{}", self.kindType) 105 | } 106 | } 107 | 108 | impl AstTypeBuiltinStruct { 109 | pub const fn new(kindType: BuiltinTypeKind) -> Self { 110 | Self { kindType } 111 | } 112 | } 113 | 114 | #[RustycppInheritanceConstructors] 115 | impl AstTypeBuiltinStructNode { 116 | pub fn new(kindType: BuiltinTypeKind) -> Self { 117 | Self { 118 | base: ::new(kindType), 119 | parent: ::new(), 120 | } 121 | } 122 | } 123 | 124 | impl TypeAst for &AstTypeBuiltinStructNode { 125 | fn getBaseType(&self) -> BaseType { 126 | #[allow(clippy::match_same_arms)] 127 | let (size, align) = match self.base.kindType { 128 | BuiltinTypeKind::Void => (0, 0), 129 | BuiltinTypeKind::Bool => (1, 1), 130 | BuiltinTypeKind::Char => (1, 1), 131 | BuiltinTypeKind::SChar => (1, 1), 132 | BuiltinTypeKind::UChar => (1, 1), 133 | BuiltinTypeKind::Short => (2, 2), 134 | BuiltinTypeKind::UShort => (2, 2), 135 | BuiltinTypeKind::Int => (4, 4), 136 | BuiltinTypeKind::UInt => (4, 4), 137 | BuiltinTypeKind::Long => (8, 8), 138 | BuiltinTypeKind::ULong => (8, 8), 139 | BuiltinTypeKind::LongLong => (8, 8), 140 | BuiltinTypeKind::ULongLong => (8, 8), 141 | BuiltinTypeKind::Float => (4, 4), 142 | BuiltinTypeKind::Double => (8, 8), 143 | BuiltinTypeKind::LongDouble => (16, 16), 144 | BuiltinTypeKind::WChar => (4, 4), 145 | BuiltinTypeKind::Char16 => (2, 2), 146 | BuiltinTypeKind::Char32 => (4, 4), 147 | BuiltinTypeKind::Int8 => (1, 1), 148 | BuiltinTypeKind::UInt8 => (1, 1), 149 | BuiltinTypeKind::Int16 => (2, 2), 150 | BuiltinTypeKind::UInt16 => (2, 2), 151 | BuiltinTypeKind::Int32 => (4, 4), 152 | BuiltinTypeKind::UInt32 => (4, 4), 153 | BuiltinTypeKind::Int64 => (8, 8), 154 | BuiltinTypeKind::UInt64 => (8, 8), 155 | BuiltinTypeKind::Int128 => (16, 16), 156 | BuiltinTypeKind::UInt128 => (16, 16), 157 | BuiltinTypeKind::NullPtr => (8, 8), 158 | BuiltinTypeKind::Auto => (0, 0), 159 | BuiltinTypeKind::DecltypeAuto => (0, 0), 160 | BuiltinTypeKind::Unknown => (0, 0), 161 | }; 162 | BaseType { size, align } 163 | } 164 | 165 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 166 | self.base.fmt(f) 167 | } 168 | } 169 | 170 | impl Foldable for &AstTypeBuiltinStructNode { 171 | fn foldNode(&self, node: &mut FoldingNode) { 172 | node.push(&(self.base.kindType as u8)); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/Ast/Type/Pointer.rs: -------------------------------------------------------------------------------- 1 | use crate::Utils::FoldingContainer::PushFoldingNode; 2 | use crate::{ 3 | Ast::{ 4 | Common::{AstTypePointer, AstTypePointerStructNode}, 5 | Type::{BaseType, QualType, TypeAst}, 6 | }, 7 | Base, Parent, 8 | Utils::FoldingContainer::{Foldable, FoldingNode}, 9 | }; 10 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 11 | use std::fmt::Display; 12 | 13 | #[derive(CommonAst)] 14 | pub struct AstTypePointerStruct { 15 | #[AstChild] 16 | base: QualType, 17 | } 18 | 19 | impl Display for AstTypePointerStruct { 20 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 21 | write!(f, "{} *", self.base) 22 | } 23 | } 24 | 25 | impl AstTypePointerStruct { 26 | pub const fn new(base: QualType) -> Self { 27 | Self { base } 28 | } 29 | } 30 | 31 | #[RustycppInheritanceConstructors] 32 | impl AstTypePointerStructNode { 33 | pub fn new(base: QualType) -> Self { 34 | Self { 35 | base: ::new(base), 36 | parent: ::new(), 37 | } 38 | } 39 | } 40 | 41 | impl TypeAst for &AstTypePointerStructNode { 42 | fn getBaseType(&self) -> BaseType { 43 | BaseType { size: 8, align: 8 } 44 | } 45 | 46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 47 | self.base.fmt(f) 48 | } 49 | } 50 | 51 | impl Foldable for &AstTypePointerStructNode { 52 | fn foldNode(&self, node: &mut FoldingNode) { 53 | node.push(&self.base.base); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Ast/Type/Reference.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::{ 3 | Common::{AstTypeReference, AstTypeReferenceStructNode}, 4 | Type::{BaseType, QualType, TypeAst}, 5 | }, 6 | Base, Parent, 7 | Utils::FoldingContainer::{Foldable, FoldingNode, PushFoldingNode}, 8 | }; 9 | use deriveMacros::{CommonAst, RustycppInheritanceConstructors}; 10 | use std::fmt::Display; 11 | 12 | #[derive(CommonAst)] 13 | pub struct AstTypeReferenceStruct { 14 | #[AstChild] 15 | base: QualType, 16 | } 17 | 18 | impl Display for AstTypeReferenceStruct { 19 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 20 | write!(f, "{} *", self.base) 21 | } 22 | } 23 | 24 | impl AstTypeReferenceStruct { 25 | pub const fn new(base: QualType) -> Self { 26 | Self { base } 27 | } 28 | } 29 | 30 | #[RustycppInheritanceConstructors] 31 | impl AstTypeReferenceStructNode { 32 | pub fn new(base: QualType) -> Self { 33 | Self { 34 | base: ::new(base), 35 | parent: ::new(), 36 | } 37 | } 38 | } 39 | 40 | impl TypeAst for &AstTypeReferenceStructNode { 41 | fn getBaseType(&self) -> BaseType { 42 | BaseType { size: 8, align: 8 } 43 | } 44 | 45 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 46 | self.base.fmt(f) 47 | } 48 | } 49 | 50 | impl Foldable for &AstTypeReferenceStructNode { 51 | fn foldNode(&self, node: &mut FoldingNode) { 52 | node.push(&self.base.base); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Grammars.rs: -------------------------------------------------------------------------------- 1 | //! All the grammars used by the compiler 2 | pub mod DefineAst; 3 | pub mod DefineParser; 4 | pub mod MacroIntConstantExpressionAst; 5 | pub mod MacroIntConstantExpressionParser; 6 | -------------------------------------------------------------------------------- /src/Grammars/.antlr/macrointconstantexpressionast.interp: -------------------------------------------------------------------------------- 1 | token literal names: 2 | null 3 | null 4 | null 5 | null 6 | null 7 | null 8 | null 9 | null 10 | null 11 | null 12 | null 13 | null 14 | null 15 | null 16 | null 17 | null 18 | null 19 | null 20 | null 21 | null 22 | null 23 | null 24 | null 25 | null 26 | null 27 | null 28 | null 29 | null 30 | null 31 | null 32 | null 33 | null 34 | null 35 | null 36 | null 37 | null 38 | null 39 | 40 | token symbolic names: 41 | null 42 | Num 43 | LParen 44 | RParen 45 | Colon 46 | Question 47 | Tilde 48 | Exclamation 49 | Plus 50 | Minus 51 | Star 52 | Slash 53 | Percent 54 | Caret 55 | Ampersand 56 | Pipe 57 | DoubleEqual 58 | ExclamationEqual 59 | Less 60 | Greater 61 | LessEqual 62 | GreaterEqual 63 | Spaceship 64 | DoubleAmpersand 65 | DoublePipe 66 | DoubleLess 67 | DoubleGreater 68 | DoublePlus 69 | DoubleMinus 70 | Comma 71 | And 72 | Or 73 | Xor 74 | Not 75 | Bitand 76 | Bitor 77 | Compl 78 | 79 | rule names: 80 | exprRes 81 | expr 82 | 83 | 84 | atn: 85 | [3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 38, 72, 4, 2, 9, 2, 4, 3, 9, 3, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 3, 21, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 67, 10, 3, 12, 3, 14, 3, 70, 11, 3, 3, 3, 2, 3, 4, 4, 2, 4, 2, 14, 3, 2, 29, 30, 3, 2, 10, 11, 5, 2, 8, 9, 35, 35, 38, 38, 3, 2, 12, 14, 3, 2, 27, 28, 3, 2, 20, 23, 3, 2, 18, 19, 4, 2, 16, 16, 36, 36, 4, 2, 15, 15, 34, 34, 4, 2, 17, 17, 33, 33, 4, 2, 25, 25, 32, 32, 4, 2, 26, 26, 33, 33, 2, 87, 2, 6, 3, 2, 2, 2, 4, 20, 3, 2, 2, 2, 6, 7, 5, 4, 3, 2, 7, 3, 3, 2, 2, 2, 8, 9, 8, 3, 1, 2, 9, 10, 7, 4, 2, 2, 10, 11, 5, 4, 3, 2, 11, 12, 7, 5, 2, 2, 12, 21, 3, 2, 2, 2, 13, 14, 9, 2, 2, 2, 14, 21, 5, 4, 3, 19, 15, 16, 9, 3, 2, 2, 16, 21, 5, 4, 3, 18, 17, 18, 9, 4, 2, 2, 18, 21, 5, 4, 3, 17, 19, 21, 7, 3, 2, 2, 20, 8, 3, 2, 2, 2, 20, 13, 3, 2, 2, 2, 20, 15, 3, 2, 2, 2, 20, 17, 3, 2, 2, 2, 20, 19, 3, 2, 2, 2, 21, 68, 3, 2, 2, 2, 22, 23, 12, 16, 2, 2, 23, 24, 9, 5, 2, 2, 24, 67, 5, 4, 3, 17, 25, 26, 12, 15, 2, 2, 26, 27, 9, 3, 2, 2, 27, 67, 5, 4, 3, 16, 28, 29, 12, 14, 2, 2, 29, 30, 9, 6, 2, 2, 30, 67, 5, 4, 3, 15, 31, 32, 12, 13, 2, 2, 32, 33, 7, 24, 2, 2, 33, 67, 5, 4, 3, 14, 34, 35, 12, 12, 2, 2, 35, 36, 9, 7, 2, 2, 36, 67, 5, 4, 3, 13, 37, 38, 12, 11, 2, 2, 38, 39, 9, 8, 2, 2, 39, 67, 5, 4, 3, 12, 40, 41, 12, 10, 2, 2, 41, 42, 9, 9, 2, 2, 42, 67, 5, 4, 3, 11, 43, 44, 12, 9, 2, 2, 44, 45, 9, 10, 2, 2, 45, 67, 5, 4, 3, 10, 46, 47, 12, 8, 2, 2, 47, 48, 9, 11, 2, 2, 48, 67, 5, 4, 3, 9, 49, 50, 12, 7, 2, 2, 50, 51, 9, 12, 2, 2, 51, 67, 5, 4, 3, 8, 52, 53, 12, 6, 2, 2, 53, 54, 9, 13, 2, 2, 54, 67, 5, 4, 3, 7, 55, 56, 12, 5, 2, 2, 56, 57, 7, 7, 2, 2, 57, 58, 5, 4, 3, 2, 58, 59, 7, 6, 2, 2, 59, 60, 5, 4, 3, 5, 60, 67, 3, 2, 2, 2, 61, 62, 12, 4, 2, 2, 62, 63, 7, 31, 2, 2, 63, 67, 5, 4, 3, 5, 64, 65, 12, 20, 2, 2, 65, 67, 9, 2, 2, 2, 66, 22, 3, 2, 2, 2, 66, 25, 3, 2, 2, 2, 66, 28, 3, 2, 2, 2, 66, 31, 3, 2, 2, 2, 66, 34, 3, 2, 2, 2, 66, 37, 3, 2, 2, 2, 66, 40, 3, 2, 2, 2, 66, 43, 3, 2, 2, 2, 66, 46, 3, 2, 2, 2, 66, 49, 3, 2, 2, 2, 66, 52, 3, 2, 2, 2, 66, 55, 3, 2, 2, 2, 66, 61, 3, 2, 2, 2, 66, 64, 3, 2, 2, 2, 67, 70, 3, 2, 2, 2, 68, 66, 3, 2, 2, 2, 68, 69, 3, 2, 2, 2, 69, 5, 3, 2, 2, 2, 70, 68, 3, 2, 2, 2, 5, 20, 66, 68] -------------------------------------------------------------------------------- /src/Grammars/.antlr/macrointconstantexpressionast.tokens: -------------------------------------------------------------------------------- 1 | Num=1 2 | LParen=2 3 | RParen=3 4 | Colon=4 5 | Question=5 6 | Tilde=6 7 | Exclamation=7 8 | Plus=8 9 | Minus=9 10 | Star=10 11 | Slash=11 12 | Percent=12 13 | Caret=13 14 | Ampersand=14 15 | Pipe=15 16 | DoubleEqual=16 17 | ExclamationEqual=17 18 | Less=18 19 | Greater=19 20 | LessEqual=20 21 | GreaterEqual=21 22 | Spaceship=22 23 | DoubleAmpersand=23 24 | DoublePipe=24 25 | DoubleLess=25 26 | DoubleGreater=26 27 | DoublePlus=27 28 | DoubleMinus=28 29 | Comma=29 30 | And=30 31 | Or=31 32 | Xor=32 33 | Not=33 34 | Bitand=34 35 | Bitor=35 36 | Compl=36 37 | -------------------------------------------------------------------------------- /src/Grammars/DefineAst.rs: -------------------------------------------------------------------------------- 1 | //! Related classes used during the parsing of a #define expression 2 | 3 | use std::collections::VecDeque; 4 | use std::fmt::Debug; 5 | use std::marker::Send; 6 | 7 | use crate::Preprocessor::Pretoken::PreToken; 8 | use crate::Preprocessor::Structs::ExpandData; 9 | use crate::Utils::Structs::CompileMsg; 10 | 11 | use crate::Utils::Structs::FileTokPos; 12 | 13 | #[derive(Debug, Clone)] 14 | /// Is the macro variadic? Supports named variadics. 15 | pub enum IsVariadic { 16 | /// The macro is variadic, with a custom name if String is not empty 17 | True(String), 18 | /// The macro is not variadic 19 | False, 20 | } 21 | 22 | impl PartialEq for IsVariadic { 23 | fn eq(&self, other: &Self) -> bool { 24 | matches!( 25 | (self, other), 26 | (Self::False, Self::False) | (Self::True(_), Self::True(_)) 27 | ) 28 | } 29 | } 30 | 31 | #[derive(Debug, Clone)] 32 | /// Before the macro definition is parsed, some tokens need to be distinguished 33 | pub enum PreTokenDefinePreParse { 34 | /// Normal token 35 | Normal(PreToken), 36 | /// A token that is one of the arguments of the macro 37 | Arg(String), 38 | /// # operator 39 | Hash, 40 | /// ## operator 41 | HashHash, 42 | /// __VA_ARG__ 43 | VariadicArg, 44 | /// __VA_OPT__ 45 | VariadicOpt, 46 | /// Left paren of a previous __VA_OPT__ 47 | VariadicOptParenL, 48 | /// Right paren of a previous __VA_OPT__ 49 | VariadicOptParenR, 50 | } 51 | 52 | #[derive(Debug, Clone)] 53 | /// Resulting AST of the macro definition. The definition will be a list of these tokens 54 | pub enum PreTokenDefine { 55 | /// Normal token 56 | Normal(FileTokPos), 57 | /// Argument of the macro 58 | Arg(FileTokPos), 59 | /// Variadic argument of the macro 60 | VariadicArg(FileTokPos<()>), 61 | /// A # operator. It contains the tokens it will stringify 62 | Hash(FileTokPos<()>, Vec), 63 | /// A ## operator. It contains the tokens it will concatenate 64 | HashHash(FileTokPos<()>, Vec, Vec), 65 | /// A __VA_OPT__ operator. It contains the tokens It will place if the variadic argument is not empty 66 | VariadicOpt(FileTokPos<()>, Vec), 67 | } 68 | 69 | #[doc(hidden)] 70 | type DefineExpansionFunc = 71 | dyn Fn(&ExpandData) -> Result>, CompileMsg>; 72 | #[derive(Clone)] 73 | /// A macro definition, with all the needed data 74 | pub struct DefineAst { 75 | /// Name of the macro 76 | pub id: String, 77 | /// Parmeters of the macro, if any 78 | pub param: Option>, 79 | /// Whether the macro is variadic 80 | pub variadic: IsVariadic, 81 | /// The parsed replacement list, used in macro subsitution 82 | pub replacement: Vec, 83 | /// Function used for expansion. Intended to aid in the implementation of custom macros 84 | pub expandFunc: &'static DefineExpansionFunc, 85 | } 86 | 87 | impl Debug for DefineAst { 88 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 89 | f.debug_struct("DefineAst") 90 | .field("id", &self.id) 91 | .field("param", &self.param) 92 | .field("variadic", &self.variadic) 93 | .field("replacement", &self.replacement) 94 | .finish_non_exhaustive() 95 | } 96 | } 97 | unsafe impl Send for DefineAst {} 98 | -------------------------------------------------------------------------------- /src/Grammars/MacroIntConstantExpressionAst.rs: -------------------------------------------------------------------------------- 1 | //! Evaluator of the macro integer constant expression ast. 2 | use std::fmt::Debug; 3 | 4 | #[derive(Debug, Clone)] 5 | #[doc(hidden)] 6 | pub enum PreTokenIf { 7 | Num(i128), 8 | LParen, 9 | RParen, 10 | Colon, 11 | Question, 12 | Tilde, 13 | Exclamation, 14 | Plus, 15 | Minus, 16 | Star, 17 | Slash, 18 | Percent, 19 | Caret, 20 | Ampersand, 21 | Pipe, 22 | DoubleEqual, 23 | ExclamationEqual, 24 | Less, 25 | Greater, 26 | LessEqual, 27 | GreaterEqual, 28 | Spaceship, 29 | DoubleAmpersand, 30 | DoublePipe, 31 | DoubleLess, 32 | DoubleGreater, 33 | DoublePlus, 34 | DoubleMinus, 35 | Comma, 36 | And, 37 | Or, 38 | Xor, 39 | Not, 40 | Bitand, 41 | Bitor, 42 | Compl, 43 | } 44 | 45 | impl std::fmt::Display for PreTokenIf { 46 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 47 | match self { 48 | Self::Num(n) => write!(f, "{n}"), 49 | Self::LParen => write!(f, "("), 50 | Self::RParen => write!(f, ")"), 51 | Self::Colon => write!(f, ":"), 52 | Self::Question => write!(f, "?"), 53 | Self::Tilde => write!(f, "~"), 54 | Self::Exclamation => write!(f, "!"), 55 | Self::Plus => write!(f, "+"), 56 | Self::Minus => write!(f, "-"), 57 | Self::Star => write!(f, "*"), 58 | Self::Slash => write!(f, "/"), 59 | Self::Percent => write!(f, "%"), 60 | Self::Caret => write!(f, "^"), 61 | Self::Ampersand => write!(f, "&"), 62 | Self::Pipe => write!(f, "|"), 63 | Self::DoubleEqual => write!(f, "=="), 64 | Self::ExclamationEqual => write!(f, "!="), 65 | Self::Less => write!(f, "<"), 66 | Self::Greater => write!(f, ">"), 67 | Self::LessEqual => write!(f, "<="), 68 | Self::GreaterEqual => write!(f, ">="), 69 | Self::Spaceship => write!(f, "<=>"), 70 | Self::DoubleAmpersand => write!(f, "&&"), 71 | Self::DoublePipe => write!(f, "||"), 72 | Self::DoubleLess => write!(f, "<<"), 73 | Self::DoubleGreater => write!(f, ">>"), 74 | Self::DoublePlus => write!(f, "++"), 75 | Self::DoubleMinus => write!(f, "--"), 76 | Self::Comma => write!(f, ","), 77 | Self::And => write!(f, "and"), 78 | Self::Or => write!(f, "or"), 79 | Self::Xor => write!(f, "xor"), 80 | Self::Not => write!(f, "not"), 81 | Self::Bitand => write!(f, "bitand"), 82 | Self::Bitor => write!(f, "bitor"), 83 | Self::Compl => write!(f, "compl"), 84 | } 85 | } 86 | } 87 | 88 | impl PreTokenIf { 89 | #[doc(hidden)] 90 | pub fn stringToPreTokenIfOperator(s: &str) -> Self { 91 | match s { 92 | r"(" => Self::LParen, 93 | r")" => Self::RParen, 94 | r":" => Self::Colon, 95 | r"?" => Self::Question, 96 | r"~" => Self::Tilde, 97 | r"!" => Self::Exclamation, 98 | r"+" => Self::Plus, 99 | r"-" => Self::Minus, 100 | r"*" => Self::Star, 101 | r"/" => Self::Slash, 102 | r"%" => Self::Percent, 103 | r"^" => Self::Caret, 104 | r"&" => Self::Ampersand, 105 | r"|" => Self::Pipe, 106 | r"==" => Self::DoubleEqual, 107 | r"!=" => Self::ExclamationEqual, 108 | r"<" => Self::Less, 109 | r">" => Self::Greater, 110 | r"<=" => Self::LessEqual, 111 | r">=" => Self::GreaterEqual, 112 | r"<=>" => Self::Spaceship, 113 | r"&&" => Self::DoubleAmpersand, 114 | r"||" => Self::DoublePipe, 115 | r"<<" => Self::DoubleLess, 116 | r">>" => Self::DoubleGreater, 117 | r"++" => Self::DoublePlus, 118 | r"--" => Self::DoubleMinus, 119 | r"," => Self::Comma, 120 | r"and" => Self::And, 121 | r"or" => Self::Or, 122 | r"xor" => Self::Xor, 123 | r"not" => Self::Not, 124 | r"bitand" => Self::Bitand, 125 | r"bitor" => Self::Bitor, 126 | r"compl" => Self::Compl, 127 | _ => unreachable!(), 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Lex.rs: -------------------------------------------------------------------------------- 1 | //! Responsible for passing from `PreTokens` to Tokens. 2 | 3 | pub mod Lexer; 4 | pub mod Token; 5 | -------------------------------------------------------------------------------- /src/ModuleTree.rs: -------------------------------------------------------------------------------- 1 | //! Finds the module related operations, translates them to custom tokens, and 2 | //! generates the module dependency tree. 3 | mod DependencyAnnotate; 4 | mod DependencyDfs; 5 | mod DependencyInterpreter; 6 | pub mod DependencyIterator; 7 | pub mod DependencyParser; 8 | pub mod Generate; 9 | pub mod Structs; 10 | -------------------------------------------------------------------------------- /src/ModuleTree/DependencyAnnotate.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{Compiler::TranslationUnit, Utils::StateCompileUnit::StateCompileUnit}; 4 | 5 | use super::Structs::{self, Node}; 6 | 7 | pub fn annotateTuWithKind( 8 | res: HashMap, 9 | compileUnits: &HashMap, 10 | ) -> HashMap { 11 | for node in res.values() { 12 | *compileUnits 13 | .get(&node.module.1) 14 | .unwrap() 15 | .moduleKind 16 | .lock() 17 | .unwrap() = node.clone(); 18 | } 19 | res 20 | } 21 | -------------------------------------------------------------------------------- /src/ModuleTree/DependencyDfs.rs: -------------------------------------------------------------------------------- 1 | //! Create the dependency tree from the translation units 2 | 3 | use std::collections::{HashMap, HashSet}; 4 | use std::vec; 5 | 6 | use crate::Utils::Structs::{CompileError, CompileMsg, CompileMsgImpl}; 7 | 8 | use super::Structs::{ModuleDeclaration, ModuleTree, Node}; 9 | 10 | /// Check for loops in the dependency graph. 11 | fn dfsLoops(tree: &mut ModuleTree) -> Result<(), Vec> { 12 | if tree.roots.is_empty() && !tree.childModules.is_empty() { 13 | let mut showALoopTree = tree.clone(); 14 | let candidate = *showALoopTree.childModules.keys().next().unwrap(); 15 | let candidate = showALoopTree 16 | .childModules 17 | .get_key_value(&candidate) 18 | .unwrap(); 19 | showALoopTree 20 | .roots 21 | .insert(*candidate.0, candidate.1.clone()); 22 | dfsLoops(&mut showALoopTree)?; 23 | unreachable!(); 24 | } 25 | 26 | let mut err = vec![]; 27 | let mut visited = HashSet::new(); 28 | let mut stack = vec![]; 29 | let mut leftToVisit = vec![]; 30 | 31 | #[allow(clippy::needless_collect)] /*Obscure borrow checker error otherwise*/ 32 | for root in tree 33 | .roots 34 | .iter() 35 | .map(|x| (x.1.module.clone(), x.1.dependedBy.clone())) 36 | .collect::>() 37 | { 38 | stack.push(root.0 .0); 39 | leftToVisit.push(root.1.clone()); 40 | visited.insert(root.0 .0); 41 | 42 | loop { 43 | if leftToVisit.is_empty() { 44 | break; 45 | } 46 | 47 | if leftToVisit.last().unwrap().is_empty() { 48 | visited.remove(stack.last().unwrap()); 49 | leftToVisit.pop(); 50 | let currStackDecl = stack.last().unwrap(); 51 | let maxChild = tree 52 | .childModules 53 | .get(currStackDecl) 54 | .unwrap_or_else(|| tree.roots.get(currStackDecl).unwrap()) 55 | .dependedBy 56 | .iter() 57 | .map(|x| tree.childModules.get(&x.0).unwrap().depth) 58 | .max(); 59 | let currStack = tree 60 | .childModules 61 | .get_mut(currStackDecl) 62 | .unwrap_or_else(|| tree.roots.get_mut(currStackDecl).unwrap()); 63 | currStack.depth = maxChild.unwrap_or(0) + 1; 64 | stack.pop(); 65 | continue; 66 | } 67 | 68 | let next = tree 69 | .childModules 70 | .get_mut(&leftToVisit.last_mut().unwrap().pop().unwrap().0) 71 | .unwrap(); 72 | 73 | if visited.contains(&next.module.0) { 74 | err.push(CompileError::onFile( 75 | format!( 76 | "Loop detected: stack reached: {:?}, which also depends on {}", 77 | &stack.iter(), 78 | &next.module.0 79 | ), 80 | next.module.1, 81 | )); 82 | continue; 83 | } 84 | 85 | stack.push(next.module.0); 86 | leftToVisit.push(next.dependedBy.clone()); 87 | visited.insert(next.module.0); 88 | } 89 | } 90 | 91 | if err.is_empty() { 92 | Ok(()) 93 | } else { 94 | Err(err) 95 | } 96 | } 97 | 98 | /// Generates the module tree of the modules, and checks for loops. 99 | pub fn generateModuleTree( 100 | nodes: HashMap, 101 | ) -> Result> { 102 | let mut tree = ModuleTree { 103 | roots: HashMap::new(), 104 | childModules: HashMap::new(), 105 | }; 106 | 107 | for node in nodes { 108 | if node.1.dependsOn.is_empty() { 109 | tree.roots.insert(node.0, node.1); 110 | } else { 111 | tree.childModules.insert(node.0, node.1); 112 | } 113 | } 114 | 115 | dfsLoops(&mut tree)?; 116 | Ok(tree) 117 | } 118 | -------------------------------------------------------------------------------- /src/ModuleTree/DependencyIterator.rs: -------------------------------------------------------------------------------- 1 | //! Iterates over a dependency tree as smartly as possible to reduce bottlenecks. 2 | use lazy_static::lazy_static; 3 | use std::collections::HashMap; 4 | use std::sync::atomic::Ordering; 5 | use std::sync::{Condvar, Mutex}; 6 | 7 | use priority_queue::PriorityQueue; 8 | 9 | use crate::Compiler::TranslationUnit; 10 | 11 | use super::Structs::{ModuleDeclaration, ModuleTree, Node}; 12 | 13 | /// Mutex-protected internal state, as `DependencyIterator` will be accessible 14 | /// from multiple threads. 15 | struct Data { 16 | /// Roots that may not have completed their min stage required. 17 | rootsNotReady: Vec, 18 | /// Roots that have completed their min stage required. 19 | rootsReady: PriorityQueue, 20 | /// Roots that have been sent to the consumer, but the consumer has not 21 | /// notified it is done with it. 22 | rootsSentButNotDone: HashMap, 23 | /// Child nodes that are not ready to be used yet. 24 | childModules: HashMap, 25 | /// Minimum stage completed required before sending a TU. 26 | minStageCompleted: usize, 27 | /// The total number of TU. Used for the heuristic of what to send. 28 | totalNumModules: usize, 29 | } 30 | 31 | /// Iterator over the dependency tree, with priorization based on depth and stage completed. 32 | pub struct DependencyIterator { 33 | /// Condvar for waiting for new roots once all roots have been sent. 34 | waitForNewRoots: Condvar, 35 | /// Mutex-protected internal state. 36 | d: Mutex, 37 | } 38 | 39 | lazy_static! { 40 | static ref ALL_DEPENDENCY_ITERATORS_WAIT: Condvar = Condvar::new(); 41 | static ref ALL_DEPENDENCY_ITERATORS_MUTEX: Mutex<()> = Mutex::new(()); 42 | } 43 | 44 | impl DependencyIterator { 45 | /// Create a new `DependencyIterator` from a `ModuleTree`. 46 | pub fn new(dependencyTree: &ModuleTree, minStageCompleted: usize) -> Self { 47 | Self { 48 | waitForNewRoots: Condvar::new(), 49 | d: Mutex::new(Data { 50 | rootsNotReady: dependencyTree.roots.values().cloned().collect(), 51 | rootsReady: PriorityQueue::new(), 52 | rootsSentButNotDone: HashMap::new(), 53 | childModules: dependencyTree.childModules.clone(), 54 | minStageCompleted, 55 | totalNumModules: dependencyTree.roots.len() + dependencyTree.childModules.len(), 56 | }), 57 | } 58 | } 59 | 60 | /// Check if any new TU have been updated to be ready to be sent. Checks for the stage completed. 61 | fn updateReadies(d: &mut Data) { 62 | let mut stillNotReady = Vec::new(); 63 | for root in d.rootsNotReady.drain(..) { 64 | let stepsCompleted = root.stepsCompleted.load(Ordering::Relaxed); 65 | if stepsCompleted >= d.minStageCompleted { 66 | let idx = root.dependedBy.len() * d.totalNumModules + root.depth; 67 | d.rootsReady.push(root, idx); 68 | } else { 69 | stillNotReady.push(root); 70 | } 71 | } 72 | d.rootsNotReady = stillNotReady; 73 | } 74 | 75 | /// Mark a sent TU as done, allowing its childs to be used. Updates the stage completed. 76 | pub fn markDone(&self, translationUnit: TranslationUnit, newCompletionState: usize) { 77 | { 78 | let mut d = self.d.lock().unwrap(); 79 | if let Some(root) = d.rootsSentButNotDone.remove(&translationUnit) { 80 | root.stepsCompleted 81 | .fetch_max(newCompletionState, Ordering::Relaxed); 82 | for child in root.dependedBy { 83 | if let Some(childNode) = d.childModules.get_mut(&child.0) { 84 | childNode.dependsOn.remove(&root.module); 85 | if childNode.dependsOn.is_empty() { 86 | let childNode = childNode.clone(); 87 | d.childModules.remove(&child.0); 88 | d.rootsNotReady.push(childNode); 89 | } 90 | } else { 91 | unreachable!("A child module was not found?"); 92 | } 93 | } 94 | } else { 95 | unreachable!("You marked as done a TU that was not sent!"); 96 | } 97 | } 98 | Self::updateReadies(&mut self.d.lock().unwrap()); 99 | self.waitForNewRoots.notify_one(); 100 | ALL_DEPENDENCY_ITERATORS_WAIT.notify_all(); 101 | } 102 | 103 | /// [DEBUGGING PURPOSES] Checks if calling next without marking anything as done would lock the iterator 104 | pub fn wouldLockIfNext(&self) -> bool { 105 | let d = self.d.lock().unwrap(); 106 | d.rootsNotReady.is_empty() && d.rootsReady.is_empty() && !d.childModules.is_empty() 107 | } 108 | 109 | /// Get next TU. Do note that this does not implement the trait `Iterator`. 110 | /// This is because we don't want to require self to be mutable. 111 | pub fn next(&self) -> Option { 112 | let d = self 113 | .waitForNewRoots 114 | .wait_while(self.d.lock().unwrap(), |d| { 115 | d.rootsNotReady.is_empty() 116 | && d.rootsReady.is_empty() 117 | && !d.rootsSentButNotDone.is_empty() 118 | && !d.childModules.is_empty() 119 | }) 120 | .unwrap(); 121 | 122 | if d.childModules.is_empty() && d.rootsNotReady.is_empty() && d.rootsReady.is_empty() { 123 | return None; 124 | } 125 | 126 | if d.rootsNotReady.is_empty() 127 | && d.rootsReady.is_empty() 128 | && d.rootsSentButNotDone.is_empty() 129 | && !d.childModules.is_empty() 130 | { 131 | panic!( 132 | "Internal error: Invalid state: no more roots available and no way to get more!" 133 | ); 134 | } 135 | 136 | let mut d = ALL_DEPENDENCY_ITERATORS_WAIT 137 | .wait_while(d, |d| { 138 | Self::updateReadies(d); 139 | d.rootsReady.is_empty() 140 | }) 141 | .unwrap(); 142 | 143 | let toSend = d.rootsReady.pop().unwrap().0; 144 | d.rootsSentButNotDone 145 | .insert(toSend.module.1, toSend.clone()); 146 | drop(d); 147 | Some(toSend.module.1) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/ModuleTree/DependencyParser.rs: -------------------------------------------------------------------------------- 1 | //! Parse the files to search for dependency instructions. 2 | use lazy_regex::regex_is_match; 3 | 4 | use crate::fileTokPosMatchArm; 5 | use crate::Utils::Structs::{CompileError, CompileMsg, CompileMsgImpl, TokPos}; 6 | use crate::{Compiler::TranslationUnit, Lex::Token::Token, Utils::Structs::FileTokPos}; 7 | 8 | use super::Structs::ModuleOperator; 9 | 10 | /// When encountering a module operator, validates it can be used and parses it. 11 | fn parseModuleOp( 12 | translationUnit: TranslationUnit, 13 | tokens: &[FileTokPos], 14 | pos: usize, 15 | ) -> Result, CompileMsg> { 16 | let mut at = usize::MAX; 17 | let mut atEnd = usize::MIN; 18 | let mut name = String::new(); 19 | for tok in tokens.iter().skip(pos) { 20 | match tok.tokPos.tok { 21 | Token::Private => { 22 | name.push_str("private"); 23 | } 24 | Token::Identifier(string) => { 25 | name.push_str(string.as_ref()); 26 | } 27 | Token::Colon => { 28 | name.push(':'); 29 | } 30 | Token::Dot => { 31 | name.push('.'); 32 | } 33 | _ => { 34 | break; 35 | } 36 | } 37 | at = at.min(tok.tokPos.start); 38 | atEnd = atEnd.max(tok.tokPos.end); 39 | } 40 | 41 | if !regex_is_match!( 42 | r"(:?[\w\d_]+\.)*[\w\d_]+(:?:(:?[\w\d_]+\.)*[\w\d_]+)?", 43 | &name 44 | ) && !name.is_empty() 45 | && name != ":private" 46 | { 47 | return Err(CompileError::fromAt( 48 | format!("The module name \"{name}\" is invalid!"), 49 | translationUnit, 50 | at, 51 | Some(atEnd), 52 | )); 53 | } 54 | Ok(Some(ModuleOperator::Module(name))) 55 | } 56 | 57 | /// When encountering an import operator, validates it can be used and parses it. 58 | fn parseImportOp( 59 | translationUnit: TranslationUnit, 60 | tokens: &[FileTokPos], 61 | pos: usize, 62 | ) -> Result, CompileMsg> { 63 | let mut at = usize::MAX; 64 | let mut atEnd = usize::MIN; 65 | 66 | let mut name = String::new(); 67 | for tok in tokens.iter().skip(pos) { 68 | match tok.tokPos.tok { 69 | Token::ImportableHeaderName(header) => { 70 | return Ok(Some(ModuleOperator::ImportHeader(header))); 71 | } 72 | Token::Identifier(string) => { 73 | name.push_str(string.as_ref()); 74 | } 75 | Token::Colon => { 76 | name.push(':'); 77 | } 78 | Token::Dot => { 79 | name.push('.'); 80 | } 81 | _ => { 82 | break; 83 | } 84 | } 85 | at = at.min(tok.tokPos.start); 86 | atEnd = atEnd.max(tok.tokPos.end); 87 | } 88 | if !regex_is_match!( 89 | r"(:?(:?[\w\d_]+\.)*[\w\d_]+|:(:?[\w\d_]+\.)*[\w\d_]+)", 90 | &name 91 | ) { 92 | return Err(CompileError::fromAt( 93 | format!("The import name \"{name}\" is invalid!"), 94 | translationUnit, 95 | at, 96 | Some(atEnd), 97 | )); 98 | } 99 | Ok(Some(ModuleOperator::Import(name))) 100 | } 101 | 102 | /// When encountering an export operator, validates it can be used and parses it. 103 | fn parseExportOp( 104 | translationUnit: TranslationUnit, 105 | tokens: &[FileTokPos], 106 | pos: usize, 107 | ) -> Result, CompileMsg> { 108 | if let Some(fileTokPosMatchArm!(tok)) = tokens.get(pos) { 109 | return match tok { 110 | Token::Import => parseImportOp(translationUnit, tokens, pos + 1), 111 | Token::Module => parseModuleOp(translationUnit, tokens, pos + 1).map(|x| { 112 | x.map(|op| { 113 | if let ModuleOperator::Module(module) = op { 114 | ModuleOperator::ExportModule(module) 115 | } else { 116 | op 117 | } 118 | }) 119 | }), 120 | _ => Ok(None), 121 | }; 122 | } 123 | Ok(None) 124 | } 125 | 126 | /// Extract the module, export, import operations only rellevant for dependency scanning of a single file 127 | pub fn parseModuleMacroOp( 128 | translationUnit: TranslationUnit, 129 | tokens: &[FileTokPos], 130 | positions: Vec, 131 | ) -> Result, Vec> { 132 | let mut err = vec![]; 133 | let mut res = vec![]; 134 | 135 | for pos in positions { 136 | if pos > 0 && tokens[pos - 1].tokPos.tok == Token::Export { 137 | match parseExportOp(translationUnit, tokens, pos) { 138 | Ok(None) => continue, 139 | Ok(Some(v)) => { 140 | res.push(v); 141 | } 142 | Err(newErr) => err.push(newErr), 143 | }; 144 | } else { 145 | match tokens[pos].tokPos.tok { 146 | Token::Module => { 147 | match parseModuleOp(translationUnit, tokens, pos + 1) { 148 | Ok(None) => continue, 149 | Ok(Some(v)) => { 150 | res.push(v); 151 | } 152 | Err(newErr) => err.push(newErr), 153 | }; 154 | } 155 | Token::Import => { 156 | match parseImportOp(translationUnit, tokens, pos + 1) { 157 | Ok(None) => continue, 158 | Ok(Some(v)) => { 159 | res.push(v); 160 | } 161 | Err(newErr) => err.push(newErr), 162 | }; 163 | } 164 | _ => { 165 | unreachable!() 166 | } 167 | } 168 | } 169 | } 170 | 171 | if err.is_empty() { 172 | Ok(res) 173 | } else { 174 | Err(err) 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/ModuleTree/Generate.rs: -------------------------------------------------------------------------------- 1 | //! Wrapper 2 | use crate::Utils::CompilerState::CompilerState; 3 | use crate::Utils::Structs::CompileMsg; 4 | 5 | use super::DependencyAnnotate::annotateTuWithKind; 6 | use super::DependencyDfs::generateModuleTree; 7 | use super::DependencyInterpreter::generateNodes; 8 | use super::Structs::ModuleTree; 9 | 10 | /// Wrapper over all the functionality of the module tree generation. 11 | pub fn generateDependencyTree( 12 | compilerState: &CompilerState, 13 | ) -> Result> { 14 | let mut it = compilerState.compileUnits.iter().map(|(tu, state)| { 15 | let operations = state.moduleOperations.lock().unwrap().take().unwrap(); 16 | let isModuleHeaderFile = compilerState.moduleHeaderUnitsFiles.contains(tu); 17 | (*tu, operations, isModuleHeaderFile) 18 | }); 19 | 20 | generateNodes(&mut it) 21 | .map(|x| annotateTuWithKind(x, &compilerState.compileUnits)) 22 | .and_then(generateModuleTree) 23 | } 24 | -------------------------------------------------------------------------------- /src/ModuleTree/Structs.rs: -------------------------------------------------------------------------------- 1 | //! General structs used by this mod group. 2 | use std::collections::{HashMap, HashSet}; 3 | use std::fmt::Display; 4 | use std::sync::atomic::AtomicUsize; 5 | use std::sync::Arc; 6 | 7 | use crate::Compiler::TranslationUnit; 8 | use crate::Utils::StringRef::StringRef; 9 | 10 | /// Kind of module the TU is of. This also includes ones where the TU does not 11 | /// use modules, like a generated one (import
) or a classical .cpp file 12 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] 13 | pub enum ModuleDeclaration { 14 | /// Holds module name 15 | ExportPrimary(StringRef), 16 | /// Holds module name 17 | Primary(StringRef), 18 | /// Holds module name + partition 19 | ExportPartition(StringRef, StringRef), 20 | /// Holds module name + partition 21 | Partition(StringRef, StringRef), 22 | /// Holds resolved path 23 | ModuleHeaderUnit(u64), 24 | /// Holds resolved path 25 | Global(u64), 26 | } 27 | 28 | impl Display for ModuleDeclaration { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | match self { 31 | Self::ExportPrimary(module) => format!("export module {module}").fmt(f), 32 | Self::Primary(module) => format!("module {module}").fmt(f), 33 | Self::ExportPartition(module, part) => format!("export module {module}:{part}").fmt(f), 34 | Self::Partition(module, part) => format!("module {module}:{part}").fmt(f), 35 | Self::ModuleHeaderUnit(path) => format!("<{path}>").fmt(f), 36 | Self::Global(path) => format!("Global module file {path}").fmt(f), 37 | } 38 | } 39 | } 40 | 41 | /// Rellevant module operators. These ony include the rellevant ones for dependency scanning! 42 | #[derive(Debug, Clone)] 43 | pub enum ModuleOperator { 44 | /// an import directive. 45 | Import(String), 46 | /// an import
directive. 47 | ImportHeader(u64), 48 | /// an export module directive. 49 | ExportModule(String), 50 | /// a module directive. 51 | Module(String), 52 | } 53 | 54 | /// A node holds all the relevant dependency information of a TU. 55 | #[derive(Debug, Clone)] 56 | pub struct Node { 57 | /// The module of the TU, if any 58 | pub module: Arc<(ModuleDeclaration, TranslationUnit)>, 59 | /// The TU that depend on this node 60 | pub dependedBy: Vec>, 61 | /// The TU that this node depends on 62 | pub dependsOn: HashSet>, 63 | /// How deep is the node in the tree. The way this is calculated is the 64 | /// inverse from the roots: 65 | /// 66 | /// In this diagram, the letters represent modules, and the arrows go 67 | /// downwards, indicating that the upper module is depended by the lower one 68 | /// 69 | /// a b c 70 | /// \ / | 71 | /// d / 72 | /// ---\ / 73 | /// e 74 | /// In this diagram, a and b have a depth of 2, while c has a depth of 1. 75 | pub depth: usize, 76 | /// How many steps of the compilation have been completed? Can be used to 77 | /// start multiple stages of the compilation at the same time 78 | pub stepsCompleted: Arc, 79 | } 80 | 81 | impl Node { 82 | pub fn new_fake() -> Self { 83 | Self { 84 | module: Arc::new((ModuleDeclaration::Global(0), 0)), 85 | dependedBy: vec![], 86 | dependsOn: HashSet::new(), 87 | depth: 0, 88 | stepsCompleted: Arc::default(), 89 | } 90 | } 91 | } 92 | 93 | impl Eq for Node {} 94 | impl PartialEq for Node { 95 | fn eq(&self, other: &Self) -> bool { 96 | self.module == other.module 97 | } 98 | } 99 | 100 | impl std::hash::Hash for Node { 101 | fn hash(&self, state: &mut H) { 102 | self.module.hash(state); 103 | } 104 | } 105 | 106 | /// Holds the resulting module tree. The `DependencyIterator` uses it to output a TU at a time. 107 | #[derive(Debug, Clone)] 108 | pub struct ModuleTree { 109 | /// The root of the ready to compile modules 110 | pub roots: HashMap, 111 | /// The list of all modules 112 | pub childModules: HashMap, 113 | } 114 | -------------------------------------------------------------------------------- /src/Parse.rs: -------------------------------------------------------------------------------- 1 | pub mod BufferedLexer; 2 | pub mod Parser; 3 | -------------------------------------------------------------------------------- /src/Parse/Parser.rs: -------------------------------------------------------------------------------- 1 | use strum::IntoEnumIterator; 2 | 3 | use crate::{ 4 | Ast::{ 5 | Common::{AstTu, CommonAst}, 6 | Type::Builtin::BuiltinTypeKind, 7 | }, 8 | Compiler::TranslationUnit, 9 | Lex::Token::Token, 10 | Sema::AstContext::AstContext, 11 | Utils::{ 12 | CompilerState::CompilerState, 13 | Structs::{CompileMsg, FileTokPos}, 14 | }, 15 | }; 16 | 17 | use super::BufferedLexer::{BufferedLexer, StateBufferedLexer}; 18 | 19 | mod ParserParse; 20 | mod ParserSema; 21 | 22 | #[derive(Clone, Copy, PartialEq, Eq)] 23 | pub enum ModuleImportState { 24 | /// Parsing the first decl in a TU. 25 | StartFile, 26 | /// after 'module;' but before 'module X;'. 27 | GlobalSection, 28 | /// after 'module X;' but before any non-import decl. 29 | ImportSection, 30 | /// after any non-import decl. 31 | CodeSection, 32 | /// after 'module :private;'. 33 | PrivateSection, 34 | /// Not a C++20 TU, or an invalid state was found. 35 | GlobalFile, 36 | } 37 | 38 | pub struct Parser { 39 | lexer: BufferedLexer, 40 | lexerStart: StateBufferedLexer, 41 | filePath: TranslationUnit, 42 | compilerState: CompilerState, 43 | 44 | moduleImportState: ModuleImportState, 45 | 46 | errors: Vec, 47 | astContext: AstContext, 48 | } 49 | 50 | impl Parser { 51 | pub fn new( 52 | tokens: Vec>, 53 | filePath: TranslationUnit, 54 | compilerState: CompilerState, 55 | ) -> Self { 56 | let (lexer, lexerStart) = BufferedLexer::new(tokens); 57 | Self { 58 | lexer, 59 | lexerStart, 60 | filePath, 61 | compilerState, 62 | moduleImportState: ModuleImportState::StartFile, 63 | errors: vec![], 64 | astContext: AstContext::new(), 65 | } 66 | } 67 | 68 | fn initBuiltinTypes(&mut self) { 69 | for ty in BuiltinTypeKind::iter() { 70 | self.astContext.typeDict.addBuiltinType(ty); 71 | } 72 | } 73 | 74 | pub fn parse(&mut self) -> (AstTu, Vec) { 75 | self.initBuiltinTypes(); 76 | let tu = self.parseTu(); 77 | (tu, self.errors.clone()) 78 | } 79 | 80 | pub fn printStringTree(ast: AstTu) -> String { 81 | ast.getDebugNode().to_string() 82 | } 83 | 84 | pub fn lexer(&mut self) -> &mut BufferedLexer { 85 | &mut self.lexer 86 | } 87 | 88 | pub fn alloc(&self) -> &'static bumpalo::Bump { 89 | self.astContext.alloc.alloc() 90 | } 91 | 92 | pub fn addError(&mut self, msg: CompileMsg) { 93 | self.errors.push(msg); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse.rs: -------------------------------------------------------------------------------- 1 | macro_rules! m { 2 | ($id:ident) => { 3 | mod $id; 4 | pub use $id::*; 5 | }; 6 | } 7 | 8 | m! {ParseAttribute} 9 | m! {ParseDeclaration} 10 | m! {ParseMiscUtils} 11 | m! {ParseNestedNameSpecifier} 12 | m! {ParseTu} 13 | 14 | /** 15 | * Used in some parse rules to indicate if it successfully matched the rule. 16 | */ 17 | pub enum ParseMatched { 18 | /// The macro was matched, and the tokens were consumed. Might return None (or equivalent) in error cases. 19 | Matched, 20 | /// The macro was not matched, and the token was not consumed. 21 | NotMatched, 22 | } 23 | 24 | impl ParseMatched { 25 | const fn matched(&self) -> bool { 26 | match self { 27 | Self::Matched => true, 28 | Self::NotMatched => false, 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseAttribute/RustyCppCheckSymbolMatchTag.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::{AstAttributeCXX, AstAttributeCXXRustyCppCheckSymbolMatchTag}; 2 | use crate::{ 3 | Lex::Token::Token, 4 | Parse::BufferedLexer::StateBufferedLexer, 5 | Utils::Structs::{CompileError, CompileMsgImpl, FileTokPos}, 6 | }; 7 | 8 | use super::super::super::Parser; 9 | 10 | impl Parser { 11 | #[allow(clippy::unnecessary_wraps)] 12 | #[allow(clippy::unused_self)] 13 | pub fn parseRustyCppCheckSymbolMatchTag( 14 | &mut self, 15 | name: &FileTokPos, 16 | contents: Option, 17 | ) -> Option { 18 | let lexpos = &mut contents.unwrap(); 19 | let Some(number) = self 20 | .lexer() 21 | .getConsumeTokenIf(lexpos, |t| matches!(t, Token::BoolLiteral(_) | Token::IntegerLiteral(_, _))) else { 22 | self.errors.push(CompileError::fromPreTo( 23 | "This attribute expects an integer/bool literal and an identifier as parameters.", 24 | name, 25 | )); 26 | return None; 27 | }; 28 | 29 | if !self.lexer().consumeTokenIfEq(lexpos, Token::Comma) { 30 | self.errors.push(CompileError::fromPreTo( 31 | "missing comma after first parameter", 32 | name, 33 | )); 34 | } 35 | 36 | let (qualifiedNameSpecifier, matchedQualified) = 37 | self.optParseNestedNameSpecifierNoErrReport(lexpos); 38 | 39 | let Some(name) = self 40 | .lexer() 41 | .getConsumeTokenIfIdentifier(lexpos) else { 42 | self.errors.push(CompileError::fromPreTo( 43 | "This attribute expects an integer/bool literal and an identifier as parameters.", 44 | name, 45 | )); 46 | return None; 47 | }; 48 | 49 | Some( 50 | if matchedQualified.matched() { 51 | AstAttributeCXXRustyCppCheckSymbolMatchTag::new_qualified( 52 | self.alloc(), 53 | *number, 54 | *name, 55 | qualifiedNameSpecifier, 56 | ) 57 | } else { 58 | AstAttributeCXXRustyCppCheckSymbolMatchTag::new_unqualified( 59 | self.alloc(), 60 | *number, 61 | *name, 62 | ) 63 | } 64 | .into(), 65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseAttribute/RustyCppTagDecl.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::{AstAttributeCXX, AstAttributeCXXRustyCppTagDecl}; 2 | use crate::Parse::BufferedLexer::StateBufferedLexer; 3 | use crate::{ 4 | Lex::Token::Token, 5 | Utils::Structs::{CompileError, CompileMsgImpl, FileTokPos}, 6 | }; 7 | 8 | use super::super::super::Parser; 9 | 10 | impl Parser { 11 | #[allow(clippy::unnecessary_wraps)] 12 | #[allow(clippy::unused_self)] 13 | pub fn parseRustyCppTagDecl( 14 | &mut self, 15 | name: &FileTokPos, 16 | contents: Option, 17 | ) -> Option { 18 | let lexpos = &mut contents.unwrap(); 19 | let Some(number) = self 20 | .lexer() 21 | .getConsumeTokenIf(lexpos, |t| matches!(t, Token::IntegerLiteral(_, _))) else { 22 | self.errors.push(CompileError::fromPreTo( 23 | "This attribute expects an integer literal as a parameter.", 24 | name, 25 | )); 26 | return None; 27 | }; 28 | 29 | Some(AstAttributeCXXRustyCppTagDecl::new(self.alloc(), *number).into()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseAttribute/RustyCppUnused.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::{AstAttributeCXX, AstAttributeCXXRustyCppUnused}; 2 | use crate::Utils::Structs::FileTokPos; 3 | use crate::{Lex::Token::Token, Parse::BufferedLexer::StateBufferedLexer}; 4 | 5 | use super::super::super::Parser; 6 | 7 | impl Parser { 8 | #[allow(clippy::unnecessary_wraps)] 9 | #[allow(clippy::unused_self)] 10 | pub fn parseRustyCppUnused( 11 | &mut self, 12 | _: &FileTokPos, 13 | _: Option, 14 | ) -> Option { 15 | Some(AstAttributeCXXRustyCppUnused::new(self.alloc()).into()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseDeclaration/ParseAsmDeclaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::Common::{AstAttribute, AstDecl}, 3 | Lex::Token::Token, 4 | Parse::BufferedLexer::StateBufferedLexer, 5 | Utils::Structs::{CompileError, CompileMsgImpl, CompileWarning, SourceRange}, 6 | }; 7 | 8 | use super::super::super::Parser; 9 | 10 | impl Parser { 11 | /** 12 | * asm-declaration: 13 | * attribute-specifier-seq [opt] asm ( string-literal ) ; 14 | */ 15 | pub fn parseAsmDeclaration( 16 | &mut self, 17 | lexpos: &mut StateBufferedLexer, 18 | attr: &[AstAttribute], 19 | ) -> Vec { 20 | let startlexpos = *lexpos; 21 | let startedAsm = self.lexer().consumeTokenIfEq(lexpos, Token::Asm); 22 | assert!(startedAsm); 23 | if self.lexer().getIfEq(lexpos, Token::LParen).is_none() { 24 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 25 | self.errors.push(CompileError::fromPreTo( 26 | "Expected '(' after 'asm' keyword.", 27 | posErr, 28 | )); 29 | return vec![]; 30 | } 31 | let parenPos = *lexpos; 32 | let Some(mut scoped) = self.parseAlmostBalancedPattern(lexpos) else { 33 | let posErr = self.lexer().getWithOffsetSaturating(&parenPos, 0); 34 | self.errors.push(CompileError::fromPreTo( 35 | "Expected a closing parentheses for this '(' while evaluating the 'asm' declaration.", 36 | posErr 37 | )); 38 | return vec![]; 39 | }; 40 | 41 | let Some(content) = self.lexer().getConsumeToken(&mut scoped) else { 42 | let posErr = self.lexer().getWithOffsetSaturating(&parenPos, 0); 43 | self.errors.push(CompileError::fromPreTo( 44 | "Expected a string literal inside the 'asm' declaration.", posErr 45 | )); 46 | return vec![]; 47 | }; 48 | 49 | let Token::StringLiteral(_, content) = content.tokPos.tok else { 50 | self.errors.push(CompileError::fromPreTo( 51 | "Expected a string literal for the 'asm' declaration.", 52 | content, 53 | )); 54 | return vec![]; 55 | }; 56 | 57 | if let Some(unused) = self.lexer().getConsumeToken(&mut scoped) { 58 | self.errors.push(CompileWarning::fromPreTo( 59 | "Unused content after the string literal for the 'asm' declaration.", 60 | unused, 61 | )); 62 | } 63 | 64 | if self.lexer().getIfEq(lexpos, Token::Semicolon).is_none() { 65 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 66 | self.errors.push(CompileError::fromPreTo( 67 | "Expected a ';' after the 'asm' declaration.", 68 | posErr, 69 | )); 70 | } else { 71 | self.lexer().next(lexpos); 72 | } 73 | let posAsm = SourceRange::newDoubleTok( 74 | self.lexer().getWithOffsetSaturating(&startlexpos, 0), 75 | self.lexer().getWithOffsetSaturating(lexpos, -1), 76 | ); 77 | self.actOnAsmDecl(attr, posAsm, content) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseDeclaration/ParseCustomRustycppDeclaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fileTokPosMatchArm, 3 | Ast::Common::{AstAttribute, AstDecl}, 4 | Lex::Token::Token, 5 | Parse::BufferedLexer::StateBufferedLexer, 6 | Utils::Structs::{CompileError, CompileMsgImpl, FileTokPos, SourceRange, TokPos}, 7 | }; 8 | 9 | use super::super::super::Parser; 10 | 11 | impl Parser { 12 | /** 13 | * __rustycpp__ (stuff) custom operator 14 | */ 15 | pub fn parseCustom__rustycpp__Decl( 16 | &mut self, 17 | lexpos: &mut StateBufferedLexer, 18 | attr: &[AstAttribute], 19 | ) -> Vec { 20 | let Some(rustyCpp) = self 21 | .lexer() 22 | .getConsumeTokenIfEq(lexpos, Token::__rustycpp__) else 23 | { 24 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 25 | self.errors.push(CompileError::fromPreTo( 26 | "Expected '__rustycpp__' keyword. This is a bug. Report is please.", 27 | posErr 28 | )); 29 | return vec![]; 30 | }; 31 | 32 | let Some(lParen) = self.lexer().getConsumeTokenIfEq(lexpos, Token::LParen) else { 33 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 34 | self.errors.push(CompileError::fromPreTo( 35 | "Expected '(' after '__rustycpp__' keyword.", posErr 36 | )); 37 | return vec![]; 38 | }; 39 | 40 | let result = self.parseContentsOf__rustycpp__Decl(lexpos, rustyCpp, attr); 41 | 42 | while let Some(fileTokPosMatchArm!(tok)) = self.lexer().get(lexpos) { 43 | if matches!(tok, Token::RParen) { 44 | break; 45 | } 46 | self.lexer().next(lexpos); 47 | } 48 | 49 | if !self.lexer().consumeTokenIfEq(lexpos, Token::RParen) { 50 | self.errors.push(CompileError::fromPreTo( 51 | "Expected ')' to match this '('.", 52 | lParen, 53 | )); 54 | }; 55 | 56 | result 57 | } 58 | 59 | fn parseContentsOf__rustycpp__Decl( 60 | &mut self, 61 | lexpos: &mut StateBufferedLexer, 62 | rustyCpp: &FileTokPos, 63 | attr: &[AstAttribute], 64 | ) -> Vec { 65 | let Some(enumTok) = self.lexer().getConsumeTokenIfEq(lexpos, Token::Enum) else { 66 | self.errors.push(CompileError::fromPreTo( 67 | "Expected \"enum\" inside '__rustycpp__' keyword.", 68 | rustyCpp, 69 | )); 70 | return vec![]; 71 | }; 72 | 73 | let Some(nameTok) = self.lexer().getConsumeTokenIfIdentifier(lexpos) else { 74 | self.errors.push(CompileError::fromPreTo( 75 | "Expected enum name after 'enum'.", 76 | enumTok, 77 | )); 78 | return vec![]; 79 | }; 80 | 81 | let location = SourceRange::newDoubleTok(enumTok, nameTok); 82 | let fileTokPosMatchArm!(Token::Identifier(name)) = nameTok else {unreachable!()}; 83 | 84 | self.actOnRustyCppEnumDefinition(*name, location, attr) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseDeclaration/ParseNamespaceDeclaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fileTokPosMatchArm, 3 | Ast::Common::{AstAttribute, AstDecl}, 4 | Lex::Token::Token, 5 | Parse::BufferedLexer::StateBufferedLexer, 6 | Utils::Structs::{CompileError, CompileMsgImpl, FileTokPos, SourceRange, TokPos}, 7 | }; 8 | 9 | use super::super::super::Parser; 10 | 11 | impl Parser { 12 | /** 13 | * Either a namespace-definition, or a namespace-alias-definition: 14 | * 15 | * namespace-definition: 16 | * named-namespace-definition 17 | * unnamed-namespace-definition 18 | * nested-namespace-definition 19 | * named-namespace-definition: 20 | * inline [opt] namespace attribute-specifier-seq [opt] identifier { namespace-body } 21 | * unnamed-namespace-definition: 22 | * inline [opt] namespace attribute-specifier-seq [opt] { namespace-body } 23 | * nested-namespace-definition: 24 | * namespace enclosing-namespace-specifier :: inline [opt] identifier { namespace-body } 25 | * enclosing-namespace-specifier: 26 | * identifier (:: inline [opt] identifier)* 27 | * 28 | * or a block scope: 29 | * namespace-alias-definition: 30 | * namespace identifier = qualified-namespace-specifier ; 31 | */ 32 | pub fn parseNamespaceDeclaration( 33 | &mut self, 34 | lexpos: &mut StateBufferedLexer, 35 | attr: &[AstAttribute], 36 | ) -> Vec { 37 | self.actWrongAttributeLocation(attr); 38 | 39 | let isInline = self.lexer().consumeTokenIfEq(lexpos, Token::Inline); 40 | 41 | if !self.lexer().consumeTokenIfEq(lexpos, Token::Namespace) { 42 | // We already expected a namespace keyword. Reaching this is a bug. 43 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 44 | self.errors.push(CompileError::fromPreTo( 45 | "Expected 'namespace' keyword. This is a bug. Report is please.", 46 | posErr, 47 | )); 48 | } 49 | 50 | let attr: Vec = self.parseAttributes(lexpos); 51 | let name = self.lexer().getConsumeToken(lexpos); 52 | match name { 53 | Some(fileTokPosMatchArm!(Token::Identifier(nameStr))) => { 54 | self.errorAttributes(lexpos); 55 | let Some(tok) = self.lexer().get(lexpos) else { 56 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 57 | self.errors.push(CompileError::fromPreTo( 58 | "Expected '{' (to introduce a namespace) or '=' (to make a namespace alias) after the namespace name.", 59 | posErr, 60 | )); 61 | return vec![]; 62 | }; 63 | match tok.tokPos.tok { 64 | Token::LBrace => { 65 | // named-namespace-definition 66 | let astNamespace = self.actOnStartNamedNamespaceDefinition( 67 | isInline, 68 | attr.as_slice(), 69 | *nameStr, 70 | SourceRange::newSingleTok(name.unwrap()), 71 | ); 72 | let contents = self.parseNamespaceBody(lexpos); 73 | if let Some(astNamespace) = astNamespace.first() { 74 | self.actOnEndNamedNamespaceDefinition(astNamespace, &contents); 75 | } 76 | astNamespace 77 | } 78 | Token::Equal => todo!(), 79 | _ => { 80 | self.errors.push(CompileError::fromPreTo( 81 | "Expected '{' (to introduce a namespace) or '=' (to make a namespace alias) after the namespace name. Instead, we found this.", 82 | tok, 83 | )); 84 | vec![] 85 | } 86 | } 87 | } 88 | _ => todo!(), 89 | } 90 | } 91 | 92 | /** 93 | * We have not parsed the '{' yet! (but we know it's comming) 94 | * namespace-body: 95 | * declaration-seq [opt] 96 | */ 97 | pub fn parseNamespaceBody(&mut self, lexpos: &mut StateBufferedLexer) -> Vec { 98 | if !self.lexer().consumeTokenIfEq(lexpos, Token::LBrace) { 99 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 100 | self.errors.push(CompileError::fromPreTo( 101 | "Expected '{' to introduce a namespace body. This is a bug. Please report it.", 102 | posErr, 103 | )); 104 | return vec![]; 105 | } 106 | 107 | let mut decls = vec![]; 108 | loop { 109 | if let Some(fileTokPosMatchArm!(tok)) = self.lexer().get(lexpos) { 110 | if matches!(tok, Token::RBrace) { 111 | self.lexer().consumeToken(lexpos); 112 | break; 113 | } 114 | let attrs = self.parseAttributes(lexpos); 115 | let newDecls = self.parseDeclaration(lexpos, attrs.as_slice()); 116 | decls.extend(newDecls); 117 | } else { 118 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, -1); 119 | self.errors.push(CompileError::fromPreTo( 120 | "Expected '}' to end the namespace body. Maybe insert one here?", 121 | posErr, 122 | )); 123 | break; 124 | } 125 | } 126 | decls 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseDeclaration/ParseUsingNamespaceDeclaration.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::Common::{AstAttribute, AstDecl}, 3 | Lex::Token::Token, 4 | Parse::{BufferedLexer::StateBufferedLexer, Parser::ParserParse::ParseMatched}, 5 | Utils::Structs::{CompileError, CompileMsgImpl, SourceRange}, 6 | }; 7 | 8 | use super::super::super::Parser; 9 | 10 | impl Parser { 11 | /** 12 | * Either a namespace-definition, or a namespace-alias-definition: 13 | * using-directive: 14 | * attribute-specifier-seq [opt] using namespace nested-name-specifier [opt] namespace-name ; 15 | */ 16 | pub fn parseUsingNamespaceDeclaration( 17 | &mut self, 18 | lexpos: &mut StateBufferedLexer, 19 | attr: &[AstAttribute], 20 | ) -> Vec { 21 | let startlexpos = &mut lexpos.clone(); 22 | let Some(usingTok) = self.lexer().getConsumeTokenIfEq(lexpos, Token::Using) else { 23 | // We already expected a using keyword. Reaching this is a bug. 24 | let posErr = self.lexer().getWithOffsetSaturating(startlexpos, 0); 25 | self.errors.push(CompileError::fromPreTo( 26 | "Expected 'using namespace'. This is a bug. Report is please.", 27 | posErr, 28 | )); 29 | return vec![]; 30 | }; 31 | if !self.lexer().consumeTokenIfEq(lexpos, Token::Namespace) { 32 | // We already expected a using keyword. Reaching this is a bug. 33 | let posErr = self.lexer().getWithOffsetSaturating(startlexpos, 0); 34 | self.errors.push(CompileError::fromPreTo( 35 | "Expected 'using namespace'. This is a bug. Report is please.", 36 | posErr, 37 | )); 38 | return vec![]; 39 | } 40 | 41 | let (nestedNamespace, nestedNamespaceMatched) = self.optParseNestedNameSpecifier(lexpos); 42 | 43 | let Some(nameTok) = self.lexer().getConsumeTokenIfIdentifier(lexpos) else { 44 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, 0); 45 | self.errors.push(CompileError::fromPreTo( 46 | "using namespace expects a namespace name after the namespace keyword", 47 | posErr, 48 | )); 49 | return vec![]; 50 | }; 51 | let Token::Identifier(name) = nameTok.tokPos.tok else { 52 | unreachable!(); 53 | }; 54 | 55 | let Some(semiTok) = self.lexer().getConsumeTokenIfEq(lexpos, Token::Semicolon) else { 56 | let posErr = self.lexer().getWithOffsetSaturating(lexpos, 0); 57 | self.errors.push(CompileError::fromPreTo( 58 | "using namespace expects a semicolon at the end", 59 | posErr, 60 | )); 61 | return vec![]; 62 | }; 63 | 64 | let scope = match nestedNamespaceMatched { 65 | ParseMatched::Matched => { 66 | let Some(scope) = nestedNamespace.last().and_then(|nestedName| { 67 | nestedName.scope.borrow().clone() 68 | }) else { 69 | self.errors.push(CompileError::fromPreTo( 70 | "We were unable to resolve this name. Something's wrong with the nested name specifier", 71 | nameTok, 72 | )); 73 | return vec![]; 74 | }; 75 | Some(scope) 76 | } 77 | ParseMatched::NotMatched => None, 78 | }; 79 | self.actOnUsingNamespaceDefinition( 80 | name, 81 | SourceRange::newDoubleTok(usingTok, semiTok), 82 | attr, 83 | nestedNamespace, 84 | scope.as_ref(), 85 | ) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserParse/ParseMiscUtils.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Lex::Token::Token, 3 | Parse::BufferedLexer::{BufferedLexer, StateBufferedLexer}, 4 | }; 5 | 6 | use super::super::Parser; 7 | 8 | impl Parser { 9 | /** Balances a pattern starting with either a '(', '{' or '[' (otherwise, UB), and ending with the corresponding closing character. 10 | * It returns a range containing the contents inside the pattern, without the outer characters (aka, "(hello)" returns "hello") 11 | * It advances the lexpos past the balanced pattern. 12 | * If the pattern is not balanced, it returns None, and the lexpos is advanced to the end of its range or to the character that broke the balance 13 | * (aka: "(hello]", would break at the ']'. for "[(hello]", we will try and recover by returning the range "(hello") 14 | */ 15 | pub fn parseAlmostBalancedPattern( 16 | &mut self, 17 | lexpos: &mut StateBufferedLexer, 18 | ) -> Option { 19 | #[derive(Clone, Copy, PartialEq, Eq)] 20 | enum BalancedPattern { 21 | Paren, 22 | Brace, 23 | Bracket, 24 | } 25 | let getMatchingBalancedPattern = |tok| match tok { 26 | Token::LParen => Some((false, BalancedPattern::Paren)), 27 | Token::LBrace => Some((false, BalancedPattern::Brace)), 28 | Token::LBracket => Some((false, BalancedPattern::Bracket)), 29 | Token::RParen => Some((true, BalancedPattern::Paren)), 30 | Token::RBrace => Some((true, BalancedPattern::Brace)), 31 | Token::RBracket => Some((true, BalancedPattern::Bracket)), 32 | _ => None, 33 | }; 34 | 35 | let startTok = self.lexer().getConsumeToken(lexpos); 36 | let startPos = *lexpos; 37 | 38 | if startTok.is_none() { 39 | unreachable!(); 40 | } 41 | let startTok = startTok.unwrap(); 42 | let startTokType = startTok.tokPos.tok; 43 | let Some((false, startBalancedPattern)) = getMatchingBalancedPattern(startTokType) else { 44 | unreachable!(); 45 | }; 46 | let mut stack = vec![startBalancedPattern]; 47 | 48 | loop { 49 | let candidate = self.lexer().getConsumeToken(lexpos)?; 50 | let Some((isClosing, kind)) = getMatchingBalancedPattern(candidate.tokPos.tok) else { 51 | continue; 52 | }; 53 | if isClosing { 54 | if let Some((i, _)) = stack.iter().enumerate().rev().find(|(_, t)| **t == kind) { 55 | stack.resize(i, BalancedPattern::Brace); 56 | if stack.is_empty() { 57 | break; 58 | } 59 | } else { 60 | // We have a completely mismatched pattern, no way to recover. We'll backtrack one position, and let the parser continue from there. 61 | self.lexer().moveBack(lexpos, 1); 62 | return None; 63 | } 64 | } else { 65 | stack.push(kind); 66 | } 67 | } 68 | let mut endPos = *lexpos; 69 | self.lexer().moveBack(&mut endPos, 2); 70 | 71 | Some(BufferedLexer::makeProtectedRange(&startPos, &endPos)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserSema.rs: -------------------------------------------------------------------------------- 1 | mod SemaAttribute; 2 | mod SemaDeclaration; 3 | mod SemaLookups; 4 | mod SemaNestedNameSpecifier; 5 | mod SemaTu; 6 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserSema/SemaAttribute.rs: -------------------------------------------------------------------------------- 1 | use crate::Ast::Common::AstAttribute; 2 | use crate::Utils::Structs::{CompileError, CompileMsgImpl}; 3 | 4 | use super::super::Parser; 5 | 6 | impl Parser { 7 | /** 8 | * We parsed some attributes at a safe location, but after further parsing we concluded that this was the wrong place for them. 9 | */ 10 | pub fn actWrongAttributeLocation(&mut self, attr: &[AstAttribute]) { 11 | for a in attr { 12 | self.errors.push(CompileError::fromSourceRange( 13 | "Attribute is not allowed here", 14 | &a.getSourceRange(), 15 | )); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserSema/SemaNestedNameSpecifier.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::NestedNameSpecifier::AstNestedNameSpecifier, 3 | Sema::Scope::{Child, ScopeKind}, 4 | Utils::Structs::{CompileError, CompileMsgImpl}, 5 | }; 6 | 7 | use super::super::Parser; 8 | 9 | impl Parser { 10 | fn resolveNestedNameSpecifier( 11 | &mut self, 12 | nestedNameSpecifier: &[AstNestedNameSpecifier], 13 | reportNormalErrors: bool, 14 | ) { 15 | if nestedNameSpecifier.len() > 1 { 16 | let mut currentResolvedScope = &nestedNameSpecifier[0]; 17 | let mut remainingNameSpecifier = &nestedNameSpecifier[1..]; 18 | // Resolve all namespaces 19 | loop { 20 | let currentUnresolved = &remainingNameSpecifier[0]; 21 | let currentResolvedScopeScope = currentResolvedScope.scope.borrow(); 22 | if currentResolvedScopeScope.is_none() { 23 | return; // Previous resolution failed... 24 | } 25 | let currentResolvedScopeScope = currentResolvedScopeScope.as_ref().unwrap(); 26 | let candidates = Self::qualifiedNameLookup( 27 | currentUnresolved.getName(), 28 | currentResolvedScopeScope, 29 | ) 30 | .into_iter() 31 | /* We want only types, enums and namespaces*/ 32 | .filter(|x| match x { 33 | Child::Decl(_) => false, 34 | Child::Scope(scope) => scope 35 | .borrow() 36 | .flags 37 | .intersects(ScopeKind::NAMESPACE | ScopeKind::ENUM | ScopeKind::CLASS), 38 | }) 39 | .collect::>(); 40 | if candidates.len() > 1 { 41 | self.errors.push(CompileError::fromSourceRange( 42 | "Ambiguous name, compiler bug, please report.", 43 | ¤tUnresolved.sourceRange, 44 | )); 45 | } else if candidates.is_empty() { 46 | if reportNormalErrors { 47 | self.errors.push(CompileError::fromSourceRange( 48 | "The name could not be resolved to a type, enum or namespace.", 49 | ¤tUnresolved.sourceRange, 50 | )); 51 | } 52 | } else { 53 | match &candidates[0] { 54 | Child::Decl(_) => unreachable!(), 55 | Child::Scope(scope) => { 56 | currentUnresolved.setScope(scope.clone()); 57 | } 58 | } 59 | } 60 | if remainingNameSpecifier.len() == 1 { 61 | break; 62 | } 63 | currentResolvedScope = currentUnresolved; 64 | remainingNameSpecifier = &remainingNameSpecifier[1..]; 65 | } 66 | } 67 | } 68 | pub fn actOnNestedNameSpecifier( 69 | &mut self, 70 | nestedNameSpecifier: &[AstNestedNameSpecifier], 71 | reportNormalErrors: bool, 72 | ) -> &'static [AstNestedNameSpecifier] { 73 | // We need to perform qualified name resolution 74 | self.resolveNestedNameSpecifier(nestedNameSpecifier, reportNormalErrors); 75 | self.alloc().alloc_slice_clone(nestedNameSpecifier) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Parse/Parser/ParserSema/SemaTu.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::Common::AstDecl, 3 | Parse::Parser::ModuleImportState, 4 | Utils::Structs::{CompileError, CompileMsgImpl, SourceRange}, 5 | }; 6 | 7 | use super::super::Parser; 8 | 9 | impl Parser { 10 | /** 11 | * Act on module fragment introductor. 12 | * global-module-fragment: 13 | * module-keyword ; declaration-seq [opt] 14 | * 15 | * private-module-fragment: 16 | * module-keyword : private ; declaration-seq [opt] 17 | * 18 | * module-declaration: 19 | * export-keyword [opt] module-keyword module-name module-partition [opt] attribute-specifier-seq [opt]; 20 | */ 21 | pub fn actOnModuleDecl( 22 | &mut self, 23 | isExport: bool, 24 | moduleName: String, 25 | modulePartition: Option, 26 | location: SourceRange, 27 | ) { 28 | if !isExport && moduleName.is_empty() && modulePartition.is_none() 29 | /* global */ 30 | { 31 | if self.moduleImportState == ModuleImportState::StartFile { 32 | self.moduleImportState = ModuleImportState::GlobalSection; 33 | } else { 34 | self.errors.push(CompileError::fromSourceRange( 35 | "Can't start a global module section if it is not at the start of the file", 36 | &location, 37 | )); 38 | } 39 | } else if moduleName.is_empty() && modulePartition.as_ref().is_some_and(|p| p == "private") 40 | { 41 | if isExport { 42 | self.errors.push(CompileError::fromSourceRange( 43 | "Can't start a private module section marked as export", 44 | &location, 45 | )); 46 | return; 47 | } 48 | if matches!( 49 | self.moduleImportState, 50 | ModuleImportState::CodeSection | ModuleImportState::ImportSection 51 | ) { 52 | self.moduleImportState = ModuleImportState::PrivateSection; 53 | } else { 54 | self.errors.push(CompileError::fromSourceRange( 55 | "Can't start a private module section if it is not after you declare a module", 56 | &location, 57 | )); 58 | return; 59 | } 60 | } else if !moduleName.is_empty() { 61 | if matches!( 62 | self.moduleImportState, 63 | ModuleImportState::StartFile | ModuleImportState::GlobalSection 64 | ) { 65 | self.moduleImportState = ModuleImportState::ImportSection; 66 | 67 | let initialModuleDecl = self 68 | .compilerState 69 | .compileUnits 70 | .get(&self.filePath) 71 | .unwrap() 72 | .moduleKind 73 | .lock() 74 | .unwrap() 75 | .module 76 | .0; 77 | #[rustfmt::skip] 78 | let mismatch = match (initialModuleDecl, isExport, moduleName, modulePartition) { 79 | (crate::ModuleTree::Structs::ModuleDeclaration::ExportPrimary(iniModuleName), true, moduleName, None) if iniModuleName.as_ref() == moduleName.as_str() => {false}, 80 | (crate::ModuleTree::Structs::ModuleDeclaration::Primary(iniModuleName), false, moduleName, None) if iniModuleName.as_ref() == moduleName.as_str() => {false}, 81 | (crate::ModuleTree::Structs::ModuleDeclaration::ExportPartition(iniModuleName, iniModulePartition), true, moduleName, Some(modulePartition)) if iniModuleName.as_ref() == moduleName.as_str() && iniModulePartition.as_ref() == modulePartition.as_str() => {false}, 82 | (crate::ModuleTree::Structs::ModuleDeclaration::Partition(iniModuleName, iniModulePartition), false, moduleName, Some(modulePartition)) if iniModuleName.as_ref() == moduleName.as_str() && iniModulePartition.as_ref() == modulePartition.as_str() => {false}, 83 | _ => {true}, 84 | }; 85 | if mismatch { 86 | self.errors.push(CompileError::fromSourceRange( 87 | format!( 88 | "Module declaration mismatch; First non-macro pass detected a {initialModuleDecl}, but we are now finding something else! Are you using macros in this specific line? That's a no-no for now (There's a chicken-and-egg problem here, as macros can alter modules/imports, but imports can import macros. (It's complicated, so I've decided to not suport macros in module declarations while still allowing importing macros.)), sorry!" 89 | ), 90 | &location, 91 | )); 92 | return; 93 | } 94 | } 95 | } else { 96 | self.errors.push(CompileError::fromSourceRange( 97 | "Can't start a module section declared as: ".to_owned() 98 | + if isExport { "export" } else { "" } 99 | + " module " 100 | + &moduleName 101 | + " " 102 | + &modulePartition.map_or(String::new(), |s| ":".to_owned() + &s) 103 | + " ", 104 | &location, 105 | )); 106 | return; 107 | } 108 | } 109 | 110 | pub fn actOnTopLevelDecl(&mut self, decl: &Vec) { 111 | if !decl.is_empty() { 112 | self.moduleImportState = match self.moduleImportState { 113 | ModuleImportState::GlobalSection => ModuleImportState::GlobalSection, 114 | ModuleImportState::ImportSection | ModuleImportState::CodeSection => { 115 | ModuleImportState::CodeSection 116 | } 117 | ModuleImportState::PrivateSection => ModuleImportState::PrivateSection, 118 | ModuleImportState::StartFile | ModuleImportState::GlobalFile => { 119 | ModuleImportState::GlobalFile 120 | } 121 | }; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/Preprocessor.rs: -------------------------------------------------------------------------------- 1 | //! Component responsible of running the preprocessor on an input file. 2 | //! 3 | //! The preporcessing step of a compilation is composed of a first step of 4 | //! tokenization done by the [prelexer], followed by the preprocessor, mostly 5 | //! driven by the [driver], and it's submodules. 6 | 7 | pub mod Driver; 8 | pub mod Multilexer; 9 | pub mod Prelexer; 10 | pub mod Pretoken; 11 | pub mod Structs; 12 | 13 | pub use Driver::*; 14 | -------------------------------------------------------------------------------- /src/Preprocessor/Driver/Includer.rs: -------------------------------------------------------------------------------- 1 | //! handle the #include directive 2 | 3 | #![allow(non_camel_case_types, clippy::string_to_string)] 4 | 5 | use std::collections::{HashMap, VecDeque}; 6 | 7 | use crate::Grammars::DefineAst::DefineAst; 8 | use crate::Preprocessor::Prelexer::PreLexer; 9 | use crate::Preprocessor::Pretoken::PreToken; 10 | use crate::Utils::CompilerState::CompilerState; 11 | use crate::Utils::Structs::TokPos; 12 | use crate::Utils::Structs::{CompileError, CompileMsg, CompileMsgImpl, FileTokPos}; 13 | use crate::{fileTokPosMatchArm, fileTokPosMatches}; 14 | 15 | use multiset::HashMultiSet; 16 | 17 | use super::Preprocessor; 18 | use crate::Preprocessor::Multilexer::MultiLexer; 19 | 20 | impl Preprocessor { 21 | /// Include a file in the current position of the preprocessor 22 | pub fn includeFile( 23 | &mut self, 24 | preToken: &FileTokPos, 25 | file: &str, 26 | ) -> Result<(), CompileMsg> { 27 | if self.multilexer.hasFileAccess(file) { 28 | self.multilexer.pushFile(file); 29 | } else { 30 | return Err(CompileError::fromPreTo( 31 | format!("Can't include the file in path: {file}"), 32 | preToken, 33 | )); 34 | } 35 | Ok(()) 36 | } 37 | 38 | /// Evaluates the #include directive. Finds a candidate and returns the file path 39 | pub fn consumeMacroInclude( 40 | &mut self, 41 | preToken: &FileTokPos, 42 | ) -> Result { 43 | let multilexer = &mut self.multilexer; 44 | let tokens = multilexer 45 | .take_while(|x| !fileTokPosMatches!(x, PreToken::Newline)) 46 | .collect::>(); 47 | Self::tokensToValidIncludeablePath( 48 | &self.compilerState, 49 | &self.multilexer, 50 | &self.definitions, 51 | &self.disabledMacros, 52 | preToken, 53 | tokens, 54 | ) 55 | } 56 | 57 | /// Expands the tokens if necessary, and returns the path found, if any 58 | pub fn tokensToValidIncludeablePath( 59 | compilerState: &CompilerState, 60 | lexer: &MultiLexer, 61 | definitions: &HashMap, 62 | disabledMacros: &HashMultiSet, 63 | preToken: &FileTokPos, 64 | tokensInclude: VecDeque>, 65 | ) -> Result { 66 | let mut path = String::new(); 67 | 68 | if tokensInclude.is_empty() { 69 | return Err(CompileError::fromPreTo( 70 | "The empty path can't be opened", 71 | preToken, 72 | )); 73 | } 74 | 75 | if let Some(newPath) = Self::checkForInclude(&tokensInclude) { 76 | path = newPath; 77 | } 78 | 79 | let mut paramLexer = MultiLexer::new_def(lexer.fileMapping()); 80 | paramLexer.pushTokensDec(tokensInclude); 81 | let toks = 82 | Self::expandASequenceOfTokens(compilerState, paramLexer, definitions, disabledMacros)?; 83 | 84 | if let Some(newPath) = Self::checkForInclude(&toks) { 85 | path = newPath; 86 | } else { 87 | for s in toks.into_iter().map(|t| t.tokPos.tok.to_str().to_owned()) { 88 | path.push_str(&s); 89 | } 90 | } 91 | 92 | Ok(path) 93 | } 94 | 95 | /// Is the current tokens a valid include path token? Re-lexes them if necessary 96 | pub fn checkForInclude(toks: &VecDeque>) -> Option { 97 | let mut res = String::new(); 98 | 99 | let mut iter = toks 100 | .iter() 101 | .skip_while(|x| { 102 | fileTokPosMatches!( 103 | x, 104 | PreToken::Whitespace(_) 105 | | PreToken::Newline 106 | | PreToken::ValidNop 107 | | PreToken::EnableMacro(_) 108 | | PreToken::DisableMacro(_) 109 | ) 110 | }) 111 | .peekable(); 112 | 113 | let nextTok = iter.peek()?; 114 | 115 | if let fileTokPosMatchArm!(PreToken::HeaderName(pathWithSurroundingChars)) = nextTok { 116 | let mut chars = pathWithSurroundingChars.chars(); 117 | chars.next(); 118 | chars.next_back(); 119 | return Some(chars.as_str().to_owned()); 120 | } else if { 121 | fileTokPosMatches!( 122 | nextTok, 123 | PreToken::StringLiteral(_) 124 | | PreToken::OperatorPunctuator("<" | "<=" | "<<" | "<<=") 125 | | PreToken::UdStringLiteral(_) 126 | ) 127 | } { 128 | for s in iter.map(|x| x.tokPos.tok.to_str().to_owned()) { 129 | res.push_str(&s); 130 | } 131 | let mut lexer = PreLexer::new(res); 132 | lexer.expectHeader(); 133 | if let Some(PreToken::HeaderName(pathWithSurroundingChars)) = 134 | lexer.next().map(|x| x.tok) 135 | { 136 | let mut chars = pathWithSurroundingChars.chars(); 137 | chars.next(); 138 | chars.next_back(); 139 | return Some(chars.as_str().to_owned()); 140 | } 141 | } 142 | None 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Preprocessor/Multilexer.rs: -------------------------------------------------------------------------------- 1 | //! An aggregation of lexers that can be used to represent the preprocessor 2 | //! state of file inclussions. 3 | 4 | use std::{ 5 | collections::VecDeque, 6 | sync::{Arc, Mutex}, 7 | }; 8 | 9 | use crate::Preprocessor::Prelexer::PreLexer; 10 | use crate::Utils::FileMap::FileMap; 11 | use crate::Utils::Structs::FileTokPos; 12 | 13 | use super::Pretoken::PreToken; 14 | 15 | #[derive(Debug)] 16 | #[doc(hidden)] 17 | struct FileLexer { 18 | pub compFile: u64, 19 | pub lexer: PreLexer, 20 | } 21 | 22 | #[derive(Debug)] 23 | /// An aggregation of lexers that can be used to represent the preprocessor 24 | /// state of file inclussions. 25 | pub struct MultiLexer { 26 | /// The current files opened by the hole compiler 27 | fileMapping: Arc>, 28 | /// Files in the order they were opened 29 | files: Vec, 30 | /// Pushed tokens to return back. This is specially useful when reevaluating an expanded macro 31 | pushedTokens: VecDeque>, 32 | } 33 | 34 | impl MultiLexer { 35 | /// Creates a new empty multilexer 36 | pub fn new_def(files: Arc>) -> Self { 37 | Self { 38 | fileMapping: files, 39 | files: vec![], 40 | pushedTokens: VecDeque::new(), 41 | } 42 | } 43 | 44 | /// Creates a new multilexer with the starting file 45 | pub fn new((files, file): (Arc>, u64)) -> Self { 46 | let currFile = files.lock().unwrap().getOpenedFile(file); 47 | let lexer = PreLexer::new(currFile.content().clone()); 48 | 49 | Self { 50 | fileMapping: files, 51 | files: vec![FileLexer { 52 | compFile: file, 53 | lexer, 54 | }], 55 | pushedTokens: VecDeque::new(), 56 | } 57 | } 58 | 59 | /// Push tokens to be returned back 60 | pub fn pushTokensDec(&mut self, toks: VecDeque>) { 61 | for i in toks.into_iter().rev() { 62 | self.pushedTokens.push_front(i); 63 | } 64 | } 65 | 66 | /// Push tokens to be returned back 67 | pub fn pushTokensVec(&mut self, toks: Vec>) { 68 | for i in toks.into_iter().rev() { 69 | self.pushedTokens.push_front(i); 70 | } 71 | } 72 | 73 | /// Push token to be returned back 74 | pub fn pushToken(&mut self, tok: FileTokPos) { 75 | self.pushedTokens.push_back(tok); 76 | } 77 | 78 | /// Push a new file. Please be careful when you're doing this, as the pushed 79 | /// tokens will still be returned first! 80 | pub fn pushFile(&mut self, path: &str) { 81 | let mut fileMapping = self.fileMapping.lock().unwrap(); 82 | let compFile = fileMapping.getAddFile(path); 83 | let lexer = PreLexer::new(fileMapping.getOpenedFile(compFile).content().to_string()); 84 | drop(fileMapping); 85 | self.files.push(FileLexer { compFile, lexer }); 86 | } 87 | 88 | /// Push a new file. Please be careful when you're doing this, as the pushed 89 | /// tokens will still be returned first! 90 | pub fn expectHeader(&mut self) { 91 | if let Some(lex) = self.files.last_mut() { 92 | lex.lexer.expectHeader(); 93 | } 94 | } 95 | 96 | /// Current mapping of files. 97 | pub fn fileMapping(&self) -> Arc> { 98 | self.fileMapping.clone() 99 | } 100 | 101 | /// Can this multilexer access the file? It does not need to be previously 102 | /// oppened. 103 | pub fn hasFileAccess(&self, file: &str) -> bool { 104 | self.fileMapping.lock().unwrap().hasFileAccess(file) 105 | } 106 | } 107 | 108 | impl Iterator for MultiLexer { 109 | type Item = FileTokPos; 110 | 111 | fn next(&mut self) -> Option { 112 | if let Some(t) = self.pushedTokens.pop_front() { 113 | return Some(t); 114 | } 115 | loop { 116 | if let Some(lexer) = self.files.last_mut() { 117 | match lexer.lexer.next() { 118 | None => {} 119 | Some(tok) => { 120 | return Some(FileTokPos::new(lexer.compFile, tok)); 121 | } 122 | } 123 | } else { 124 | return None; 125 | } 126 | // If we reach here, the single-lexer is empty. We pop it and hope the next one provides more content. 127 | self.files.pop(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Preprocessor/Structs.rs: -------------------------------------------------------------------------------- 1 | //! Structs used only in the preprocessor 2 | 3 | use std::collections::HashMap; 4 | 5 | use multiset::HashMultiSet; 6 | 7 | use crate::Grammars::DefineAst::{DefineAst, PreTokenDefine}; 8 | use crate::Utils::CompilerState::CompilerState; 9 | use crate::Utils::Structs::FileTokPos; 10 | 11 | use super::Multilexer::MultiLexer; 12 | use super::Pretoken::PreToken; 13 | 14 | #[derive(Debug, Clone)] 15 | /// When a macro is expanded, this struct is passed to the expand functions so 16 | /// they have all the necessary context. 17 | pub struct ExpandData<'a> { 18 | /// Current definitions of the preprocessor 19 | pub definitions: &'a HashMap, 20 | /// Disabled macros at this point 21 | pub disabledMacros: &'a HashMultiSet, 22 | /// The lexer. Not guaranteed to be the same as the one in the preprocessor. Can't be modified. 23 | pub lexer: &'a MultiLexer, 24 | /// Conent of the named args, if macro was function-like 25 | pub namedArgs: &'a HashMap>>, 26 | /// Conent of the variadic args, if macro was function-like 27 | pub variadic: &'a Vec>>, 28 | /// Macro name 29 | pub astId: &'a String, 30 | /// The replacement list of the macro. 31 | pub replacement: &'a Vec, 32 | /// The new token that generated this instantiation 33 | pub newToken: &'a FileTokPos, 34 | 35 | pub compilerState: &'a CompilerState, 36 | 37 | /// Should the macro expand its arguments? Can be used for operator #, so it 38 | /// can disable expansion of arguments. That way, when this is encountered: 39 | /// #define A a 40 | /// #define B(b) #b 41 | /// B(A) 42 | /// The result is "A", not "a". 43 | pub expandArg: bool, 44 | } 45 | -------------------------------------------------------------------------------- /src/Sema.rs: -------------------------------------------------------------------------------- 1 | pub mod AstContext; 2 | pub mod Scope; 3 | pub mod TypeDict; 4 | -------------------------------------------------------------------------------- /src/Sema/AstContext.rs: -------------------------------------------------------------------------------- 1 | use crate::Sema::{ 2 | Scope::{Scope, ScopeRef}, 3 | TypeDict::TypeDict, 4 | }; 5 | use crate::Utils::UnsafeAllocator::UnsafeAllocator; 6 | 7 | pub struct AstContext { 8 | pub rootScope: ScopeRef, 9 | pub currentScope: ScopeRef, 10 | 11 | pub alloc: UnsafeAllocator, 12 | pub typeDict: TypeDict, 13 | } 14 | 15 | impl AstContext { 16 | pub fn new() -> Self { 17 | let rootScope = Scope::new_root(); 18 | let alloc: UnsafeAllocator = UnsafeAllocator::default(); 19 | Self { 20 | rootScope: rootScope.clone(), 21 | currentScope: rootScope, 22 | 23 | typeDict: TypeDict::new(alloc.alloc()), 24 | alloc, 25 | } 26 | } 27 | } 28 | 29 | impl Default for AstContext { 30 | fn default() -> Self { 31 | Self::new() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Sema/TypeDict.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Ast::Common::{AstTypePointer, AstTypeReference}, 3 | Utils::FoldingContainer::{Foldable, FoldingNode}, 4 | }; 5 | use std::collections::HashMap; 6 | 7 | use bumpalo::Bump; 8 | 9 | use crate::Ast::{ 10 | Common::AstTypeBuiltin, 11 | Type::{Builtin::BuiltinTypeKind, QualType}, 12 | }; 13 | 14 | pub struct TypeDict { 15 | builtin: Vec, 16 | pointer: HashMap, 17 | lvalueReference: HashMap, 18 | alloc: &'static bumpalo::Bump, 19 | } 20 | 21 | impl TypeDict { 22 | pub fn new(alloc: &'static bumpalo::Bump) -> Self { 23 | Self { 24 | builtin: Vec::new(), 25 | pointer: HashMap::new(), 26 | lvalueReference: HashMap::new(), 27 | alloc, 28 | } 29 | } 30 | 31 | const fn alloc(&self) -> &'static Bump { 32 | self.alloc 33 | } 34 | 35 | pub fn addBuiltinType(&mut self, t: BuiltinTypeKind) { 36 | assert!(t as usize == self.builtin.len()); 37 | self.builtin.push(AstTypeBuiltin::new(self.alloc(), t)); 38 | } 39 | 40 | pub fn getBuiltinType(&self, t: BuiltinTypeKind) -> AstTypeBuiltin { 41 | self.builtin[t as usize] 42 | } 43 | 44 | pub fn getPtrType(&mut self, t: QualType) -> AstTypePointer { 45 | let nodeId = t.newFoldNode(); 46 | let alloc = self.alloc(); 47 | *self 48 | .pointer 49 | .entry(nodeId) 50 | .or_insert_with(|| AstTypePointer::new(alloc, t)) 51 | } 52 | 53 | pub fn getLValueReference(&mut self, t: QualType) -> AstTypeReference { 54 | let nodeId = t.newFoldNode(); 55 | let alloc = self.alloc(); 56 | *self 57 | .lvalueReference 58 | .entry(nodeId) 59 | .or_insert_with(|| AstTypeReference::new(alloc, t)) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Test.rs: -------------------------------------------------------------------------------- 1 | //! Test suite of the compiler 2 | #![allow(missing_docs, clippy::missing_docs_in_private_items)] 3 | #[cfg(test)] 4 | pub mod TestIncluder; 5 | #[cfg(test)] 6 | pub mod TestLexer; 7 | #[cfg(test)] 8 | pub mod TestPreprocessorDefine; 9 | #[cfg(test)] 10 | pub mod TestPreprocessorIf; 11 | #[cfg(test)] 12 | pub mod TestProject; 13 | #[cfg(test)] 14 | pub mod TestSingleFile; 15 | -------------------------------------------------------------------------------- /src/Test/TestIncluder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::path::Path; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use crate::Preprocessor::Preprocessor; 6 | use crate::Preprocessor::Pretoken::PreToken; 7 | use crate::Utils::CompilerState::CompilerState; 8 | use crate::Utils::FileMap::FileMap; 9 | use crate::Utils::Parameters::Parameters; 10 | use crate::Utils::StateCompileUnit::StateCompileUnit; 11 | use crate::Utils::Structs::{CompileMsg, FileTokPos}; 12 | 13 | use test_log::test; 14 | 15 | fn generateFileMap(files: &[(&'static str, &'static str)]) -> (CompilerState, u64) { 16 | let mut params = Parameters::new(); 17 | params.includeDirs.push( 18 | Path::new(file!()) 19 | .parent() 20 | .unwrap() 21 | .join("include") 22 | .to_str() 23 | .unwrap() 24 | .to_string(), 25 | ); 26 | 27 | for (filePath, _) in files { 28 | params.translationUnits.push((*filePath).to_string()); 29 | } 30 | 31 | let parameters = Arc::new(params); 32 | let fileMap = Arc::new(Mutex::new(FileMap::new(parameters.clone()))); 33 | let mut compileUnits = HashMap::new(); 34 | for (i, (filePath, fileContents)) in files.iter().enumerate() { 35 | fileMap 36 | .lock() 37 | .unwrap() 38 | .addTestFile((*filePath).to_string(), fileContents); 39 | compileUnits.insert(i as u64 + 1, StateCompileUnit::new()); 40 | } 41 | 42 | ( 43 | CompilerState { 44 | parameters, 45 | compileFiles: fileMap, 46 | compileUnits: Arc::new(compileUnits), 47 | translationUnitsFiles: Arc::new((1..2).collect::>()), 48 | moduleHeaderUnitsFiles: Arc::new(HashSet::new()), 49 | foundErrors: Arc::default(), 50 | }, 51 | 1, 52 | ) 53 | } 54 | 55 | fn getToksPreprocessed(files: &[(&'static str, &'static str)]) -> Vec { 56 | let f = generateFileMap(files); 57 | let prep = Preprocessor::new(f.clone()); 58 | prep.filter_map(|x: Result, CompileMsg>| { 59 | x.map_or_else( 60 | |err| { 61 | log::error!("{}", err.to_string(&f.0.compileFiles)); 62 | None 63 | }, 64 | Some, 65 | ) 66 | }) 67 | .map(|x| x.tokPos.tok) 68 | .collect::>() 69 | } 70 | 71 | fn getErrsPreprocessed(files: &[(&'static str, &'static str)]) -> Vec { 72 | let prep = Preprocessor::new(generateFileMap(files)); 73 | prep.filter_map(Result::err).collect::>() 74 | } 75 | 76 | fn getToksPreprocessedNoWs(files: &[(&'static str, &'static str)]) -> Vec { 77 | let mut res = getToksPreprocessed(files); 78 | res.retain(|x| { 79 | !matches!( 80 | x, 81 | PreToken::Whitespace(_) 82 | | PreToken::Newline 83 | | PreToken::ValidNop 84 | | PreToken::EnableMacro(_) 85 | | PreToken::DisableMacro(_) 86 | ) 87 | }); 88 | res 89 | } 90 | 91 | fn toksToString(toks: &[PreToken]) -> String { 92 | let mut res = String::new(); 93 | for s in toks.iter().map /*TODO: FILTER NOPS?*/ (PreToken::to_str) { 94 | res.push_str(s); 95 | res.push(' '); 96 | } 97 | res 98 | } 99 | 100 | fn preprocessAndStringify(string: &'static str) -> String { 101 | let info = &[("test", string)]; 102 | toksToString(&getToksPreprocessedNoWs(info)) 103 | } 104 | #[test] 105 | fn testHeaderOpening1() { 106 | let toks = getToksPreprocessedNoWs(&[("test", "#include \n")]); 107 | println!("{toks:?}"); 108 | assert_eq!(toks.len(), 1); 109 | assert_eq!(toks[0].to_str(), "SUCCESS"); 110 | } 111 | 112 | #[test] 113 | fn testHeaderOpening2() { 114 | let toks = getToksPreprocessedNoWs(&[("test", r#"#include "header.h"\n"#)]); 115 | println!("{toks:?}"); 116 | assert_eq!(toks.len(), 1); 117 | assert_eq!(toks[0].to_str(), "SUCCESS"); 118 | } 119 | 120 | #[test] 121 | fn testHeaderOpeningMacro() { 122 | let toks = getToksPreprocessedNoWs(&[( 123 | "test", 124 | r#" 125 | #define HEADERIZE(arg) 126 | #include HEADERIZE(header) 127 | "#, 128 | )]); 129 | println!("{toks:?}"); 130 | assert_eq!(toks.len(), 1); 131 | assert_eq!(toks[0].to_str(), "SUCCESS"); 132 | } 133 | 134 | #[test] 135 | fn testHeaderOpeningTwice() { 136 | let toks = getToksPreprocessedNoWs(&[( 137 | "test", 138 | r#" 139 | #include 140 | #include 141 | "#, 142 | )]); 143 | println!("{toks:?}"); 144 | assert_eq!(toks.len(), 2); 145 | assert_eq!(toks[0].to_str(), "SUCCESS"); 146 | assert_eq!(toks[1].to_str(), "SUCCESS"); 147 | } 148 | -------------------------------------------------------------------------------- /src/Test/TestPreprocessorIf.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{HashMap, HashSet}; 2 | use std::path::Path; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use crate::Preprocessor::Preprocessor; 6 | use crate::Preprocessor::Pretoken::PreToken; 7 | use crate::Utils::CompilerState::CompilerState; 8 | use crate::Utils::FileMap::FileMap; 9 | use crate::Utils::Parameters::Parameters; 10 | use crate::Utils::StateCompileUnit::StateCompileUnit; 11 | use crate::Utils::Structs::CompileMsg; 12 | 13 | use test_log::test; 14 | 15 | fn generateFileMap(files: &[(&'static str, String)]) -> (CompilerState, u64) { 16 | let mut params = Parameters::new(); 17 | params.includeDirs.push( 18 | Path::new(file!()) 19 | .parent() 20 | .unwrap() 21 | .join("include") 22 | .to_str() 23 | .unwrap() 24 | .to_string(), 25 | ); 26 | 27 | for (filePath, _) in files { 28 | params.translationUnits.push((*filePath).to_string()); 29 | } 30 | 31 | let parameters = Arc::new(params); 32 | let fileMap = Arc::new(Mutex::new(FileMap::new(parameters.clone()))); 33 | let mut compileUnits = HashMap::new(); 34 | for (i, (filePath, fileContents)) in files.iter().enumerate() { 35 | fileMap 36 | .lock() 37 | .unwrap() 38 | .addTestFile((*filePath).to_string(), fileContents); 39 | compileUnits.insert(i as u64 + 1, StateCompileUnit::new()); 40 | } 41 | 42 | ( 43 | CompilerState { 44 | parameters, 45 | compileFiles: fileMap, 46 | compileUnits: Arc::new(compileUnits), 47 | translationUnitsFiles: Arc::new((1..2).collect::>()), 48 | moduleHeaderUnitsFiles: Arc::new(HashSet::new()), 49 | foundErrors: Arc::default(), 50 | }, 51 | 1, 52 | ) 53 | } 54 | 55 | fn getToksPreprocessed(files: &[(&'static str, String)]) -> Vec> { 56 | let prep = Preprocessor::new(generateFileMap(files)); 57 | prep.map(|x| x.map(|x| x.tokPos.tok)).collect::>() 58 | } 59 | 60 | fn getToksPreprocessedNoWs(files: &[(&'static str, String)]) -> Vec> { 61 | let mut res = getToksPreprocessed(files); 62 | res.retain(|x| { 63 | x.as_ref().map_or(true, |x| { 64 | !matches!( 65 | x, 66 | PreToken::Whitespace(_) 67 | | PreToken::Newline 68 | | PreToken::ValidNop 69 | | PreToken::EnableMacro(_) 70 | | PreToken::DisableMacro(_) 71 | ) 72 | }) 73 | }); 74 | res 75 | } 76 | 77 | fn checkForCorrectEvalOfIfClause(string: &'static str) { 78 | let info = &[("test", string.to_string() + "\nSUCCESS\n#endif")]; 79 | let tokens = getToksPreprocessedNoWs(info); 80 | let res = !tokens.iter().any(Result::is_err) 81 | && tokens.iter().any(|x| { 82 | if let Ok(PreToken::Ident(ref val)) = x { 83 | val == "SUCCESS" 84 | } else { 85 | false 86 | } 87 | }); 88 | assert!(res, "The expression does not yield a trueish value"); 89 | } 90 | 91 | fn checkForAnyEvalOfIfClause(string: &'static str) { 92 | let info = &[( 93 | "test", 94 | string.to_string() + "\nSUCCESS\n#else\nSUCCESS\n#endif", 95 | )]; 96 | let tokens = getToksPreprocessedNoWs(info); 97 | let res = !tokens.iter().any(Result::is_err) 98 | && tokens.iter().any(|x| { 99 | if let Ok(PreToken::Ident(ref val)) = x { 100 | val == "SUCCESS" 101 | } else { 102 | false 103 | } 104 | }); 105 | assert!(res, "The expression does not yield a trueish value"); 106 | } 107 | 108 | fn checkForBorkenEvalOfIfClause(string: &'static str) { 109 | let info = &[("test", string.to_string() + "\nSUCCESS\n#endif")]; 110 | let tokens = getToksPreprocessedNoWs(info); 111 | let res = tokens.iter().any(Result::is_err); 112 | log::debug!("Tokens: {:?}", tokens); 113 | assert!(res, "The expression does not yield an error"); 114 | } 115 | 116 | #[test] 117 | fn simpleCase() { 118 | checkForCorrectEvalOfIfClause(r##"#if 1"##); 119 | } 120 | 121 | #[test] 122 | fn checkElse() { 123 | checkForCorrectEvalOfIfClause( 124 | r##" 125 | #if 0 126 | #else 127 | "##, 128 | ); 129 | } 130 | 131 | #[test] 132 | fn checkElif() { 133 | checkForCorrectEvalOfIfClause( 134 | r##" 135 | #if 0 136 | #elif 1 137 | "##, 138 | ); 139 | } 140 | 141 | #[test] 142 | fn checkIfdef() { 143 | checkForCorrectEvalOfIfClause( 144 | r##" 145 | #define TEST 146 | #ifdef TEST 147 | "##, 148 | ); 149 | } 150 | 151 | #[test] 152 | fn checkDefined1() { 153 | checkForCorrectEvalOfIfClause( 154 | r##" 155 | #define TEST 156 | #if defined ( TEST ^) 157 | "##, 158 | ); 159 | } 160 | 161 | #[test] 162 | fn checkDefined2() { 163 | checkForCorrectEvalOfIfClause( 164 | r##" 165 | #define TEST 166 | #if defined TEST 167 | "##, 168 | ); 169 | } 170 | 171 | #[test] 172 | fn checkDefined3() { 173 | checkForBorkenEvalOfIfClause( 174 | r##" 175 | #if defined 176 | "##, 177 | ); 178 | } 179 | 180 | #[test] 181 | fn checkDefined4() { 182 | checkForBorkenEvalOfIfClause( 183 | r##" 184 | #if defined( 185 | "##, 186 | ); 187 | } 188 | 189 | #[test] 190 | fn checkDefined5() { 191 | checkForCorrectEvalOfIfClause( 192 | r##" 193 | #define L defined(L) 194 | #if L 195 | "##, 196 | ); 197 | } 198 | 199 | #[test] 200 | fn checkDefined6() { 201 | checkForCorrectEvalOfIfClause( 202 | r##" 203 | #define L(defined) defined(L) 204 | #if L(defined) 205 | "##, 206 | ); 207 | } 208 | 209 | #[test] 210 | fn checkSum() { 211 | checkForCorrectEvalOfIfClause( 212 | r##" 213 | #if 1+2+3+4+5 214 | "##, 215 | ); 216 | } 217 | 218 | #[test] 219 | fn checkParen1() { 220 | checkForCorrectEvalOfIfClause( 221 | r##" 222 | #if (1+1)+1*5 == 7 223 | "##, 224 | ); 225 | } 226 | 227 | #[test] 228 | fn checkStuff() { 229 | checkForCorrectEvalOfIfClause( 230 | r##" 231 | #if __cplusplus / 100 >= 2011 232 | "##, 233 | ); 234 | } 235 | 236 | #[test] 237 | fn checkHasInclude() { 238 | checkForAnyEvalOfIfClause( 239 | r##" 240 | #if __has_include() 241 | "##, 242 | ); 243 | } 244 | 245 | #[test] 246 | fn checkBrokenParen() { 247 | checkForBorkenEvalOfIfClause( 248 | r##" 249 | #if (1+1 250 | "##, 251 | ); 252 | } 253 | 254 | #[test] 255 | fn checkBrokenOp() { 256 | checkForBorkenEvalOfIfClause( 257 | r##" 258 | #if 1+ 259 | "##, 260 | ); 261 | } 262 | 263 | #[test] 264 | fn checkValidOp() { 265 | checkForCorrectEvalOfIfClause( 266 | r##" 267 | #if -1 268 | "##, 269 | ); 270 | } 271 | -------------------------------------------------------------------------------- /src/Test/TestProject.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, path::Path}; 2 | 3 | use ::function_name::named; 4 | use test_log::test; 5 | 6 | use crate::{ 7 | Ast::Common::AstTu, 8 | Compiler::Compiler, 9 | Utils::{ 10 | CompilerState::CompilerState, 11 | Parameters::Parameters, 12 | Structs::{CompileMsg, CompileMsgKind}, 13 | }, 14 | }; 15 | 16 | macro_rules! testProject { 17 | () => {{ 18 | testProject(function_name!(), file!()) 19 | }}; 20 | } 21 | 22 | macro_rules! testSuccessfulProject { 23 | () => {{ 24 | testProject!() 25 | .into_iter() 26 | .inspect(|(_, errors, compilerState)| assertErrors(&errors, &compilerState)) 27 | .map(|(asts, _, compilerState)| (asts, compilerState)) 28 | .collect::>() 29 | }}; 30 | } 31 | 32 | macro_rules! testUnsuccessfulProject { 33 | ($($lines:expr),*) => {{ 34 | testProject!() 35 | }}; 36 | } 37 | 38 | fn testProject( 39 | funcName: &str, 40 | file: &str, 41 | ) -> Vec<(HashMap, Vec, CompilerState)> { 42 | let dirTest = std::path::Path::new(file) 43 | .canonicalize() 44 | .unwrap() 45 | .parent() 46 | .unwrap() 47 | .join("testProject") 48 | .join(funcName); 49 | 50 | let fileTest = dirTest.join("compile_list.json"); 51 | 52 | println!("{}", fileTest.to_string_lossy()); 53 | 54 | let mut parameters = Parameters::new_file(fileTest.to_str().unwrap()).unwrap(); 55 | parameters 56 | .includeDirs 57 | .push(dirTest.to_str().unwrap().to_string()); 58 | let compute = move |parameters| { 59 | let mut tmpRes = (HashMap::new(), Vec::new()); 60 | let stateCompiler = Compiler::new(parameters).parsed_tree_test(&mut tmpRes); 61 | let (ast, errors) = tmpRes; 62 | 63 | (ast, errors, stateCompiler) 64 | }; 65 | let mut res = vec![compute(parameters.clone())]; 66 | parameters.threadNum = Some(8); 67 | res.push(compute(parameters.clone())); 68 | parameters.threadNum = Some(4); 69 | res.push(compute(parameters.clone())); 70 | parameters.threadNum = Some(2); 71 | res.push(compute(parameters.clone())); 72 | parameters.threadNum = Some(1); 73 | res.push(compute(parameters)); 74 | res 75 | } 76 | 77 | fn assertErrors(errors: &[CompileMsg], state: &CompilerState) { 78 | errors.iter().for_each(|e| e.print(&state.compileFiles)); 79 | assert!(!errors.iter().any(|e| e.severity() >= CompileMsgKind::Error)); 80 | } 81 | 82 | fn checkErrors( 83 | mut errors: Vec, 84 | state: &CompilerState, 85 | expectedErrors: &[(usize, String, bool, CompileMsgKind)], 86 | ) { 87 | errors.iter().for_each(|e| e.print(&state.compileFiles)); 88 | 89 | for (expectedLocation, path, optional, expectedErrorType) in expectedErrors { 90 | let mut compileFiles = state.compileFiles.lock().unwrap(); 91 | let pos = errors.iter().position(|e| { 92 | let (file, at, _) = e.loc(); 93 | at.is_some_and(|at| { 94 | let fileArc = compileFiles.getOpenedFile(file); 95 | fileArc.getRowColumn(at).0 == *expectedLocation 96 | && Path::new(fileArc.path()).file_name().unwrap() 97 | == Path::new(path).file_name().unwrap() 98 | }) && e.severity() == *expectedErrorType 99 | }); 100 | if let Some(pos) = pos { 101 | errors.remove(pos); 102 | continue; 103 | } 104 | if *optional { 105 | continue; 106 | } 107 | 108 | panic!("Expected error not found"); 109 | } 110 | 111 | assert!( 112 | errors 113 | .into_iter() 114 | .filter(|e| e.severity() > CompileMsgKind::Warning) 115 | .count() 116 | == 0 117 | ); 118 | } 119 | 120 | macro_rules! e { 121 | ($n:literal, $path:literal, true) => { 122 | ($n, $path.to_string(), true, CompileMsgKind::Error) 123 | }; 124 | ($n:literal, $path:literal) => { 125 | ($n, $path.to_string(), false, CompileMsgKind::Error) 126 | }; 127 | } 128 | 129 | #[allow(unused_macros)] 130 | macro_rules! w { 131 | ($n:literal, $path:literal, true) => { 132 | ($n, $path.to_string(), true, CompileMsgKind::Warning) 133 | }; 134 | ($n:literal, $path:literal) => { 135 | ($n, $path.to_string(), false, CompileMsgKind::Warning) 136 | }; 137 | } 138 | 139 | #[test] 140 | #[named] 141 | fn simpleModule() { 142 | let _ = testSuccessfulProject!(); 143 | } 144 | 145 | #[test] 146 | #[named] 147 | fn headerModuleErr1() { 148 | testUnsuccessfulProject!() 149 | .into_iter() 150 | .for_each(|(_, e, s)| { 151 | checkErrors(e, &s, &[e!(1, "foo.hpp", true), e!(1, "bar.hpp", true)]); 152 | }); 153 | } 154 | -------------------------------------------------------------------------------- /src/Test/include/header.h: -------------------------------------------------------------------------------- 1 | SUCCESS -------------------------------------------------------------------------------- /src/Test/testProject/headerModuleErr1/bar.hpp: -------------------------------------------------------------------------------- 1 | #include "foo.hpp" -------------------------------------------------------------------------------- /src/Test/testProject/headerModuleErr1/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp" 6 | ], 7 | "moduleHeaderUnits": [ 8 | "bar.hpp", 9 | "foo.hpp" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/Test/testProject/headerModuleErr1/foo.hpp: -------------------------------------------------------------------------------- 1 | #include "bar.hpp" -------------------------------------------------------------------------------- /src/Test/testProject/headerModuleErr1/main.cpp: -------------------------------------------------------------------------------- 1 | inport "bar.hpp"; -------------------------------------------------------------------------------- /src/Test/testProject/simpleModule/bar.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module bar; -------------------------------------------------------------------------------- /src/Test/testProject/simpleModule/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp", 6 | "bar.cpp" 7 | ], 8 | "moduleHeaderUnits": [] 9 | } 10 | -------------------------------------------------------------------------------- /src/Test/testProject/simpleModule/main.cpp: -------------------------------------------------------------------------------- 1 | import bar; 2 | 3 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAsmDecl.cpp: -------------------------------------------------------------------------------- 1 | asm("hello"); -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAsmDeclError1.cpp: -------------------------------------------------------------------------------- 1 | asm 2 | asm() 3 | asm("foo" unused); 4 | asm(ident); 5 | asm("foo") 6 | asm( -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAttrDecl.cpp: -------------------------------------------------------------------------------- 1 | [[rustycpp::unused]][[rustycpp::unused(contents), rustycpp::unused()]][[using rustycpp: unused]][[rustycpp::attrdoesnotexist]]; -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAttrDeclError1.cpp: -------------------------------------------------------------------------------- 1 | [[rustycpp::ignore]][[rustycpp::ignore]] -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAttrDeclError2.cpp: -------------------------------------------------------------------------------- 1 | [[name(]] 2 | [[using name: name::name()]] 3 | [[using 4 | [[using name]] 5 | [[name::]] 6 | [[using name: name::name]] 7 | ; -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAttrError1.cpp: -------------------------------------------------------------------------------- 1 | module [[] alignas alignas(; -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesAttrError2.cpp: -------------------------------------------------------------------------------- 1 | module [[; -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesEmpty.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Destroyerrrocket/rustycpp/b5756acca3ffd830de3c3586b731bb8334823565/src/Test/testSingleFile/parsesEmpty.cpp -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModule.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module foo.foo:bar.bar [[foo::bar]] alignas(invalid); 3 | module :private; 4 | 5 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModuleError1.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module foo.foo.:bar.bar; 3 | 4 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModuleError2.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module foo.foo:; 3 | 4 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModuleError3.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module foo.foo:bar.; 3 | 4 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModuleError4.cpp: -------------------------------------------------------------------------------- 1 | export 2 | 3 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesModuleError5.cpp: -------------------------------------------------------------------------------- 1 | export 2 | module abc; -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesNamedNamespace.cpp: -------------------------------------------------------------------------------- 1 | namespace A { 2 | namespace B { 3 | namespace C {} 4 | } 5 | namespace C {} 6 | namespace C {} 7 | inline namespace D { 8 | namespace E {} 9 | } 10 | } -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesNamedNamespaceError1.cpp: -------------------------------------------------------------------------------- 1 | namespace A 2 | namespace B { 3 | namespace C -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesUsingNamespaceDirective.cpp: -------------------------------------------------------------------------------- 1 | [[rustycpp::checkSymbolMatchTag(false, A)]]; 2 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 3 | [[rustycpp::checkSymbolMatchTag(false, ::A)]]; 4 | [[rustycpp::checkSymbolMatchTag(false, ::B)]]; 5 | [[rustycpp::checkSymbolMatchTag(false, ::A::B)]]; 6 | [[rustycpp::checkSymbolMatchTag(false, A::B)]]; 7 | namespace [[rustycpp::tagDecl(1)]] A { 8 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 9 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 10 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 11 | [[rustycpp::checkSymbolMatchTag(false, ::B)]]; 12 | [[rustycpp::checkSymbolMatchTag(false, ::A::B)]]; 13 | [[rustycpp::checkSymbolMatchTag(false, A::B)]]; 14 | namespace [[rustycpp::tagDecl(2)]] B { 15 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 16 | [[rustycpp::checkSymbolMatchTag(2, B)]]; 17 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 18 | [[rustycpp::checkSymbolMatchTag(false, ::B)]]; 19 | [[rustycpp::checkSymbolMatchTag(2, ::A::B)]]; 20 | [[rustycpp::checkSymbolMatchTag(2, A::B)]]; 21 | } 22 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 23 | [[rustycpp::checkSymbolMatchTag(2, B)]]; 24 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 25 | [[rustycpp::checkSymbolMatchTag(false, ::B)]]; 26 | [[rustycpp::checkSymbolMatchTag(2, ::A::B)]]; 27 | [[rustycpp::checkSymbolMatchTag(2, A::B)]]; 28 | } 29 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 30 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 31 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 32 | [[rustycpp::checkSymbolMatchTag(false, ::B)]]; 33 | [[rustycpp::checkSymbolMatchTag(2, ::A::B)]]; 34 | [[rustycpp::checkSymbolMatchTag(2, A::B)]]; 35 | using namespace A; 36 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 37 | [[rustycpp::checkSymbolMatchTag(2, B)]]; 38 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 39 | [[rustycpp::checkSymbolMatchTag(2, ::B)]]; 40 | [[rustycpp::checkSymbolMatchTag(2, ::A::B)]]; 41 | [[rustycpp::checkSymbolMatchTag(2, A::B)]]; 42 | 43 | 44 | [[rustycpp::checkSymbolMatchTag(false, C::D::E)]]; 45 | [[rustycpp::checkSymbolMatchTag(false, E)]]; 46 | namespace C { 47 | namespace D { 48 | namespace [[rustycpp::tagDecl(3)]] E { 49 | 50 | } 51 | } 52 | } 53 | [[rustycpp::checkSymbolMatchTag(3, C::D::E)]]; 54 | [[rustycpp::checkSymbolMatchTag(false, E)]]; 55 | using namespace C::D; 56 | [[rustycpp::checkSymbolMatchTag(3, C::D::E)]]; 57 | [[rustycpp::checkSymbolMatchTag(3, E)]]; 58 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/parsesUsingNamespaceDirectiveError1.cpp: -------------------------------------------------------------------------------- 1 | namespace A { 2 | 3 | } 4 | using namespace B; 5 | using namespace A::B; 6 | using namespace; 7 | using namespace A -------------------------------------------------------------------------------- /src/Test/testSingleFile/parses__rustycpp__enum.cpp: -------------------------------------------------------------------------------- 1 | namespace Enum { 2 | __rustycpp__(enum A) 3 | } -------------------------------------------------------------------------------- /src/Test/testSingleFile/parses__rustycpp__enumError1.cpp: -------------------------------------------------------------------------------- 1 | __rustycpp__ 2 | __rustycpp__() 3 | __rustycpp__(enum) 4 | __rustycpp__(enum A unused) 5 | __rustycpp__( -------------------------------------------------------------------------------- /src/Test/testSingleFile/qualifiedNameResolution.cpp: -------------------------------------------------------------------------------- 1 | [[rustycpp::checkSymbolMatchTag(false, ::A)]]; 2 | [[rustycpp::checkSymbolMatchTag(false, Enum::A)]]; 3 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::A)]]; 4 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 5 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 6 | [[rustycpp::tagDecl(1)]] 7 | __rustycpp__(enum A); 8 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 9 | [[rustycpp::checkSymbolMatchTag(false, Enum::A)]]; 10 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 11 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 12 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 13 | namespace [[rustycpp::tagDecl(2)]] Enum { 14 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 15 | [[rustycpp::checkSymbolMatchTag(false, Enum::A)]]; 16 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::A)]]; 17 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 18 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 19 | [[rustycpp::tagDecl(3)]] 20 | __rustycpp__(enum A); 21 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 22 | [[rustycpp::checkSymbolMatchTag(3, Enum::A)]]; 23 | [[rustycpp::checkSymbolMatchTag(3, ::Enum::A)]]; 24 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 25 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 26 | namespace [[rustycpp::tagDecl(4)]] Enum { 27 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 28 | [[rustycpp::checkSymbolMatchTag(false, Enum::A)]]; 29 | [[rustycpp::checkSymbolMatchTag(3, ::Enum::A)]]; 30 | [[rustycpp::checkSymbolMatchTag(false, ::Enum::Enum::A)]]; 31 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 32 | [[rustycpp::tagDecl(5)]] 33 | __rustycpp__(enum A); 34 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 35 | [[rustycpp::checkSymbolMatchTag(5, Enum::A)]]; 36 | [[rustycpp::checkSymbolMatchTag(3, ::Enum::A)]]; 37 | [[rustycpp::checkSymbolMatchTag(5, ::Enum::Enum::A)]]; 38 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 39 | } 40 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 41 | [[rustycpp::checkSymbolMatchTag(5, Enum::A)]]; 42 | [[rustycpp::checkSymbolMatchTag(3, ::Enum::A)]]; 43 | [[rustycpp::checkSymbolMatchTag(5, ::Enum::Enum::A)]]; 44 | [[rustycpp::checkSymbolMatchTag(false, Enum::Enum::A)]]; 45 | } 46 | [[rustycpp::checkSymbolMatchTag(1, ::A)]]; 47 | [[rustycpp::checkSymbolMatchTag(3, Enum::A)]]; 48 | [[rustycpp::checkSymbolMatchTag(3, ::Enum::A)]]; 49 | [[rustycpp::checkSymbolMatchTag(5, ::Enum::Enum::A)]]; 50 | [[rustycpp::checkSymbolMatchTag(5, Enum::Enum::A)]]; 51 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/unqualifiedNameResolutionGlobal.cpp: -------------------------------------------------------------------------------- 1 | namespace [[rustycpp::tagDecl(1)]] Enum { 2 | [[rustycpp::tagDecl(2)]] 3 | __rustycpp__(enum A); 4 | } 5 | [[rustycpp::unused]] 6 | alignas(1) 7 | [[rustycpp::tagDecl(3)]] 8 | __rustycpp__(enum A); 9 | 10 | [[rustycpp::checkSymbolMatchTag(true, A)]]; 11 | [[rustycpp::checkSymbolMatchTag(3, A)]]; 12 | [[rustycpp::checkSymbolMatchTag(1, Enum)]]; 13 | [[rustycpp::checkSymbolMatchTag(false, NotFound)]]; 14 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/unqualifiedNameResolutionGlobalErr1.cpp: -------------------------------------------------------------------------------- 1 | namespace [[rustycpp::tagDecl(1)]] Enum { 2 | [[rustycpp::tagDecl(2)]] 3 | __rustycpp__(enum A); 4 | } 5 | 6 | [[rustycpp::tagDecl(3)]] 7 | __rustycpp__(enum A); 8 | __rustycpp__(enum Untagged); 9 | __rustycpp__(enum Repeated); 10 | __rustycpp__(enum Repeated); 11 | 12 | 13 | [[rustycpp::checkSymbolMatchTag(1, A)]]; 14 | [[rustycpp::checkSymbolMatchTag(false, A)]]; 15 | [[rustycpp::checkSymbolMatchTag(true, NotFound)]]; 16 | [[rustycpp::checkSymbolMatchTag(1, Untagged)]]; 17 | [[rustycpp::checkSymbolMatchTag(1, Repeated)]]; 18 | -------------------------------------------------------------------------------- /src/Test/testSingleFile/unqualifiedNameResolutionNamespace.cpp: -------------------------------------------------------------------------------- 1 | [[rustycpp::checkSymbolMatchTag(false, Enum)]]; 2 | [[rustycpp::checkSymbolMatchTag(false, A)]]; 3 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 4 | namespace [[rustycpp::tagDecl(1)]] Enum { 5 | [[rustycpp::checkSymbolMatchTag(1, Enum)]]; 6 | [[rustycpp::checkSymbolMatchTag(false, A)]]; 7 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 8 | [[rustycpp::tagDecl(2)]] 9 | __rustycpp__(enum A); 10 | [[rustycpp::checkSymbolMatchTag(1, Enum)]]; 11 | [[rustycpp::checkSymbolMatchTag(2, A)]]; 12 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 13 | namespace [[rustycpp::tagDecl(3)]] Enum { 14 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 15 | [[rustycpp::checkSymbolMatchTag(2, A)]]; 16 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 17 | [[rustycpp::tagDecl(4)]] 18 | __rustycpp__(enum A); 19 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 20 | [[rustycpp::checkSymbolMatchTag(4, A)]]; 21 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 22 | } 23 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 24 | [[rustycpp::checkSymbolMatchTag(2, A)]]; 25 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 26 | namespace [[rustycpp::tagDecl(5)]] Enum { 27 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 28 | [[rustycpp::checkSymbolMatchTag(4, A)]]; 29 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 30 | [[rustycpp::tagDecl(6)]] 31 | __rustycpp__(enum B); 32 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 33 | [[rustycpp::checkSymbolMatchTag(4, A)]]; 34 | [[rustycpp::checkSymbolMatchTag(6, B)]]; 35 | namespace [[rustycpp::tagDecl(7)]] Enum { 36 | [[rustycpp::checkSymbolMatchTag(7, Enum)]]; 37 | [[rustycpp::checkSymbolMatchTag(4, A)]]; 38 | [[rustycpp::checkSymbolMatchTag(6, B)]]; 39 | [[rustycpp::tagDecl(8)]] 40 | __rustycpp__(enum A); 41 | [[rustycpp::checkSymbolMatchTag(7, Enum)]]; 42 | [[rustycpp::checkSymbolMatchTag(8, A)]]; 43 | [[rustycpp::checkSymbolMatchTag(6, B)]]; 44 | } 45 | [[rustycpp::checkSymbolMatchTag(7, Enum)]]; 46 | [[rustycpp::checkSymbolMatchTag(4, A)]]; 47 | [[rustycpp::checkSymbolMatchTag(6, B)]]; 48 | } 49 | [[rustycpp::checkSymbolMatchTag(3, Enum)]]; 50 | [[rustycpp::checkSymbolMatchTag(2, A)]]; 51 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 52 | } 53 | [[rustycpp::checkSymbolMatchTag(1, Enum)]]; 54 | [[rustycpp::checkSymbolMatchTag(false, A)]]; 55 | [[rustycpp::checkSymbolMatchTag(false, B)]]; 56 | [[rustycpp::tagDecl(9)]] 57 | __rustycpp__(enum A); 58 | [[rustycpp::checkSymbolMatchTag(9, A)]]; 59 | -------------------------------------------------------------------------------- /src/Utils.rs: -------------------------------------------------------------------------------- 1 | //! Various misc utilities for the compiler. 2 | 3 | pub mod Funcs; 4 | #[macro_use] 5 | pub mod Structs; 6 | pub mod CompilerState; 7 | pub mod DebugNode; 8 | pub mod FileMap; 9 | pub mod FoldingContainer; 10 | pub mod ModuleHeaderAtomicLexingList; 11 | pub mod NomLike; 12 | pub mod Parameters; 13 | pub mod StateCompileUnit; 14 | pub mod StringRef; 15 | pub mod UnsafeAllocator; 16 | -------------------------------------------------------------------------------- /src/Utils/CompilerState.rs: -------------------------------------------------------------------------------- 1 | //! State of the compiler 2 | use std::{collections::HashMap, sync::atomic::AtomicBool}; 3 | use std::{ 4 | collections::HashSet, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use crate::Compiler::TranslationUnit; 9 | 10 | use super::FileMap::FileMap; 11 | use super::Parameters::Parameters; 12 | use super::StateCompileUnit::StateCompileUnit; 13 | 14 | /// State of the compiler 15 | #[derive(Debug, Clone)] 16 | pub struct CompilerState { 17 | /// The parameters of the compilation 18 | pub parameters: Arc, 19 | /// The files opened by the compiler 20 | pub compileFiles: Arc>, 21 | /// The translation units that are being compiled 22 | pub translationUnitsFiles: Arc>, 23 | /// The translation units that are being compiled (These are module headers) 24 | pub moduleHeaderUnitsFiles: Arc>, 25 | /// State of the compilation units 26 | pub compileUnits: Arc>, 27 | pub foundErrors: Arc, 28 | } 29 | -------------------------------------------------------------------------------- /src/Utils/DebugNode.rs: -------------------------------------------------------------------------------- 1 | use colored::Colorize; 2 | 3 | #[derive(Clone, Eq)] 4 | pub struct DebugNode { 5 | nameColorless: String, 6 | name: String, 7 | children: Vec, 8 | } 9 | 10 | impl PartialEq for DebugNode { 11 | fn eq(&self, other: &Self) -> bool { 12 | self.nameColorless == other.nameColorless && self.children == other.children 13 | } 14 | } 15 | 16 | impl std::fmt::Debug for DebugNode { 17 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 18 | f.debug_struct("DebugNode") 19 | .field("name", &self.nameColorless) 20 | .field("children", &self.children) 21 | .finish() 22 | } 23 | } 24 | 25 | impl DebugNode { 26 | pub fn new(name: String) -> Self { 27 | let mut result = String::new(); 28 | Self::colorizedTag(&mut result, &name); 29 | Self { 30 | nameColorless: name, 31 | name: result, 32 | children: vec![], 33 | } 34 | } 35 | 36 | pub fn add_child(mut self, child: Self) -> Self { 37 | self.children.push(child); 38 | self 39 | } 40 | 41 | pub fn add_children(mut self, children: Vec) -> Self { 42 | self.children.extend(children); 43 | self 44 | } 45 | 46 | fn colorizedTag(result: &mut String, tag: &str) { 47 | colored::control::set_override(true); 48 | #[allow(clippy::unnecessary_to_owned)] 49 | match tag.char_indices().find(|(_, c)| *c == ':') { 50 | Some((pos, _)) => { 51 | let (a, b) = tag.split_at(pos); 52 | result.push_str(&a.to_owned().bright_cyan().bold().to_string()); 53 | result.push_str(&":".to_owned().bright_white().bold().to_string()); 54 | result.push_str(&b[1..].to_owned().bright_yellow().bold().to_string()); 55 | } 56 | None => { 57 | result.push_str(&tag.to_owned().bright_blue().bold().to_string()); 58 | } 59 | } 60 | colored::control::unset_override(); 61 | } 62 | 63 | fn to_string_rec(&self, depth: &mut Vec, isLast: bool, result: &mut String) { 64 | if !depth.is_empty() { 65 | for last in &depth[1..depth.len()] { 66 | result.push_str(if *last { " " } else { "│ " }); 67 | } 68 | result.push_str(if isLast { "╰─ " } else { "├─ " }); 69 | }; 70 | result.push_str(&self.name); 71 | result.push('\n'); 72 | 73 | if self.children.is_empty() { 74 | return; 75 | } 76 | 77 | for (i, child) in self.children.iter().enumerate() { 78 | depth.push(isLast); 79 | let isLast = i == self.children.len() - 1; 80 | child.to_string_rec(depth, isLast, result); 81 | depth.pop(); 82 | } 83 | } 84 | 85 | fn to_string_mem(&self, depth: u32) -> usize { 86 | self.children 87 | .iter() 88 | .map(|c| c.to_string_mem(depth + 1)) 89 | .sum::() 90 | + self.name.as_bytes().len() 91 | + (depth as usize) * 5 92 | + 1 // nl 93 | } 94 | 95 | pub const fn getChilds(&self) -> &Vec { 96 | &self.children 97 | } 98 | } 99 | 100 | impl ToString for DebugNode { 101 | fn to_string(&self) -> String { 102 | let mut result = String::new(); 103 | result.reserve(self.to_string_mem(0)); 104 | self.to_string_rec(&mut vec![], true, &mut result); 105 | result.pop(); 106 | result 107 | } 108 | } 109 | 110 | /** 111 | * Generate a `DebugNode` tree for testing. 112 | * Usage: debugTree!(name, (child1, ((child11), (child12))), (child2), (child3)) 113 | */ 114 | #[macro_export] 115 | macro_rules! debugTree { 116 | () => { 117 | $crate::Utils::DebugNode::DebugNode::new("".to_string()) 118 | }; 119 | (($name:literal, $($child:tt),*)) => { 120 | debugTree!($name, $($child),*) 121 | }; 122 | ($name:literal, $($child:tt),*) => { 123 | $crate::Utils::DebugNode::DebugNode::new($name.to_string()).add_children(vec![$(debugTree!($child)),*]) 124 | }; 125 | (($name:literal)) => { 126 | debugTree!($name) 127 | }; 128 | ($name:literal) => { 129 | $crate::Utils::DebugNode::DebugNode::new($name.to_string()) 130 | }; 131 | } 132 | -------------------------------------------------------------------------------- /src/Utils/FileMap.rs: -------------------------------------------------------------------------------- 1 | //! Map of paths to files 2 | #![allow(clippy::verbose_file_reads, clippy::cast_possible_truncation)] 3 | 4 | use std::path::{Path, PathBuf}; 5 | use std::{collections::HashMap, fs::File, fs::OpenOptions, io::Read, sync::Arc}; 6 | 7 | use crate::Utils::Structs::CompileFile; 8 | 9 | use super::Parameters::Parameters; 10 | 11 | #[derive(Debug)] 12 | enum Either { 13 | CompileFile(Arc), 14 | NotReadFile(Option), 15 | } 16 | 17 | #[derive(Debug)] 18 | /// A map of all the files that are being used. This is used to avoid opening the same file twice. 19 | pub struct FileMap { 20 | /// Parameters of the compilation 21 | params: Arc, 22 | /// Files opened 23 | files: Vec, 24 | /// Resolved paths 25 | resolvedPaths: HashMap, 26 | /// Reverse-resolved paths 27 | reverseResolved: HashMap, 28 | } 29 | 30 | impl<'a> FileMap { 31 | /// New file map. 32 | pub fn new(params: Arc) -> Self { 33 | let mut me = Self { 34 | params, 35 | files: vec![], 36 | resolvedPaths: HashMap::new(), 37 | reverseResolved: HashMap::new(), 38 | }; 39 | me.files.push(Either::CompileFile(Arc::new(CompileFile::new( 40 | "".to_string(), 41 | "You are trying to read an invalid file", 42 | )))); 43 | me.resolvedPaths.insert("".to_owned(), 0); 44 | me 45 | } 46 | 47 | fn internalReadFile(&mut self, path: u64, mut file: &File) -> Arc { 48 | let mut filecontents: String = String::new(); 49 | let pathStr = self.reverseResolved.get(&path).unwrap(); 50 | if let Err(err) = file.read_to_string(&mut filecontents) { 51 | panic!("Error reading {pathStr}. Error: {err}"); 52 | } 53 | 54 | let res = Arc::new(CompileFile::new(pathStr.clone(), &filecontents)); 55 | *self.files.get_mut(path as usize).unwrap() = Either::CompileFile(res.clone()); 56 | res 57 | } 58 | 59 | /// Get an already opened file. On error, crash. 60 | pub fn getOpenedFile(&mut self, path: u64) -> Arc { 61 | match self.files.get_mut(path as usize) { 62 | Some(Either::CompileFile(v)) => v.clone(), 63 | Some(Either::NotReadFile(file)) => { 64 | let fileRef = file.take().unwrap(); 65 | self.internalReadFile(path, &fileRef) 66 | } 67 | _ => panic!("File not found in visited files: {path}"), 68 | } 69 | } 70 | 71 | /// Get file. If not present, open it. On error, crash. 72 | pub fn getAddFile(&'a mut self, path: &str) -> u64 { 73 | self.getPath(path).unwrap() 74 | } 75 | 76 | /// Can it access the file? Does not need to be previously opened. 77 | pub fn hasFileAccess(&mut self, path: &str) -> bool { 78 | let absolutePath = self.getPath(path); 79 | absolutePath.is_ok() 80 | } 81 | 82 | fn hasFileAccessImpl(&mut self, absolutePath: &str) -> Result { 83 | if let Some(pos) = self.resolvedPaths.get(absolutePath) { 84 | Ok(*pos) 85 | } else { 86 | let filename = std::path::Path::new(absolutePath); 87 | if !filename.extension().map_or(false, |ext| { 88 | ext.eq_ignore_ascii_case("cpp") 89 | || ext.eq_ignore_ascii_case("hpp") 90 | || ext.eq_ignore_ascii_case("h") 91 | }) { 92 | log::error!("Unsuported file type: {}", absolutePath); 93 | } 94 | let file: File = match OpenOptions::new().read(true).open(absolutePath) { 95 | Ok(it) => it, 96 | Err(err) => { 97 | return Err(err.to_string()); 98 | } 99 | }; 100 | let pos = self.files.len() as u64; 101 | self.files.push(Either::NotReadFile(Some(file))); 102 | Ok(pos) 103 | } 104 | } 105 | 106 | /// Add a fake test file. Intened for testing. 107 | #[cfg(test)] 108 | pub fn addTestFile(&mut self, path: String, content: &str) { 109 | self.resolvedPaths 110 | .insert(path.clone(), self.files.len() as u64); 111 | self.reverseResolved 112 | .insert(self.files.len() as u64, path.clone()); 113 | self.files 114 | .push(Either::CompileFile(Arc::new(CompileFile::new( 115 | path, content, 116 | )))); 117 | } 118 | 119 | fn findBestPath(params: &Arc, pathStr: &str) -> Result { 120 | let res: Result = (|| { 121 | let path = Path::new(&pathStr).to_path_buf(); 122 | if path.is_absolute() && path.exists() { 123 | return Ok(path); 124 | } 125 | for dir in ¶ms.includeDirs { 126 | let resultingPath = Path::new(dir).join(&path); 127 | if resultingPath.exists() { 128 | return Ok(resultingPath); 129 | } 130 | } 131 | for dir in ¶ms.includeSystemDirs { 132 | let resultingPath = Path::new(dir).join(&path); 133 | if resultingPath.exists() { 134 | return Ok(resultingPath); 135 | } 136 | } 137 | Err(format!("Could not find file: {pathStr}")) 138 | })(); 139 | res.map(|path| path.canonicalize().unwrap().to_str().unwrap().to_string()) 140 | } 141 | 142 | /// Resolve a path. On error, return error. 143 | pub fn getPath(&mut self, pathStr: &str) -> Result { 144 | if let Some(v) = self.resolvedPaths.get(pathStr) { 145 | Ok(*v) 146 | } else { 147 | let canonical = Self::findBestPath(&self.params, pathStr)?; 148 | if let Some(v) = self.resolvedPaths.get(&canonical) { 149 | let v = *v; 150 | self.resolvedPaths.insert(pathStr.to_string(), v); 151 | Ok(v) 152 | } else { 153 | let pos = self.hasFileAccessImpl(&canonical)?; 154 | self.reverseResolved.insert(pos, canonical.clone()); 155 | if canonical != pathStr { 156 | self.resolvedPaths.insert(canonical, pos); 157 | } 158 | self.resolvedPaths.insert(pathStr.to_string(), pos); 159 | Ok(pos) 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/Utils/FoldingContainer.rs: -------------------------------------------------------------------------------- 1 | use std::hash::Hash; 2 | use std::hash::Hasher; 3 | use std::{any::Any, collections::hash_map::DefaultHasher}; 4 | 5 | use enum_dispatch::enum_dispatch; 6 | 7 | use crate::Utils::StringRef::StringRef; 8 | 9 | #[derive(Hash, Eq, PartialEq, Default)] 10 | pub struct FoldingNode { 11 | bytes: Vec, 12 | } 13 | 14 | impl FoldingNode { 15 | pub const fn new() -> Self { 16 | Self { bytes: vec![] } 17 | } 18 | } 19 | 20 | pub trait PushFoldingNode { 21 | fn push(&mut self, param: &T); 22 | } 23 | 24 | #[enum_dispatch] 25 | pub trait Foldable { 26 | fn foldNode(&self, node: &mut FoldingNode); 27 | 28 | fn newFoldNode(&self) -> FoldingNode { 29 | let mut node = FoldingNode::new(); 30 | self.foldNode(&mut node); 31 | node 32 | } 33 | } 34 | 35 | impl PushFoldingNode for FoldingNode { 36 | fn push(&mut self, param: &T) { 37 | let mut hash = DefaultHasher::new(); 38 | param.type_id().hash(&mut hash); 39 | self.push(&hash.finish()); 40 | param.foldNode(self); 41 | } 42 | } 43 | 44 | impl PushFoldingNode for FoldingNode { 45 | fn push(&mut self, param: &u8) { 46 | self.bytes.push(*param); 47 | } 48 | } 49 | 50 | macro_rules! impl_push_folding_node { 51 | ($($t:ty),*) => { 52 | $( 53 | impl PushFoldingNode<$t> for FoldingNode { 54 | fn push(&mut self, param: &$t) { 55 | self.bytes.extend_from_slice(¶m.to_le_bytes()); 56 | } 57 | } 58 | )* 59 | }; 60 | } 61 | impl_push_folding_node! {i8, i16, i32, i64, i128, isize, u16, u32, u64, u128, usize, f32, f64} 62 | impl PushFoldingNode<&str> for FoldingNode { 63 | fn push(&mut self, param: &&str) { 64 | self.bytes.extend_from_slice(param.as_bytes()); 65 | } 66 | } 67 | 68 | impl PushFoldingNode for FoldingNode { 69 | fn push(&mut self, param: &StringRef) { 70 | let mut hasher = DefaultHasher::new(); 71 | param.hash(&mut hasher); 72 | self.push(&hasher.finish()); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Utils/Funcs.rs: -------------------------------------------------------------------------------- 1 | //! Amalgamation of random funcions 2 | use core::hash::Hash; 3 | use std::collections::HashSet; 4 | 5 | /// All elements of the collection are unique? 6 | pub fn all_unique_elements(iter: T) -> bool 7 | where 8 | T: IntoIterator, 9 | T::Item: Eq + Hash, 10 | { 11 | let mut uniq = HashSet::new(); 12 | iter.into_iter().all(move |x| uniq.insert(x)) 13 | } 14 | -------------------------------------------------------------------------------- /src/Utils/ModuleHeaderAtomicLexingList.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{atomic::AtomicUsize, Mutex}; 2 | 3 | /// This is an overly specific class to handle the atomic lexing of the module 4 | /// headers in parallel 5 | /// 6 | /// This is a tad complicated, you see: we want to lex the module headers in 7 | /// parallel, but we also want to transform includes to these module headers to 8 | /// imports. This becomes a major issue during lexing, where macros from one 9 | /// header can be used in another. This means that while lexing a header, we 10 | /// might need to stop to lex another (We can't just ask the users to provide us 11 | /// the dependency graph of headers, that's like, our job). So, what do we do if 12 | /// two headers depend in another header? We could let one parse that one, and 13 | /// the other one could wait. This would make detecting include loops 14 | /// "relatively" easy, but this can and will introduce bottlenecks. So, instead, 15 | /// We will start lexing whichever header is available next. Unfortunately, this 16 | /// means that we also need to keep track of which headers are not lexed, which 17 | /// are being lexed, which headers are stuck parsing another header, and which 18 | /// ones are done. 19 | /// 20 | /// This class allows parsers to start another parser 21 | 22 | pub struct ModuleHeaderAtomicLexingList { 23 | available: Mutex>>, 24 | lockedThreads: AtomicUsize, 25 | totalThreads: usize, 26 | } 27 | 28 | impl std::fmt::Debug for ModuleHeaderAtomicLexingList { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | f.debug_struct("ModuleHeaderAtomicLexingList") 31 | .field("available", &self.available.lock().unwrap().len()) 32 | .finish_non_exhaustive() 33 | } 34 | } 35 | 36 | impl ModuleHeaderAtomicLexingList { 37 | pub fn new(totalThreads: usize) -> Self { 38 | Self { 39 | available: Mutex::new(Vec::new()), 40 | lockedThreads: AtomicUsize::new(0), 41 | totalThreads, 42 | } 43 | } 44 | 45 | pub fn push(&self, f: Vec>) { 46 | self.available.lock().unwrap().extend(f); 47 | } 48 | 49 | pub fn pop(&self) -> Option> { 50 | self.available.lock().unwrap().pop() 51 | } 52 | 53 | pub fn markThreadLocked(&self) -> bool { 54 | let prevValue = self 55 | .lockedThreads 56 | .fetch_add(1, std::sync::atomic::Ordering::SeqCst); 57 | if prevValue + 1 == self.totalThreads { 58 | self.markThreadUnlocked(); 59 | } 60 | prevValue + 1 == self.totalThreads 61 | } 62 | 63 | pub fn markThreadUnlocked(&self) { 64 | self.lockedThreads 65 | .fetch_sub(1, std::sync::atomic::Ordering::SeqCst); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Utils/NomLike.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::redundant_else)] 2 | 3 | /// This macro is used to create a parser that will return the result of the first parser that succeeds. 4 | #[macro_export] 5 | macro_rules! p_alt { 6 | (@ $input:ident, $r:expr, $parser:expr) => { 7 | $r.or_else(|_| $parser($input)) 8 | }; 9 | (@ $input:ident, $r:expr, $parser:expr, $($parsers:expr)+) => { 10 | p_alt!(@ $input, $r.or_else(|_| $parser(&mut *$input)), $($parsers)+) 11 | }; 12 | 13 | ($type:ty, $parser:expr) => { 14 | |input:$type| { 15 | $parser(input) 16 | } 17 | }; 18 | 19 | ($type:ty, $parser:expr, $($parsers:expr),+) => { 20 | |input:$type| { 21 | p_alt!(@ input, $parser(&mut *input), $($parsers)+) 22 | } 23 | }; 24 | } 25 | 26 | /// This macro is used to select a parser based on a condition. 27 | #[macro_export] 28 | macro_rules! p_guard_condition_select { 29 | (@ $error:expr, $input:ident, ($parser_cond:expr, $parser:expr)) => { 30 | if $parser_cond(&mut *$input) { 31 | return $parser($input); 32 | } else {return $error($input)} 33 | }; 34 | (@ $error:expr, $input:ident, ($parser_cond:expr, $parser:expr), $(($parsers_cond:expr, $parsers:expr)),+) => { 35 | if $parser_cond(&mut *$input) { 36 | return $parser($input); 37 | } else { 38 | p_guard_condition_select!(@ $error, $input, $(($parsers_cond, $parsers))+) 39 | } 40 | }; 41 | 42 | ($type:ty, $error:expr, ($parser_cond:expr, $parser:expr)) => { 43 | |input: $type| { 44 | if $parser_cond(&mut *input) { 45 | return $parser(input); 46 | } else {return $error(input);} 47 | } 48 | }; 49 | 50 | ($type:ty, $error:expr, ($parser_cond:expr, $parser:expr), $(($parsers_cond:expr, $parsers:expr)),+) => { 51 | |input: $type| { 52 | if $parser_cond(&mut *input) { 53 | return $parser(input); 54 | } else { 55 | p_guard_condition_select!(@ $error, input, $(($parsers_cond, $parsers))+) 56 | } 57 | } 58 | }; 59 | } 60 | 61 | /// This macro is used to accumulate all the elements of a parser until it fails or a condition is not met. 62 | #[macro_export] 63 | macro_rules! pvec_accumulate_while { 64 | ($parser:expr) => { 65 | let res = vec![]; 66 | |input| loop { 67 | let r = $parser(input); 68 | if r.is_err() { 69 | return Ok(res); 70 | } 71 | res.push(r.unwrap()); 72 | } 73 | }; 74 | 75 | ($type:ty, $parser:expr, $eval:expr) => { 76 | |input: $type| { 77 | let mut res = vec![]; 78 | while $eval(input) { 79 | let r = $parser(input)?; 80 | res.push(r); 81 | } 82 | return Ok(res); 83 | } 84 | }; 85 | } 86 | 87 | /// This macro is used to drop all the elements of a parser until it fails. 88 | #[macro_export] 89 | macro_rules! pvoid_drop_until_fail { 90 | ($type:ty, $parser:expr) => { 91 | |input: $type| while $parser(&mut *input).is_ok() {} 92 | }; 93 | } 94 | 95 | /// This macro is used to concatenate parsers together. If any of the parsers fail, the macro will return the result including the first parser that failed. 96 | #[macro_export] 97 | macro_rules! p_concatenate_return_on_fail { 98 | (@ $input:ident $res:ident $parser:expr) => { 99 | $res.push($parser($input)); 100 | if $res[$res.len() - 1].is_err() { 101 | return $res; 102 | } 103 | }; 104 | 105 | (@ $input:ident $res:ident $parser:expr, $($parsers:expr),+) => { 106 | $res.push($parser($input)); 107 | if $res[$res.len() - 1].is_err() { 108 | return $res; 109 | } 110 | concatenate_return_on_fail!(@ $input $res $($parsers),+) 111 | }; 112 | 113 | ($parser:expr) => { 114 | |input| vec![$parser(input)] 115 | }; 116 | ($parser:expr, $($parsers:expr),+) => { 117 | |input| { 118 | let mut res = vec![$parser(input)]; 119 | if res[0].is_err() { 120 | return res; 121 | } 122 | } 123 | }; 124 | } 125 | 126 | /// This macro is used to wrap a parser in a closure that takes the input as a parameter. Fixes some issues with the borrow checker. 127 | #[macro_export] 128 | macro_rules! wrap { 129 | ($type:ty, $parser:expr) => { 130 | |input: $type| $parser(input) 131 | }; 132 | } 133 | -------------------------------------------------------------------------------- /src/Utils/Parameters.rs: -------------------------------------------------------------------------------- 1 | //! Parsing of the input config file. 2 | use std::fs; 3 | 4 | use json::{parse, JsonValue}; 5 | 6 | #[derive(Debug, Clone)] 7 | /// The parsed config file. 8 | pub struct Parameters { 9 | /// The path to the input files. 10 | pub translationUnits: Vec, 11 | /// System Include paths. 12 | pub moduleHeaderUnits: Vec, 13 | /// Include paths. 14 | pub includeDirs: Vec, 15 | /// System Include paths. 16 | pub includeSystemDirs: Vec, 17 | pub threadNum: Option, 18 | } 19 | 20 | impl Parameters { 21 | /// new Parameters. 22 | pub const fn new() -> Self { 23 | Self { 24 | translationUnits: Vec::new(), 25 | includeDirs: Vec::new(), 26 | includeSystemDirs: Vec::new(), 27 | moduleHeaderUnits: Vec::new(), 28 | threadNum: None, 29 | } 30 | } 31 | 32 | /// Parses the config file, and returns the results. 33 | pub fn new_file(file: &str) -> Result { 34 | let contents = fs::read_to_string(file).map_err(|x| x.to_string())?; 35 | Self::new().parse(&contents) 36 | } 37 | 38 | /// Parses the config file. 39 | fn parse(mut self, contents: &str) -> Result { 40 | let parsing = parse(contents).map_err(|x| x.to_string())?; 41 | if let JsonValue::Object(obj) = parsing { 42 | for (key, value) in obj.iter() { 43 | match key { 44 | "translationUnits" => { 45 | self.translationUnits = Self::parseStringArray(value, "translationUnits")?; 46 | } 47 | "includeDirs" => { 48 | self.includeDirs = Self::parseStringArray(value, "includeDirs")?; 49 | } 50 | "includeSystemDirs" => { 51 | self.includeSystemDirs = 52 | Self::parseStringArray(value, "includeSystemDirs")?; 53 | } 54 | "moduleHeaderUnits" => { 55 | self.moduleHeaderUnits = 56 | Self::parseStringArray(value, "includeSystemDirs")?; 57 | } 58 | "threadNum" => { 59 | if let JsonValue::Number(num) = value { 60 | self.threadNum = 61 | Some(num.as_fixed_point_u64(0).unwrap().try_into().unwrap()); 62 | } else { 63 | return Err( 64 | "Invalid JSON Paramater: threadNum must be a number".to_string() 65 | ); 66 | } 67 | } 68 | _ => {} 69 | } 70 | } 71 | } else { 72 | return Err("Invalid JSON Paramater: Missing object body".to_string()); 73 | } 74 | Ok(self) 75 | } 76 | 77 | /// Parse a vector of strings. uses the name for error reporting only. 78 | fn parseStringArray(value: &JsonValue, name: &str) -> Result, String> { 79 | let mut res = vec![]; 80 | if let JsonValue::Array(arr) = value { 81 | for val in arr { 82 | if let JsonValue::String(str) = val { 83 | res.push(str.clone()); 84 | } else if let JsonValue::Short(str) = val { 85 | res.push(str.to_string()); 86 | } else { 87 | return Err(format!("Invalid value for {name}: {val:?}")); 88 | } 89 | } 90 | } else { 91 | return Err(format!("Invalid value for {name}: {value:?}")); 92 | } 93 | Ok(res) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/Utils/StateCompileUnit.rs: -------------------------------------------------------------------------------- 1 | //! `StateCompileUnit` used across the compilation in order to allow interaction 2 | //! between the different stages and compilation units 3 | 4 | use std::{ 5 | collections::HashMap, 6 | sync::{atomic::AtomicU64, Mutex}, 7 | }; 8 | 9 | use atomic_enum::atomic_enum; 10 | 11 | use crate::{ 12 | Grammars::DefineAst::DefineAst, 13 | Lex::Token::Token, 14 | ModuleTree::{self, Structs::ModuleOperator}, 15 | Utils::Structs::{CompileMsg, FileTokPos}, 16 | }; 17 | 18 | #[atomic_enum] 19 | #[derive(PartialEq, Eq)] 20 | pub enum StageCompileUnit { 21 | Start, 22 | Lexer, 23 | 24 | Parser, 25 | // Mostly a placeholder, we'll probably need to do more... 26 | Compiler, 27 | } 28 | 29 | /// Used across the compilation in order to allow interaction between the 30 | /// different stages and compilation units 31 | #[derive(Debug)] 32 | pub struct StateCompileUnit { 33 | /// Macro definitions that are enabled at the end of the file 34 | pub macroDefintionsAtTheEndOfTheFile: Mutex>, 35 | /// The module kind of the compilation unit 36 | pub moduleKind: Mutex, 37 | /// Stage done of the translation unit 38 | pub finishedStage: AtomicStageCompileUnit, 39 | /// Stage being done to the translation unit 40 | pub processingStage: AtomicStageCompileUnit, 41 | /// Errors that happened during the compilation 42 | pub errors: Mutex>, 43 | /// tokens (available only after lexing. Parsing will consume t) 44 | pub tokens: Mutex>>>, 45 | 46 | /// The token indexes of the module directives (import/module) 47 | pub moduleOperationPositions: Mutex>, 48 | /// Module operations 49 | pub moduleOperations: Mutex>>, 50 | /// Blocked by an import header. This can happen when we're lexing a module header, and we are unable to continue due to another import. 51 | pub blockedByImportHeader: AtomicU64, 52 | } 53 | 54 | impl StateCompileUnit { 55 | /// Creates a new `StateCompileUnit` 56 | pub fn new() -> Self { 57 | Self { 58 | macroDefintionsAtTheEndOfTheFile: Mutex::new(HashMap::new()), 59 | moduleKind: Mutex::new(ModuleTree::Structs::Node::new_fake()), 60 | finishedStage: AtomicStageCompileUnit::new(StageCompileUnit::Start), 61 | processingStage: AtomicStageCompileUnit::new(StageCompileUnit::Start), 62 | errors: Mutex::new(Vec::new()), 63 | tokens: Mutex::new(None), 64 | moduleOperationPositions: Mutex::new(Vec::new()), 65 | moduleOperations: Mutex::new(None), 66 | blockedByImportHeader: AtomicU64::new(0), 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Utils/StringRef.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::HashMap, 3 | fmt::Display, 4 | ops::Add, 5 | sync::{Arc, Mutex}, 6 | }; 7 | 8 | use lazy_static::lazy_static; 9 | 10 | use bumpalo::Bump; 11 | 12 | struct ProtectedBump { 13 | bump: Bump, 14 | } 15 | unsafe impl Sync for ProtectedBump {} 16 | 17 | struct StringRefMap { 18 | stringsBump: &'static ProtectedBump, 19 | map: HashMap<&'static str, StringRef>, 20 | } 21 | unsafe impl Sync for StringRefMap {} 22 | 23 | lazy_static! { 24 | static ref STRING_REF_MAP: Arc> = { 25 | lazy_static! { 26 | static ref PROTECTED_BUMP: ProtectedBump = ProtectedBump { bump: Bump::new() }; 27 | } 28 | Arc::new(Mutex::new(StringRefMap { 29 | stringsBump: &PROTECTED_BUMP, 30 | map: HashMap::new(), 31 | })) 32 | }; 33 | } 34 | 35 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 36 | struct StringRefImpl { 37 | ptr: &'static str, 38 | } 39 | 40 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 41 | pub struct StringRef { 42 | ptr: &'static StringRefImpl, 43 | } 44 | 45 | impl std::hash::Hash for StringRef { 46 | fn hash(&self, state: &mut H) { 47 | let string: *const str = self.ptr.ptr; 48 | let void: *const () = string.cast::<()>(); 49 | void.hash(state); 50 | } 51 | } 52 | 53 | impl Add for StringRef { 54 | type Output = Self; 55 | 56 | fn add(self, rhs: Self) -> Self::Output { 57 | (self.ptr.ptr.to_string() + rhs.ptr.ptr).to_StringRef() 58 | } 59 | } 60 | unsafe impl Sync for StringRef {} 61 | 62 | impl Display for StringRef { 63 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 64 | self.as_ref().fmt(f) 65 | } 66 | } 67 | 68 | impl StringRef { 69 | pub fn from_str(str: &str) -> Self { 70 | let mut map = STRING_REF_MAP.lock().unwrap(); 71 | 72 | #[allow(clippy::option_if_let_else)] 73 | if let Some(v) = map.map.get(str) { 74 | *v 75 | } else { 76 | let ptr_str = map.stringsBump.bump.alloc_str(str); 77 | let ptr_refStr = map.stringsBump.bump.alloc(StringRefImpl { ptr: ptr_str }); 78 | let me = Self { ptr: ptr_refStr }; 79 | map.map.insert(ptr_str, me); 80 | me 81 | } 82 | } 83 | } 84 | 85 | impl AsRef for StringRef { 86 | fn as_ref(&self) -> &str { 87 | self.ptr.ptr 88 | } 89 | } 90 | 91 | pub trait ToStringRef { 92 | fn to_StringRef(&self) -> StringRef; 93 | } 94 | 95 | impl ToStringRef for String { 96 | fn to_StringRef(&self) -> StringRef { 97 | StringRef::from_str(self.as_str()) 98 | } 99 | } 100 | 101 | impl ToStringRef for &str { 102 | fn to_StringRef(&self) -> StringRef { 103 | StringRef::from_str(self) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Utils/UnsafeAllocator.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | 3 | /** 4 | * This is a wrapper around a bumpalo allocator that will pretend that it has a static lifetime. This is not true, 5 | * but if only used while creating the AST, and as long as the `AstTu` has a copy alive, nothing bad will happen. 6 | * 7 | * This is basically needed because otherwise the creation of the AST would be very annoying, as the lifetimes 8 | * would constantly be nagging me. 9 | */ 10 | pub struct UnsafeAllocatorImpl { 11 | alloc: UnsafeCell, 12 | } 13 | 14 | impl UnsafeAllocatorImpl { 15 | pub fn new() -> Self { 16 | Self { 17 | alloc: UnsafeCell::new(bumpalo::Bump::new()), 18 | } 19 | } 20 | 21 | pub fn alloc(&self) -> &'static bumpalo::Bump { 22 | unsafe { UnsafeCell::get(&self.alloc).as_ref().unwrap_unchecked() } 23 | } 24 | } 25 | 26 | impl Default for UnsafeAllocatorImpl { 27 | fn default() -> Self { 28 | Self::new() 29 | } 30 | } 31 | 32 | pub type UnsafeAllocator = Box; 33 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! A C++ compiler (on the works). 2 | //! 3 | //! Please see the readme at [github](https://github.com/Destroyerrrocket/rustycpp) 4 | //! for more information. 5 | //! 6 | #![feature(const_refs_to_cell)] 7 | #![warn( 8 | missing_docs, 9 | clippy::all, 10 | clippy::pedantic, 11 | clippy::nursery, 12 | clippy::cargo, 13 | clippy::verbose_file_reads, 14 | clippy::unneeded_field_pattern, 15 | clippy::unnecessary_self_imports, 16 | clippy::string_to_string, 17 | clippy::if_then_some_else_none, 18 | clippy::empty_structs_with_brackets, 19 | //clippy::missing_docs_in_private_items 20 | )] 21 | #![allow( 22 | clippy::multiple_crate_versions, 23 | clippy::module_name_repetitions, 24 | non_snake_case, 25 | dead_code 26 | )] 27 | 28 | mod Ast; 29 | mod Compiler; 30 | mod Grammars; 31 | mod Lex; 32 | mod ModuleTree; 33 | mod Parse; 34 | mod Preprocessor; 35 | mod Sema; 36 | mod Utils; 37 | 38 | mod Test; 39 | 40 | use clap::Parser; 41 | use Utils::CompilerState::CompilerState; 42 | use Utils::Parameters::Parameters; 43 | use Utils::Structs::CompileMsg; 44 | 45 | #[derive(Parser)] 46 | #[clap(author, version, about, long_about = None)] 47 | #[doc(hidden)] 48 | struct Args { 49 | /// Filelist to compile 50 | #[clap(short, long)] 51 | files: String, 52 | 53 | /// Print the module depenedency tree of the provided set of files. 54 | #[clap(long, value_parser, default_value = "false")] 55 | printDependencyTree: bool, 56 | 57 | /// Preprocess files and print the result to stdout. 58 | #[clap(long, value_parser, default_value = "false")] 59 | preprocess: bool, 60 | 61 | /// Lexify files and print the result to stdout. 62 | #[clap(long, value_parser, default_value = "false")] 63 | lexify: bool, 64 | } 65 | 66 | /// Wrapper for main, to allow for the use of `?` in main 67 | fn execCompiler( 68 | parameters: Parameters, 69 | args: &Args, 70 | ) -> Result<(), (CompilerState, Vec)> { 71 | let mut compiler = Compiler::Compiler::new(parameters); 72 | if args.printDependencyTree { 73 | compiler.print_dependency_tree() 74 | } else if args.preprocess { 75 | compiler.print_preprocessor() 76 | } else if args.lexify { 77 | compiler.print_lexer() 78 | } else { 79 | compiler.doTheThing() 80 | } 81 | } 82 | 83 | fn main() { 84 | env_logger::init(); 85 | let args = Args::parse(); 86 | if args.files.is_empty() { 87 | log::error!("File list not specified!"); 88 | return; 89 | } 90 | 91 | let parameters = Parameters::new_file(&args.files).unwrap(); 92 | if let Err((compilerState, errors)) = execCompiler(parameters, &args) { 93 | for err in errors { 94 | err.print(&compilerState.compileFiles); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test_samples/check_lexer_string_lit_concat/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test_samples/check_lexer_string_lit_concat/main.cpp: -------------------------------------------------------------------------------- 1 | "hi""ho", 2 | u"hi""ho", 3 | u""u8"ho", 4 | "hi""ho""ha", 5 | u"hi"u"hi"u, 6 | u"hi"u"hi"u"hi", 7 | -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/bar.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | export module bar; 3 | import 4 | import 5 | export void hello() { 6 | HELLO(hello); 7 | std::cout << "Hello World!\n"; 8 | if (GOOD_INCLUSION) { 9 | std::cout << "Good inclusion!\n"; 10 | } 11 | } -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/classic_normal.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | class ImANormalCppFile { 3 | void normalInTheSenceThatIDoNotUseModules() {} 4 | }; -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp", 6 | "main2.cpp", 7 | "bar.cpp", 8 | "foo_interface.cpp", 9 | "foo.cpp", 10 | "foo_partition.cpp", 11 | "foo_partition_interface.cpp", 12 | "foo_partition_2.cpp", 13 | "foo_partition_interface_2.cpp", 14 | "classic_normal.cpp" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo.cpp: -------------------------------------------------------------------------------- 1 | module foo; 2 | import bar; 3 | import :internal; 4 | void foo() { 5 | bar(); 6 | otherfunc(); 7 | } -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo_interface.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | #include 3 | void illegalfunctionthatwewontdetect() { 4 | std::cout << "Hello World!\n"; 5 | module.not.a.real.module(); 6 | export.not.a.real.export(); 7 | import.not.a.real.import(); 8 | } 9 | export module foo; 10 | export import :otherfunc; 11 | import : internal; 12 | export void foo(); 13 | 14 | module : private; 15 | void privFunc() { 16 | internal(); 17 | otherfunc(); 18 | } -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo_partition.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | module foo : otherfunc; 3 | 4 | void otherfunc(); -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo_partition_2.cpp: -------------------------------------------------------------------------------- 1 | module foo : internal ; 2 | import : internal; 3 | void internal(); 4 | -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo_partition_interface.cpp: -------------------------------------------------------------------------------- 1 | module; 2 | // I am aware that this is not a valid module declaration, but for creating the dependency tree, I'm going to be significantly more lenient. 3 | // This will be squashed later on in the compilation. 4 | export module foo : otherfunc &&otherwrongtokens||->*<=>=<=+-%^&|~!@#$?/.,;:[]{}()=internal; 5 | void otherfunc(); 6 | module : private; // I am aware that this is not valid, but I'm choosing to ignore it -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/foo_partition_interface_2.cpp: -------------------------------------------------------------------------------- 1 | export /**/ module foo : internal ; 2 | export void internal(); 3 | -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/hello.h: -------------------------------------------------------------------------------- 1 | #define hell\ 2 | o(E) int hello; -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/importable_header.hpp: -------------------------------------------------------------------------------- 1 | #define GOOD_INCLUSION 1 -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/main.cpp: -------------------------------------------------------------------------------- 1 | import bar 2 | import foo 3 | 4 | int main() { 5 | hello(); 6 | return 1+2; 7 | } -------------------------------------------------------------------------------- /test_samples/module_tree_showcase/main2.cpp: -------------------------------------------------------------------------------- 1 | import foo 2 | 3 | void main() { 4 | } -------------------------------------------------------------------------------- /test_samples/parser_validator/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test_samples/parser_validator/main.cpp: -------------------------------------------------------------------------------- 1 | typedef int MILES, *KLICKSP; 2 | 3 | MILES miles; 4 | KLICKSP klicksp; -------------------------------------------------------------------------------- /test_samples/parser_validator/out.lsp: -------------------------------------------------------------------------------- 1 | //file:/home/pol/Documentos/rustycpp/test_samples/parser_validator/main.cpp 2 | (translation_unit 3 | (declaration_seq 4 | (declaration 5 | (block_declaration 6 | (simple_declaration 7 | (decl_specifier_seq 8 | (decl_specifier typedef) 9 | (decl_specifier 10 | (defining_type_specifier 11 | (type_specifier 12 | (simple_type_specifier int))))) 13 | (init_declarator_list 14 | (init_declarator 15 | (declarator 16 | (ptr_declarator 17 | (noptr_declarator 18 | (declarator_id 19 | (id_expression 20 | (unqualified_id MILES))))))) , 21 | (init_declarator 22 | (declarator 23 | (ptr_declarator 24 | (ptr_operator *) 25 | (ptr_declarator 26 | (noptr_declarator 27 | (declarator_id 28 | (id_expression 29 | (unqualified_id KLICKSP))))))))) ;))))) 30 | -------------------------------------------------------------------------------- /test_samples/tokenizer_mix/compile_list.json: -------------------------------------------------------------------------------- 1 | { 2 | "includeDirs": [], 3 | "includeSystemDirs": [], 4 | "translationUnits": [ 5 | "main.cpp" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /test_samples/tokenizer_mix/hello.h: -------------------------------------------------------------------------------- 1 | #define hell\ 2 | o(E) int hello; -------------------------------------------------------------------------------- /test_samples/tokenizer_mix/main.cpp: -------------------------------------------------------------------------------- 1 | #define HelloBois boisSayHello() 2 | #define HelloWorld(abab, bobo) baba 3 | #define Empty() void 4 | #define STRINGFY(a) ab ## a get() {# a # a ab ## a} 5 | #define LotsOfArgs(format, ...) fprintf(stderr, format __VA_OPT__((,)) __VA_ARGS__) 6 | #define SendToTheVoid(sacrifice...) voidingMan(sacrifice) 7 | #define SendToTheVoid(sacrifice) voidingMan(sacrifice) 8 | #define SendToTheVoid(sacrifice...) voidingMan(sacrifice); 9 | #define SendToTheVoid(sacrifice...) voidingMan (sacrifice/* txt */) 10 | #define MixEverythingHardExtension(a, sacrifice...) voidingMan(__VA_OPT__(#a, __VA_OPT__(data##sacrifice))) 11 | #define BadOne(e) e # 12 | #define PleaseImplementSemanticChecks(one, two, one, two) duplicateThis(two, one) 13 | #undef HelloWorld 14 | #undef Empty 15 | #undef LotsOfArgs 16 | #undef SendToTheVoid 17 | 18 | #if __has_include() 19 | #include "hello.h" 20 | #endif 21 | hello(\ 22 | <\ 23 | ::<)\ 24 | \ 25 | \ 26 | \ 27 | \ 28 | \ 29 | 30 | #define ELEPH\ 31 | ANT el 32 | const char* fo\ 33 | o() { 34 | "hello"; 35 | "\ 36 | hello, fellow tokenizer! \ 37 | I enjoy your 312312 and \xde\ 38 | "; 39 | R"(\ 40 | )"; 41 | R\ 42 | "Hello(\ 43 | )Hello 44 | )Hello"; 45 | 'e'; 46 | auto e = 3'32'3232'323232'32e+123; 47 | return "" /*R"(\ 48 | )"*/; 49 | } 50 | // ELEPHANT 51 | void ELEPHANT() {} 52 | 53 | template 54 | auto add(T a, auto b) {return a+b;} 55 | 56 | int main() { 57 | return 1+2; 58 | } --------------------------------------------------------------------------------