├── options.h ├── .travis.yml ├── .gitmodules ├── CMakeLists.txt ├── .gitignore ├── token.h ├── main.h ├── examples ├── example.h └── example.json ├── LICENSE ├── handler.h ├── main.cc ├── parser.h ├── tokenizer.h ├── type_node.h ├── README.md ├── tokenizer.cc └── parser.cc /options.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct Options 7 | { 8 | std::string classNameMacro; 9 | std::vector functionNameMacro; 10 | std::string enumNameMacro; 11 | std::string propertyNameMacro; 12 | std::vector customMacros; 13 | std::string constructorNameMacro; 14 | }; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - clang 5 | 6 | git: 7 | submodules: false 8 | 9 | before_install: 10 | - sed -i 's/git@github.com:/https:\/\/github.com\//' .gitmodules 11 | - git submodule update --init --recursive 12 | 13 | before_script: 14 | - mkdir build 15 | - cd build 16 | - cmake .. 17 | 18 | script: make 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/rapidjson"] 2 | path = external/rapidjson 3 | url = ../../miloyip/rapidjson.git 4 | [submodule "external/cmdline"] 5 | path = external/cmdline 6 | url = ../../tanakh/cmdline.git 7 | [submodule "external/cxxopts"] 8 | path = external/cxxopts 9 | url = ../../jarro2783/cxxopts.git 10 | [submodule "external/tclap"] 11 | path = external/tclap 12 | url = ../../mirror/tclap.git 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(header-parser) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 2.4) 3 | 4 | SET(SOURCES 5 | "main.cc" 6 | "options.h" 7 | "token.h" 8 | "tokenizer.cc" 9 | "tokenizer.h" 10 | "parser.cc" 11 | "parser.h" 12 | "type_node.h" 13 | ) 14 | 15 | INCLUDE_DIRECTORIES( 16 | "${PROJECT_SOURCE_DIR}/external/rapidjson/include" 17 | "${PROJECT_SOURCE_DIR}/external/tclap/include" 18 | ) 19 | 20 | if(${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR 21 | ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") 22 | add_definitions(-std=c++11) 23 | endif() 24 | ADD_EXECUTABLE(header-parser ${SOURCES} parser.cc parser.h main.h) 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | build 31 | Debug 32 | RelWithDebInfo 33 | Win32 34 | header-parser.dir 35 | 36 | # Qt Creator 37 | CMakeLists.txt.user 38 | 39 | # Visual Studio files 40 | *.vcxproj 41 | *.vcxproj.user 42 | *.vcxproj.filters 43 | *.sln 44 | .vs 45 | 46 | # CMAKE files 47 | CMakeSettings.json 48 | CMakeCache.txt 49 | cmake_install.cmake 50 | CMakeFiles 51 | 52 | # CLion files 53 | cmake-build-*/ 54 | .idea -------------------------------------------------------------------------------- /token.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | enum class TokenType 8 | { 9 | kNone, 10 | kSymbol, 11 | kIdentifier, 12 | kConst 13 | }; 14 | 15 | enum class ConstType 16 | { 17 | kString, 18 | kBoolean, 19 | kUInt32, 20 | kInt32, 21 | kUInt64, 22 | kInt64, 23 | kReal 24 | }; 25 | 26 | struct Token 27 | { 28 | TokenType tokenType; 29 | std::size_t startPos; 30 | std::size_t startLine; 31 | std::string token; 32 | 33 | ConstType constType; 34 | union 35 | { 36 | bool boolConst; 37 | uint32_t uint32Const; 38 | int32_t int32Const; 39 | uint64_t uint64Const; 40 | int64_t int64Const; 41 | double realConst; 42 | }; 43 | std::string stringConst; 44 | }; -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Bas Zalmstra on 19/05/15. 3 | // 4 | 5 | #pragma once 6 | 7 | #include "main.h" 8 | 9 | R_ENUM() 10 | enum Numbers 11 | { 12 | Zero, 13 | One, 14 | Two, 15 | Three =0, 16 | }; 17 | 18 | R_CLASS(Abstract) 19 | class HenkClass : Foo, public Bar 20 | { 21 | public: 22 | R_CLASS() 23 | class SubClass 24 | { 25 | 26 | }; 27 | 28 | class AnonymousClass 29 | { 30 | 31 | }; 32 | 33 | R_FUNCTION(Serializable="yes", Setter=set_member_method, meta(Foo=true, Bar=0xF, Hello=0.5f, World=0.10)) 34 | inline virtual String const & member_method(const String& name, bool enable = true) const = 0; 35 | 36 | R_ENUM() 37 | enum Numbers 38 | { 39 | Zero, 40 | One = 1, 41 | Two, 42 | Three =0, 43 | }; 44 | }; 45 | 46 | enum IgnoredNumbers 47 | { 48 | Four = 4, 49 | Five, 50 | }; 51 | 52 | namespace test { 53 | 54 | R_ENUM() 55 | enum class CXX11Numbers : uint8_t 56 | { 57 | Six, 58 | Seven, 59 | Eight, 60 | Nine, 61 | Ten = 10 62 | }; 63 | 64 | } -------------------------------------------------------------------------------- /examples/example.h: -------------------------------------------------------------------------------- 1 | // Run with: header-parser example.h -c TCLASS -e TENUM -f TFUNC -p TPROPERTY -q TCONSTRUCTOR 2 | 3 | #include 4 | 5 | namespace test 6 | { 7 | TCLASS() 8 | class Foo : public Bar 9 | { 10 | TPROPERTY() 11 | int ThisIsAPrivateProperty; 12 | protected: 13 | TFUNC(Arg=3) 14 | bool ProtectedFunction(std::vector args) const; 15 | 16 | public: 17 | TCONSTRUCTOR() 18 | Foo() = default; 19 | TCONSTRUCTOR(Arg=5) 20 | Foo(int value); 21 | 22 | TENUM() 23 | enum Enum 24 | { 25 | FirstValue, 26 | SecondValue = 3 27 | }; 28 | 29 | public: 30 | TPROPERTY() 31 | int ThisIsAProperty; 32 | }; 33 | 34 | 35 | 36 | TCLASS() 37 | template 38 | class TemplatedFoo : public Base 39 | { 40 | 41 | }; 42 | } 43 | 44 | TCLASS() 45 | struct Test { 46 | TPROPERTY() 47 | int ThisIsAPublicArray1[1]; 48 | 49 | TPROPERTY() 50 | int ThisIsAPublicArray2[CONST]; 51 | 52 | TPROPERTY() 53 | int ThisIsAPublicProperty; 54 | private: 55 | TPROPERTY() 56 | int ThisIsAPrivateProperty; 57 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Bas Zalmstra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // ---------------------------------------------------------------------------------------------------- 7 | 8 | enum AccessSpecifierType 9 | { 10 | PUBLIC, 11 | PROTECTED, 12 | PRIVATE 13 | }; 14 | 15 | // ---------------------------------------------------------------------------------------------------- 16 | 17 | typedef std::string Type; 18 | 19 | // ---------------------------------------------------------------------------------------------------- 20 | 21 | struct FieldInfo 22 | { 23 | std::string name; 24 | Type type; 25 | AccessSpecifierType access_specifier; 26 | }; 27 | 28 | // ---------------------------------------------------------------------------------------------------- 29 | 30 | struct ArgumentInfo 31 | { 32 | std::string name; 33 | Type type; 34 | }; 35 | 36 | // ---------------------------------------------------------------------------------------------------- 37 | 38 | struct FunctionInfo 39 | { 40 | std::string name; 41 | Type return_type; 42 | std::vector arguments; 43 | bool is_const; 44 | }; 45 | 46 | // ---------------------------------------------------------------------------------------------------- 47 | 48 | struct EnumInfo 49 | { 50 | std::string name; 51 | std::vector enumerators; 52 | }; 53 | 54 | // ---------------------------------------------------------------------------------------------------- 55 | 56 | struct ClassInfo 57 | { 58 | std::string name; 59 | std::vector methods; 60 | std::vector fields; 61 | }; 62 | 63 | // ---------------------------------------------------------------------------------------------------- 64 | 65 | struct StructInfo 66 | { 67 | std::string name; 68 | std::vector methods; 69 | std::vector fields; 70 | }; 71 | 72 | // ---------------------------------------------------------------------------------------------------- 73 | 74 | class Handler 75 | { 76 | 77 | public: 78 | 79 | virtual void Class(const ClassInfo& info) {} 80 | virtual void Struct(const StructInfo& info) {} 81 | virtual void Function(const FunctionInfo& info) {} 82 | virtual void Enum(const EnumInfo& info) {} 83 | }; 84 | -------------------------------------------------------------------------------- /main.cc: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | #include "handler.h" 3 | #include "options.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | //---------------------------------------------------------------------------------------------------- 10 | void print_usage() 11 | { 12 | std::cout << "Usage: inputFile" << std::endl; 13 | } 14 | 15 | //---------------------------------------------------------------------------------------------------- 16 | int main(int argc, char** argv) 17 | { 18 | Options options; 19 | std::string inputFile; 20 | try 21 | { 22 | using namespace TCLAP; 23 | 24 | CmdLine cmd("Header Parser"); 25 | 26 | ValueArg enumName("e", "enum", "The name of the enum macro", false, "ENUM", "", cmd); 27 | ValueArg className("c", "class", "The name of the class macro", false, "CLASS", "", cmd); 28 | ValueArg constructorName("q", "constructor", "The name of the constructor macro", false, "CONSTRUCTOR", "", cmd); 29 | MultiArg functionName("f", "function", "The name of the function macro", false, "", cmd); 30 | ValueArg propertyName("p", "property", "The name of the property macro", false, "PROPERTY", "", cmd); 31 | MultiArg customMacro("m", "macro", "Custom macro names to parse", false, "", cmd); 32 | UnlabeledValueArg inputFileArg("inputFile", "The file to process", true, "", "", cmd); 33 | 34 | cmd.parse(argc, argv); 35 | 36 | inputFile = inputFileArg.getValue(); 37 | options.classNameMacro = className.getValue(); 38 | options.enumNameMacro = enumName.getValue(); 39 | options.functionNameMacro = functionName.getValue(); 40 | options.customMacros = customMacro.getValue(); 41 | options.propertyNameMacro = propertyName.getValue(); 42 | options.constructorNameMacro = constructorName.getValue(); 43 | } 44 | catch (TCLAP::ArgException& e) 45 | { 46 | std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl; 47 | return -1; 48 | } 49 | 50 | // Open from file 51 | std::ifstream t(inputFile); 52 | if (!t.is_open()) 53 | { 54 | std::cerr << "Could not open " << inputFile << std::endl; 55 | return -1; 56 | } 57 | 58 | std::stringstream buffer; 59 | buffer << t.rdbuf(); 60 | 61 | Parser parser(options); 62 | if (parser.Parse(buffer.str().c_str())) 63 | std::cout << parser.result() << std::endl; 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tokenizer.h" 4 | #include "options.h" 5 | #include "type_node.h" 6 | #include 7 | #include 8 | #include 9 | 10 | enum class ScopeType 11 | { 12 | kGlobal, 13 | kNamespace, 14 | kClass 15 | }; 16 | 17 | enum class AccessControlType 18 | { 19 | kPublic, 20 | kPrivate, 21 | kProtected 22 | }; 23 | 24 | class Parser : private Tokenizer 25 | { 26 | public: 27 | Parser(const Options& options); 28 | virtual ~Parser(); 29 | 30 | // No copying of parser 31 | Parser(const Parser& other) = delete; 32 | Parser(Parser&& other) = delete; 33 | 34 | // Parses the given input 35 | bool Parse(const char* input); 36 | 37 | /// Returns the result of a previous parse 38 | std::string result() const { return std::string(buffer_.GetString(), buffer_.GetString() + buffer_.GetSize()); } 39 | 40 | protected: 41 | /// Called to parse the next statement. Returns false if there are no more statements. 42 | bool ParseStatement(); 43 | bool ParseDeclaration(Token &token); 44 | bool ParseDirective(); 45 | bool SkipDeclaration(Token &token); 46 | bool ParseProperty(Token &token); 47 | bool ParseEnum(Token &token); 48 | bool ParseMacroMeta(); 49 | bool ParseMetaSequence(); 50 | 51 | void PushScope(const std::string& name, ScopeType scopeType, AccessControlType accessControlType); 52 | void PopScope(); 53 | 54 | bool ParseNamespace(); 55 | bool ParseAccessControl(const Token& token, AccessControlType& type); 56 | 57 | AccessControlType current_access_control_type() const { return topScope_->currentAccessControlType; } 58 | void WriteCurrentAccessControlType(); 59 | 60 | void WriteAccessControlType(AccessControlType type); 61 | bool ParseClass(Token &token); 62 | bool ParseClassTemplate(); 63 | bool ParseFunction(Token &token, const std::string& macroName); 64 | bool ParseConstructor(Token& token); 65 | 66 | bool ParseComment(); 67 | 68 | bool ParseType(); 69 | 70 | std::unique_ptr ParseTypeNode(); 71 | std::string ParseTypeNodeDeclarator(); 72 | 73 | std::string ParseTypename(); 74 | 75 | void WriteToken(const Token &token); 76 | bool ParseCustomMacro(Token & token, const std::string& macroName); 77 | 78 | private: 79 | Options options_; 80 | rapidjson::StringBuffer buffer_; 81 | rapidjson::PrettyWriter writer_; 82 | 83 | struct Scope 84 | { 85 | ScopeType type; 86 | std::string name; 87 | AccessControlType currentAccessControlType; 88 | }; 89 | 90 | Scope scopes_[64]; 91 | Scope *topScope_; 92 | 93 | bool ParseClassTemplateArgument(); 94 | }; 95 | 96 | 97 | -------------------------------------------------------------------------------- /tokenizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct Token; 8 | 9 | class Tokenizer 10 | { 11 | public: 12 | Tokenizer(); 13 | virtual ~Tokenizer(); 14 | 15 | // Do not allow copy or move 16 | Tokenizer(const Tokenizer& other) = delete; 17 | Tokenizer(Tokenizer &&other) = delete; 18 | 19 | /// Reset the parser with the given input text 20 | void Reset(const char* input, std::size_t startingLine = 1); 21 | 22 | /// Parses a token from the stream 23 | bool GetToken(Token& token, bool angleBracketsForStrings = false, bool seperateBraces = false); 24 | 25 | /// Parses an constant from the stream 26 | bool GetConst(Token& token); 27 | 28 | /// Parses an identifier from the stream 29 | bool GetIdentifier(Token& token); 30 | 31 | /// Returns a token to the stream, effectively resetting the cursor to the start of the token 32 | void UngetToken(const Token &token); 33 | 34 | protected: 35 | /** 36 | * @brief Returns the next character from the stream. 37 | * @details Returns the next character from the stream while advancing the cursor position. 38 | */ 39 | char GetChar(); 40 | 41 | /// Resets the cursor to the last read character 42 | void UngetChar(); 43 | 44 | /// Returns the next character from the stream but skips comments and white spaces. 45 | char GetLeadingChar(); 46 | 47 | /// Returns the next character from the stream without modifying the cursor position. 48 | char peek() const; 49 | 50 | /// Returns true if the stream is at the end 51 | bool is_eof() const; 52 | 53 | protected: 54 | /// Returns true if the current token is an identifier with the given text 55 | bool MatchIdentifier(const char* identifier); 56 | 57 | /// Returns true if the current token is a symbol with the given text 58 | bool MatchSymbol(const char* symbol); 59 | 60 | /// Advances the tokenizer past the expected identifier or errors if the symbol is not encountered. 61 | bool RequireIdentifier(const char* identifier); 62 | 63 | /// Advances the tokenizer past the expected symbol or errors if the symbol is not encountered. 64 | bool RequireSymbol(const char* symbol); 65 | 66 | protected: 67 | bool Error(const char* fmt, ...); 68 | bool HasError() const { return hasError_; } 69 | 70 | protected: 71 | /// The input 72 | const char *input_; 73 | 74 | /// The length of the input 75 | std::size_t inputLength_; 76 | 77 | /// Current position in the input 78 | std::size_t cursorPos_; 79 | 80 | /// Current line of the cursor 81 | std::size_t cursorLine_; 82 | 83 | /// The cursor position of the last read character 84 | std::size_t prevCursorPos_; 85 | 86 | /// The cursor line of the the last read character 87 | std::size_t prevCursorLine_; 88 | 89 | /// Stores the last comment block 90 | struct Comment { 91 | std::string text; 92 | std::size_t startLine; 93 | std::size_t endLine; 94 | }; 95 | 96 | Comment comment_; 97 | Comment lastComment_; 98 | 99 | bool hasError_ = false; 100 | }; -------------------------------------------------------------------------------- /type_node.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct TypeNode 7 | { 8 | enum class Type 9 | { 10 | kPointer, 11 | kReference, 12 | kLReference, 13 | kLiteral, 14 | kTemplate, 15 | kFunction 16 | }; 17 | 18 | TypeNode(Type t) : 19 | type(t) {} 20 | 21 | bool isConst = false; 22 | bool isVolatile = false; 23 | bool isMutable = false; 24 | 25 | Type type; 26 | }; 27 | 28 | struct PointerNode : public TypeNode 29 | { 30 | PointerNode(std::unique_ptr && b) : 31 | TypeNode(TypeNode::Type::kPointer), 32 | base(std::forward>(b)){} 33 | 34 | std::unique_ptr base; 35 | }; 36 | 37 | struct ReferenceNode : public TypeNode 38 | { 39 | ReferenceNode(std::unique_ptr && b) : 40 | TypeNode(TypeNode::Type::kReference), 41 | base(std::forward>(b)){} 42 | 43 | std::unique_ptr base; 44 | }; 45 | 46 | struct LReferenceNode : public TypeNode 47 | { 48 | LReferenceNode(std::unique_ptr && b) : 49 | TypeNode(TypeNode::Type::kLReference), 50 | base(std::forward>(b)){} 51 | 52 | std::unique_ptr base; 53 | }; 54 | 55 | struct TemplateNode : public TypeNode 56 | { 57 | TemplateNode(const std::string& n) : 58 | TypeNode(TypeNode::Type::kTemplate), 59 | name(n) {} 60 | 61 | std::string name; 62 | std::vector> arguments; 63 | }; 64 | 65 | struct LiteralNode : public TypeNode 66 | { 67 | LiteralNode(const std::string& ref) : 68 | TypeNode(TypeNode::Type::kLiteral), 69 | name(ref) {} 70 | 71 | std::string name; 72 | }; 73 | 74 | struct FunctionNode : public TypeNode 75 | { 76 | FunctionNode() : TypeNode(TypeNode::Type::kFunction) {} 77 | 78 | struct Argument 79 | { 80 | std::string name; 81 | std::unique_ptr type; 82 | }; 83 | 84 | 85 | std::unique_ptr returns; 86 | std::vector> arguments; 87 | }; 88 | 89 | struct ITypeNodeVisitor 90 | { 91 | virtual void VisitNode(TypeNode &node) = 0; 92 | virtual void Visit(PointerNode& node) = 0; 93 | virtual void Visit(ReferenceNode& node) = 0; 94 | virtual void Visit(LReferenceNode& node) = 0; 95 | virtual void Visit(LiteralNode& node) = 0; 96 | virtual void Visit(TemplateNode& node) = 0; 97 | virtual void Visit(FunctionNode& node) = 0; 98 | }; 99 | 100 | struct TypeNodeVisitor : public ITypeNodeVisitor 101 | { 102 | void VisitNode(TypeNode &node) override 103 | { 104 | switch (node.type) 105 | { 106 | case TypeNode::Type::kPointer: 107 | Visit(reinterpret_cast(node)); 108 | break; 109 | case TypeNode::Type::kReference: 110 | Visit(reinterpret_cast(node)); 111 | break; 112 | case TypeNode::Type::kLReference: 113 | Visit(reinterpret_cast(node)); 114 | break; 115 | case TypeNode::Type::kLiteral: 116 | Visit(reinterpret_cast(node)); 117 | break; 118 | case TypeNode::Type::kTemplate: 119 | Visit(reinterpret_cast(node)); 120 | break; 121 | case TypeNode::Type::kFunction: 122 | Visit(reinterpret_cast(node)); 123 | break; 124 | } 125 | } 126 | }; 127 | -------------------------------------------------------------------------------- /examples/example.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "include", 4 | "file": "vector" 5 | }, 6 | { 7 | "type": "namespace", 8 | "name": "test", 9 | "members": [ 10 | { 11 | "type": "class", 12 | "line": 7, 13 | "meta": {}, 14 | "isstruct": false, 15 | "name": "Foo", 16 | "parents": [ 17 | { 18 | "access": "public", 19 | "name": { 20 | "type": "literal", 21 | "name": "Bar" 22 | } 23 | } 24 | ], 25 | "members": [ 26 | { 27 | "type": "property", 28 | "line": 10, 29 | "meta": {}, 30 | "access": "private", 31 | "dataType": { 32 | "type": "literal", 33 | "name": "int" 34 | }, 35 | "name": "ThisIsAPrivateProperty", 36 | "elements": null 37 | }, 38 | { 39 | "type": "function", 40 | "macro": "TFUNC", 41 | "line": 13, 42 | "meta": { 43 | "Arg": 3 44 | }, 45 | "access": "protected", 46 | "returnType": { 47 | "type": "literal", 48 | "name": "bool" 49 | }, 50 | "name": "ProtectedFunction", 51 | "arguments": [ 52 | { 53 | "type": { 54 | "type": "template", 55 | "name": "std::vector", 56 | "arguments": [ 57 | { 58 | "type": "literal", 59 | "name": "int" 60 | } 61 | ] 62 | }, 63 | "name": "args" 64 | } 65 | ], 66 | "const": true 67 | }, 68 | { 69 | "type": "constructor", 70 | "line": 17, 71 | "meta": {}, 72 | "access": "public", 73 | "name": "Foo", 74 | "arguments": [], 75 | "default": true 76 | }, 77 | { 78 | "type": "constructor", 79 | "line": 19, 80 | "meta": { 81 | "Arg": 5 82 | }, 83 | "access": "public", 84 | "name": "Foo", 85 | "arguments": [ 86 | { 87 | "type": { 88 | "type": "literal", 89 | "name": "int" 90 | }, 91 | "name": "value" 92 | } 93 | ] 94 | }, 95 | { 96 | "type": "enum", 97 | "line": 22, 98 | "access": "public", 99 | "meta": {}, 100 | "name": "Enum", 101 | "members": [ 102 | { 103 | "key": "FirstValue" 104 | }, 105 | { 106 | "key": "SecondValue", 107 | "value": "3" 108 | } 109 | ] 110 | }, 111 | { 112 | "type": "property", 113 | "line": 30, 114 | "meta": {}, 115 | "access": "public", 116 | "dataType": { 117 | "type": "literal", 118 | "name": "int" 119 | }, 120 | "name": "ThisIsAProperty", 121 | "elements": null 122 | } 123 | ] 124 | }, 125 | { 126 | "type": "class", 127 | "line": 36, 128 | "meta": {}, 129 | "template": { 130 | "arguments": [ 131 | { 132 | "typeParameterKey": "typename", 133 | "name": "T" 134 | }, 135 | { 136 | "typeParameterKey": "typename", 137 | "name": "Base", 138 | "defaultType": { 139 | "type": "literal", 140 | "name": "Foo" 141 | } 142 | } 143 | ] 144 | }, 145 | "isstruct": false, 146 | "name": "TemplatedFoo", 147 | "parents": [ 148 | { 149 | "access": "public", 150 | "name": { 151 | "type": "literal", 152 | "name": "Base" 153 | } 154 | } 155 | ], 156 | "members": [] 157 | } 158 | ] 159 | }, 160 | { 161 | "type": "class", 162 | "line": 44, 163 | "meta": {}, 164 | "isstruct": true, 165 | "name": "Test", 166 | "members": [ 167 | { 168 | "type": "property", 169 | "line": 46, 170 | "meta": {}, 171 | "access": "public", 172 | "dataType": { 173 | "type": "literal", 174 | "name": "int" 175 | }, 176 | "name": "ThisIsAPublicArray1", 177 | "elements": "1" 178 | }, 179 | { 180 | "type": "property", 181 | "line": 49, 182 | "meta": {}, 183 | "access": "public", 184 | "dataType": { 185 | "type": "literal", 186 | "name": "int" 187 | }, 188 | "name": "ThisIsAPublicArray2", 189 | "elements": "CONST" 190 | }, 191 | { 192 | "type": "property", 193 | "line": 52, 194 | "meta": {}, 195 | "access": "public", 196 | "dataType": { 197 | "type": "literal", 198 | "name": "int" 199 | }, 200 | "name": "ThisIsAPublicProperty", 201 | "elements": null 202 | }, 203 | { 204 | "type": "property", 205 | "line": 55, 206 | "meta": {}, 207 | "access": "private", 208 | "dataType": { 209 | "type": "literal", 210 | "name": "int" 211 | }, 212 | "name": "ThisIsAPrivateProperty", 213 | "elements": null 214 | } 215 | ] 216 | } 217 | ] 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # header-parser 2 | Extracts information from an annotated C++ header and outputs it as JSON for use in code generation tools. 3 | 4 | [![Build Status](https://travis-ci.org/baszalmstra/header-parser.svg)](https://travis-ci.org/baszalmstra/header-parser) 5 | 6 | # Why? 7 | During my game development carreer I have found the availability of RunTime Type Information very useful in for example the following use cases: 8 | 9 | * Serialization (binary and text based) 10 | * Script bindings 11 | * Remote procedure calls 12 | * Generating models (for use in other languages) 13 | * Dependency injection 14 | * Reflection 15 | 16 | Adding this information to my C++ source always resulted in a lot of code bloat. This library provides a way of extracting RTTI information for processing in other tools almost straight from C++ constructs. 17 | 18 | Because the library is used to generate code it assumes the passed in code will compile. This makes sense because if the code already doesn't compile it also doesn't make sense to generate code for it (because it doesn't compile anyway). This allows the parser to be very simple and most importantly be extremely fast. For example it does not perform name lookups because it can assume names are already valid in the code project. It also doesn't substitute macros or includes. However, because of this, macros that define C++ constructs will not resolve correctly and therefor will not be included in the output. 19 | 20 | This library is currently used in production at [Abbey Games](http://abbeygames.com) to generate extensive C++ Lua bindings and documentation for internal use. 21 | 22 | # Example 23 | 24 | Given the input file: 25 | 26 | ```cpp 27 | #include 28 | 29 | namespace test 30 | { 31 | TCLASS() 32 | class Foo : public Bar 33 | { 34 | TPROPERTY() 35 | int ThisIsAPrivateProperty; 36 | protected: 37 | TFUNC(Arg=3) 38 | bool ProtectedFunction(std::vector args) const; 39 | 40 | public: 41 | TCONSTRUCTOR() 42 | Foo() = default; 43 | TCONSTRUCTOR(Arg=5) 44 | Foo(int value); 45 | 46 | TENUM() 47 | enum Enum 48 | { 49 | FirstValue, 50 | SecondValue = 3 51 | }; 52 | 53 | public: 54 | TPROPERTY() 55 | int ThisIsAProperty; 56 | }; 57 | 58 | 59 | 60 | TCLASS() 61 | template 62 | class TemplatedFoo : public Base 63 | { 64 | 65 | }; 66 | } 67 | 68 | TCLASS() 69 | struct Test { 70 | TPROPERTY() 71 | int ThisIsAPublicArray1[1]; 72 | 73 | TPROPERTY() 74 | int ThisIsAPublicArray2[CONST]; 75 | 76 | TPROPERTY() 77 | int ThisIsAPublicProperty; 78 | private: 79 | TPROPERTY() 80 | int ThisIsAPrivateProperty; 81 | }; 82 | ``` 83 | 84 | When ran with `header-parser example.h -c TCLASS -e TENUM -f TFUNC -p TPROPERTY -q TCONSTRUCTOR` produces the following output: 85 | 86 | ```json 87 | [ 88 | { 89 | "type": "include", 90 | "file": "vector" 91 | }, 92 | { 93 | "type": "namespace", 94 | "name": "test", 95 | "members": [ 96 | { 97 | "type": "class", 98 | "line": 7, 99 | "meta": {}, 100 | "isstruct": false, 101 | "name": "Foo", 102 | "parents": [ 103 | { 104 | "access": "public", 105 | "name": { 106 | "type": "literal", 107 | "name": "Bar" 108 | } 109 | } 110 | ], 111 | "members": [ 112 | { 113 | "type": "property", 114 | "line": 10, 115 | "meta": {}, 116 | "access": "private", 117 | "dataType": { 118 | "type": "literal", 119 | "name": "int" 120 | }, 121 | "name": "ThisIsAPrivateProperty", 122 | "elements": null 123 | }, 124 | { 125 | "type": "function", 126 | "macro": "TFUNC", 127 | "line": 13, 128 | "meta": { 129 | "Arg": 3 130 | }, 131 | "access": "protected", 132 | "returnType": { 133 | "type": "literal", 134 | "name": "bool" 135 | }, 136 | "name": "ProtectedFunction", 137 | "arguments": [ 138 | { 139 | "type": { 140 | "type": "template", 141 | "name": "std::vector", 142 | "arguments": [ 143 | { 144 | "type": "literal", 145 | "name": "int" 146 | } 147 | ] 148 | }, 149 | "name": "args" 150 | } 151 | ], 152 | "const": true 153 | }, 154 | { 155 | "type": "constructor", 156 | "line": 17, 157 | "meta": {}, 158 | "access": "public", 159 | "name": "Foo", 160 | "arguments": [], 161 | "default": true 162 | }, 163 | { 164 | "type": "constructor", 165 | "line": 19, 166 | "meta": { 167 | "Arg": 5 168 | }, 169 | "access": "public", 170 | "name": "Foo", 171 | "arguments": [ 172 | { 173 | "type": { 174 | "type": "literal", 175 | "name": "int" 176 | }, 177 | "name": "value" 178 | } 179 | ] 180 | }, 181 | { 182 | "type": "enum", 183 | "line": 22, 184 | "access": "public", 185 | "meta": {}, 186 | "name": "Enum", 187 | "members": [ 188 | { 189 | "key": "FirstValue" 190 | }, 191 | { 192 | "key": "SecondValue", 193 | "value": "3" 194 | } 195 | ] 196 | }, 197 | { 198 | "type": "property", 199 | "line": 30, 200 | "meta": {}, 201 | "access": "public", 202 | "dataType": { 203 | "type": "literal", 204 | "name": "int" 205 | }, 206 | "name": "ThisIsAProperty", 207 | "elements": null 208 | } 209 | ] 210 | }, 211 | { 212 | "type": "class", 213 | "line": 36, 214 | "meta": {}, 215 | "template": { 216 | "arguments": [ 217 | { 218 | "typeParameterKey": "typename", 219 | "name": "T" 220 | }, 221 | { 222 | "typeParameterKey": "typename", 223 | "name": "Base", 224 | "defaultType": { 225 | "type": "literal", 226 | "name": "Foo" 227 | } 228 | } 229 | ] 230 | }, 231 | "isstruct": false, 232 | "name": "TemplatedFoo", 233 | "parents": [ 234 | { 235 | "access": "public", 236 | "name": { 237 | "type": "literal", 238 | "name": "Base" 239 | } 240 | } 241 | ], 242 | "members": [] 243 | } 244 | ] 245 | }, 246 | { 247 | "type": "class", 248 | "line": 44, 249 | "meta": {}, 250 | "isstruct": true, 251 | "name": "Test", 252 | "members": [ 253 | { 254 | "type": "property", 255 | "line": 46, 256 | "meta": {}, 257 | "access": "public", 258 | "dataType": { 259 | "type": "literal", 260 | "name": "int" 261 | }, 262 | "name": "ThisIsAPublicArray1", 263 | "elements": "1" 264 | }, 265 | { 266 | "type": "property", 267 | "line": 49, 268 | "meta": {}, 269 | "access": "public", 270 | "dataType": { 271 | "type": "literal", 272 | "name": "int" 273 | }, 274 | "name": "ThisIsAPublicArray2", 275 | "elements": "CONST" 276 | }, 277 | { 278 | "type": "property", 279 | "line": 52, 280 | "meta": {}, 281 | "access": "public", 282 | "dataType": { 283 | "type": "literal", 284 | "name": "int" 285 | }, 286 | "name": "ThisIsAPublicProperty", 287 | "elements": null 288 | }, 289 | { 290 | "type": "property", 291 | "line": 55, 292 | "meta": {}, 293 | "access": "private", 294 | "dataType": { 295 | "type": "literal", 296 | "name": "int" 297 | }, 298 | "name": "ThisIsAPrivateProperty", 299 | "elements": null 300 | } 301 | ] 302 | } 303 | ] 304 | ``` -------------------------------------------------------------------------------- /tokenizer.cc: -------------------------------------------------------------------------------- 1 | #include "tokenizer.h" 2 | #include "token.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace { 11 | static const char EndOfFileChar = std::char_traits::to_char_type(std::char_traits::eof()); 12 | } 13 | 14 | //-------------------------------------------------------------------------------------------------- 15 | Tokenizer::Tokenizer() : 16 | input_(nullptr), 17 | inputLength_(0), 18 | cursorPos_(0), 19 | cursorLine_(0) 20 | { 21 | 22 | } 23 | 24 | //-------------------------------------------------------------------------------------------------- 25 | Tokenizer::~Tokenizer() 26 | { 27 | 28 | } 29 | 30 | //-------------------------------------------------------------------------------------------------- 31 | void Tokenizer::Reset(const char* input, std::size_t startingLine) 32 | { 33 | input_ = input; 34 | inputLength_ = std::char_traits::length(input); 35 | cursorPos_ = 0; 36 | cursorLine_ = startingLine; 37 | } 38 | 39 | //-------------------------------------------------------------------------------------------------- 40 | char Tokenizer::GetChar() 41 | { 42 | prevCursorPos_ = cursorPos_; 43 | prevCursorLine_ = cursorLine_; 44 | 45 | if(is_eof()) 46 | { 47 | ++cursorPos_; // Do continue so UngetChar does what you think it does 48 | return EndOfFileChar; 49 | } 50 | 51 | char c = input_[cursorPos_]; 52 | 53 | // New line moves the cursor to the new line 54 | if(c == '\n') 55 | cursorLine_++; 56 | 57 | cursorPos_++; 58 | return c; 59 | } 60 | 61 | //-------------------------------------------------------------------------------------------------- 62 | void Tokenizer::UngetChar() 63 | { 64 | cursorLine_ = prevCursorLine_; 65 | cursorPos_ = prevCursorPos_; 66 | } 67 | 68 | //-------------------------------------------------------------------------------------------------- 69 | char Tokenizer::peek() const 70 | { 71 | return !is_eof() ? 72 | input_[cursorPos_] : 73 | EndOfFileChar; 74 | } 75 | 76 | //-------------------------------------------------------------------------------------------------- 77 | char Tokenizer::GetLeadingChar() 78 | { 79 | if (!comment_.text.empty()) 80 | lastComment_ = comment_; 81 | 82 | comment_.text = ""; 83 | comment_.startLine = cursorLine_; 84 | comment_.endLine = cursorLine_; 85 | 86 | char c; 87 | for(c = GetChar(); c != EndOfFileChar; c = GetChar()) 88 | { 89 | // If this is a whitespace character skip it 90 | std::char_traits::int_type intc = std::char_traits::to_int_type(c); 91 | 92 | // In case of a new line 93 | if (c == '\n') 94 | { 95 | if (!comment_.text.empty()) 96 | comment_.text += "\n"; 97 | continue; 98 | } 99 | 100 | if(std::isspace(intc) || std::iscntrl(intc)) 101 | continue; 102 | 103 | // If this is a single line comment 104 | char next = peek(); 105 | if(c == '/' && next == '/') 106 | { 107 | std::vector lines; 108 | 109 | size_t indentationLastLine = 0; 110 | while (!is_eof() && c == '/' && next == '/') 111 | { 112 | // Search for the end of the line 113 | std::string line; 114 | for (c = GetChar(); 115 | c != EndOfFileChar && c != '\n'; 116 | c = GetChar()) 117 | { 118 | line += c; 119 | } 120 | 121 | // Store the line 122 | size_t lastSlashIndex = line.find_first_not_of("/"); 123 | if (lastSlashIndex == std::string::npos) 124 | line = ""; 125 | else 126 | line = line.substr(lastSlashIndex); 127 | 128 | size_t firstCharIndex = line.find_first_not_of(" \t"); 129 | if (firstCharIndex == std::string::npos) 130 | line = ""; 131 | else 132 | line = line.substr(firstCharIndex); 133 | 134 | if (firstCharIndex > indentationLastLine && !lines.empty()) 135 | lines.back() += std::string(" ") + line; 136 | else 137 | { 138 | lines.emplace_back(std::move(line)); 139 | indentationLastLine = firstCharIndex; 140 | } 141 | 142 | // Check the next line 143 | while (!is_eof() && std::isspace(c = GetChar())); 144 | 145 | if (!is_eof()) 146 | next = peek(); 147 | } 148 | 149 | // Unget previously get char 150 | if (!is_eof()) 151 | UngetChar(); 152 | 153 | // Build comment string 154 | std::stringstream ss; 155 | for (size_t i = 0; i < lines.size(); ++i) 156 | { 157 | if (i > 0) 158 | ss << "\n"; 159 | ss << lines[i]; 160 | } 161 | 162 | comment_.text = ss.str(); 163 | comment_.endLine = cursorLine_; 164 | 165 | // Go to the next 166 | continue; 167 | } 168 | 169 | // If this is a block comment 170 | if(c == '/' && next == '*') 171 | { 172 | // Search for the end of the block comment 173 | std::vector lines; 174 | std::string line; 175 | for (c = GetChar(), next = peek(); 176 | c != EndOfFileChar && (c != '*' || next != '/'); 177 | c = GetChar(), next = peek()) 178 | { 179 | if (c == '\n') 180 | { 181 | if (!lines.empty() || !line.empty()) 182 | lines.emplace_back(line); 183 | line.clear(); 184 | } 185 | else 186 | { 187 | if (!line.empty() || !(std::isspace(c) || c == '*')) 188 | line += c; 189 | } 190 | } 191 | 192 | // Skip past the slash 193 | if(c != EndOfFileChar) 194 | GetChar(); 195 | 196 | // Skip past new lines and spaces 197 | while (!is_eof() && std::isspace(c = GetChar())); 198 | if (!is_eof()) 199 | UngetChar(); 200 | 201 | // Remove empty lines from the back 202 | while (!lines.empty() && lines.back().empty()) 203 | lines.pop_back(); 204 | 205 | // Build comment string 206 | std::stringstream ss; 207 | for (size_t i = 0; i < lines.size(); ++i) 208 | { 209 | if (i > 0) 210 | ss << "\n"; 211 | ss << lines[i]; 212 | } 213 | 214 | comment_.text = ss.str(); 215 | comment_.endLine = cursorLine_; 216 | 217 | // Move to the next character 218 | continue; 219 | } 220 | 221 | break; 222 | } 223 | 224 | return c; 225 | } 226 | 227 | //-------------------------------------------------------------------------------------------------- 228 | bool Tokenizer::GetToken(Token &token, bool angleBracketsForStrings, bool seperateBraces) 229 | { 230 | // Get the next character 231 | char c = GetLeadingChar(); 232 | char p = peek(); 233 | std::char_traits::int_type intc = std::char_traits::to_int_type(c); 234 | std::char_traits::int_type intp = std::char_traits::to_int_type(p); 235 | 236 | if(c == EndOfFileChar) 237 | { 238 | UngetChar(); 239 | return false; 240 | } 241 | 242 | // Record the start of the token position 243 | token.startPos = prevCursorPos_; 244 | token.startLine = prevCursorLine_; 245 | token.token.clear(); 246 | token.tokenType = TokenType::kNone; 247 | 248 | // Alphanumeric token 249 | if(std::isalpha(intc) || c == '_') 250 | { 251 | // Read the rest of the alphanumeric characters 252 | do 253 | { 254 | token.token.push_back(c); 255 | c = GetChar(); 256 | intc = std::char_traits::to_int_type(c); 257 | } while(std::isalnum(intc) || c == '_'); 258 | 259 | // Put back the last read character since it's not part of the identifier 260 | UngetChar(); 261 | 262 | // Set the type of the token 263 | token.tokenType = TokenType::kIdentifier; 264 | 265 | if(token.token == "true") 266 | { 267 | token.tokenType = TokenType::kConst; 268 | token.constType = ConstType::kBoolean; 269 | token.boolConst = true; 270 | } 271 | else if(token.token == "false") 272 | { 273 | token.tokenType = TokenType::kConst; 274 | token.constType = ConstType::kBoolean; 275 | token.boolConst = false; 276 | } 277 | 278 | return true; 279 | } 280 | // Constant 281 | else if(std::isdigit(intc) || ((c == '-' || c == '+') && std::isdigit(intp))) 282 | { 283 | bool isFloat = false; 284 | bool isHex = false; 285 | bool isNegated = c == '-'; 286 | do 287 | { 288 | if(c == '.') 289 | isFloat = true; 290 | 291 | if(c == 'x' || c == 'X') 292 | isHex = true; 293 | 294 | token.token.push_back(c); 295 | c = GetChar(); 296 | intc = std::char_traits::to_int_type(c); 297 | 298 | } while(std::isdigit(intc) || 299 | (!isFloat && c == '.') || 300 | (!isHex && (c == 'X' || c == 'x')) || 301 | (isHex && std::isxdigit(intc))); 302 | 303 | if(!isFloat || (c != 'f' && c != 'F')) 304 | UngetChar(); 305 | 306 | token.tokenType = TokenType::kConst; 307 | if(!isFloat) 308 | { 309 | try 310 | { 311 | if(isNegated) 312 | { 313 | token.int32Const = std::stoi(token.token, 0, 0); 314 | token.constType = ConstType::kInt32; 315 | } 316 | else 317 | { 318 | token.uint32Const = std::stoul(token.token, 0, 0); 319 | token.constType = ConstType::kUInt32; 320 | } 321 | } 322 | catch(std::out_of_range) 323 | { 324 | if(isNegated) 325 | { 326 | token.int64Const = std::stoll(token.token, 0, 0); 327 | token.constType = ConstType::kInt64; 328 | } 329 | else 330 | { 331 | token.uint64Const = std::stoull(token.token, 0, 0); 332 | token.constType = ConstType::kUInt64; 333 | } 334 | } 335 | } 336 | else 337 | { 338 | token.realConst = std::stod(token.token); 339 | token.constType = ConstType::kReal; 340 | } 341 | 342 | return true; 343 | } 344 | else if (c == '"' || (angleBracketsForStrings && c == '<')) 345 | { 346 | const char closingElement = c == '"' ? '"' : '>'; 347 | 348 | c = GetChar(); 349 | while (c != closingElement && std::char_traits::not_eof(std::char_traits::to_int_type(c))) 350 | { 351 | if(c == '\\') 352 | { 353 | c = GetChar(); 354 | if(!std::char_traits::not_eof(std::char_traits::to_int_type(c))) 355 | break; 356 | else if(c == 'n') 357 | c = '\n'; 358 | else if(c == 't') 359 | c = '\t'; 360 | else if(c == 'r') 361 | c = '\r'; 362 | else if(c == '"') 363 | c = '"'; 364 | } 365 | 366 | token.token.push_back(c); 367 | c = GetChar(); 368 | } 369 | 370 | if (c != closingElement) 371 | UngetChar(); 372 | 373 | token.tokenType = TokenType::kConst; 374 | token.constType = ConstType::kString; 375 | token.stringConst = token.token; 376 | 377 | return true; 378 | } 379 | // Symbol 380 | else 381 | { 382 | // Push back the symbol 383 | token.token.push_back(c); 384 | 385 | #define PAIR(cc,dd) (c==cc&&d==dd) /* Comparison macro for two characters */ 386 | const char d = GetChar(); 387 | if(PAIR('<', '<') || 388 | PAIR('-', '>') || 389 | (!seperateBraces && PAIR('>', '>')) || 390 | PAIR('!', '=') || 391 | PAIR('<', '=') || 392 | PAIR('>', '=') || 393 | PAIR('+', '+') || 394 | PAIR('-', '-') || 395 | PAIR('+', '=') || 396 | PAIR('-', '=') || 397 | PAIR('*', '=') || 398 | PAIR('/', '=') || 399 | PAIR('^', '=') || 400 | PAIR('|', '=') || 401 | PAIR('&', '=') || 402 | PAIR('~', '=') || 403 | PAIR('%', '=') || 404 | PAIR('&', '&') || 405 | PAIR('|', '|') || 406 | PAIR('=', '=') || 407 | PAIR(':', ':') 408 | ) 409 | #undef PAIR 410 | { 411 | token.token.push_back(d); 412 | } 413 | else 414 | UngetChar(); 415 | 416 | token.tokenType = TokenType::kSymbol; 417 | 418 | return true; 419 | } 420 | 421 | return false; 422 | } 423 | 424 | //-------------------------------------------------------------------------------------------------- 425 | bool Tokenizer::is_eof() const 426 | { 427 | return cursorPos_ >= inputLength_; 428 | } 429 | 430 | //-------------------------------------------------------------------------------------------------- 431 | bool Tokenizer::GetConst(Token &token) 432 | { 433 | if (!GetToken(token)) 434 | return false; 435 | 436 | if (token.tokenType == TokenType::kConst) 437 | return true; 438 | 439 | UngetToken(token); 440 | return false; 441 | } 442 | 443 | //-------------------------------------------------------------------------------------------------- 444 | bool Tokenizer::GetIdentifier(Token &token) 445 | { 446 | if(!GetToken(token)) 447 | return false; 448 | 449 | if(token.tokenType == TokenType::kIdentifier) 450 | return true; 451 | 452 | UngetToken(token); 453 | return false; 454 | } 455 | 456 | //-------------------------------------------------------------------------------------------------- 457 | void Tokenizer::UngetToken(const Token &token) 458 | { 459 | cursorLine_ = token.startLine; 460 | cursorPos_ = token.startPos; 461 | } 462 | 463 | //-------------------------------------------------------------------------------------------------- 464 | bool Tokenizer::MatchIdentifier(const char *identifier) 465 | { 466 | Token token; 467 | if(GetToken(token)) 468 | { 469 | if(token.tokenType == TokenType::kIdentifier && token.token == identifier) 470 | return true; 471 | 472 | UngetToken(token); 473 | } 474 | 475 | return false; 476 | } 477 | 478 | //-------------------------------------------------------------------------------------------------- 479 | bool Tokenizer::MatchSymbol(const char *symbol) 480 | { 481 | Token token; 482 | if(GetToken(token, false, std::char_traits::length(symbol) == 1 && symbol[0] == '>')) 483 | { 484 | if(token.tokenType == TokenType::kSymbol && token.token == symbol) 485 | return true; 486 | 487 | UngetToken(token); 488 | } 489 | 490 | return false; 491 | } 492 | 493 | //-------------------------------------------------------------------------------------------------- 494 | bool Tokenizer::RequireIdentifier(const char *identifier) 495 | { 496 | if(!MatchIdentifier(identifier)) 497 | return Error("Missing identifier %s", identifier); 498 | return true; 499 | } 500 | 501 | //-------------------------------------------------------------------------------------------------- 502 | bool Tokenizer::RequireSymbol(const char *symbol) 503 | { 504 | if (!MatchSymbol(symbol)) 505 | return Error("Missing symbol %s", symbol); 506 | return true; 507 | } 508 | 509 | //------------------------------------------------------------------------------------------------- 510 | bool Tokenizer::Error(const char* fmt, ...) 511 | { 512 | char buffer[512]; 513 | va_list args; 514 | va_start(args, fmt); 515 | vsnprintf(buffer, 512, fmt, args); 516 | va_end(args); 517 | printf("ERROR: %d:%d: %s", static_cast(cursorLine_), 0, buffer); 518 | hasError_ = true; 519 | return false; 520 | } -------------------------------------------------------------------------------- /parser.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "parser.h" 5 | #include "token.h" 6 | #include 7 | 8 | //------------------------------------------------------------------------------------------------- 9 | // Class used to write a typenode structure to json 10 | //------------------------------------------------------------------------------------------------- 11 | class TypeNodeWriter : public TypeNodeVisitor 12 | { 13 | public: 14 | TypeNodeWriter(rapidjson::PrettyWriter& writer) : 15 | writer_(writer) {} 16 | 17 | //------------------------------------------------------------------------------------------------- 18 | virtual void Visit(FunctionNode& node) override 19 | { 20 | writer_.String("type"); 21 | writer_.String("function"); 22 | 23 | writer_.String("returnType"); 24 | VisitNode(*node.returns); 25 | 26 | writer_.String("arguments"); 27 | writer_.StartArray(); 28 | for (auto& arg : node.arguments) 29 | { 30 | writer_.StartObject(); 31 | if (!arg->name.empty()) 32 | { 33 | writer_.String("name"); 34 | writer_.String(arg->name.c_str()); 35 | } 36 | writer_.String("type"); 37 | VisitNode(*arg->type); 38 | writer_.EndObject(); 39 | } 40 | writer_.EndArray(); 41 | } 42 | 43 | //------------------------------------------------------------------------------------------------- 44 | virtual void Visit(LReferenceNode& node) override 45 | { 46 | writer_.String("type"); 47 | writer_.String("lreference"); 48 | 49 | writer_.String("baseType"); 50 | VisitNode(*node.base); 51 | } 52 | 53 | //------------------------------------------------------------------------------------------------- 54 | virtual void Visit(LiteralNode& node) override 55 | { 56 | writer_.String("type"); 57 | writer_.String("literal"); 58 | 59 | writer_.String("name"); 60 | writer_.String(node.name.c_str()); 61 | } 62 | 63 | //------------------------------------------------------------------------------------------------- 64 | virtual void Visit(PointerNode& node) override 65 | { 66 | writer_.String("type"); 67 | writer_.String("pointer"); 68 | 69 | writer_.String("baseType"); 70 | VisitNode(*node.base); 71 | } 72 | 73 | //------------------------------------------------------------------------------------------------- 74 | virtual void Visit(ReferenceNode& node) override 75 | { 76 | writer_.String("type"); 77 | writer_.String("reference"); 78 | 79 | writer_.String("baseType"); 80 | VisitNode(*node.base); 81 | } 82 | 83 | //------------------------------------------------------------------------------------------------- 84 | virtual void Visit(TemplateNode& node) override 85 | { 86 | writer_.String("type"); 87 | writer_.String("template"); 88 | 89 | writer_.String("name"); 90 | writer_.String(node.name.c_str()); 91 | 92 | writer_.String("arguments"); 93 | writer_.StartArray(); 94 | for (auto& arg : node.arguments) 95 | VisitNode(*arg); 96 | writer_.EndArray(); 97 | } 98 | 99 | //------------------------------------------------------------------------------------------------- 100 | virtual void VisitNode(TypeNode &node) override 101 | { 102 | writer_.StartObject(); 103 | if (node.isConst) 104 | { 105 | writer_.String("const"); 106 | writer_.Bool(true); 107 | } 108 | if (node.isMutable) 109 | { 110 | writer_.String("mutable"); 111 | writer_.Bool(true); 112 | } 113 | if (node.isVolatile) 114 | { 115 | writer_.String("volatile"); 116 | writer_.Bool(true); 117 | } 118 | TypeNodeVisitor::VisitNode(node); 119 | writer_.EndObject(); 120 | } 121 | 122 | private: 123 | rapidjson::PrettyWriter &writer_; 124 | }; 125 | 126 | //-------------------------------------------------------------------------------------------------- 127 | Parser::Parser(const Options &options) : options_(options), writer_(buffer_) 128 | { 129 | 130 | } 131 | 132 | //-------------------------------------------------------------------------------------------------- 133 | Parser::~Parser() 134 | { 135 | 136 | } 137 | 138 | //-------------------------------------------------------------------------------------------------- 139 | bool Parser::Parse(const char *input) 140 | { 141 | // Pass the input to the tokenizer 142 | Reset(input); 143 | 144 | // Start the array 145 | writer_.StartArray(); 146 | 147 | // Reset scope 148 | topScope_ = scopes_; 149 | topScope_->name = ""; 150 | topScope_->type = ScopeType::kGlobal; 151 | topScope_->currentAccessControlType = AccessControlType::kPublic; 152 | 153 | // Parse all statements in the file 154 | while(ParseStatement()) 155 | { 156 | } 157 | 158 | // End the array 159 | if(!HasError()) 160 | writer_.EndArray(); 161 | 162 | return !HasError(); 163 | } 164 | 165 | //-------------------------------------------------------------------------------------------------- 166 | bool Parser::ParseStatement() 167 | { 168 | Token token; 169 | if(!GetToken(token)) 170 | return false; 171 | 172 | if (!ParseDeclaration(token)) 173 | return false; 174 | 175 | return true; 176 | } 177 | 178 | //-------------------------------------------------------------------------------------------------- 179 | bool Parser::ParseDeclaration(Token &token) 180 | { 181 | std::vector::const_iterator customMacroIt; 182 | if (token.token == "#") 183 | return ParseDirective(); 184 | else if (token.token == ";") 185 | return true; // Empty statement 186 | else if (token.token == options_.enumNameMacro) 187 | return ParseEnum(token); 188 | else if (token.token == options_.classNameMacro) 189 | return ParseClass(token); 190 | else if ((customMacroIt = std::find(options_.functionNameMacro.begin(), options_.functionNameMacro.end(), token.token)) != options_.functionNameMacro.end()) 191 | return ParseFunction(token, *customMacroIt); 192 | else if (token.token == options_.constructorNameMacro) 193 | return ParseConstructor(token); 194 | else if(token.token == options_.propertyNameMacro) 195 | return ParseProperty(token); 196 | else if (token.token == "namespace") 197 | return ParseNamespace(); 198 | else if (ParseAccessControl(token, topScope_->currentAccessControlType)) 199 | return RequireSymbol(":"); 200 | else if ((customMacroIt = std::find(options_.customMacros.begin(), options_.customMacros.end(), token.token)) != options_.customMacros.end()) 201 | return ParseCustomMacro(token, *customMacroIt); 202 | else 203 | return SkipDeclaration(token); 204 | 205 | return true; 206 | } 207 | 208 | //-------------------------------------------------------------------------------------------------- 209 | bool Parser::ParseDirective() 210 | { 211 | Token token; 212 | 213 | // Check the compiler directive 214 | if(!GetIdentifier(token)) 215 | return Error("Missing compiler directive after #"); 216 | 217 | bool multiLineEnabled = false; 218 | if(token.token == "define") 219 | { 220 | multiLineEnabled = true; 221 | } 222 | else if(token.token == "include") 223 | { 224 | Token includeToken; 225 | GetToken(includeToken, true); 226 | 227 | writer_.StartObject(); 228 | writer_.String("type"); 229 | writer_.String("include"); 230 | writer_.String("file"); 231 | writer_.String(includeToken.token.c_str()); 232 | writer_.EndObject(); 233 | } 234 | 235 | // Skip past the end of the token 236 | char lastChar = '\n'; 237 | do 238 | { 239 | // Skip to the end of the line 240 | char c; 241 | while(!this->is_eof() && (c = GetChar()) != '\n') 242 | lastChar = c; 243 | 244 | } while(multiLineEnabled && lastChar == '\\'); 245 | 246 | return true; 247 | } 248 | 249 | //-------------------------------------------------------------------------------------------------- 250 | bool Parser::SkipDeclaration(Token &token) 251 | { 252 | int32_t scopeDepth = 0; 253 | while(GetToken(token)) 254 | { 255 | if(token.token == ";" && scopeDepth == 0) 256 | break; 257 | 258 | if(token.token == "{") 259 | scopeDepth++; 260 | 261 | if(token.token == "}") 262 | { 263 | --scopeDepth; 264 | if(scopeDepth == 0) 265 | break; 266 | } 267 | } 268 | 269 | return true; 270 | } 271 | 272 | //-------------------------------------------------------------------------------------------------- 273 | bool Parser::ParseEnum(Token &startToken) 274 | { 275 | writer_.StartObject(); 276 | writer_.String("type"); 277 | writer_.String("enum"); 278 | writer_.String("line"); 279 | writer_.Uint((unsigned)startToken.startLine); 280 | 281 | WriteCurrentAccessControlType(); 282 | 283 | if (!ParseMacroMeta()) 284 | return false; 285 | 286 | if (!RequireIdentifier("enum")) 287 | return false; 288 | 289 | // C++1x enum class type? 290 | bool isEnumClass = MatchIdentifier("class"); 291 | 292 | // Parse enum name 293 | Token enumToken; 294 | if (!GetIdentifier(enumToken)) 295 | return Error("Missing enum name"); 296 | 297 | writer_.String("name"); 298 | writer_.String(enumToken.token.c_str()); 299 | 300 | if (isEnumClass) 301 | { 302 | writer_.String("cxxclass"); 303 | writer_.Bool(isEnumClass); 304 | } 305 | 306 | // Parse C++1x enum base 307 | if(isEnumClass && MatchSymbol(":")) 308 | { 309 | Token baseToken; 310 | if (!GetIdentifier(baseToken)) 311 | return Error("Missing enum type specifier after :"); 312 | 313 | // Validate base token 314 | writer_.String("base"); 315 | writer_.String(baseToken.token.c_str()); 316 | } 317 | 318 | // Require opening brace 319 | RequireSymbol("{"); 320 | 321 | writer_.String("members"); 322 | writer_.StartArray(); 323 | 324 | // Parse all the values 325 | Token token; 326 | while(GetIdentifier(token)) 327 | { 328 | writer_.StartObject(); 329 | 330 | // Store the identifier 331 | writer_.String("key"); 332 | writer_.String(token.token.c_str()); 333 | 334 | // Parse constant 335 | if(MatchSymbol("=")) 336 | { 337 | // Just parse the value, not doing anything with it atm 338 | std::string value; 339 | while (GetToken(token) && (token.tokenType != TokenType::kSymbol || (token.token != "," && token.token != "}"))) 340 | { 341 | value += token.token; 342 | } 343 | UngetToken(token); 344 | 345 | writer_.String("value"); 346 | writer_.String(value.c_str()); 347 | } 348 | 349 | writer_.EndObject(); 350 | 351 | // Next value? 352 | if(!MatchSymbol(",")) 353 | break; 354 | } 355 | 356 | if (!RequireSymbol("}")) 357 | return false; 358 | writer_.EndArray(); 359 | 360 | MatchSymbol(";"); 361 | 362 | writer_.EndObject(); 363 | 364 | return true; 365 | } 366 | 367 | //-------------------------------------------------------------------------------------------------- 368 | bool Parser::ParseMacroMeta() 369 | { 370 | writer_.String("meta"); 371 | 372 | if (!RequireSymbol("(")) 373 | return false; 374 | if (!ParseMetaSequence()) 375 | return false; 376 | 377 | // Possible ; 378 | MatchSymbol(";"); 379 | 380 | return true; 381 | } 382 | 383 | //-------------------------------------------------------------------------------------------------- 384 | bool Parser::ParseMetaSequence() 385 | { 386 | writer_.StartObject(); 387 | 388 | if(!MatchSymbol(")")) 389 | { 390 | do 391 | { 392 | // Parse key value 393 | Token keyToken; 394 | if (!GetIdentifier(keyToken)) 395 | return Error("Expected identifier in meta sequence"); 396 | 397 | writer_.String(keyToken.token.c_str()); 398 | 399 | // Simple value? 400 | if (MatchSymbol("=")) { 401 | Token token; 402 | if (!GetToken(token)) 403 | throw; // Expected token 404 | 405 | WriteToken(token); 406 | } 407 | // Compound value 408 | else if (MatchSymbol("(")) 409 | { 410 | if (!ParseMetaSequence()) 411 | return false; 412 | // No value 413 | } 414 | else 415 | writer_.Null(); 416 | } while (MatchSymbol(",")); 417 | 418 | MatchSymbol(")"); 419 | } 420 | 421 | writer_.EndObject(); 422 | return true; 423 | } 424 | 425 | //-------------------------------------------------------------------------------------------------- 426 | void Parser::PushScope(const std::string &name, ScopeType scopeType, AccessControlType accessControlType) 427 | { 428 | if(topScope_ == scopes_ + (sizeof(scopes_) / sizeof(Scope)) - 1) 429 | throw; // Max scope depth 430 | 431 | topScope_++; 432 | topScope_->type = scopeType; 433 | topScope_->name = name; 434 | topScope_->currentAccessControlType = accessControlType; 435 | } 436 | 437 | //-------------------------------------------------------------------------------------------------- 438 | void Parser::PopScope() 439 | { 440 | if(topScope_ == scopes_) 441 | throw; // Scope error 442 | 443 | topScope_--; 444 | } 445 | 446 | //-------------------------------------------------------------------------------------------------- 447 | bool Parser::ParseNamespace() 448 | { 449 | writer_.StartObject(); 450 | writer_.String("type"); 451 | writer_.String("namespace"); 452 | 453 | Token token; 454 | if (!GetIdentifier(token)) 455 | return Error("Missing namespace name"); 456 | 457 | writer_.String("name"); 458 | writer_.String(token.token.c_str()); 459 | 460 | if (!RequireSymbol("{")) 461 | return false; 462 | 463 | writer_.String("members"); 464 | writer_.StartArray(); 465 | 466 | PushScope(token.token, ScopeType::kNamespace, AccessControlType::kPublic); 467 | 468 | while (!MatchSymbol("}")) 469 | if (!ParseStatement()) 470 | return false; 471 | 472 | PopScope(); 473 | 474 | writer_.EndArray(); 475 | 476 | writer_.EndObject(); 477 | return true; 478 | } 479 | 480 | //------------------------------------------------------------------------------------------------- 481 | bool Parser::ParseAccessControl(const Token &token, AccessControlType& type) 482 | { 483 | if (token.token == "public") 484 | { 485 | type = AccessControlType::kPublic; 486 | return true; 487 | } 488 | else if (token.token == "protected") 489 | { 490 | type = AccessControlType::kProtected; 491 | return true; 492 | } 493 | else if (token.token == "private") 494 | { 495 | type = AccessControlType::kPrivate; 496 | return true; 497 | } 498 | 499 | return false; 500 | } 501 | 502 | //------------------------------------------------------------------------------------------------- 503 | void Parser::WriteCurrentAccessControlType() 504 | { 505 | // Writing access is not required if the current scope is not owned by a class 506 | if (topScope_->type != ScopeType::kClass) 507 | return; 508 | 509 | WriteAccessControlType(current_access_control_type()); 510 | } 511 | 512 | //------------------------------------------------------------------------------------------------- 513 | void Parser::WriteAccessControlType(AccessControlType type) 514 | { 515 | writer_.String("access"); 516 | switch (type) 517 | { 518 | case AccessControlType::kPublic: 519 | writer_.String("public"); 520 | break; 521 | case AccessControlType::kProtected: 522 | writer_.String("protected"); 523 | break; 524 | case AccessControlType::kPrivate: 525 | writer_.String("private"); 526 | break; 527 | default: 528 | throw; // Unknown access control type 529 | } 530 | } 531 | 532 | //-------------------------------------------------------------------------------------------------- 533 | bool Parser::ParseClass(Token &token) 534 | { 535 | writer_.StartObject(); 536 | writer_.String("type"); 537 | writer_.String("class"); 538 | writer_.String("line"); 539 | writer_.Uint((unsigned)token.startLine); 540 | 541 | WriteCurrentAccessControlType(); 542 | if (!ParseComment()) 543 | return false; 544 | if (!ParseMacroMeta()) 545 | return false; 546 | 547 | if(MatchIdentifier("template") && !ParseClassTemplate()) 548 | return false; 549 | 550 | bool isStruct = MatchIdentifier("struct"); 551 | if (!(MatchIdentifier("class") || isStruct)) 552 | return Error("Missing identifier class or struct"); 553 | 554 | writer_.String("isstruct"); 555 | writer_.Bool(isStruct); 556 | 557 | // Get the class name 558 | Token classNameToken; 559 | if(!GetIdentifier(classNameToken)) 560 | throw; // Missing class name 561 | 562 | writer_.String("name"); 563 | writer_.String(classNameToken.token.c_str()); 564 | 565 | // Match base types 566 | if(MatchSymbol(":")) 567 | { 568 | writer_.String("parents"); 569 | writer_.StartArray(); 570 | 571 | do 572 | { 573 | writer_.StartObject(); 574 | 575 | Token accessOrName; 576 | if (!GetIdentifier(accessOrName)) 577 | throw; // Missing class or access control specifier 578 | 579 | // Parse the access control specifier 580 | AccessControlType accessControlType = AccessControlType::kPrivate; 581 | if (!ParseAccessControl(accessOrName, accessControlType)) 582 | UngetToken(accessOrName); 583 | WriteAccessControlType(accessControlType); 584 | 585 | // Get the name of the class 586 | writer_.String("name"); 587 | if (!ParseType()) 588 | return false; 589 | 590 | writer_.EndObject(); 591 | } 592 | while (MatchSymbol(",")); 593 | 594 | writer_.EndArray(); 595 | } 596 | 597 | if (!RequireSymbol("{")) 598 | return false; 599 | 600 | writer_.String("members"); 601 | writer_.StartArray(); 602 | 603 | PushScope(classNameToken.token, ScopeType::kClass, isStruct ? AccessControlType::kPublic : AccessControlType::kPrivate); 604 | 605 | while (!MatchSymbol("}")) 606 | if (!ParseStatement()) 607 | return false; 608 | 609 | PopScope(); 610 | 611 | writer_.EndArray(); 612 | 613 | if (!RequireSymbol(";")) 614 | return false; 615 | 616 | writer_.EndObject(); 617 | return true; 618 | } 619 | 620 | //------------------------------------------------------------------------------------------------- 621 | bool Parser::ParseProperty(Token &token) 622 | { 623 | writer_.StartObject(); 624 | writer_.String("type"); 625 | writer_.String("property"); 626 | writer_.String("line"); 627 | writer_.Uint((unsigned) token.startLine); 628 | 629 | if (!ParseMacroMeta()) 630 | return false; 631 | 632 | WriteCurrentAccessControlType(); 633 | 634 | // Process method specifiers in any particular order 635 | bool isMutable = false, isStatic = false; 636 | for (bool matched = true; matched;) 637 | { 638 | matched = (!isMutable && (isMutable = MatchIdentifier("mutable"))) || 639 | (!isStatic && (isStatic = MatchIdentifier("static"))); 640 | } 641 | 642 | // Check mutable 643 | if(isMutable) 644 | { 645 | writer_.String("mutable"); 646 | writer_.Bool(true); 647 | } 648 | 649 | // Check mutable 650 | if (isStatic) 651 | { 652 | writer_.String("static"); 653 | writer_.Bool(true); 654 | } 655 | 656 | // Parse the type 657 | writer_.String("dataType"); 658 | if (!ParseType()) 659 | return false; 660 | 661 | // Parse the name 662 | Token nameToken; 663 | if(!GetIdentifier(nameToken)) 664 | throw; // Expected a property name 665 | 666 | writer_.String("name"); 667 | writer_.String(nameToken.token.c_str()); 668 | 669 | // Parse array 670 | writer_.String("elements"); 671 | if (MatchSymbol("[")) 672 | { 673 | Token arrayToken; 674 | if(!GetConst(arrayToken)) 675 | if(!GetIdentifier(arrayToken)) 676 | throw; // Expected a property name 677 | writer_.String(arrayToken.token.c_str()); 678 | 679 | if(!MatchSymbol("]")) 680 | throw; 681 | } 682 | else 683 | { 684 | writer_.Null(); 685 | } 686 | 687 | writer_.EndObject(); 688 | 689 | 690 | // Skip until the end of the definition 691 | Token t; 692 | while(GetToken(t)) 693 | if(t.token == ";") 694 | break; 695 | 696 | return true; 697 | } 698 | 699 | //-------------------------------------------------------------------------------------------------- 700 | bool Parser::ParseConstructor(Token& token) 701 | { 702 | writer_.StartObject(); 703 | writer_.String("type"); 704 | writer_.String("constructor"); 705 | writer_.String("line"); 706 | writer_.Uint((unsigned) token.startLine); 707 | 708 | if (!ParseComment()) return false; 709 | if (!ParseMacroMeta()) return false; 710 | 711 | WriteCurrentAccessControlType(); 712 | 713 | bool isInline = false; 714 | for (bool matched = true; matched;) 715 | { 716 | matched = !isInline && (isInline = MatchIdentifier("inline")); 717 | } 718 | 719 | if (isInline) 720 | { 721 | writer_.String("inline"); 722 | writer_.Bool(isInline); 723 | } 724 | 725 | // Parse the name of the constructor 726 | Token nameToken; 727 | if (!GetIdentifier(nameToken)) throw; 728 | 729 | writer_.String("name"); 730 | writer_.String(nameToken.token.c_str()); 731 | 732 | writer_.String("arguments"); 733 | writer_.StartArray(); 734 | 735 | // Start argument list from here 736 | MatchSymbol("("); 737 | 738 | // Is there an argument list in the first place or is it closed right away? 739 | if (!MatchSymbol(")")) 740 | { 741 | // Walk over all arguments 742 | do 743 | { 744 | writer_.StartObject(); 745 | 746 | // Get the type of the argument 747 | writer_.String("type"); 748 | if (!ParseType()) 749 | return false; 750 | 751 | // Parse the name of the function 752 | writer_.String("name"); 753 | if (!GetIdentifier(nameToken)) 754 | throw; // Expected identifier 755 | writer_.String(nameToken.token.c_str()); 756 | 757 | // Parse default value 758 | if (MatchSymbol("=")) 759 | { 760 | writer_.String("defaultValue"); 761 | 762 | std::string defaultValue; 763 | Token token; 764 | GetToken(token); 765 | if (token.tokenType == TokenType::kConst) 766 | WriteToken(token); 767 | else 768 | { 769 | do 770 | { 771 | if (token.token == "," || 772 | token.token == ")") 773 | { 774 | UngetToken(token); 775 | break; 776 | } 777 | defaultValue += token.token; 778 | } while (GetToken(token)); 779 | writer_.String(defaultValue.c_str()); 780 | } 781 | } 782 | 783 | writer_.EndObject(); 784 | } while (MatchSymbol(",")); // Only in case another is expected 785 | 786 | MatchSymbol(")"); 787 | } 788 | 789 | writer_.EndArray(); 790 | 791 | // Is default? 792 | if (MatchSymbol("=")) 793 | { 794 | Token token; 795 | if (!GetToken(token) || token.token != "default") 796 | throw; // Expected nothing else than default 797 | 798 | writer_.String("default"); 799 | writer_.Bool(true); 800 | } 801 | 802 | writer_.EndObject(); 803 | 804 | // Skip either the ; or the body of the function 805 | Token skipToken; 806 | if (!SkipDeclaration(skipToken)) 807 | return false; 808 | return true; 809 | } 810 | 811 | //-------------------------------------------------------------------------------------------------- 812 | bool Parser::ParseFunction(Token &token, const std::string& macroName) 813 | { 814 | writer_.StartObject(); 815 | writer_.String("type"); 816 | writer_.String("function"); 817 | writer_.String("macro"); 818 | writer_.String(macroName.c_str()); 819 | writer_.String("line"); 820 | writer_.Uint((unsigned) token.startLine); 821 | 822 | if (!ParseComment()) 823 | return false; 824 | 825 | if (!ParseMacroMeta()) 826 | return false; 827 | 828 | WriteCurrentAccessControlType(); 829 | 830 | // Process method specifiers in any particular order 831 | bool isVirtual = false, isInline = false, isConstExpr = false, isStatic = false; 832 | for(bool matched = true; matched;) 833 | { 834 | matched = (!isVirtual && (isVirtual = MatchIdentifier("virtual"))) || 835 | (!isInline && (isInline = MatchIdentifier("inline"))) || 836 | (!isConstExpr && (isConstExpr = MatchIdentifier("constexpr"))) || 837 | (!isStatic && (isStatic = MatchIdentifier("static"))); 838 | } 839 | 840 | // Write method specifiers 841 | if (isVirtual) 842 | { 843 | writer_.String("virtual"); 844 | writer_.Bool(isVirtual); 845 | } 846 | if (isInline) 847 | { 848 | writer_.String("inline"); 849 | writer_.Bool(isInline); 850 | } 851 | if (isConstExpr) 852 | { 853 | writer_.String("constexpr"); 854 | writer_.Bool(isConstExpr); 855 | } 856 | if (isStatic) 857 | { 858 | writer_.String("static"); 859 | writer_.Bool(isStatic); 860 | } 861 | 862 | // Parse the return type 863 | writer_.String("returnType"); 864 | if (!ParseType()) 865 | return false; 866 | 867 | // Parse the name of the method 868 | Token nameToken; 869 | if(!GetIdentifier(nameToken)) 870 | throw; // Expected method name 871 | 872 | writer_.String("name"); 873 | writer_.String(nameToken.token.c_str()); 874 | 875 | writer_.String("arguments"); 876 | writer_.StartArray(); 877 | 878 | // Start argument list from here 879 | MatchSymbol("("); 880 | 881 | // Is there an argument list in the first place or is it closed right away? 882 | if (!MatchSymbol(")")) 883 | { 884 | // Walk over all arguments 885 | do 886 | { 887 | writer_.StartObject(); 888 | 889 | // Get the type of the argument 890 | writer_.String("type"); 891 | if (!ParseType()) 892 | return false; 893 | 894 | // Parse the name of the function 895 | writer_.String("name"); 896 | if (!GetIdentifier(nameToken)) 897 | throw; // Expected identifier 898 | writer_.String(nameToken.token.c_str()); 899 | 900 | // Parse default value 901 | if (MatchSymbol("=")) 902 | { 903 | writer_.String("defaultValue"); 904 | 905 | std::string defaultValue; 906 | Token token; 907 | GetToken(token); 908 | if(token.tokenType == TokenType::kConst) 909 | WriteToken(token); 910 | else 911 | { 912 | do 913 | { 914 | if (token.token == "," || 915 | token.token == ")") 916 | { 917 | UngetToken(token); 918 | break; 919 | } 920 | defaultValue += token.token; 921 | } while (GetToken(token)); 922 | writer_.String(defaultValue.c_str()); 923 | } 924 | } 925 | 926 | writer_.EndObject(); 927 | } while (MatchSymbol(",")); // Only in case another is expected 928 | 929 | MatchSymbol(")"); 930 | } 931 | 932 | writer_.EndArray(); 933 | 934 | // Optionally parse constness 935 | if (MatchIdentifier("const")) 936 | { 937 | writer_.String("const"); 938 | writer_.Bool(true); 939 | } 940 | 941 | // Pure? 942 | if (MatchSymbol("=")) 943 | { 944 | Token token; 945 | if (!GetToken(token) || token.token != "0") 946 | throw; // Expected nothing else than null 947 | 948 | writer_.String("abstract"); 949 | writer_.Bool(true); 950 | } 951 | 952 | writer_.EndObject(); 953 | 954 | // Skip either the ; or the body of the function 955 | Token skipToken; 956 | if (!SkipDeclaration(skipToken)) 957 | return false; 958 | return true; 959 | } 960 | 961 | //------------------------------------------------------------------------------------------------- 962 | bool Parser::ParseComment() 963 | { 964 | std::string comment = lastComment_.endLine == cursorLine_ ? lastComment_.text : ""; 965 | if (!comment.empty()) 966 | { 967 | writer_.String("comment"); 968 | writer_.String(comment.c_str()); 969 | } 970 | 971 | return true; 972 | } 973 | 974 | //-------------------------------------------------------------------------------------------------- 975 | bool Parser::ParseType() 976 | { 977 | std::unique_ptr node = ParseTypeNode(); 978 | if (node == nullptr) 979 | return false; 980 | TypeNodeWriter writer(writer_); 981 | writer.VisitNode(*node); 982 | return true; 983 | } 984 | 985 | //------------------------------------------------------------------------------------------------- 986 | std::unique_ptr Parser::ParseTypeNode() 987 | { 988 | std::unique_ptr node; 989 | Token token; 990 | 991 | bool isConst = false, isVolatile = false, isMutable = false; 992 | for (bool matched = true; matched;) 993 | { 994 | matched = (!isConst && (isConst = MatchIdentifier("const"))) || 995 | (!isVolatile && (isVolatile = MatchIdentifier("volatile"))) || 996 | (!isMutable && (isMutable = MatchIdentifier("mutable"))); 997 | } 998 | 999 | // Parse a literal value 1000 | std::string declarator = ParseTypeNodeDeclarator(); 1001 | 1002 | // Postfix const specifier 1003 | isConst |= MatchIdentifier("const"); 1004 | 1005 | // Template? 1006 | if (MatchSymbol("<")) 1007 | { 1008 | std::unique_ptr templateNode(new TemplateNode(declarator)); 1009 | do 1010 | { 1011 | auto node = ParseTypeNode(); 1012 | if (node == nullptr) 1013 | return nullptr; 1014 | templateNode->arguments.emplace_back(std::move(node)); 1015 | } while (MatchSymbol(",")); 1016 | 1017 | if (!MatchSymbol(">")) 1018 | { 1019 | Error("Expected closing >"); 1020 | return nullptr; 1021 | } 1022 | 1023 | node.reset(templateNode.release()); 1024 | } 1025 | else 1026 | { 1027 | node.reset(new LiteralNode(declarator)); 1028 | } 1029 | 1030 | // Store gathered stuff 1031 | node->isConst = isConst; 1032 | 1033 | // Check reference or pointer types 1034 | while (GetToken(token)) 1035 | { 1036 | if (token.token == "&") 1037 | node.reset(new ReferenceNode(std::move(node))); 1038 | else if (token.token == "&&") 1039 | node.reset(new LReferenceNode(std::move(node))); 1040 | else if (token.token == "*") 1041 | node.reset(new PointerNode(std::move(node))); 1042 | else 1043 | { 1044 | UngetToken(token); 1045 | break; 1046 | } 1047 | 1048 | if (MatchIdentifier("const")) 1049 | node->isConst = true; 1050 | } 1051 | 1052 | // Function pointer? 1053 | if (MatchSymbol("(")) 1054 | { 1055 | // Parse void(*)(args, ...) 1056 | // ^ 1057 | // | 1058 | if (MatchSymbol("*")) 1059 | { 1060 | Token token; 1061 | GetToken(token); 1062 | if (token.token != ")" || (token.tokenType != TokenType::kIdentifier && !MatchSymbol(")"))) 1063 | throw; 1064 | } 1065 | 1066 | // Parse arguments 1067 | std::unique_ptr funcNode(new FunctionNode()); 1068 | funcNode->returns = std::move(node); 1069 | 1070 | if (!MatchSymbol(")")) 1071 | { 1072 | do 1073 | { 1074 | std::unique_ptr argument(new FunctionNode::Argument); 1075 | argument->type = ParseTypeNode(); 1076 | if (argument->type == nullptr) 1077 | return nullptr; 1078 | 1079 | // Get , or name identifier 1080 | if (!GetToken(token)) 1081 | { 1082 | Error("Unexpected end of file"); 1083 | return nullptr; 1084 | } 1085 | 1086 | // Parse optional name 1087 | if (token.tokenType == TokenType::kIdentifier) 1088 | argument->name = token.token; 1089 | else 1090 | UngetToken(token); 1091 | 1092 | funcNode->arguments.emplace_back(std::move(argument)); 1093 | 1094 | } while (MatchSymbol(",")); 1095 | if (!MatchSymbol(")")) 1096 | throw; 1097 | } 1098 | 1099 | node = std::move(funcNode); 1100 | } 1101 | 1102 | // This stuff refers to the top node 1103 | node->isVolatile = isVolatile; 1104 | node->isMutable = isMutable; 1105 | 1106 | return std::move(node); 1107 | } 1108 | 1109 | //------------------------------------------------------------------------------------------------- 1110 | std::string Parser::ParseTypeNodeDeclarator() 1111 | { 1112 | // Skip optional forward declaration specifier 1113 | MatchIdentifier("class"); 1114 | MatchIdentifier("struct"); 1115 | MatchIdentifier("typename"); 1116 | 1117 | // Parse a type name 1118 | std::string declarator; 1119 | Token token; 1120 | bool first = true; 1121 | do 1122 | { 1123 | // Parse the declarator 1124 | if (MatchSymbol("::")) 1125 | declarator += "::"; 1126 | else if (!first) 1127 | break; 1128 | 1129 | // Mark that this is not the first time in this loop 1130 | first = false; 1131 | 1132 | // Match an identifier or constant 1133 | if (!GetIdentifier(token) && !GetConst(token)) 1134 | throw; // Expected identifier 1135 | 1136 | declarator += token.token; 1137 | 1138 | } while (true); 1139 | 1140 | return declarator; 1141 | } 1142 | 1143 | //------------------------------------------------------------------------------------------------- 1144 | std::string Parser::ParseTypename() 1145 | { 1146 | return ""; 1147 | } 1148 | 1149 | //---------------------------------------------------------------------------------------------------------------------- 1150 | void Parser::WriteToken(const Token &token) 1151 | { 1152 | if(token.tokenType == TokenType::kConst) 1153 | { 1154 | switch(token.constType) 1155 | { 1156 | case ConstType::kBoolean: 1157 | writer_.Bool(token.boolConst); 1158 | break; 1159 | case ConstType::kUInt32: 1160 | writer_.Uint(token.uint32Const); 1161 | break; 1162 | case ConstType::kInt32: 1163 | writer_.Int(token.int32Const); 1164 | break; 1165 | case ConstType::kUInt64: 1166 | writer_.Uint64(token.uint64Const); 1167 | break; 1168 | case ConstType::kInt64: 1169 | writer_.Int64(token.int64Const); 1170 | break; 1171 | case ConstType::kReal: 1172 | writer_.Double(token.realConst); 1173 | break; 1174 | case ConstType::kString: 1175 | //writer_.String((std::string("\"") + token.stringConst + "\"").c_str()); 1176 | writer_.String(token.stringConst.c_str()); 1177 | break; 1178 | } 1179 | } 1180 | else 1181 | writer_.String(token.token.c_str()); 1182 | } 1183 | 1184 | //------------------------------------------------------------------------------------------------- 1185 | bool Parser::ParseCustomMacro(Token & token, const std::string& macroName) 1186 | { 1187 | writer_.StartObject(); 1188 | writer_.String("type"); 1189 | writer_.String("macro"); 1190 | writer_.String("name"); 1191 | writer_.String(macroName.c_str()); 1192 | writer_.String("line"); 1193 | writer_.Uint((unsigned) token.startLine); 1194 | 1195 | WriteCurrentAccessControlType(); 1196 | 1197 | if (!ParseMacroMeta()) 1198 | return false; 1199 | 1200 | writer_.EndObject(); 1201 | return true; 1202 | } 1203 | 1204 | //------------------------------------------------------------------------------------------------- 1205 | bool Parser::ParseClassTemplate() 1206 | { 1207 | writer_.String("template"); 1208 | writer_.StartObject(); 1209 | 1210 | if(!RequireSymbol("<")) 1211 | return false; 1212 | 1213 | writer_.String("arguments"); 1214 | writer_.StartArray(); 1215 | 1216 | do 1217 | { 1218 | if(!ParseClassTemplateArgument()) 1219 | return false; 1220 | } while(MatchSymbol(",")); 1221 | 1222 | writer_.EndArray(); 1223 | 1224 | if(!RequireSymbol(">")) 1225 | return false; 1226 | writer_.EndObject(); 1227 | 1228 | return true; 1229 | } 1230 | 1231 | //------------------------------------------------------------------------------------------------- 1232 | bool Parser::ParseClassTemplateArgument() 1233 | { 1234 | writer_.StartObject(); 1235 | 1236 | Token token; 1237 | if(!GetToken(token) || token.tokenType != TokenType::kIdentifier || !(token.token == "class" || token.token == "typename")) 1238 | { 1239 | Error("expected either 'class' or 'identifier' in template argument"); 1240 | return false; 1241 | } 1242 | 1243 | writer_.String("typeParameterKey"); 1244 | writer_.String(token.token.c_str()); 1245 | 1246 | // Parse the name 1247 | GetToken(token); 1248 | if(token.tokenType != TokenType::kIdentifier) 1249 | { 1250 | Error("expected identifier"); 1251 | return false; 1252 | } 1253 | 1254 | writer_.String("name"); 1255 | writer_.String(token.token.c_str()); 1256 | 1257 | // Optionally check if there is a default initializer 1258 | if(MatchSymbol("=")) 1259 | { 1260 | writer_.String("defaultType"); 1261 | if(!ParseType()) 1262 | return false; 1263 | } 1264 | 1265 | writer_.EndObject(); 1266 | return true; 1267 | } 1268 | --------------------------------------------------------------------------------