├── .clang-format ├── .gitignore ├── .gitmodules ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── appveyor.yml ├── common.gypi ├── configure ├── ext └── markdown-parser │ ├── .gitignore │ ├── .travis.yml │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── markdownparser.xcodeproj │ └── project.pbxproj │ ├── msvc │ ├── markdown │ │ └── markdown.vcproj │ ├── msvc.sln │ ├── sundown │ │ └── sundown.vcproj │ └── test-markdown │ │ └── test-markdown.vcproj │ ├── src │ ├── ByteBuffer.cc │ ├── ByteBuffer.h │ ├── MarkdownNode.cc │ ├── MarkdownNode.h │ ├── MarkdownParser.cc │ └── MarkdownParser.h │ └── test │ ├── test-ByteBuffer.cc │ ├── test-MarkdownParser.cc │ └── test-libmarkdownparser.cc ├── snowcrash.gyp ├── snowcrash.xcodeproj └── project.pbxproj ├── src ├── ActionParser.h ├── AssetParser.h ├── AttributesParser.h ├── Blueprint.cc ├── Blueprint.h ├── BlueprintParser.h ├── BlueprintSourcemap.cc ├── BlueprintSourcemap.h ├── BlueprintUtility.h ├── CodeBlockUtility.h ├── DataStructureGroupParser.h ├── HTTP.cc ├── HTTP.h ├── HeadersParser.cc ├── HeadersParser.h ├── MSON.cc ├── MSON.h ├── MSONMixinParser.h ├── MSONNamedTypeParser.h ├── MSONOneOfParser.cc ├── MSONOneOfParser.h ├── MSONParameterParser.h ├── MSONPropertyMemberParser.h ├── MSONSourcemap.cc ├── MSONSourcemap.h ├── MSONTypeSectionParser.cc ├── MSONTypeSectionParser.h ├── MSONUtility.h ├── MSONValueMemberParser.cc ├── MSONValueMemberParser.h ├── ModelTable.h ├── ParameterParser.h ├── ParametersParser.h ├── PayloadParser.h ├── Platform.h ├── RegexMatch.h ├── RelationParser.h ├── ResourceGroupParser.h ├── ResourceParser.h ├── Section.cc ├── Section.h ├── SectionParser.h ├── SectionParserData.h ├── SectionProcessor.h ├── Signature.cc ├── Signature.h ├── SignatureSectionProcessor.h ├── SourceAnnotation.h ├── StringUtility.h ├── UriTemplateParser.cc ├── UriTemplateParser.h ├── ValuesParser.h ├── posix │ └── RegexMatch.cc ├── snowcrash.cc ├── snowcrash.h └── win │ └── RegexMatch.cc ├── test ├── README.md ├── performance │ ├── fixtures │ │ ├── fixture-1.apib │ │ ├── fixture-2.apib │ │ ├── fixture-3.apib │ │ └── fixture-4.apib │ └── perf-snowcrash.cc ├── snowcrashtest.h ├── test-ActionParser.cc ├── test-AssetParser.cc ├── test-AttributesParser.cc ├── test-Blueprint.cc ├── test-BlueprintParser.cc ├── test-BlueprintUtility.cc ├── test-DataStructureGroupParser.cc ├── test-HeadersParser.cc ├── test-Indentation.cc ├── test-MSONMixinParser.cc ├── test-MSONNamedTypeParser.cc ├── test-MSONOneOfParser.cc ├── test-MSONParameterParser.cc ├── test-MSONPropertyMemberParser.cc ├── test-MSONTypeSectionParser.cc ├── test-MSONUtility.cc ├── test-MSONValueMemberParser.cc ├── test-ModelTable.cc ├── test-ParameterParser.cc ├── test-ParametersParser.cc ├── test-PayloadParser.cc ├── test-RegexMatch.cc ├── test-RelationParser.cc ├── test-ResourceGroupParser.cc ├── test-ResourceParser.cc ├── test-SectionParser.cc ├── test-Signature.cc ├── test-StringUtility.cc ├── test-SymbolIdentifier.cc ├── test-UriTemplateParser.cc ├── test-ValuesParser.cc ├── test-Warnings.cc ├── test-csnowcrash.cc └── test-snowcrash.cc ├── tools ├── README.md └── gyp │ ├── .gitignore │ ├── AUTHORS │ ├── DEPS │ ├── LICENSE │ ├── OWNERS │ ├── PRESUBMIT.py │ ├── README.md │ ├── buildbot │ ├── buildbot_run.py │ └── commit_queue │ │ ├── OWNERS │ │ ├── README │ │ └── cq_config.json │ ├── codereview.settings │ ├── data │ └── win │ │ └── large-pdb-shim.cc │ ├── gyp │ ├── gyp.bat │ ├── gyp_main.py │ ├── gyptest.py │ ├── pylib │ └── gyp │ │ ├── MSVSNew.py │ │ ├── MSVSProject.py │ │ ├── MSVSSettings.py │ │ ├── MSVSSettings_test.py │ │ ├── MSVSToolFile.py │ │ ├── MSVSUserFile.py │ │ ├── MSVSUtil.py │ │ ├── MSVSVersion.py │ │ ├── __init__.py │ │ ├── common.py │ │ ├── common_test.py │ │ ├── easy_xml.py │ │ ├── easy_xml_test.py │ │ ├── flock_tool.py │ │ ├── generator │ │ ├── __init__.py │ │ ├── analyzer.py │ │ ├── android.py │ │ ├── cmake.py │ │ ├── dump_dependency_json.py │ │ ├── eclipse.py │ │ ├── gypd.py │ │ ├── gypsh.py │ │ ├── make.py │ │ ├── msvs.py │ │ ├── msvs_test.py │ │ ├── ninja.py │ │ ├── ninja_test.py │ │ ├── xcode.py │ │ └── xcode_test.py │ │ ├── input.py │ │ ├── input_test.py │ │ ├── mac_tool.py │ │ ├── msvs_emulation.py │ │ ├── ninja_syntax.py │ │ ├── ordered_dict.py │ │ ├── simple_copy.py │ │ ├── win_tool.py │ │ ├── xcode_emulation.py │ │ ├── xcode_ninja.py │ │ ├── xcodeproj_file.py │ │ └── xml_fix.py │ ├── pylintrc │ ├── samples │ ├── samples │ └── samples.bat │ ├── setup.py │ └── tools │ ├── README │ ├── Xcode │ ├── README │ └── Specifications │ │ ├── gyp.pbfilespec │ │ └── gyp.xclangspec │ ├── emacs │ ├── README │ ├── gyp-tests.el │ ├── gyp.el │ ├── run-unit-tests.sh │ └── testdata │ │ ├── media.gyp │ │ └── media.gyp.fontified │ ├── graphviz.py │ ├── pretty_gyp.py │ ├── pretty_sln.py │ └── pretty_vcproj.py └── vcbuild.bat /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | # BasedOnStyle: WebKit 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: false 5 | AlignConsecutiveAssignments: false 6 | AlignEscapedNewlinesLeft: false 7 | AlignOperands: false 8 | AlignTrailingComments: true 9 | AllowAllParametersOfDeclarationOnNextLine: true 10 | AllowShortBlocksOnASingleLine: false 11 | AllowShortCaseLabelsOnASingleLine: false 12 | AllowShortFunctionsOnASingleLine: Empty 13 | AllowShortIfStatementsOnASingleLine: false 14 | AllowShortLoopsOnASingleLine: false 15 | AlwaysBreakAfterDefinitionReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: true 17 | AlwaysBreakTemplateDeclarations: true 18 | BinPackArguments: false 19 | BinPackParameters: false 20 | BreakBeforeBinaryOperators: All 21 | BreakBeforeBraces: Custom 22 | BraceWrapping: 23 | AfterClass: true 24 | AfterControlStatement: false 25 | AfterEnum: true 26 | AfterFunction: true 27 | AfterNamespace: true 28 | AfterStruct: false 29 | AfterUnion: true 30 | BeforeCatch: false 31 | BeforeElse: false 32 | IndentBraces: false 33 | BreakBeforeTernaryOperators: false 34 | BreakConstructorInitializersBeforeComma: true 35 | ColumnLimit: 120 36 | CommentPragmas: '^ IWYU pragma:' 37 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 38 | ConstructorInitializerIndentWidth: 4 39 | ContinuationIndentWidth: 4 40 | Cpp11BracedListStyle: false 41 | DerivePointerAlignment: false 42 | DisableFormat: false 43 | ExperimentalAutoDetectBinPacking: false 44 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 45 | IndentCaseLabels: true 46 | IndentWidth: 4 47 | IndentWrappedFunctionNames: false 48 | KeepEmptyLinesAtTheStartOfBlocks: true 49 | MacroBlockBegin: '' 50 | MacroBlockEnd: '' 51 | MaxEmptyLinesToKeep: 1 52 | NamespaceIndentation: All 53 | PenaltyBreakBeforeFirstCallParameter: 19 54 | PenaltyBreakComment: 300 55 | PenaltyBreakFirstLessLess: 120 56 | PenaltyBreakString: 1000 57 | PenaltyExcessCharacter: 1000000 58 | PenaltyReturnTypeOnItsOwnLine: 60 59 | PointerAlignment: Left 60 | SpaceAfterCStyleCast: false 61 | SpaceBeforeAssignmentOperators: true 62 | SpaceBeforeParens: ControlStatements 63 | SpaceInEmptyParentheses: false 64 | SpacesBeforeTrailingComments: 1 65 | SpacesInAngles: false 66 | SpacesInContainerLiterals: true 67 | SpacesInCStyleCastParentheses: false 68 | SpacesInParentheses: false 69 | SpacesInSquareBrackets: false 70 | Standard: Cpp03 71 | TabWidth: 4 72 | UseTab: Never 73 | SortIncludes: false 74 | 75 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | 15 | # OS X and Xcode specific 16 | .DS_Store 17 | build/ 18 | *.pbxuser 19 | !default.pbxuser 20 | *.mode1v3 21 | !default.mode1v3 22 | *.mode2v3 23 | !default.mode2v3 24 | *.perspectivev3 25 | !default.perspectivev3 26 | *.xcodeproj/*.xcworkspace 27 | xcuserdata 28 | xcshareddata 29 | profile 30 | *.moved-aside 31 | DerivedData 32 | .idea/ 33 | 34 | # Generated files 35 | config.mk 36 | config.gypi 37 | bin/ 38 | vendor/ 39 | .vagrant/ 40 | tmp/ 41 | 42 | # Ruby 43 | .bundle/ 44 | features/support/env-win.rb 45 | 46 | # Eclipse specific 47 | .cproject 48 | .project 49 | .settings 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/markdown-parser/ext/sundown"] 2 | path = ext/markdown-parser/ext/sundown 3 | url = https://github.com/apiaryio/sundown.git 4 | [submodule "test/vendor/Catch"] 5 | path = test/vendor/Catch 6 | url = https://github.com/philsquared/Catch.git 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | addons: 5 | apt: 6 | sources: 7 | - llvm-toolchain-trusty-4.0 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - clang-4.0 11 | - g++-5 12 | - gcc-5 13 | compiler: 14 | - gcc 15 | - clang 16 | script: 17 | - make test 18 | before_install: 19 | - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi 20 | - if [ "$CXX" = "clang++" ]; then export CXX="clang++-4.0" CC="clang-4.0"; fi 21 | - git submodule update --init --recursive 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Apiary Inc. . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | -include config.mk 2 | 3 | BUILDTYPE ?= Release 4 | BUILD_DIR ?= ./build 5 | PYTHON ?= python 6 | GYP ?= ./tools/gyp/gyp 7 | DESTDIR ?= /usr/local/bin 8 | 9 | # Default to verbose builds 10 | V ?= 1 11 | 12 | # Targets 13 | all: libsnowcrash 14 | 15 | .PHONY: libsnowcrash test-libsnowcrash 16 | 17 | libsnowcrash: config.gypi $(BUILD_DIR)/Makefile 18 | $(MAKE) -C $(BUILD_DIR) V=$(V) libsnowcrash 19 | 20 | test-libsnowcrash: config.gypi $(BUILD_DIR)/Makefile 21 | $(MAKE) -C $(BUILD_DIR) V=$(V) test-libsnowcrash 22 | mkdir -p ./bin 23 | cp -f $(BUILD_DIR)/out/$(BUILDTYPE)/test-libsnowcrash ./bin/test-libsnowcrash 24 | 25 | perf-libsnowcrash: config.gypi $(BUILD_DIR)/Makefile 26 | $(MAKE) -C $(BUILD_DIR) V=$(V) perf-libsnowcrash 27 | mkdir -p ./bin 28 | cp -f $(BUILD_DIR)/out/$(BUILDTYPE)/perf-libsnowcrash ./bin/perf-libsnowcrash 29 | 30 | config.gypi: configure 31 | $(PYTHON) ./configure 32 | 33 | $(BUILD_DIR)/Makefile: 34 | $(GYP) -f make --generator-output $(BUILD_DIR) --depth=. 35 | 36 | $(BUILD_DIR)/out/$(BUILDTYPE)/libsnowcrash.a: libsnowcrash 37 | 38 | clean: 39 | rm -rf $(BUILD_DIR)/out 40 | rm -rf ./bin 41 | 42 | distclean: 43 | rm -rf ./build 44 | rm -f ./config.mk 45 | rm -f ./config.gypi 46 | rm -rf ./bin 47 | 48 | test: test-libsnowcrash 49 | $(BUILD_DIR)/out/$(BUILDTYPE)/test-libsnowcrash 50 | 51 | ifdef INTEGRATION_TESTS 52 | bundle exec cucumber 53 | endif 54 | 55 | perf: perf-libsnowcrash 56 | $(BUILD_DIR)/out/$(BUILDTYPE)/perf-libsnowcrash ./test/performance/fixtures/fixture-1.apib 57 | 58 | .PHONY: libsnowcrash test-libsnowcrash perf-libsnowcrash clean distclean test 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![logo](https://raw.github.com/apiaryio/api-blueprint/master/assets/logo_apiblueprint.png) 2 | 3 | # Snow Crash [![Build Status](https://travis-ci.org/apiaryio/snowcrash.svg?branch=master)](https://travis-ci.org/apiaryio/snowcrash) [![Build status](https://ci.appveyor.com/api/projects/status/gupp1f7ymifxh3yn/branch/master?svg=true)](https://ci.appveyor.com/project/Apiary/snowcrash) 4 | 5 | 6 | ### API Blueprint Parser 7 | Snow Crash is the reference [API Blueprint](http://apiblueprint.org) parser built on top of the [Sundown](https://github.com/vmg/sundown) Markdown parser. 8 | 9 | API Blueprint is Web API documentation language. You can find API Blueprint documentation on the [API Blueprint site](http://apiblueprint.org). 10 | 11 | ## Status 12 | - [Format 1A8](https://github.com/apiaryio/api-blueprint/releases/tag/format-1A8) fully implemented 13 | 14 | ## Use 15 | 16 | ### C++ library 17 | 18 | ```c++ 19 | #include "snowcrash.h" 20 | 21 | mdp::ByteBuffer blueprint = R"( 22 | # My API 23 | ## GET /message 24 | + Response 200 (text/plain) 25 | 26 | Hello World! 27 | )"; 28 | 29 | snowcrash::ParseResult ast; 30 | snowcrash::parse(blueprint, 0, ast); 31 | 32 | std::cout << "API Name: " << ast.node.name << std::endl; 33 | ``` 34 | 35 | Refer to [`Blueprint.h`](src/Blueprint.h) for the details about the Snow Crash AST and [`BlueprintSourcemap.h`](src/BlueprintSourcemap.h) for details about Source Maps tree. 36 | 37 | ### Command line tool 38 | 39 | CLI was removed. It is replaced by utility named [drafter](https://github.com/apiaryio/drafter) 40 | 41 | ## Build 42 | 1. Clone the repo + fetch the submodules: 43 | 44 | ```sh 45 | $ git clone --recursive git://github.com/apiaryio/snowcrash.git 46 | $ cd snowcrash 47 | ``` 48 | 49 | 2. Build & test Snow Crash: 50 | 51 | ```sh 52 | $ ./configure 53 | $ make test 54 | ``` 55 | 56 | We love **Windows** too! Please refer to [Building on Windows](https://github.com/apiaryio/snowcrash/wiki/Building-on-Windows). 57 | 58 | 59 | ## Contribute 60 | Fork & Pull Request 61 | 62 | ## License 63 | MIT License. See the [LICENSE](https://github.com/apiaryio/snowcrash/blob/master/LICENSE) file. 64 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | 3 | image: Visual Studio 2015 4 | 5 | install: 6 | - git submodule update --init --recursive 7 | 8 | build_script: 9 | - vcbuild.bat MSVC2015 test 10 | -------------------------------------------------------------------------------- /ext/markdown-parser/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | # OS X and Xcode specific 24 | .DS_Store 25 | build/ 26 | *.pbxuser 27 | !default.pbxuser 28 | *.mode1v3 29 | !default.mode1v3 30 | *.mode2v3 31 | !default.mode2v3 32 | *.perspectivev3 33 | !default.perspectivev3 34 | *.xcworkspace 35 | !default.xcworkspace 36 | xcuserdata 37 | profile 38 | *.moved-aside 39 | DerivedData 40 | .idea/ 41 | 42 | # MSVC specific 43 | *.suo 44 | *.ncb 45 | *.user 46 | *.vcxproj 47 | *.sdf 48 | *.opensdf 49 | *.filters 50 | msvc/_UpgradeReport_Files/ 51 | msvc/Backup/ 52 | msvc/UpgradeLog.htm 53 | msvc/UpgradeLog.XML 54 | 55 | # Eclipse specific 56 | .project 57 | .cproject 58 | .settings 59 | -------------------------------------------------------------------------------- /ext/markdown-parser/.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: cpp 4 | addons: 5 | apt: 6 | sources: 7 | - llvm-toolchain-trusty-4.0 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - clang-4.0 11 | - g++-5 12 | - gcc-5 13 | compiler: 14 | - gcc 15 | - clang 16 | script: 17 | - make test 18 | before_install: 19 | - if [ "$CXX" = "g++" ]; then export CXX="g++-5" CC="gcc-5"; fi 20 | - if [ "$CXX" = "clang++" ]; then export CXX="clang++-4.0" CC="clang-4.0"; fi 21 | - git submodule update --init --recursive 22 | -------------------------------------------------------------------------------- /ext/markdown-parser/LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Apiary Inc. . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /ext/markdown-parser/Makefile: -------------------------------------------------------------------------------- 1 | # Default Shell 2 | SHELL = /bin/bash 3 | 4 | # Flags 5 | CXXFLAGS = -std=c++14 6 | 7 | # Default build path 8 | BUILD_PATH = build 9 | 10 | # Default C++ extension 11 | CXX_SRC_EXT = cc 12 | 13 | # Default C extension 14 | C_SRC_EXT = c 15 | 16 | # Markdown parser source directory 17 | SRC_PATH = src 18 | 19 | # Test source directory 20 | TEST_SRC_PATH = test 21 | 22 | # Sundown include paths 23 | SUNDOWN_INCLUDES = -Iext/sundown/src/ -Iext/sundown/html/ 24 | 25 | # Markdown parser include paths 26 | INCLUDES = -I$(SRC_PATH) $(SUNDOWN_INCLUDES) 27 | 28 | # Test include paths 29 | TEST_INCLUDES = $(INCLUDES) -I$(TEST_SRC_PATH) -I../../test/vendor/Catch/single_include 30 | 31 | # Markdown parser objects 32 | LIB_SRC = $(shell find $(SRC_PATH)/ -name '*.$(CXX_SRC_EXT)') 33 | LIB_OBJ = $(LIB_SRC:$(SRC_PATH)/%.$(CXX_SRC_EXT)=$(BUILD_PATH)/%.o) 34 | LIB_DEPS = $(LIB_OBJ:.o=.d) 35 | 36 | # Sundown library objects 37 | SUNDOWN_SRC=\ 38 | ext/sundown/src/markdown.c \ 39 | ext/sundown/src/stack.c \ 40 | ext/sundown/src/buffer.c \ 41 | ext/sundown/src/autolink.c \ 42 | ext/sundown/src/src_map.c \ 43 | ext/sundown/html/html.c \ 44 | ext/sundown/html/html_smartypants.c \ 45 | ext/sundown/html/houdini_html_e.c \ 46 | ext/sundown/html/houdini_href_e.c 47 | SUNDOWN_OBJ = $(addprefix $(BUILD_PATH)/,$(addsuffix .o,$(basename $(SUNDOWN_SRC)))) 48 | 49 | # Markdown parser tests object 50 | TEST_SRC = $(shell find $(TEST_SRC_PATH)/ -name '*.$(CXX_SRC_EXT)') 51 | TEST_OBJ = $(TEST_SRC:$(TEST_SRC_PATH)/%.$(CXX_SRC_EXT)=$(BUILD_PATH)/%.o) 52 | TEST_DEPS = $(TEST_OBJ:.o=.d) 53 | 54 | .PHONY: all 55 | all: libmarkdownparser.a test-libmarkdownparser libsundown.a 56 | 57 | libsundown.a: $(SUNDOWN_OBJ) 58 | $(AR) rcs $(BUILD_PATH)/libsundown.a $^ 59 | 60 | libmarkdownparser.a: $(LIB_OBJ) 61 | $(AR) rcs $(BUILD_PATH)/libmarkdownparser.a $^ 62 | 63 | test-libmarkdownparser: libmarkdownparser.a libsundown.a $(TEST_OBJ) 64 | $(CXX) $(TEST_OBJ) $(BUILD_PATH)/libmarkdownparser.a $(BUILD_PATH)/libsundown.a $(LDFLAGS) -o $(BUILD_PATH)/$@ 65 | 66 | .PHONY: test 67 | test: test-libmarkdownparser 68 | $(BUILD_PATH)/test-libmarkdownparser 69 | 70 | .PHONY: clean 71 | clean: 72 | $(RM) -r build 73 | $(RM) -r bin 74 | 75 | $(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(CXX_SRC_EXT) 76 | $(CXX) $(CXXFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@ 77 | 78 | $(BUILD_PATH)/%.o: $(TEST_SRC_PATH)/%.$(CXX_SRC_EXT) 79 | $(CXX) $(CXXFLAGS) $(TEST_INCLUDES) -MP -MMD -c $< -o $@ 80 | 81 | $(BUILD_PATH)/%.o: %.$(C_SRC_EXT) 82 | $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ 83 | 84 | $(LIB_OBJ): | $(BUILD_PATH) 85 | $(SUNDOWN_OBJ): | $(BUILD_PATH) 86 | 87 | $(BUILD_PATH): 88 | mkdir -p $(BUILD_PATH) 89 | mkdir -p $(BUILD_PATH)/ext/sundown/src/ 90 | mkdir -p $(BUILD_PATH)/ext/sundown/html/ 91 | -------------------------------------------------------------------------------- /ext/markdown-parser/README.md: -------------------------------------------------------------------------------- 1 | # Markdown Parser [![Build Status](https://travis-ci.org/apiaryio/markdown-parser.svg?branch=master)](https://travis-ci.org/apiaryio/markdown-parser) 2 | 3 | `Markdown Parser` purpose is to process a Markdown file and produce its abstract syntax tree (AST for short) which is suitable for further machine-processing. The AST also includes source maps for its nodes. 4 | 5 | Markdown Parser is currently built on top of the (now deprecated) [`sundown`](https://github.com/apiaryio/sundown) library. In the future the `sundown` may be replaced with [`hoedown`](https://github.com/hoedown/hoedown/) libray or even [`Redcarpet`](https://github.com/vmg/redcarpet) "C" core. 6 | 7 | ## Contribute 8 | Fork & Pull Request 9 | 10 | ## License 11 | MIT License. See the [LICENSE](LICENSE) file. 12 | -------------------------------------------------------------------------------- /ext/markdown-parser/msvc/markdown/markdown.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 31 | 34 | 37 | 40 | 50 | 53 | 56 | 59 | 62 | 65 | 68 | 71 | 74 | 77 | 78 | 86 | 89 | 92 | 95 | 98 | 101 | 111 | 114 | 117 | 120 | 123 | 126 | 129 | 132 | 135 | 138 | 139 | 140 | 141 | 142 | 143 | 148 | 151 | 152 | 155 | 156 | 159 | 160 | 161 | 166 | 169 | 170 | 173 | 174 | 177 | 178 | 179 | 184 | 185 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /ext/markdown-parser/msvc/msvc.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "markdown", "markdown\markdown.vcproj", "{288550BA-B6D6-4254-B5E3-9E8CE20970AE}" 5 | ProjectSection(ProjectDependencies) = postProject 6 | {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6} = {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6} 7 | EndProjectSection 8 | EndProject 9 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sundown", "sundown\sundown.vcproj", "{08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6}" 10 | EndProject 11 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test-markdown", "test-markdown\test-markdown.vcproj", "{DB045416-DCDB-45D3-904C-C1F15D608FC3}" 12 | ProjectSection(ProjectDependencies) = postProject 13 | {288550BA-B6D6-4254-B5E3-9E8CE20970AE} = {288550BA-B6D6-4254-B5E3-9E8CE20970AE} 14 | EndProjectSection 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Win32 = Debug|Win32 19 | Release|Win32 = Release|Win32 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {288550BA-B6D6-4254-B5E3-9E8CE20970AE}.Debug|Win32.ActiveCfg = Debug|Win32 23 | {288550BA-B6D6-4254-B5E3-9E8CE20970AE}.Debug|Win32.Build.0 = Debug|Win32 24 | {288550BA-B6D6-4254-B5E3-9E8CE20970AE}.Release|Win32.ActiveCfg = Release|Win32 25 | {288550BA-B6D6-4254-B5E3-9E8CE20970AE}.Release|Win32.Build.0 = Release|Win32 26 | {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6}.Debug|Win32.ActiveCfg = Debug|Win32 27 | {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6}.Debug|Win32.Build.0 = Debug|Win32 28 | {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6}.Release|Win32.ActiveCfg = Release|Win32 29 | {08CAEF5D-9ED7-4CB1-844D-D39AA17D3AD6}.Release|Win32.Build.0 = Release|Win32 30 | {DB045416-DCDB-45D3-904C-C1F15D608FC3}.Debug|Win32.ActiveCfg = Debug|Win32 31 | {DB045416-DCDB-45D3-904C-C1F15D608FC3}.Debug|Win32.Build.0 = Debug|Win32 32 | {DB045416-DCDB-45D3-904C-C1F15D608FC3}.Release|Win32.ActiveCfg = Release|Win32 33 | {DB045416-DCDB-45D3-904C-C1F15D608FC3}.Release|Win32.Build.0 = Release|Win32 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /ext/markdown-parser/msvc/sundown/sundown.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 31 | 34 | 37 | 40 | 49 | 52 | 55 | 58 | 61 | 64 | 67 | 70 | 73 | 76 | 77 | 85 | 88 | 91 | 94 | 97 | 100 | 109 | 112 | 115 | 118 | 121 | 124 | 127 | 130 | 133 | 136 | 137 | 138 | 139 | 140 | 141 | 146 | 149 | 150 | 153 | 154 | 157 | 158 | 161 | 162 | 165 | 166 | 167 | 172 | 175 | 176 | 179 | 180 | 183 | 184 | 187 | 188 | 191 | 192 | 195 | 196 | 197 | 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /ext/markdown-parser/msvc/test-markdown/test-markdown.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 25 | 28 | 31 | 34 | 37 | 40 | 50 | 53 | 56 | 59 | 65 | 68 | 71 | 74 | 77 | 80 | 83 | 86 | 87 | 95 | 98 | 101 | 104 | 107 | 110 | 120 | 123 | 126 | 129 | 136 | 139 | 142 | 145 | 148 | 151 | 154 | 157 | 158 | 159 | 160 | 161 | 162 | 167 | 170 | 171 | 174 | 175 | 178 | 179 | 180 | 185 | 186 | 191 | 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /ext/markdown-parser/src/ByteBuffer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ByteBuffer.h 3 | // markdownparser 4 | // 5 | // Created by Zdenek Nemec on 4/18/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef MARKDOWNPARSER_BYTEBUFFER_H 10 | #define MARKDOWNPARSER_BYTEBUFFER_H 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace mdp 17 | { 18 | 19 | /** 20 | * \brief Source data byte buffer 21 | * 22 | * Note this is a byte buffer, a sequence of 23 | * UTF8 bytes note necessarily characters. 24 | */ 25 | typedef std::string ByteBuffer; 26 | 27 | /** Byte buffer stream */ 28 | typedef std::stringstream ByteBufferStream; 29 | 30 | /** A generic continuous range */ 31 | struct Range { 32 | size_t location; 33 | size_t length; 34 | 35 | Range(size_t loc = 0, size_t len = 0) : location(loc), length(len) {} 36 | }; 37 | 38 | /** Range of bytes */ 39 | typedef Range BytesRange; 40 | 41 | /** Range of characters */ 42 | typedef Range CharactersRange; 43 | 44 | /** A generic set of non-continuous of ranges */ 45 | template 46 | class RangeSet : public std::vector 47 | { 48 | public: 49 | /** Append another range set to this one, merging continuous blocks */ 50 | void append(const RangeSet& val) 51 | { 52 | if (val.empty()) 53 | return; 54 | if (this->empty() || val.front().location != this->back().location + this->back().length) { 55 | this->insert(this->end(), val.begin(), val.end()); 56 | } else { 57 | // merge 58 | this->back().length += val.front().length; 59 | 60 | if (val.size() > 1) { 61 | this->insert(this->end(), ++val.begin(), val.end()); 62 | } 63 | } 64 | } 65 | }; 66 | 67 | /** Set of non-continuous byte ranges */ 68 | typedef RangeSet BytesRangeSet; 69 | 70 | /** Set of non-continuous character ranges */ 71 | typedef RangeSet CharactersRangeSet; 72 | 73 | /** Map byte index into utf-8 chracter index */ 74 | typedef std::vector ByteBufferCharacterIndex; 75 | 76 | /** Fill character map - cache of characters positions */ 77 | void BuildCharacterIndex(ByteBufferCharacterIndex& index, const ByteBuffer& byteBuffer); 78 | 79 | /** Convert ranges of bytes to ranges of characters */ 80 | CharactersRangeSet BytesRangeSetToCharactersRangeSet(const BytesRangeSet& rangeSet, const ByteBuffer& byteBuffer); 81 | CharactersRangeSet BytesRangeSetToCharactersRangeSet( 82 | const BytesRangeSet& rangeSet, const ByteBufferCharacterIndex& index); 83 | 84 | /** Maps bytes range set to byte buffer */ 85 | ByteBuffer MapBytesRangeSet(const BytesRangeSet& rangeSet, const ByteBuffer& byteBuffer); 86 | } 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /ext/markdown-parser/src/MarkdownNode.cc: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownNode.cc 3 | // markdownparser 4 | // 5 | // Created by Zdenek Nemec on 4/19/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "MarkdownNode.h" 10 | 11 | using namespace mdp; 12 | 13 | MarkdownNode::MarkdownNode(MarkdownNodeType type_, MarkdownNode* parent_, const ByteBuffer& text_, const Data& data_) 14 | : type(type_), text(text_), data(data_), m_parent(parent_) 15 | { 16 | m_children.reset(::new MarkdownNodes); 17 | } 18 | 19 | MarkdownNode::MarkdownNode(const MarkdownNode& rhs) 20 | { 21 | this->type = rhs.type; 22 | this->text = rhs.text; 23 | this->data = rhs.data; 24 | this->sourceMap = rhs.sourceMap; 25 | this->m_children.reset(::new MarkdownNodes(*rhs.m_children.get())); 26 | this->m_parent = rhs.m_parent; 27 | } 28 | 29 | MarkdownNode& MarkdownNode::operator=(const MarkdownNode& rhs) 30 | { 31 | this->type = rhs.type; 32 | this->text = rhs.text; 33 | this->data = rhs.data; 34 | this->sourceMap = rhs.sourceMap; 35 | this->m_children.reset(::new MarkdownNodes(*rhs.m_children.get())); 36 | this->m_parent = rhs.m_parent; 37 | return *this; 38 | } 39 | 40 | MarkdownNode::~MarkdownNode() {} 41 | 42 | MarkdownNode& MarkdownNode::parent() 43 | { 44 | if (!hasParent()) 45 | throw "no parent set"; 46 | return *m_parent; 47 | } 48 | 49 | const MarkdownNode& MarkdownNode::parent() const 50 | { 51 | if (!hasParent()) 52 | throw "no parent set"; 53 | return *m_parent; 54 | } 55 | 56 | void MarkdownNode::setParent(MarkdownNode* parent) 57 | { 58 | m_parent = parent; 59 | } 60 | 61 | bool MarkdownNode::hasParent() const 62 | { 63 | return (m_parent != NULL); 64 | } 65 | 66 | MarkdownNodes& MarkdownNode::children() 67 | { 68 | if (!m_children.get()) 69 | throw "no children set"; 70 | 71 | return *m_children; 72 | } 73 | 74 | const MarkdownNodes& MarkdownNode::children() const 75 | { 76 | if (!m_children.get()) 77 | throw "no children set"; 78 | 79 | return *m_children; 80 | } 81 | 82 | void MarkdownNode::printNode(size_t level) const 83 | { 84 | using std::cout; 85 | for (size_t i = 0; i < level; ++i) 86 | std::cout << " "; 87 | 88 | cout << "+ "; 89 | switch (type) { 90 | case RootMarkdownNodeType: 91 | cout << "root"; 92 | break; 93 | 94 | case CodeMarkdownNodeType: 95 | cout << "code"; 96 | break; 97 | 98 | case QuoteMarkdownNodeType: 99 | cout << "quote"; 100 | break; 101 | 102 | case HTMLMarkdownNodeType: 103 | cout << "HTML"; 104 | break; 105 | 106 | case HeaderMarkdownNodeType: 107 | cout << "header"; 108 | break; 109 | 110 | case HRuleMarkdownNodeType: 111 | cout << "hrul"; 112 | break; 113 | 114 | case ListItemMarkdownNodeType: 115 | cout << "list item"; 116 | break; 117 | 118 | case ParagraphMarkdownNodeType: 119 | cout << "paragraph"; 120 | break; 121 | 122 | default: 123 | cout << "undefined"; 124 | break; 125 | } 126 | 127 | cout << " (type " << type << ", data " << data << ") - "; 128 | cout << "`" << text << "`"; 129 | 130 | if (!sourceMap.empty()) { 131 | for (mdp::BytesRangeSet::const_iterator it = sourceMap.begin(); it != sourceMap.end(); ++it) { 132 | std::cout << ((it == sourceMap.begin()) ? " :" : ";"); 133 | std::cout << it->location << ":" << it->length; 134 | } 135 | } 136 | 137 | cout << std::endl; 138 | 139 | for (MarkdownNodeIterator it = m_children->begin(); it != m_children->end(); ++it) { 140 | it->printNode(level + 1); 141 | } 142 | 143 | if (level == 0) 144 | cout << std::endl << std::endl; 145 | } 146 | -------------------------------------------------------------------------------- /ext/markdown-parser/src/MarkdownNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownNode.h 3 | // markdownparser 4 | // 5 | // Created by Zdenek Nemec on 4/16/14. 6 | // Copyright (c) 2014 Apiary. All rights reserved. 7 | // 8 | 9 | #ifndef MARKDOWNPARSER_NODE_H 10 | #define MARKDOWNPARSER_NODE_H 11 | 12 | #include 13 | #include 14 | #include 15 | #include "ByteBuffer.h" 16 | 17 | namespace mdp 18 | { 19 | 20 | /** 21 | * AST block node types 22 | */ 23 | enum MarkdownNodeType 24 | { 25 | RootMarkdownNodeType = 0, 26 | CodeMarkdownNodeType, 27 | QuoteMarkdownNodeType, 28 | HTMLMarkdownNodeType, 29 | HeaderMarkdownNodeType, 30 | HRuleMarkdownNodeType, 31 | ListItemMarkdownNodeType, 32 | ParagraphMarkdownNodeType, 33 | TableMarkdownNodeType, 34 | TableRowMarkdownNodeType, 35 | TableCellMarkdownNodeType, 36 | UndefinedMarkdownNodeType = -1 37 | }; 38 | 39 | /* Forward declaration of AST Node */ 40 | class MarkdownNode; 41 | 42 | /** Markdown AST nodes collection */ 43 | typedef std::deque MarkdownNodes; 44 | 45 | /** 46 | * AST node 47 | */ 48 | class MarkdownNode 49 | { 50 | public: 51 | typedef int Data; 52 | 53 | /** Node type */ 54 | MarkdownNodeType type; 55 | 56 | /** Textual content, where applicable */ 57 | ByteBuffer text; 58 | 59 | /** Additinonal data, if applicable */ 60 | Data data; 61 | 62 | /** Source map of the node including any and all children */ 63 | BytesRangeSet sourceMap; 64 | 65 | /** Parent node, throws exception if no parent is defined */ 66 | MarkdownNode& parent(); 67 | const MarkdownNode& parent() const; 68 | 69 | /** Sets parent node */ 70 | void setParent(MarkdownNode* parent); 71 | 72 | /** True if section's parent is specified, false otherwise */ 73 | bool hasParent() const; 74 | 75 | /** Children nodes */ 76 | MarkdownNodes& children(); 77 | const MarkdownNodes& children() const; 78 | 79 | /** Constructor */ 80 | MarkdownNode(MarkdownNodeType type_ = UndefinedMarkdownNodeType, 81 | MarkdownNode* parent_ = NULL, 82 | const ByteBuffer& text_ = ByteBuffer(), 83 | const Data& data_ = Data()); 84 | 85 | /** Copy constructor */ 86 | MarkdownNode(const MarkdownNode& rhs); 87 | 88 | /** Assignment operator */ 89 | MarkdownNode& operator=(const MarkdownNode& rhs); 90 | 91 | /** Destructor */ 92 | ~MarkdownNode(); 93 | 94 | /** Prints the node to the stdout */ 95 | void printNode(size_t level = 0) const; 96 | 97 | private: 98 | MarkdownNode* m_parent; 99 | std::unique_ptr m_children; 100 | }; 101 | 102 | /** Markdown AST nodes collection iterator */ 103 | typedef MarkdownNodes::iterator MarkdownNodeIterator; 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /ext/markdown-parser/src/MarkdownParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MarkdownParser.h 3 | // markdownparser 4 | // 5 | // Created by Zdenek Nemec on 4/18/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef MARKDOWNPARSER_MARKDOWNPARSER_H 10 | #define MARKDOWNPARSER_MARKDOWNPARSER_H 11 | 12 | #include "ByteBuffer.h" 13 | #include "MarkdownNode.h" 14 | #include "markdown.h" 15 | 16 | namespace mdp 17 | { 18 | 19 | const char* const MarkdownEmphasisChars = "*_"; 20 | const char* const MarkdownListItemChars = "+-*"; 21 | 22 | const char MarkdownBeginReference = '['; 23 | const char MarkdownEndReference = ']'; 24 | 25 | const char* const MarkdownLinkRegex 26 | = "^[[:blank:]]*\\[(.*)][[:blank:]]*(\\[([^][()]*)]|\\(([^][()]+)\\))[[:blank:]]*$"; 27 | 28 | /** 29 | * GitHub-flavored Markdown Parser 30 | */ 31 | class MarkdownParser 32 | { 33 | public: 34 | MarkdownParser(); 35 | MarkdownParser(const MarkdownParser&); 36 | MarkdownParser& operator=(const MarkdownParser&); 37 | 38 | /** 39 | * \brief Parse source buffer 40 | * 41 | * \param source Markdown source data to be parsed 42 | * \param ast Parsed AST (root node) 43 | */ 44 | void parse(const ByteBuffer& source, MarkdownNode& ast); 45 | 46 | private: 47 | MarkdownNode* m_workingNode; 48 | bool m_listBlockContext; 49 | const ByteBuffer* m_source; 50 | size_t m_sourceLength; 51 | 52 | static const size_t OutputUnitSize; 53 | static const size_t MaxNesting; 54 | static const int ParserExtensions; 55 | 56 | typedef sd_callbacks RenderCallbacks; 57 | RenderCallbacks renderCallbacks(); 58 | 59 | typedef void* RenderCallbackData; 60 | RenderCallbackData renderCallbackData(); 61 | 62 | // Header 63 | static void renderHeader(struct buf* ob, const struct buf* text, int level, void* opaque); 64 | void renderHeader(const ByteBuffer& text, int level); 65 | 66 | // List 67 | static void beginList(int flags, void* opaque); 68 | void beginList(int flags); 69 | 70 | static void renderList(struct buf* ob, const struct buf* text, int flags, void* opaque); 71 | void renderList(const ByteBuffer& text, int flags); 72 | 73 | // List item 74 | static void beginListItem(int flags, void* opaque); 75 | void beginListItem(int flags); 76 | 77 | static void renderListItem(struct buf* ob, const struct buf* text, int flags, void* opaque); 78 | void renderListItem(const ByteBuffer& text, int flags); 79 | 80 | // Code block 81 | static void renderBlockCode(struct buf* ob, const struct buf* text, const struct buf* lang, void* opaque); 82 | void renderBlockCode(const ByteBuffer& text, const ByteBuffer& language); 83 | 84 | // Paragraph 85 | static void renderParagraph(struct buf* ob, const struct buf* text, void* opaque); 86 | void renderParagraph(const ByteBuffer& text); 87 | 88 | // Horizontal Rule 89 | static void renderHorizontalRule(struct buf* ob, void* opaque); 90 | void renderHorizontalRule(); 91 | 92 | // HTML 93 | static void renderHTML(struct buf* ob, const struct buf* text, void* opaque); 94 | void renderHTML(const ByteBuffer& text); 95 | 96 | // Quote 97 | static void beginQuote(void* opaque); 98 | void beginQuote(); 99 | 100 | static void renderQuote(struct buf* ob, const struct buf* text, void* opaque); 101 | void renderQuote(const std::string& text); 102 | 103 | // Source maps 104 | static void blockDidParse(const src_map* map, const uint8_t* txt_data, size_t size, void* opaque); 105 | void blockDidParse(const BytesRangeSet& sourceMap); 106 | }; 107 | } 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /ext/markdown-parser/test/test-libmarkdownparser.cc: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // test-libmarkdownparser 4 | // 5 | // Created by Zdenek Nemec on 4/18/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #define CATCH_CONFIG_MAIN /// < Let catch generate the main() 10 | #include "catch.hpp" 11 | -------------------------------------------------------------------------------- /src/AssetParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // AssetParser.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/17/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_ASSETPARSER_H 10 | #define SNOWCRASH_ASSETPARSER_H 11 | 12 | #include "SectionParser.h" 13 | #include "RegexMatch.h" 14 | #include "CodeBlockUtility.h" 15 | 16 | namespace snowcrash 17 | { 18 | 19 | /// Asset signature 20 | enum AssetSignature 21 | { 22 | NoAssetSignature = 0, 23 | BodyAssetSignature, /// < Explicit body asset 24 | ImplicitBodyAssetSignature, /// < Body asset using abbreviated syntax 25 | SchemaAssetSignature, /// < Explicit Schema asset 26 | UndefinedAssetSignature = -1 27 | }; 28 | 29 | /** Body matching regex */ 30 | const char* const BodyRegex = "^[[:blank:]]*[Bb]ody[[:blank:]]*$"; 31 | 32 | /** Schema matching regex */ 33 | const char* const SchemaRegex = "^[[:blank:]]*[Ss]chema[[:blank:]]*$"; 34 | 35 | /** 36 | * Asset Section Processor 37 | */ 38 | template <> 39 | struct SectionProcessor : public SectionProcessorBase { 40 | 41 | static MarkdownNodeIterator processSignature(const MarkdownNodeIterator& node, 42 | const MarkdownNodes& siblings, 43 | SectionParserData& pd, 44 | SectionLayout& layout, 45 | const ParseResultRef& out) 46 | { 47 | 48 | out.node = ""; 49 | CodeBlockUtility::signatureContentAsCodeBlock(node, pd, out.report, out.node); 50 | 51 | if (pd.exportSourceMap() && !out.node.empty()) { 52 | out.sourceMap.sourceMap.append(node->sourceMap); 53 | } 54 | 55 | return ++MarkdownNodeIterator(node); 56 | } 57 | 58 | NO_SECTION_DESCRIPTION(Asset) 59 | 60 | static MarkdownNodeIterator processContent(const MarkdownNodeIterator& node, 61 | const MarkdownNodes& siblings, 62 | SectionParserData& pd, 63 | const ParseResultRef& out) 64 | { 65 | 66 | mdp::ByteBuffer content; 67 | CodeBlockUtility::contentAsCodeBlock(node, pd, out.report, content); 68 | 69 | out.node += content; 70 | 71 | if (pd.exportSourceMap() && !content.empty()) { 72 | out.sourceMap.sourceMap.append(node->sourceMap); 73 | } 74 | 75 | return ++MarkdownNodeIterator(node); 76 | } 77 | 78 | static bool isContentNode(const MarkdownNodeIterator& node, SectionType sectionType) 79 | { 80 | 81 | return (SectionKeywordSignature(node) == UndefinedSectionType); 82 | } 83 | 84 | static SectionType sectionType(const MarkdownNodeIterator& node) 85 | { 86 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 87 | 88 | AssetSignature signature = assetSignature(node); 89 | 90 | switch (signature) { 91 | case BodyAssetSignature: 92 | case ImplicitBodyAssetSignature: 93 | return BodySectionType; 94 | 95 | case SchemaAssetSignature: 96 | return SchemaSectionType; 97 | 98 | default: 99 | return UndefinedSectionType; 100 | } 101 | } 102 | 103 | return UndefinedSectionType; 104 | } 105 | 106 | /** Resolve asset signature */ 107 | static AssetSignature assetSignature(const MarkdownNodeIterator& node) 108 | { 109 | 110 | mdp::ByteBuffer remaining, subject = node->children().front().text; 111 | subject = GetFirstLine(subject, remaining); 112 | TrimString(subject); 113 | 114 | if (RegexMatch(subject, BodyRegex)) 115 | return BodyAssetSignature; 116 | 117 | if (RegexMatch(subject, SchemaRegex)) 118 | return SchemaAssetSignature; 119 | 120 | return NoAssetSignature; 121 | } 122 | }; 123 | 124 | /** Asset Section Parser */ 125 | typedef SectionParser AssetParser; 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/AttributesParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // AttributesParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 11/25/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_ATTRIBUTESPARSER_H 10 | #define SNOWCRASH_ATTRIBUTESPARSER_H 11 | 12 | #include "RegexMatch.h" 13 | #include "MSONValueMemberParser.h" 14 | 15 | using namespace scpl; 16 | 17 | namespace snowcrash 18 | { 19 | 20 | /** Attributes matching regex */ 21 | const char* const AttributesRegex = "^[[:blank:]]*[Aa]ttributes?[[:blank:]]*(\\(.*\\))?$"; 22 | 23 | /** 24 | * Attributes section processor 25 | */ 26 | template <> 27 | struct SectionProcessor : public SignatureSectionProcessorBase { 28 | 29 | static SignatureTraits signatureTraits() 30 | { 31 | 32 | SignatureTraits signatureTraits(SignatureTraits::IdentifierTrait | SignatureTraits::AttributesTrait); 33 | 34 | return signatureTraits; 35 | } 36 | 37 | static MarkdownNodeIterator finalizeSignature(const MarkdownNodeIterator& node, 38 | SectionParserData& pd, 39 | const Signature& signature, 40 | const ParseResultRef& out) 41 | { 42 | 43 | if (!IEqual()(signature.identifier, "Attribute") 44 | && !IEqual()(signature.identifier, "Attributes")) { 45 | 46 | return node; 47 | } 48 | 49 | mson::parseTypeDefinition(node, pd, signature.attributes, out.report, out.node.typeDefinition); 50 | 51 | if (pd.exportSourceMap()) { 52 | 53 | if (!out.node.typeDefinition.empty()) { 54 | out.sourceMap.typeDefinition.sourceMap = node->sourceMap; 55 | } 56 | 57 | out.sourceMap.sourceMap = node->sourceMap; 58 | } 59 | 60 | // Default to `object` type specification 61 | if (out.node.typeDefinition.baseType == mson::UndefinedBaseType) { 62 | out.node.typeDefinition.baseType = mson::ImplicitObjectBaseType; 63 | } 64 | 65 | SectionProcessor::parseRemainingContent( 66 | node, pd, signature.remainingContent, out.node.sections, out.sourceMap.sections); 67 | 68 | return ++MarkdownNodeIterator(node); 69 | } 70 | 71 | static MarkdownNodeIterator processDescription(const MarkdownNodeIterator& node, 72 | const MarkdownNodes& siblings, 73 | SectionParserData& pd, 74 | const ParseResultRef& out) 75 | { 76 | 77 | return SectionProcessor::blockDescription( 78 | node, pd, out.node.sections, out.sourceMap.sections); 79 | } 80 | 81 | static MarkdownNodeIterator processNestedSection(const MarkdownNodeIterator& node, 82 | const MarkdownNodes& siblings, 83 | SectionParserData& pd, 84 | const ParseResultRef& out) 85 | { 86 | 87 | ParseResultRef typeSections(out.report, out.node.sections, out.sourceMap.sections); 88 | 89 | return SectionProcessor::processNestedMembers( 90 | node, siblings, pd, typeSections, out.node.typeDefinition.baseType); 91 | } 92 | 93 | static bool isDescriptionNode(const MarkdownNodeIterator& node, SectionType sectionType) 94 | { 95 | 96 | return SectionProcessor::isDescriptionNode(node, sectionType); 97 | } 98 | 99 | static SectionType sectionType(const MarkdownNodeIterator& node) 100 | { 101 | 102 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 103 | 104 | mdp::ByteBuffer remaining, subject = node->children().front().text; 105 | 106 | subject = GetFirstLine(subject, remaining); 107 | TrimString(subject); 108 | 109 | if (RegexMatch(subject, AttributesRegex)) { 110 | return AttributesSectionType; 111 | } 112 | } 113 | 114 | return UndefinedSectionType; 115 | } 116 | 117 | static SectionType nestedSectionType(const MarkdownNodeIterator& node) 118 | { 119 | 120 | return SectionProcessor::nestedSectionType(node); 121 | } 122 | }; 123 | 124 | /** Attributes Section Parser */ 125 | typedef SectionParser AttributesParser; 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /src/Blueprint.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Blueprint.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 26/01/15. 6 | // Copyright (c) 2015 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "Blueprint.h" 10 | 11 | using namespace snowcrash; 12 | 13 | DataStructure& DataStructure::operator=(const mson::NamedType& rhs) 14 | { 15 | this->name = rhs.name; 16 | this->typeDefinition = rhs.typeDefinition; 17 | this->sections = rhs.sections; 18 | 19 | return *this; 20 | } 21 | 22 | Elements& Element::Content::elements() 23 | { 24 | if (!m_elements.get()) 25 | throw ELEMENTS_NOT_SET_ERR; 26 | 27 | return *m_elements; 28 | } 29 | 30 | const Elements& Element::Content::elements() const 31 | { 32 | if (!m_elements.get()) 33 | throw ELEMENTS_NOT_SET_ERR; 34 | 35 | return *m_elements; 36 | } 37 | 38 | Element::Content::Content() 39 | { 40 | m_elements.reset(::new Elements); 41 | } 42 | 43 | Element::Content::Content(const Element::Content& rhs) 44 | { 45 | this->copy = rhs.copy; 46 | this->resource = rhs.resource; 47 | this->dataStructure = rhs.dataStructure; 48 | m_elements.reset(::new Elements(*rhs.m_elements.get())); 49 | } 50 | 51 | Element::Content& Element::Content::operator=(const Element::Content& rhs) 52 | { 53 | this->copy = rhs.copy; 54 | this->resource = rhs.resource; 55 | this->dataStructure = rhs.dataStructure; 56 | m_elements.reset(::new Elements(*rhs.m_elements.get())); 57 | 58 | return *this; 59 | } 60 | 61 | Element::Content::~Content() 62 | { 63 | } 64 | 65 | Element::Element(const Element::Class& element_) : element(element_) 66 | { 67 | } 68 | 69 | Element::Element(const Element& rhs) 70 | { 71 | this->element = rhs.element; 72 | this->attributes = rhs.attributes; 73 | this->content = rhs.content; 74 | this->category = rhs.category; 75 | } 76 | 77 | Element& Element::operator=(const Element& rhs) 78 | { 79 | this->element = rhs.element; 80 | this->attributes = rhs.attributes; 81 | this->content = rhs.content; 82 | this->category = rhs.category; 83 | 84 | return *this; 85 | } 86 | 87 | Element::~Element() 88 | { 89 | } 90 | -------------------------------------------------------------------------------- /src/BlueprintSourcemap.cc: -------------------------------------------------------------------------------- 1 | // 2 | // BlueprintSourcemap.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 26/01/15. 6 | // Copyright (c) 2015 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "BlueprintSourcemap.h" 10 | 11 | using namespace snowcrash; 12 | 13 | SourceMap& SourceMap::Content::elements() 14 | { 15 | if (!m_elements.get()) 16 | throw ELEMENTS_NOT_SET_ERR; 17 | 18 | return *m_elements; 19 | } 20 | 21 | const SourceMap& SourceMap::Content::elements() const 22 | { 23 | if (!m_elements.get()) 24 | throw ELEMENTS_NOT_SET_ERR; 25 | 26 | return *m_elements; 27 | } 28 | 29 | SourceMap::Content::Content() 30 | { 31 | m_elements.reset(::new SourceMap); 32 | } 33 | 34 | SourceMap::Content::Content(const SourceMap::Content& rhs) 35 | { 36 | this->copy = rhs.copy; 37 | this->resource = rhs.resource; 38 | this->dataStructure = rhs.dataStructure; 39 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 40 | } 41 | 42 | SourceMap::Content& SourceMap::Content::operator=(const SourceMap::Content& rhs) 43 | { 44 | this->copy = rhs.copy; 45 | this->resource = rhs.resource; 46 | this->dataStructure = rhs.dataStructure; 47 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 48 | 49 | return *this; 50 | } 51 | 52 | SourceMap::Content::~Content() 53 | { 54 | } 55 | 56 | SourceMap::SourceMap(const Element::Class& element_) : element(element_) 57 | { 58 | } 59 | 60 | SourceMap::SourceMap(const SourceMap& rhs) 61 | { 62 | this->element = rhs.element; 63 | this->attributes = rhs.attributes; 64 | this->content = rhs.content; 65 | this->category = rhs.category; 66 | } 67 | 68 | SourceMap& SourceMap::operator=(const SourceMap& rhs) 69 | { 70 | this->element = rhs.element; 71 | this->attributes = rhs.attributes; 72 | this->content = rhs.content; 73 | this->category = rhs.category; 74 | 75 | return *this; 76 | } 77 | 78 | SourceMap::~SourceMap() 79 | { 80 | } 81 | -------------------------------------------------------------------------------- /src/BlueprintUtility.h: -------------------------------------------------------------------------------- 1 | // 2 | // BlueprintUtility.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/11/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_BLUEPRINTUTILITY_H 10 | #define SNOWCRASH_BLUEPRINTUTILITY_H 11 | 12 | #include 13 | #include 14 | #include "Blueprint.h" 15 | #include "HTTP.h" 16 | 17 | namespace snowcrash 18 | { 19 | 20 | /** 21 | * \brief Pair firsts matching predicate. 22 | * 23 | * Two pairs are a match if their %first matches. 24 | */ 25 | template > 26 | struct MatchFirsts : std::binary_function { 27 | bool operator()(const T& left, const T& right) const 28 | { 29 | return Predicate()(left.first, right.first); 30 | } 31 | }; 32 | 33 | /** 34 | * \brief Matches a pair's first against a value. 35 | */ 36 | template > 37 | struct MatchFirstWith : std::binary_function { 38 | bool operator()(const T& left, const R& right) const 39 | { 40 | return Predicate()(left.first, right); 41 | } 42 | }; 43 | 44 | /** A name matching predicate. */ 45 | template 46 | struct MatchName : std::binary_function { 47 | bool operator()(const T& first, const T& second) const 48 | { 49 | return first.name == second.name; 50 | } 51 | }; 52 | 53 | /** 54 | * \brief Payload matching predicate. 55 | * 56 | * Matches payloads if their name and media type matches. 57 | */ 58 | struct MatchPayload : std::binary_function { 59 | bool operator()(const first_argument_type& left, const second_argument_type& right) const 60 | { 61 | 62 | if (left.name != right.name) 63 | return false; 64 | 65 | // Resolve left content type 66 | Collection
::const_iterator header; 67 | 68 | header = std::find_if(left.headers.begin(), 69 | left.headers.end(), 70 | std::bind2nd(MatchFirstWith(), HTTPHeaderName::ContentType)); 71 | 72 | std::string leftContentType; 73 | 74 | if (header != left.headers.end()) 75 | leftContentType = header->second; 76 | 77 | // Resolve right content type 78 | header = std::find_if(right.headers.begin(), 79 | right.headers.end(), 80 | std::bind2nd(MatchFirstWith(), HTTPHeaderName::ContentType)); 81 | 82 | std::string rightContentType; 83 | 84 | if (header != right.headers.end()) 85 | rightContentType = header->second; 86 | 87 | return leftContentType == rightContentType; 88 | } 89 | }; 90 | 91 | /** URI matching predicate. */ 92 | struct MatchResource : std::binary_function { 93 | bool operator()(const first_argument_type& first, const second_argument_type& second) const 94 | { 95 | return first.uriTemplate == second.uriTemplate; 96 | } 97 | }; 98 | 99 | /** Action matching predicate. */ 100 | struct MatchAction : std::binary_function { 101 | bool operator()(const first_argument_type& first, const second_argument_type& second) const 102 | { 103 | return first.uriTemplate == second.uriTemplate && first.method == second.method; 104 | } 105 | }; 106 | 107 | /** Relation matching predicate. */ 108 | struct MatchRelation : std::binary_function { 109 | bool operator()(const first_argument_type& first, const second_argument_type& second) const 110 | { 111 | return !first.relation.str.empty() && !second.str.empty() && first.relation.str == second.str; 112 | } 113 | }; 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /src/HTTP.cc: -------------------------------------------------------------------------------- 1 | // 2 | // HTTP.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/11/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "HTTP.h" 10 | 11 | using namespace snowcrash; 12 | 13 | const std::string HTTPHeaderName::Accept = "Accept"; 14 | const std::string HTTPHeaderName::ContentLength = "Content-Length"; 15 | const std::string HTTPHeaderName::ContentType = "Content-Type"; 16 | const std::string HTTPHeaderName::TransferEncoding = "Transfer-Encoding"; 17 | 18 | const std::string HTTPHeaderName::SetCookie = "Set-Cookie"; 19 | const std::string HTTPHeaderName::Link = "Link"; 20 | 21 | const std::string HTTPMethodName::Head = "HEAD"; 22 | const std::string HTTPMethodName::Connect = "CONNECT"; 23 | 24 | StatusCodeTraits snowcrash::GetStatusCodeTrait(HTTPStatusCode code) 25 | { 26 | StatusCodeTraits traits; 27 | traits.code = code; 28 | 29 | // Following status codes MUST NOT contain response body 30 | if (code == 204 || code == 304 || code / 100 == 1) { 31 | traits.allowBody = false; 32 | } 33 | 34 | return traits; 35 | } 36 | 37 | HTTPMethodTraits snowcrash::GetMethodTrait(HTTPMethod method) 38 | { 39 | HTTPMethodTraits traits; 40 | traits.method = method; 41 | 42 | // Following HTTP methods MUST NOT contain response body 43 | // FIXME: When refactoring traits don't forget that 'CONNECT' has no body only when 1xx-2xx 44 | if (method == HTTPMethodName::Head || method == HTTPMethodName::Connect) { 45 | traits.allowBody = false; 46 | } 47 | 48 | return traits; 49 | } 50 | -------------------------------------------------------------------------------- /src/HTTP.h: -------------------------------------------------------------------------------- 1 | // 2 | // HTTP.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/11/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_HTTP_H 10 | #define SNOWCRASH_HTTP_H 11 | 12 | #include 13 | #include "Blueprint.h" 14 | 15 | /** 16 | * \brief HTTP Methods 17 | * 18 | * Technical note: Using preprocessor macro instead of strict 19 | * defined type due to C++98 string concatenation limitations. 20 | * FIXME: To be improved with migration to C++11. 21 | */ 22 | #define HTTP_REQUEST_METHOD \ 23 | "(GET|POST|PUT|DELETE|OPTIONS|PATCH|PROPPATCH|LOCK|UNLOCK|COPY|MOVE|MKCOL|HEAD|LINK|UNLINK|CONNECT)" 24 | 25 | /** 26 | * \brief URI Template. 27 | * 28 | * See previous technical note (using macro). 29 | */ 30 | #define URI_TEMPLATE "(/.*)" 31 | 32 | namespace snowcrash 33 | { 34 | 35 | /** 36 | * Selected HTTP Header names. 37 | */ 38 | struct HTTPHeaderName { 39 | static const std::string Accept; 40 | static const std::string ContentLength; 41 | static const std::string ContentType; 42 | static const std::string TransferEncoding; 43 | 44 | static const std::string SetCookie; 45 | static const std::string Link; 46 | }; 47 | 48 | /** 49 | * Selected HTTP Method names. 50 | */ 51 | struct HTTPMethodName { 52 | static const std::string Head; 53 | static const std::string Connect; 54 | }; 55 | 56 | /** 57 | * A HTTP Status code. 58 | */ 59 | typedef unsigned int HTTPStatusCode; 60 | 61 | /** 62 | * Traits of a HTTP response. 63 | */ 64 | struct HTTPResponseTraits { 65 | 66 | bool allowBody; /// < Response body is allowed. 67 | 68 | HTTPResponseTraits() : allowBody(true) 69 | { 70 | } 71 | }; 72 | 73 | /** 74 | * Response traits for a HTTP method. 75 | * 76 | * HTTP request method related response prescription 77 | * Ref: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html 78 | */ 79 | struct HTTPMethodTraits : HTTPResponseTraits { 80 | HTTPMethod method; 81 | HTTPMethodTraits() : method("") 82 | { 83 | } 84 | }; 85 | 86 | /** 87 | * Response traits for a HTTP status code. 88 | * 89 | * Status-related response prescription. 90 | * Ref: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 91 | */ 92 | struct StatusCodeTraits : HTTPResponseTraits { 93 | HTTPStatusCode code; 94 | StatusCodeTraits() : code(0) 95 | { 96 | } 97 | }; 98 | 99 | /** 100 | * \brief Retrieve response traits for given HTTP method. 101 | * \param method HTTP method to retrieve traits for. 102 | * \return A %HTTPMethodTraits for given method. 103 | */ 104 | extern HTTPMethodTraits GetMethodTrait(HTTPMethod method); 105 | 106 | /** 107 | * \brief Retrieve response traits for given status code. 108 | * \param code A HTTP status code to retrieve traits for. 109 | * \return A %StatusCodeTraits for given code. 110 | */ 111 | extern StatusCodeTraits GetStatusCodeTrait(HTTPStatusCode code); 112 | } 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/HeadersParser.cc: -------------------------------------------------------------------------------- 1 | #include "HeadersParser.h" 2 | 3 | using namespace snowcrash; 4 | 5 | /** Finds a header in its containment group by its key (first) */ 6 | static HeaderIterator findHeader(const Headers& headers, const Header& header) 7 | { 8 | 9 | return std::find_if( 10 | headers.begin(), headers.end(), std::bind2nd(MatchFirsts >(), header)); 11 | } 12 | 13 | typedef std::vector HeadersKeyCollection; 14 | 15 | /** Get collection of allowed keywords - workaround due to C++98 restriction - static initialization of vector */ 16 | static const HeadersKeyCollection& getAllowedMultipleDefinitions() 17 | { 18 | 19 | static std::string keys[] = { 20 | HTTPHeaderName::SetCookie, HTTPHeaderName::Link, 21 | }; 22 | 23 | static const HeadersKeyCollection allowedMultipleDefinitions(keys, keys + (sizeof(keys) / sizeof(keys[0]))); 24 | 25 | return allowedMultipleDefinitions; 26 | } 27 | 28 | /** Check if Header name has allowed multiple definitions */ 29 | static bool isAllowedMultipleDefinition(const Header& header) 30 | { 31 | 32 | const HeadersKeyCollection& keys = getAllowedMultipleDefinitions(); 33 | 34 | return std::find_if(keys.begin(), 35 | keys.end(), 36 | std::bind1st(MatchFirstWith >(), header)) 37 | != keys.end(); 38 | } 39 | 40 | static bool isNotValidTokenChar(const std::string::value_type& c) 41 | { 42 | static const std::string validChars("-#$%&'*+.^_`|~"); 43 | 44 | return !(std::isalnum(c) || (validChars.find(c) != std::string::npos)); 45 | } 46 | 47 | static std::string::const_iterator findNonValidCharInHeaderName(const std::string& token) 48 | { 49 | 50 | return std::find_if(token.begin(), token.end(), isNotValidTokenChar); 51 | } 52 | 53 | bool HeaderNameTokenChecker::operator()() const 54 | { 55 | 56 | return findNonValidCharInHeaderName(headerName) == headerName.end(); 57 | } 58 | 59 | std::string HeaderNameTokenChecker::getMessage() const 60 | { 61 | 62 | const char invalidChar = *findNonValidCharInHeaderName(headerName); 63 | std::stringstream ss; 64 | ss << "HTTP header name '" << headerName << "' contains illegal character '" << invalidChar << "' (0x" << std::hex 65 | << static_cast(invalidChar) << ") skipping the header"; 66 | 67 | return ss.str(); 68 | } 69 | 70 | bool ColonPresentedChecker::operator()() const 71 | { 72 | 73 | return captures[3].size() >= 1 && (captures[3].find(':') != std::string::npos); 74 | } 75 | 76 | std::string ColonPresentedChecker::getMessage() const 77 | { 78 | 79 | std::stringstream ss; 80 | ss << "missing colon after header name '" << captures[1] << "'"; 81 | 82 | return ss.str(); 83 | } 84 | 85 | bool HeadersDuplicateChecker::operator()() const 86 | { 87 | 88 | return findHeader(headers, header) == headers.end() || isAllowedMultipleDefinition(header); 89 | } 90 | 91 | std::string HeadersDuplicateChecker::getMessage() const 92 | { 93 | 94 | std::stringstream ss; 95 | ss << "duplicate definition of '" << header.first << "' header"; 96 | 97 | return ss.str(); 98 | } 99 | 100 | bool HeaderValuePresentedChecker::operator()() const 101 | { 102 | 103 | return !header.second.empty(); 104 | } 105 | 106 | std::string HeaderValuePresentedChecker::getMessage() const 107 | { 108 | 109 | std::stringstream ss; 110 | ss << "HTTP header '" << header.first << "' has no value"; 111 | 112 | return ss.str(); 113 | } 114 | 115 | bool HeaderParserValidator::operator()(const ValidateFunctorBase& rule) 116 | { 117 | bool rc = rule(); 118 | 119 | if (!rc) { 120 | out.report.warnings.push_back(Warning(rule.getMessage(), HTTPWarning, sourceMap)); 121 | } 122 | 123 | return rc; 124 | } 125 | -------------------------------------------------------------------------------- /src/MSONMixinParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSONMixinParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 11/5/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_MSONMIXINPARSER_H 10 | #define SNOWCRASH_MSONMIXINPARSER_H 11 | 12 | #include "SectionParser.h" 13 | #include "MSONUtility.h" 14 | 15 | using namespace scpl; 16 | 17 | namespace snowcrash 18 | { 19 | 20 | /** MSON Mixin matching regex */ 21 | const char* const MSONMixinRegex = "^[[:blank:]]*([Ii]nclude[[:blank:]]+)"; 22 | 23 | /** 24 | * MSON Mixin Section Processor 25 | */ 26 | template <> 27 | struct SectionProcessor : public SignatureSectionProcessorBase { 28 | 29 | static SignatureTraits signatureTraits() 30 | { 31 | 32 | SignatureTraits signatureTraits(SignatureTraits::IdentifierTrait | SignatureTraits::AttributesTrait); 33 | 34 | return signatureTraits; 35 | } 36 | 37 | static MarkdownNodeIterator finalizeSignature(const MarkdownNodeIterator& node, 38 | SectionParserData& pd, 39 | const Signature& signature, 40 | const ParseResultRef& out) 41 | { 42 | 43 | CaptureGroups captureGroups; 44 | std::vector attributes = signature.attributes; 45 | 46 | if (RegexCapture(signature.identifier, MSONMixinRegex, captureGroups, 2) && !captureGroups[1].empty()) { 47 | 48 | // Get the type name and insert it into attributes 49 | std::string typeName = signature.identifier.substr(captureGroups[1].length()); 50 | attributes.insert(attributes.begin(), typeName); 51 | } 52 | 53 | mson::parseTypeDefinition(node, pd, attributes, out.report, out.node); 54 | 55 | if (pd.exportSourceMap()) { 56 | out.sourceMap.sourceMap = node->sourceMap; 57 | } 58 | 59 | if ((out.node.baseType == mson::PrimitiveBaseType) || (out.node.baseType == mson::UndefinedBaseType)) { 60 | 61 | // WARN: invalid mixin base type 62 | mdp::CharactersRangeSet sourceMap 63 | = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceCharacterIndex); 64 | out.report.warnings.push_back( 65 | Warning("mixin type may not include a type of a primitive sub-type", FormattingWarning, sourceMap)); 66 | } 67 | 68 | // Check circular references 69 | if (out.node.typeSpecification.name.base == mson::UndefinedTypeName 70 | && !out.node.typeSpecification.name.symbol.literal.empty() 71 | && !out.node.typeSpecification.name.symbol.variable) { 72 | 73 | mson::addDependency( 74 | node, pd, out.node.typeSpecification.name.symbol.literal, pd.namedTypeContext, out.report, true); 75 | } 76 | 77 | return ++MarkdownNodeIterator(node); 78 | } 79 | 80 | NO_SECTION_DESCRIPTION(mson::Mixin) 81 | 82 | static SectionType sectionType(const MarkdownNodeIterator& node) 83 | { 84 | 85 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 86 | 87 | mdp::ByteBuffer subject = node->children().front().text; 88 | 89 | TrimString(subject); 90 | 91 | if (RegexMatch(subject, MSONMixinRegex)) { 92 | return MSONMixinSectionType; 93 | } 94 | } 95 | 96 | return UndefinedSectionType; 97 | } 98 | }; 99 | 100 | /** MSON Mixin Section Parser */ 101 | typedef SectionParser MSONMixinParser; 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/MSONOneOfParser.cc: -------------------------------------------------------------------------------- 1 | // 2 | // MSONOneOfParser.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 11/12/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "MSONOneOfParser.h" 10 | #include "MSONTypeSectionParser.h" 11 | #include "MSONPropertyMemberParser.h" 12 | 13 | namespace snowcrash 14 | { 15 | 16 | /** Implementation of processNestedSection */ 17 | MarkdownNodeIterator SectionProcessor::processNestedSection(const MarkdownNodeIterator& node, 18 | const MarkdownNodes& siblings, 19 | SectionParserData& pd, 20 | const ParseResultRef& out) 21 | { 22 | 23 | MarkdownNodeIterator cur = node; 24 | mson::Element element; 25 | SourceMap elementSM; 26 | 27 | switch (pd.sectionContext()) { 28 | case MSONMixinSectionType: { 29 | IntermediateParseResult mixin(out.report); 30 | cur = MSONMixinParser::parse(node, siblings, pd, mixin); 31 | 32 | element.build(mixin.node); 33 | 34 | if (pd.exportSourceMap()) { 35 | elementSM.mixin = mixin.sourceMap; 36 | } 37 | 38 | break; 39 | } 40 | 41 | case MSONOneOfSectionType: { 42 | IntermediateParseResult oneOf(out.report); 43 | cur = MSONOneOfParser::parse(node, siblings, pd, oneOf); 44 | 45 | element.build(oneOf.node); 46 | 47 | if (pd.exportSourceMap()) { 48 | elementSM = oneOf.sourceMap; 49 | } 50 | 51 | break; 52 | } 53 | 54 | case MSONPropertyMembersSectionType: { 55 | IntermediateParseResult typeSection(out.report); 56 | typeSection.node.baseType = mson::ObjectBaseType; 57 | 58 | cur = MSONTypeSectionListParser::parse(node, siblings, pd, typeSection); 59 | 60 | element.buildFromElements(typeSection.node.content.elements()); 61 | 62 | if (pd.exportSourceMap()) { 63 | elementSM = typeSection.sourceMap.elements(); 64 | } 65 | 66 | break; 67 | } 68 | 69 | case MSONPropertyMemberSectionType: { 70 | IntermediateParseResult propertyMember(out.report); 71 | cur = MSONPropertyMemberParser::parse(node, siblings, pd, propertyMember); 72 | 73 | element.build(propertyMember.node); 74 | 75 | if (pd.exportSourceMap()) { 76 | elementSM.property = propertyMember.sourceMap; 77 | } 78 | 79 | break; 80 | } 81 | 82 | default: 83 | break; 84 | } 85 | 86 | if (element.klass != mson::Element::UndefinedClass) { 87 | out.node.push_back(element); 88 | 89 | if (pd.exportSourceMap()) { 90 | out.sourceMap.collection.push_back(elementSM); 91 | } 92 | } 93 | 94 | return cur; 95 | } 96 | 97 | /** Implementation of nestedSectionType */ 98 | SectionType SectionProcessor::nestedSectionType(const MarkdownNodeIterator& node) 99 | { 100 | 101 | SectionType nestedType = UndefinedSectionType; 102 | 103 | // Check if mson mixin section 104 | nestedType = SectionProcessor::sectionType(node); 105 | 106 | if (nestedType != UndefinedSectionType) { 107 | return nestedType; 108 | } 109 | 110 | // Check if mson one of section 111 | nestedType = SectionProcessor::sectionType(node); 112 | 113 | if (nestedType != UndefinedSectionType) { 114 | return nestedType; 115 | } 116 | 117 | // Check if mson member type section section 118 | nestedType = SectionProcessor::sectionType(node); 119 | 120 | if (nestedType != UndefinedSectionType) { 121 | return nestedType; 122 | } 123 | 124 | // Return property member type section if list item 125 | if (node->type == mdp::ListItemMarkdownNodeType) { 126 | return MSONPropertyMemberSectionType; 127 | } 128 | 129 | return UndefinedSectionType; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/MSONOneOfParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSONOneOfParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 11/5/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_MSONONEOFPARSER_H 10 | #define SNOWCRASH_MSONONEOFPARSER_H 11 | 12 | #include "MSONMixinParser.h" 13 | 14 | using namespace scpl; 15 | 16 | namespace snowcrash 17 | { 18 | 19 | /** MSON One Of matching regex */ 20 | const char* const MSONOneOfRegex = "^[[:blank:]]*[Oo]ne[[:blank:]]+[Oo]f[[:blank:]]*$"; 21 | 22 | /** 23 | * MSON One Of Section Processor 24 | */ 25 | template <> 26 | struct SectionProcessor : public SignatureSectionProcessorBase { 27 | 28 | static SignatureTraits signatureTraits() 29 | { 30 | 31 | SignatureTraits signatureTraits(SignatureTraits::IdentifierTrait); 32 | 33 | return signatureTraits; 34 | } 35 | 36 | NO_SECTION_DESCRIPTION(mson::OneOf) 37 | 38 | static MarkdownNodeIterator processNestedSection( 39 | const MarkdownNodeIterator&, const MarkdownNodes&, SectionParserData&, const ParseResultRef&); 40 | 41 | static void finalize( 42 | const MarkdownNodeIterator& node, SectionParserData& pd, const ParseResultRef& out) 43 | { 44 | 45 | if (out.node.empty()) { 46 | 47 | // WARN: one of type do not have nested members 48 | mdp::CharactersRangeSet sourceMap 49 | = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceCharacterIndex); 50 | out.report.warnings.push_back( 51 | Warning("one of type must have nested members", EmptyDefinitionWarning, sourceMap)); 52 | } 53 | } 54 | 55 | static SectionType sectionType(const MarkdownNodeIterator& node) 56 | { 57 | 58 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 59 | 60 | mdp::ByteBuffer remaining, subject = node->children().front().text; 61 | 62 | subject = GetFirstLine(subject, remaining); 63 | TrimString(subject); 64 | 65 | if (RegexMatch(subject, MSONOneOfRegex)) { 66 | return MSONOneOfSectionType; 67 | } 68 | } 69 | 70 | return UndefinedSectionType; 71 | } 72 | 73 | static SectionType nestedSectionType(const MarkdownNodeIterator&); 74 | }; 75 | 76 | /** MSON One Of Section Parser */ 77 | typedef SectionParser MSONOneOfParser; 78 | } 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/MSONPropertyMemberParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // MSONPropertyMemberParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 11/5/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_MSONPROPERTYMEMBERPARSER_H 10 | #define SNOWCRASH_MSONPROPERTYMEMBERPARSER_H 11 | 12 | #include "MSONValueMemberParser.h" 13 | 14 | using namespace scpl; 15 | 16 | namespace snowcrash 17 | { 18 | 19 | /** 20 | * MSON Property Member Section Processor 21 | */ 22 | template <> 23 | struct SectionProcessor : public SignatureSectionProcessorBase { 24 | 25 | static Signature parseSignature(const MarkdownNodeIterator& node, 26 | snowcrash::SectionParserData& pd, 27 | const SignatureTraits& traits, 28 | snowcrash::Report& report, 29 | const mdp::ByteBuffer& subjectOrig = "") 30 | { 31 | 32 | std::string subject = subjectOrig; 33 | 34 | if (node->type == mdp::HeaderMarkdownNodeType) { 35 | subject = ReconstructMarkdownHeader(*node); 36 | } 37 | 38 | return SignatureSectionProcessorBase::parseSignature( 39 | node, pd, traits, report, subject); 40 | } 41 | 42 | static SignatureTraits signatureTraits() 43 | { 44 | 45 | SignatureTraits signatureTraits(SignatureTraits::IdentifierTrait | SignatureTraits::ValuesTrait 46 | | SignatureTraits::AttributesTrait 47 | | SignatureTraits::ContentTrait); 48 | 49 | return signatureTraits; 50 | } 51 | 52 | static MarkdownNodeIterator finalizeSignature(const MarkdownNodeIterator& node, 53 | SectionParserData& pd, 54 | const Signature& signature, 55 | const ParseResultRef& out) 56 | { 57 | 58 | mson::parsePropertyName(node, pd, signature.identifier, out.report, out.node.name); 59 | 60 | if (pd.exportSourceMap() && !out.node.name.empty()) { 61 | out.sourceMap.name.sourceMap = node->sourceMap; 62 | } 63 | 64 | return SectionProcessor::useSignatureData( 65 | node, pd, signature, out.report, out.node, out.sourceMap); 66 | } 67 | 68 | static MarkdownNodeIterator processDescription(const MarkdownNodeIterator& node, 69 | const MarkdownNodes& siblings, 70 | SectionParserData& pd, 71 | const ParseResultRef& out) 72 | { 73 | 74 | return SectionProcessor::blockDescription( 75 | node, pd, out.node.sections, out.sourceMap.sections); 76 | } 77 | 78 | static MarkdownNodeIterator processNestedSection(const MarkdownNodeIterator& node, 79 | const MarkdownNodes& siblings, 80 | SectionParserData& pd, 81 | const ParseResultRef& out) 82 | { 83 | 84 | ParseResultRef typeSections(out.report, out.node.sections, out.sourceMap.sections); 85 | 86 | return SectionProcessor::processNestedMembers( 87 | node, siblings, pd, typeSections, out.node.valueDefinition.typeDefinition.baseType); 88 | } 89 | 90 | static bool isDescriptionNode(const MarkdownNodeIterator& node, SectionType sectionType) 91 | { 92 | 93 | return SectionProcessor::isDescriptionNode(node, sectionType); 94 | } 95 | 96 | static SectionType nestedSectionType(const MarkdownNodeIterator& node) 97 | { 98 | 99 | return SectionProcessor::nestedSectionType(node); 100 | } 101 | 102 | static void finalize( 103 | const MarkdownNodeIterator& node, SectionParserData& pd, const ParseResultRef& out) 104 | { 105 | 106 | SectionProcessor::finalizeImplicitBaseType( 107 | out.node.valueDefinition.typeDefinition.baseType); 108 | } 109 | }; 110 | 111 | /** MSON Property Member Section Parser */ 112 | typedef SectionParser MSONPropertyMemberParser; 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /src/MSONSourcemap.cc: -------------------------------------------------------------------------------- 1 | // 2 | // MSONSourcemap.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 03/01/15. 6 | // Copyright (c) 2015 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "MSONSourcemap.h" 10 | 11 | using namespace snowcrash; 12 | 13 | bool SourceMap::empty() const 14 | { 15 | return (name.sourceMap.empty() && typeDefinition.sourceMap.empty() && sections.collection.empty()); 16 | } 17 | 18 | bool SourceMap::empty() const 19 | { 20 | return (description.sourceMap.empty() && valueDefinition.sourceMap.empty() && sections.collection.empty()); 21 | } 22 | 23 | bool SourceMap::empty() const 24 | { 25 | return (name.sourceMap.empty() && description.sourceMap.empty() && valueDefinition.sourceMap.empty() 26 | && sections.collection.empty()); 27 | } 28 | 29 | SourceMap& SourceMap::elements() 30 | { 31 | if (!m_elements.get()) 32 | throw ELEMENTS_NOT_SET_ERR; 33 | 34 | return *m_elements; 35 | } 36 | 37 | const SourceMap& SourceMap::elements() const 38 | { 39 | if (!m_elements.get()) 40 | throw ELEMENTS_NOT_SET_ERR; 41 | 42 | return *m_elements; 43 | } 44 | 45 | SourceMap::SourceMap( 46 | const SourceMap& description_, const SourceMap& value_) 47 | : description(description_), value(value_) 48 | { 49 | m_elements.reset(::new SourceMap); 50 | } 51 | 52 | SourceMap::SourceMap(const SourceMap& rhs) 53 | { 54 | this->description = rhs.description; 55 | this->value = rhs.value; 56 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 57 | } 58 | 59 | SourceMap& SourceMap::operator=(const SourceMap& rhs) 60 | { 61 | this->description = rhs.description; 62 | this->value = rhs.value; 63 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 64 | 65 | return *this; 66 | } 67 | 68 | SourceMap::~SourceMap() 69 | { 70 | } 71 | 72 | SourceMap& SourceMap::oneOf() 73 | { 74 | if (!m_elements.get()) 75 | throw ELEMENTS_NOT_SET_ERR; 76 | 77 | return *m_elements; 78 | } 79 | 80 | const SourceMap& SourceMap::oneOf() const 81 | { 82 | if (!m_elements.get()) 83 | throw ELEMENTS_NOT_SET_ERR; 84 | 85 | return *m_elements; 86 | } 87 | 88 | SourceMap& SourceMap::elements() 89 | { 90 | if (!m_elements.get()) 91 | throw ELEMENTS_NOT_SET_ERR; 92 | 93 | return *m_elements; 94 | } 95 | 96 | const SourceMap& SourceMap::elements() const 97 | { 98 | if (!m_elements.get()) 99 | throw ELEMENTS_NOT_SET_ERR; 100 | 101 | return *m_elements; 102 | } 103 | 104 | SourceMap& SourceMap::operator=(const SourceMap& rhs) 105 | { 106 | m_elements.reset(::new SourceMap(rhs)); 107 | 108 | return *this; 109 | } 110 | 111 | SourceMap::SourceMap() 112 | { 113 | m_elements.reset(::new SourceMap); 114 | } 115 | 116 | SourceMap::SourceMap(const SourceMap& rhs) 117 | { 118 | this->property = rhs.property; 119 | this->value = rhs.value; 120 | this->mixin = rhs.mixin; 121 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 122 | } 123 | 124 | SourceMap& SourceMap::operator=(const SourceMap& rhs) 125 | { 126 | this->property = rhs.property; 127 | this->value = rhs.value; 128 | this->mixin = rhs.mixin; 129 | m_elements.reset(::new SourceMap(*rhs.m_elements.get())); 130 | 131 | return *this; 132 | } 133 | 134 | SourceMap::~SourceMap() 135 | { 136 | } 137 | -------------------------------------------------------------------------------- /src/ModelTable.h: -------------------------------------------------------------------------------- 1 | // 2 | // ModelTable.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 6/9/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_MODELTABLE_H 10 | #define SNOWCRASH_MODELTABLE_H 11 | 12 | #include 13 | #include 14 | #include "ByteBuffer.h" 15 | #include "RegexMatch.h" 16 | 17 | #ifdef DEBUG 18 | #include 19 | #endif 20 | 21 | #include "BlueprintSourcemap.h" 22 | #include "StringUtility.h" 23 | 24 | // Symbol identifier regex 25 | #define SYMBOL_IDENTIFIER "([^][()]+)" 26 | 27 | namespace snowcrash 28 | { 29 | 30 | /** Symbol reference matching regex */ 31 | const char* const ModelReferenceRegex("^[[:blank:]]*\\[" SYMBOL_IDENTIFIER "]\\[][[:blank:]]*$"); 32 | 33 | // Resource Object Model Table 34 | typedef std::map ModelTable; 35 | 36 | // Resource Object Model Table source map 37 | typedef std::map > ModelSourceMapTable; 38 | 39 | // Checks whether given source data represents reference to a symbol returning true if so, 40 | // false otherwise. If source data is represent reference referred symbol name is filled in. 41 | inline bool GetModelReference(const mdp::ByteBuffer& sourceData, Identifier& referredModel) 42 | { 43 | 44 | CaptureGroups captureGroups; 45 | 46 | if (RegexCapture(sourceData, ModelReferenceRegex, captureGroups, 3)) { 47 | referredModel = captureGroups[1]; 48 | TrimString(referredModel); 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | #ifdef DEBUG 56 | // Escape new lines 57 | inline std::string EscapeNewlines(const std::string& input) 58 | { 59 | 60 | size_t pos = 0; 61 | std::string target(input); 62 | 63 | while ((pos = target.find("\n", pos)) != std::string::npos) { 64 | target.replace(pos, 1, "\\n"); 65 | pos += 2; 66 | } 67 | 68 | return target; 69 | } 70 | 71 | // Prints markdown block recursively to stdout 72 | inline void PrintModelTable(const ModelTable& modelTable) 73 | { 74 | 75 | std::cout << "Resource Model Symbols:\n"; 76 | 77 | for (ModelTable::const_iterator it = modelTable.begin(); it != modelTable.end(); ++it) { 78 | 79 | std::cout << "- " << it->first << " - body: '" << EscapeNewlines(it->second.body) << "'\n"; 80 | } 81 | 82 | std::cout << std::endl; 83 | } 84 | #endif 85 | } 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/Platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // Platform.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_PLATFORM_H 10 | #define SNOWCRASH_PLATFORM_H 11 | 12 | #define AS_TYPE(Type, Obj) reinterpret_cast(Obj) 13 | #define AS_CTYPE(Type, Obj) reinterpret_cast(Obj) 14 | 15 | #if defined(BUILDING_SNOWCRASH) 16 | #define DEPRECATED 17 | #endif 18 | 19 | #if defined(_MSC_VER) 20 | #if !defined(DEPRECATED) 21 | #define DEPRECATED __declspec(deprecated) 22 | #endif 23 | #elif defined(__clang__) || defined(__GNUC__) 24 | #if !defined(DEPRECATED) 25 | #define DEPRECATED __attribute__((deprecated)) 26 | #endif 27 | #else 28 | #if !defined(DEPRECATED) 29 | #define DEPRECATED 30 | #endif 31 | #endif 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/RegexMatch.h: -------------------------------------------------------------------------------- 1 | // 2 | // RegexMatch.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_REGEXMATCH_H 10 | #define SNOWCRASH_REGEXMATCH_H 11 | 12 | #include 13 | #include 14 | 15 | namespace snowcrash 16 | { 17 | 18 | // Perform snowcrash-specific regex evaluation 19 | // returns true if target string matches given expression, false otherwise 20 | bool RegexMatch(const std::string& target, const std::string& expression); 21 | 22 | // Performs posix-regex and returns first captured group (excluding whole target) 23 | std::string RegexCaptureFirst(const std::string& target, const std::string& expression); 24 | 25 | // Array of capture groups 26 | typedef std::vector CaptureGroups; 27 | 28 | // Performs posix-regex 29 | // returns true if target string matches given expression, false otherwise 30 | bool RegexCapture( 31 | const std::string& target, const std::string& expression, CaptureGroups& captureGroups, size_t groupSize = 8); 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/RelationParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // RelationParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 04/03/15. 6 | // Copyright (c) 2015 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_RELATIONPARSER_H 10 | #define SNOWCRASH_RELATIONPARSER_H 11 | 12 | #include "SectionParser.h" 13 | #include "RegexMatch.h" 14 | 15 | /** Macro for relation regex */ 16 | #define RELATION_REGEX "^[[:blank:]]*[Rr]elation[[:blank:]]*:" 17 | 18 | namespace snowcrash 19 | { 20 | 21 | /** Link Relation matching regex */ 22 | const char* const RelationRegex = RELATION_REGEX; 23 | 24 | const char* const RelationIdentifierRegex = RELATION_REGEX "[[:blank:]]*([a-z][a-z0-9.-]*)?[[:blank:]]*$"; 25 | 26 | /** 27 | * Relation Section Processor 28 | */ 29 | template <> 30 | struct SectionProcessor : public SectionProcessorBase { 31 | 32 | static MarkdownNodeIterator processSignature(const MarkdownNodeIterator& node, 33 | const MarkdownNodes& siblings, 34 | SectionParserData& pd, 35 | SectionLayout& layout, 36 | const ParseResultRef& out) 37 | { 38 | 39 | mdp::ByteBuffer signature, remainingContent; 40 | CaptureGroups captureGroups; 41 | 42 | signature = GetFirstLine(node->text, remainingContent); 43 | TrimString(signature); 44 | 45 | if (RegexCapture(signature, RelationIdentifierRegex, captureGroups, 3)) { 46 | out.node.str = captureGroups[1]; 47 | TrimString(out.node.str); 48 | } else { 49 | // WARN: Relation identifier contains illegal characters 50 | mdp::CharactersRangeSet sourceMap 51 | = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceCharacterIndex); 52 | out.report.warnings.push_back( 53 | Warning("relation identifier contains illegal characters (only lower case letters, numbers, '-' " 54 | "and '.' allowed)", 55 | FormattingWarning, 56 | sourceMap)); 57 | } 58 | 59 | if (pd.exportSourceMap() && !out.node.str.empty()) { 60 | out.sourceMap.sourceMap.append(node->sourceMap); 61 | } 62 | 63 | return ++MarkdownNodeIterator(node); 64 | } 65 | 66 | NO_SECTION_DESCRIPTION(Relation) 67 | 68 | static SectionType sectionType(const MarkdownNodeIterator& node) 69 | { 70 | 71 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 72 | 73 | mdp::ByteBuffer remaining, subject = node->children().front().text; 74 | 75 | subject = GetFirstLine(subject, remaining); 76 | TrimString(subject); 77 | 78 | if (RegexMatch(subject, RelationRegex)) { 79 | return RelationSectionType; 80 | } 81 | } 82 | 83 | return UndefinedSectionType; 84 | } 85 | }; 86 | 87 | /** Relation Section Parser */ 88 | typedef SectionParser RelationParser; 89 | } 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /src/Section.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Section.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/14/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "Section.h" 10 | 11 | using namespace snowcrash; 12 | 13 | std::string snowcrash::SectionName(const SectionType& section) 14 | { 15 | 16 | switch (section) { 17 | case ModelSectionType: 18 | case ModelBodySectionType: 19 | return "model"; 20 | 21 | case RequestSectionType: 22 | case RequestBodySectionType: 23 | return "request"; 24 | 25 | case ResponseSectionType: 26 | case ResponseBodySectionType: 27 | return "response"; 28 | 29 | case BodySectionType: 30 | case DanglingBodySectionType: 31 | return "message-body"; 32 | 33 | case SchemaSectionType: 34 | case DanglingSchemaSectionType: 35 | return "message-schema"; 36 | 37 | case HeadersSectionType: 38 | return "headers"; 39 | 40 | case MSONMixinSectionType: 41 | return "mixin"; 42 | 43 | case MSONOneOfSectionType: 44 | return "one of"; 45 | 46 | default: 47 | return "section"; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Section.h: -------------------------------------------------------------------------------- 1 | // 2 | // Section.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 11/1/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_SECTION_H 10 | #define SNOWCRASH_SECTION_H 11 | 12 | #include 13 | 14 | namespace snowcrash 15 | { 16 | 17 | /** 18 | * API Blueprint Sections Types. 19 | */ 20 | enum SectionType 21 | { 22 | UndefinedSectionType = 0, /// < Undefined section 23 | BlueprintSectionType, /// < Blueprint overview 24 | DataStructureGroupSectionType, /// < Data Structure Group 25 | ResourceGroupSectionType, /// < Resource group 26 | ResourceSectionType, /// < Resource 27 | ActionSectionType, /// < Action 28 | RelationSectionType, /// < Link Relation 29 | RequestSectionType, /// < Request 30 | RequestBodySectionType, /// < Request & Payload body combined (abbrev) 31 | ResponseSectionType, /// < Response 32 | ResponseBodySectionType, /// < Response & Body combined (abbrev) 33 | AttributesSectionType, /// < Attributes 34 | ModelSectionType, /// < Model 35 | ModelBodySectionType, /// < Model & Body combined (abbrev) 36 | BodySectionType, /// < Payload Body 37 | DanglingBodySectionType, /// < Dangling Body (unrecognised section considered to be Body) 38 | SchemaSectionType, /// < Payload Schema 39 | DanglingSchemaSectionType, /// < Dangling Schema (unrecognised section considered to be Schema) 40 | HeadersSectionType, /// < Headers 41 | ForeignSectionType, /// < Foreign, unexpected section 42 | ParametersSectionType, /// < Parameters 43 | ParameterSectionType, /// < One Parameter definition 44 | MSONParameterSectionType, /// < One Parameter definition using the MSON syntax 45 | ValuesSectionType, /// < Value enumeration 46 | ValueSectionType, /// < One Value 47 | MSONMixinSectionType, /// < MSON Mixin 48 | MSONNamedTypeSectionType, /// < MSON Named Type 49 | MSONOneOfSectionType, /// < MSON One Of 50 | MSONPropertyMemberSectionType, /// < MSON Property Member Type 51 | MSONSampleDefaultSectionType, /// < MSON Sample/Default Type Section 52 | MSONPropertyMembersSectionType, /// < MSON Property Members Type Section 53 | MSONValueMembersSectionType, /// < MSON Value Members Type Section 54 | MSONValueMemberSectionType, /// < MSON Value Member Type 55 | MSONSectionType /// < MSON Property Member or Value Member 56 | }; 57 | 58 | /** \return Human readable name for given %SectionType */ 59 | extern std::string SectionName(const SectionType& section); 60 | } 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/SectionParserData.h: -------------------------------------------------------------------------------- 1 | // 2 | // SectionParserData.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/21/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_SECTIONPARSERDATA_H 10 | #define SNOWCRASH_SECTIONPARSERDATA_H 11 | 12 | #include "ModelTable.h" 13 | #include "BlueprintSourcemap.h" 14 | #include "Section.h" 15 | 16 | namespace snowcrash 17 | { 18 | 19 | /** 20 | * \brief Blueprint Parser Options. 21 | * 22 | * Controls blueprint parser behavior. 23 | */ 24 | enum BlueprintParserOption 25 | { 26 | RenderDescriptionsOption = (1 << 0), /// < Render Markdown in description. 27 | RequireBlueprintNameOption = (1 << 1), /// < Treat missing blueprint name as error 28 | ExportSourcemapOption = (1 << 2) /// < Export source maps AST 29 | }; 30 | 31 | typedef unsigned int BlueprintParserOptions; 32 | 33 | /** 34 | * \brief Section Parser Data 35 | * 36 | * State of the parser. 37 | */ 38 | struct SectionParserData { 39 | SectionParserData(BlueprintParserOptions opts, const mdp::ByteBuffer& src, const Blueprint& bp) 40 | : options(opts), sourceData(src), blueprint(bp) 41 | { 42 | } 43 | 44 | /** Parser Options */ 45 | BlueprintParserOptions options; 46 | 47 | /** Named Types */ 48 | std::vector msonTypesTable; 49 | 50 | /** Table of named types and resolved base types */ 51 | mson::NamedTypeBaseTable namedTypeBaseTable; 52 | 53 | /** Table mapping named type to sub types */ 54 | mson::NamedTypeInheritanceTable namedTypeInheritanceTable; 55 | 56 | /** Table mapping named types to their dependent named types */ 57 | mson::NamedTypeDependencyTable namedTypeDependencyTable; 58 | 59 | /** Variable to store the current named type */ 60 | mson::Literal namedTypeContext; 61 | 62 | /** Model Table */ 63 | ModelTable modelTable; 64 | 65 | /** Model Table Sourcemap */ 66 | ModelSourceMapTable modelSourceMapTable; 67 | 68 | /** Source Data */ 69 | const mdp::ByteBuffer& sourceData; 70 | 71 | /** Source - map of bytes to character position - performance optimization */ 72 | mdp::ByteBufferCharacterIndex sourceCharacterIndex; 73 | 74 | /** AST being parsed **/ 75 | const Blueprint& blueprint; 76 | 77 | /** Sections Context */ 78 | typedef std::vector SectionsStack; 79 | SectionsStack sectionsContext; 80 | 81 | /** \returns Actual Section Context */ 82 | SectionType sectionContext() const 83 | { 84 | return (sectionsContext.empty()) ? UndefinedSectionType : sectionsContext.back(); 85 | } 86 | 87 | /** \returns Parent Section Context */ 88 | SectionType parentSectionContext() const 89 | { 90 | if (sectionsContext.empty()) 91 | return UndefinedSectionType; 92 | 93 | size_t size = sectionsContext.size(); 94 | 95 | if (size == 1) 96 | return sectionsContext.back(); 97 | else 98 | return sectionsContext[size - 2]; 99 | } 100 | 101 | /** \returns True if exporting source maps */ 102 | bool exportSourceMap() const 103 | { 104 | return options & ExportSourcemapOption; 105 | } 106 | 107 | private: 108 | SectionParserData(); 109 | SectionParserData(const SectionParserData&); 110 | SectionParserData& operator=(const SectionParserData&); 111 | }; 112 | } 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/Signature.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Signature.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 6/20/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "Signature.h" 10 | #include "SectionParser.h" 11 | #include "ActionParser.h" 12 | #include "AssetParser.h" 13 | #include "HeadersParser.h" 14 | #include "PayloadParser.h" 15 | #include "ParametersParser.h" 16 | #include "ResourceParser.h" 17 | #include "ResourceGroupParser.h" 18 | #include "MSONTypeSectionParser.h" 19 | #include "DataStructureGroupParser.h" 20 | 21 | using namespace snowcrash; 22 | 23 | #define TYPECHECK(T) \ 24 | if ((type = SectionProcessor::sectionType(node)) != UndefinedSectionType) { \ 25 | return type; \ 26 | } 27 | 28 | SectionType snowcrash::SectionKeywordSignature(const mdp::MarkdownNodeIterator& node) 29 | { 30 | // Note: Every-keyword defined section should be listed here... 31 | SectionType type = UndefinedSectionType; 32 | 33 | TYPECHECK(mson::TypeSection) 34 | TYPECHECK(mson::Mixin) 35 | TYPECHECK(mson::OneOf) 36 | TYPECHECK(Headers) 37 | TYPECHECK(Asset) 38 | TYPECHECK(Attributes) 39 | TYPECHECK(Payload) 40 | TYPECHECK(Values) 41 | TYPECHECK(Parameters) 42 | TYPECHECK(Relation) 43 | 44 | /* 45 | * NOTE: Order is important. Resource MUST preceed the Action. 46 | * 47 | * This is because an HTTP Request Method + URI is recognized as both %ActionSectionType and %ResourceSectionType. 48 | * This is not optimal and should be addressed in the future. 49 | */ 50 | TYPECHECK(Resource) 51 | TYPECHECK(Action) 52 | TYPECHECK(ResourceGroup) 53 | TYPECHECK(DataStructureGroup) 54 | 55 | return type; 56 | } 57 | 58 | SectionType snowcrash::RecognizeCodeBlockFirstLine(const mdp::ByteBuffer& subject) 59 | { 60 | SectionType type = UndefinedSectionType; 61 | 62 | if (RegexMatch(subject, HeadersRegex)) { 63 | return HeadersSectionType; 64 | } else if (RegexMatch(subject, BodyRegex)) { 65 | return BodySectionType; 66 | } else if (RegexMatch(subject, SchemaRegex)) { 67 | return SchemaSectionType; 68 | } 69 | 70 | return type; 71 | } 72 | 73 | #undef TYPECHECK 74 | -------------------------------------------------------------------------------- /src/Signature.h: -------------------------------------------------------------------------------- 1 | // 2 | // Signature.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/14/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_SIGNATURE_H 10 | #define SNOWCRASH_SIGNATURE_H 11 | 12 | #include "MarkdownNode.h" 13 | #include "Section.h" 14 | 15 | namespace snowcrash 16 | { 17 | 18 | /** 19 | * \brief Query whether a node has keyword-defined signature. 20 | * \param node A Markdown AST node to check. 21 | * \return Type of the node if it has a recognized keyword signature, UndefinedType otherwise 22 | */ 23 | extern SectionType SectionKeywordSignature(const mdp::MarkdownNodeIterator& node); 24 | 25 | /** 26 | * \brief Recognize the type of section given the first line from a code block 27 | * \param subject The first line that needs to be recognized 28 | * \return SectionType Type of the section if the line contains a keyword 29 | */ 30 | extern SectionType RecognizeCodeBlockFirstLine(const mdp::ByteBuffer& subject); 31 | } 32 | 33 | namespace scpl 34 | { 35 | 36 | /** 37 | * \brief Signature data after parsing section using signature traits 38 | */ 39 | struct Signature { 40 | 41 | mdp::ByteBuffer identifier; // Signature Identifier 42 | mdp::ByteBuffer value; // Signature Value (unparsed values) 43 | 44 | std::vector values; // Signature Values 45 | std::vector attributes; // Signature Attributes 46 | 47 | mdp::ByteBuffer content; // Signature content before newline character 48 | mdp::ByteBuffer remainingContent; // Signature content after newline character 49 | }; 50 | 51 | /** 52 | * \brief Customisable Delimiters for the section signature 53 | */ 54 | struct Delimiters { 55 | const char valuesDelimiter; 56 | const std::string contentDelimiter; 57 | 58 | // These are not customizable for now 59 | static const char ValueDelimiter = ','; 60 | static const char AttributesBeginDelimiter = '('; 61 | static const char AttributesEndDelimiter = ')'; 62 | static const char AttributeDelimiter = ','; 63 | 64 | Delimiters(char valuesDelimiter_ = ':', std::string contentDelimiter_ = "-") 65 | : valuesDelimiter(valuesDelimiter_), contentDelimiter(contentDelimiter_) 66 | { 67 | } 68 | }; 69 | 70 | /** 71 | * \brief Traits characteristics of a section signature 72 | * 73 | * Traits describe identifier defined or value/type defined signatures. 74 | */ 75 | struct SignatureTraits { 76 | 77 | enum Trait 78 | { 79 | IdentifierTrait = (1 << 0), // Expect an identifier in the signature 80 | ValuesTrait = (1 << 1), // Expect a (list of) value in the signature 81 | AttributesTrait = (1 << 2), // Expect a list of attributes in the signature 82 | ContentTrait = (1 << 3) // Expect inline description in the signature 83 | }; 84 | 85 | typedef unsigned int Traits; 86 | 87 | const bool identifierTrait; 88 | const bool valuesTrait; 89 | const bool attributesTrait; 90 | const bool contentTrait; 91 | 92 | Delimiters delimiters; 93 | 94 | SignatureTraits(Traits traits_ = 0, Delimiters delimiters_ = Delimiters()) 95 | : identifierTrait(traits_ & IdentifierTrait) 96 | , valuesTrait(traits_ & ValuesTrait) 97 | , attributesTrait(traits_ & AttributesTrait) 98 | , contentTrait(traits_ & ContentTrait) 99 | , delimiters(delimiters_) 100 | { 101 | } 102 | }; 103 | } 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/SourceAnnotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // SourceAnnotation.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/12/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_SOURCEANNOTATION_H 10 | #define SNOWCRASH_SOURCEANNOTATION_H 11 | 12 | #include 13 | #include 14 | #include "ByteBuffer.h" 15 | 16 | namespace snowcrash 17 | { 18 | 19 | /** 20 | * \brief A source data annotation. 21 | * 22 | * Annotation bound to a source data block. Includes an 23 | * annotation code and an optional message. 24 | */ 25 | struct SourceAnnotation { 26 | 27 | /** 28 | * \brief Default annotation code representing success. 29 | */ 30 | static const int OK; 31 | 32 | /** 33 | * \brief %SourceAnnotation default constructor. 34 | * 35 | * Creates an empty annotation with the default annotation code. 36 | */ 37 | SourceAnnotation() : code(OK) 38 | { 39 | } 40 | 41 | /** 42 | * \brief %SourceAnnotation copy constructor. 43 | * \param rhs An annotation to be copied. 44 | */ 45 | SourceAnnotation(const SourceAnnotation& rhs) 46 | { 47 | 48 | this->message = rhs.message; 49 | this->code = rhs.code; 50 | this->location = rhs.location; 51 | } 52 | 53 | /** 54 | * \brief %SourceAnnotation constructor. 55 | * \param message An annotation message. 56 | * \param code Annotation code. 57 | * \param location A location of the annotation. 58 | */ 59 | SourceAnnotation(const std::string& message, 60 | int code = OK, 61 | const mdp::CharactersRangeSet& location = mdp::CharactersRangeSet()) 62 | { 63 | 64 | this->message = message; 65 | this->code = code; 66 | 67 | this->location.clear(); 68 | if (!location.empty()) 69 | this->location.assign(location.begin(), location.end()); 70 | } 71 | 72 | /** \brief %SourceAnnotation destructor. */ 73 | ~SourceAnnotation() 74 | { 75 | } 76 | 77 | /** 78 | * \brief %SourceAnnotation assignment operator 79 | * \param rhs An annotation to be assigned to this annotation. 80 | */ 81 | SourceAnnotation& operator=(const SourceAnnotation& rhs) 82 | { 83 | this->message = rhs.message; 84 | this->code = rhs.code; 85 | this->location = rhs.location; 86 | return *this; 87 | } 88 | 89 | /** The location of this annotation within the source data buffer. */ 90 | mdp::CharactersRangeSet location; 91 | 92 | /** An annotation code. */ 93 | int code; 94 | 95 | /** A annotation message. */ 96 | std::string message; 97 | }; 98 | 99 | /** 100 | * Error source annotation. 101 | */ 102 | typedef SourceAnnotation Error; 103 | 104 | /** 105 | * Error codes 106 | */ 107 | enum ErrorCode 108 | { 109 | NoError = 0, 110 | ApplicationError = 1, 111 | BusinessError = 2, 112 | ModelError = 3, 113 | MSONError = 4 114 | }; 115 | 116 | /** 117 | * Warning source annotation. 118 | */ 119 | typedef SourceAnnotation Warning; 120 | 121 | /** 122 | * Warning codes 123 | */ 124 | enum WarningCode 125 | { 126 | NoWarning = 0, 127 | APINameWarning = 1, 128 | DuplicateWarning = 2, 129 | FormattingWarning = 3, 130 | RedefinitionWarning = 4, 131 | IgnoringWarning = 5, 132 | EmptyDefinitionWarning = 6, 133 | NotEmptyDefinitionWarning = 7, 134 | LogicalErrorWarning = 8, 135 | DeprecatedWarning = 9, 136 | IndentationWarning = 10, 137 | AmbiguityWarning = 11, 138 | URIWarning = 12, 139 | HTTPWarning = 13 140 | }; 141 | 142 | /** 143 | * A set of warning source annotations. 144 | */ 145 | typedef std::vector Warnings; 146 | 147 | /** 148 | * \brief A parsing report Report. 149 | * 150 | * Result of a source data parsing operation. 151 | * Composed of ONE error source annotation 152 | * and a set of warning source annotations. 153 | */ 154 | struct Report { 155 | 156 | /** 157 | * \brief Append a report to this one, replacing the error source annotation. 158 | * 159 | * NOTE: A binding does not need to wrap this action. 160 | */ 161 | Report& operator+=(const Report& rhs) 162 | { 163 | error = rhs.error; 164 | warnings.insert(warnings.end(), rhs.warnings.begin(), rhs.warnings.end()); 165 | return *this; 166 | } 167 | 168 | /** Result error source annotation */ 169 | Error error; 170 | 171 | /** Result warning source annotations */ 172 | Warnings warnings; 173 | }; 174 | } 175 | 176 | #endif 177 | -------------------------------------------------------------------------------- /src/ValuesParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // ValuesParser.h 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 6/12/14 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_VALUESPARSER_H 10 | #define SNOWCRASH_VALUESPARSER_H 11 | 12 | #include "SectionParser.h" 13 | #include "RegexMatch.h" 14 | #include "StringUtility.h" 15 | 16 | /** Parameter Value regex */ 17 | #define PARAMETER_VALUE "`([^`]+)`" 18 | 19 | namespace snowcrash 20 | { 21 | 22 | /** Parameter Values matching regex */ 23 | const char* const ValuesRegex = "^[[:blank:]]*[Vv]alues[[:blank:]]*$"; 24 | 25 | /** 26 | * Values section processor 27 | */ 28 | template <> 29 | struct SectionProcessor : public SectionProcessorBase { 30 | 31 | static MarkdownNodeIterator processNestedSection(const MarkdownNodeIterator& node, 32 | const MarkdownNodes& siblings, 33 | SectionParserData& pd, 34 | const ParseResultRef& out) 35 | { 36 | 37 | if (pd.sectionContext() == ValueSectionType) { 38 | 39 | mdp::ByteBuffer content = node->children().front().text; 40 | CaptureGroups captureGroups; 41 | 42 | RegexCapture(content, PARAMETER_VALUE, captureGroups); 43 | 44 | if (captureGroups.size() > 1) { 45 | out.node.push_back(captureGroups[1]); 46 | 47 | if (pd.exportSourceMap()) { 48 | SourceMap valueSM; 49 | valueSM.sourceMap = node->sourceMap; 50 | out.sourceMap.collection.push_back(valueSM); 51 | } 52 | } else { 53 | TrimString(content); 54 | 55 | // WARN: Ignoring the unexpected param value 56 | std::stringstream ss; 57 | ss << "ignoring the '" << content << "' element"; 58 | ss << ", expected '`" << content << "`'"; 59 | 60 | mdp::CharactersRangeSet sourceMap 61 | = mdp::BytesRangeSetToCharactersRangeSet(node->sourceMap, pd.sourceCharacterIndex); 62 | out.report.warnings.push_back(Warning(ss.str(), IgnoringWarning, sourceMap)); 63 | } 64 | 65 | return ++MarkdownNodeIterator(node); 66 | } 67 | 68 | return node; 69 | } 70 | 71 | NO_SECTION_DESCRIPTION(Values) 72 | 73 | static SectionType sectionType(const MarkdownNodeIterator& node) 74 | { 75 | 76 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 77 | 78 | mdp::ByteBuffer subject = node->children().front().text; 79 | TrimString(subject); 80 | 81 | if (RegexMatch(subject, ValuesRegex)) { 82 | return ValuesSectionType; 83 | } 84 | } 85 | 86 | return UndefinedSectionType; 87 | } 88 | 89 | static SectionType nestedSectionType(const MarkdownNodeIterator& node) 90 | { 91 | 92 | if (node->type == mdp::ListItemMarkdownNodeType && !node->children().empty()) { 93 | 94 | mdp::ByteBuffer subject = node->children().front().text; 95 | TrimString(subject); 96 | 97 | if (node->children().size() == 1 && !subject.empty()) { 98 | 99 | return ValueSectionType; 100 | } 101 | } 102 | 103 | return UndefinedSectionType; 104 | } 105 | }; 106 | 107 | /** Parameter Section Parser */ 108 | typedef SectionParser ValuesParser; 109 | } 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /src/posix/RegexMatch.cc: -------------------------------------------------------------------------------- 1 | // 2 | // RegexMatch.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "../RegexMatch.h" 12 | 13 | // FIXME: Migrate to C++11. 14 | // Naive implementation of regex matching using POSIX regex 15 | bool snowcrash::RegexMatch(const std::string& target, const std::string& expression) 16 | { 17 | if (target.empty() || expression.empty()) 18 | return false; 19 | 20 | regex_t regex; 21 | int reti = ::regcomp(®ex, expression.c_str(), REG_EXTENDED | REG_NOSUB); 22 | if (reti) { 23 | // Unable to compile regex 24 | return false; 25 | } 26 | 27 | // Execute regular expression 28 | reti = ::regexec(®ex, target.c_str(), 0, NULL, 0); 29 | if (!reti) { 30 | ::regfree(®ex); 31 | return true; 32 | } else if (reti == REG_NOMATCH) { 33 | ::regfree(®ex); 34 | return false; 35 | } else { 36 | char msgbuf[1024]; 37 | regerror(reti, ®ex, msgbuf, sizeof(msgbuf)); 38 | ::regfree(®ex); 39 | return false; 40 | } 41 | 42 | return false; 43 | } 44 | 45 | std::string snowcrash::RegexCaptureFirst(const std::string& target, const std::string& expression) 46 | { 47 | CaptureGroups groups; 48 | if (!RegexCapture(target, expression, groups) || groups.size() < 2) 49 | return std::string(); 50 | 51 | return groups[1]; 52 | } 53 | 54 | bool snowcrash::RegexCapture( 55 | const std::string& target, const std::string& expression, CaptureGroups& captureGroups, size_t groupSize) 56 | { 57 | if (target.empty() || expression.empty()) 58 | return false; 59 | 60 | captureGroups.clear(); 61 | 62 | try { 63 | regex_t regex; 64 | int reti = ::regcomp(®ex, expression.c_str(), REG_EXTENDED); 65 | if (reti) 66 | return false; 67 | 68 | regmatch_t* pmatch = ::new regmatch_t[groupSize]; 69 | ::memset(pmatch, 0, sizeof(regmatch_t) * groupSize); 70 | 71 | reti = ::regexec(®ex, target.c_str(), groupSize, pmatch, 0); 72 | if (!reti) { 73 | ::regfree(®ex); 74 | 75 | for (size_t i = 0; i < groupSize; ++i) { 76 | if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) 77 | captureGroups.push_back(std::string()); 78 | else 79 | captureGroups.push_back(std::string(target, pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so)); 80 | } 81 | 82 | delete[] pmatch; 83 | return true; 84 | } else { 85 | ::regfree(®ex); 86 | delete[] pmatch; 87 | return false; 88 | } 89 | } catch (...) { 90 | } 91 | 92 | return false; 93 | } 94 | -------------------------------------------------------------------------------- /src/snowcrash.cc: -------------------------------------------------------------------------------- 1 | // 2 | // snowcrash.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 4/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrash.h" 10 | #include "BlueprintParser.h" 11 | 12 | const int snowcrash::SourceAnnotation::OK = 0; 13 | 14 | using namespace snowcrash; 15 | 16 | /** 17 | * \brief Check source for unsupported character \t & \r 18 | * \return True if passed (not found), false otherwise 19 | */ 20 | static bool CheckSource(const mdp::ByteBuffer& source, Report& report) 21 | { 22 | 23 | std::string::size_type pos = source.find("\t"); 24 | 25 | if (pos != std::string::npos) { 26 | 27 | mdp::BytesRangeSet rangeSet; 28 | rangeSet.push_back(mdp::BytesRange(pos, 1)); 29 | report.error = Error("the use of tab(s) '\\t' in source data isn't currently supported, please contact makers", 30 | BusinessError, 31 | mdp::BytesRangeSetToCharactersRangeSet(rangeSet, source)); 32 | return false; 33 | } 34 | 35 | pos = source.find("\r"); 36 | 37 | if (pos != std::string::npos) { 38 | 39 | mdp::BytesRangeSet rangeSet; 40 | rangeSet.push_back(mdp::BytesRange(pos, 1)); 41 | report.error = Error( 42 | "the use of carriage return(s) '\\r' in source data isn't currently supported, please contact makers", 43 | BusinessError, 44 | mdp::BytesRangeSetToCharactersRangeSet(rangeSet, source)); 45 | return false; 46 | } 47 | 48 | return true; 49 | } 50 | 51 | int snowcrash::parse( 52 | const mdp::ByteBuffer& source, BlueprintParserOptions options, const ParseResultRef& out) 53 | { 54 | try { 55 | 56 | // Sanity Check 57 | if (!CheckSource(source, out.report)) 58 | return out.report.error.code; 59 | 60 | // Do nothing if blueprint is empty 61 | if (source.empty()) 62 | return out.report.error.code; 63 | 64 | // Parse Markdown 65 | mdp::MarkdownParser markdownParser; 66 | mdp::MarkdownNode markdownAST; 67 | markdownParser.parse(source, markdownAST); 68 | 69 | // Build SectionParserData 70 | SectionParserData pd(options, source, out.node); 71 | mdp::BuildCharacterIndex(pd.sourceCharacterIndex, source); 72 | 73 | // Parse Blueprint 74 | BlueprintParser::parse(markdownAST.children().begin(), markdownAST.children(), pd, out); 75 | } catch (const Error& e) { 76 | out.report.error = e; 77 | } catch (const std::exception& e) { 78 | 79 | std::stringstream ss; 80 | ss << "parser exception: '" << e.what() << "'"; 81 | out.report.error = Error(ss.str(), ApplicationError); 82 | } catch (...) { 83 | out.report.error = Error("parser exception has occurred", ApplicationError); 84 | } 85 | 86 | return out.report.error.code; 87 | } 88 | -------------------------------------------------------------------------------- /src/snowcrash.h: -------------------------------------------------------------------------------- 1 | // 2 | // snowcrash.h 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 4/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #ifndef SNOWCRASH_H 10 | #define SNOWCRASH_H 11 | 12 | #include "BlueprintSourcemap.h" 13 | #include "SourceAnnotation.h" 14 | #include "SectionParser.h" 15 | 16 | /** 17 | * API Blueprint Parser Interface 18 | * ------------------------------ 19 | * 20 | * This is the parser's entry point. 21 | * 22 | * For Snow Crash users, this is the only interface to use. 23 | * 24 | * For binding writers, this is the point to start wrapping. 25 | * Refer to https://github.com/apiaryio/snowcrash/wiki/Writing-a-binding 26 | * for details on how to write a Snow Crash binding. 27 | */ 28 | 29 | namespace snowcrash 30 | { 31 | 32 | /** 33 | * \brief Parse the source data into a blueprint abstract source tree (AST). 34 | * 35 | * \param source A textual source data to be parsed. 36 | * \param options Parser options. Use 0 for no additional options. 37 | * \param out Output buffer to store parsing result into. 38 | * \return Error status code. Zero represents success, non-zero a failure. 39 | */ 40 | int parse(const mdp::ByteBuffer& source, BlueprintParserOptions options, const ParseResultRef& out); 41 | } 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /src/win/RegexMatch.cc: -------------------------------------------------------------------------------- 1 | // 2 | // RegexMatch.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 7/1/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include "../RegexMatch.h" 12 | 13 | using namespace std; 14 | 15 | #if _MSC_VER == 1500 16 | using namespace std::tr1; 17 | #endif 18 | // 19 | // A C++09 implementation 20 | // 21 | 22 | bool snowcrash::RegexMatch(const string& target, const string& expression) 23 | { 24 | if (target.empty() || expression.empty()) 25 | return false; 26 | 27 | try { 28 | regex pattern(expression, regex_constants::extended); 29 | return regex_search(target, pattern); 30 | } catch (const regex_error&) { 31 | } catch (...) { 32 | } 33 | 34 | return false; 35 | } 36 | 37 | string snowcrash::RegexCaptureFirst(const string& target, const string& expression) 38 | { 39 | CaptureGroups groups; 40 | if (!RegexCapture(target, expression, groups) || groups.size() < 2) 41 | return string(); 42 | 43 | return groups[1]; 44 | } 45 | 46 | bool snowcrash::RegexCapture( 47 | const string& target, const string& expression, CaptureGroups& captureGroups, size_t groupSize) 48 | { 49 | if (target.empty() || expression.empty()) 50 | return false; 51 | 52 | captureGroups.clear(); 53 | 54 | try { 55 | 56 | regex pattern(expression, regex_constants::extended); 57 | match_results result; 58 | if (!regex_search(target, result, pattern)) 59 | return false; 60 | 61 | for (match_results::const_iterator it = result.begin(); it != result.end(); ++it) { 62 | 63 | captureGroups.push_back(*it); 64 | } 65 | 66 | return true; 67 | } catch (const regex_error&) { 68 | } catch (...) { 69 | } 70 | 71 | return false; 72 | } 73 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Snow Crash Test Suite 2 | 3 | https://github.com/philsquared/Catch/wiki 4 | 5 | --- 6 | 7 | TODO: -------------------------------------------------------------------------------- /test/performance/perf-snowcrash.cc: -------------------------------------------------------------------------------- 1 | // 2 | // perf-snowcrash.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 10/8/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "snowcrash.h" 13 | 14 | #if defined(_MSC_VER) 15 | #include 16 | #else 17 | #include 18 | #endif 19 | 20 | using snowcrash::SourceAnnotation; 21 | using snowcrash::Error; 22 | 23 | static const int TestRunCount = 1000; 24 | 25 | #if defined(_MSC_VER) 26 | const __int64 DELTA_EPOCH_IN_MICROSECS = 11644473600000000; 27 | 28 | struct timezone { 29 | int tz_minuteswest; /* minutes west of Greenwich */ 30 | int tz_dsttime; /* type of DST correction */ 31 | }; 32 | 33 | int gettimeofday(struct timeval* tv, struct timezone* tz) 34 | { 35 | FILETIME ft; 36 | TIME_ZONE_INFORMATION tz_winapi; 37 | __int64 tmpres = 0; 38 | int rez = 0; 39 | 40 | ZeroMemory(&ft, sizeof(ft)); 41 | ZeroMemory(&tz_winapi, sizeof(tz_winapi)); 42 | 43 | GetSystemTimeAsFileTime(&ft); 44 | 45 | tmpres = ft.dwHighDateTime; 46 | tmpres <<= 32; 47 | tmpres |= ft.dwLowDateTime; 48 | 49 | tmpres /= 10; 50 | tmpres -= DELTA_EPOCH_IN_MICROSECS; 51 | 52 | if (tv) { 53 | tv->tv_sec = (__int32)(tmpres * 0.000001); 54 | tv->tv_usec = (tmpres % 1000000); 55 | } 56 | 57 | if (tz) { 58 | rez = GetTimeZoneInformation(&tz_winapi); 59 | tz->tz_dsttime = (rez == 2) ? true : false; 60 | tz->tz_minuteswest = tz_winapi.Bias + ((rez == 2) ? tz_winapi.DaylightBias : 0); 61 | } 62 | 63 | return 0; 64 | } 65 | #endif 66 | 67 | /** 68 | * \brief Parse input @TestRunCount -times 69 | * \param input A blueprint source data. 70 | * \param total Total time spent parsing (s). 71 | * \param mean Mean time spent parsing (s). 72 | * \param stddev Standard deviation. 73 | * \return Result code of snowcrash::parse operation. 74 | */ 75 | static int testfunc(const std::string& input, double& total, double& mean, double& stddev) 76 | { 77 | struct timeval stime, etime; 78 | double t = 0, sum = 0, sum2 = 0; 79 | int resultCode = snowcrash::Error::OK; 80 | 81 | for (int i = 0; i < TestRunCount; ++i) { 82 | snowcrash::BlueprintParserOptions options = 0; 83 | snowcrash::ParseResult blueprint; 84 | 85 | // Do the test. 86 | if (::gettimeofday(&stime, NULL)) { 87 | std::cerr << "fatal: gettimeofday failed"; 88 | exit(EXIT_FAILURE); 89 | } 90 | 91 | snowcrash::parse(input, options, blueprint); 92 | 93 | if (::gettimeofday(&etime, NULL)) { 94 | std::cerr << "fatal: gettimeofday failed"; 95 | exit(EXIT_FAILURE); 96 | } 97 | 98 | resultCode = blueprint.report.error.code; 99 | 100 | // Compute the time taken and add it to the sums. 101 | t = (etime.tv_sec - stime.tv_sec) + (etime.tv_usec - stime.tv_usec) / 1000000.0; 102 | sum += t; 103 | sum2 += t * t; 104 | } 105 | 106 | // Compute statistics and return result 107 | total = sum; 108 | mean = sum / TestRunCount; 109 | stddev = std::sqrt((sum2 / TestRunCount) - (mean * mean)); 110 | return resultCode; 111 | } 112 | 113 | void help() 114 | { 115 | std::cout << "usage: perf-snowcrash [options] ... " << std::endl << std::endl; 116 | std::cout << "API Blueprint Parser Performance Test Tool" << std::endl << std::endl; 117 | std::cout << "options:" << std::endl << std::endl; 118 | std::cout << " -h, --help display this help message" << std::endl; 119 | exit(0); 120 | } 121 | 122 | bool helpRequest(const std::string& arg) 123 | { 124 | return arg == "-h" || arg == "--help"; 125 | } 126 | 127 | int main(int argc, const char* argv[]) 128 | { 129 | // FIXME: Instruments helper 130 | //::sleep(20); 131 | 132 | if (argc != 2) { 133 | std::cerr << "one input file expected\n"; 134 | exit(EXIT_FAILURE); 135 | } 136 | 137 | if (helpRequest(argv[1])) { 138 | help(); 139 | } 140 | 141 | // Read fixture file 142 | std::ifstream inputFileStream; 143 | std::string inputFileName = argv[1]; 144 | inputFileStream.open(inputFileName.c_str()); 145 | if (!inputFileStream.is_open()) { 146 | std::cerr << "fatal: unable to open input file '" << inputFileName << "'\n"; 147 | exit(EXIT_FAILURE); 148 | } 149 | 150 | std::stringstream inputStream; 151 | inputStream << inputFileStream.rdbuf(); 152 | inputFileStream.close(); 153 | 154 | std::cout << "running snowcrash performance test...\n"; 155 | 156 | double mean = 0, total = 0, stddev = 0; 157 | int result = testfunc(inputStream.str(), total, mean, stddev); 158 | 159 | std::cout << "parsing '" << inputFileName << "' " << TestRunCount << "-times (" << result << "):\n"; 160 | std::cout << "total: " << total << "s mean: " << mean << " +/- " << stddev << "s\n"; 161 | 162 | // FIXME: Instruments helper 163 | //::sleep(20); 164 | } 165 | -------------------------------------------------------------------------------- /test/test-Blueprint.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-Blueprint.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 4/3/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "catch.hpp" 10 | #include "Blueprint.h" 11 | 12 | using namespace snowcrash; 13 | 14 | TEST_CASE("blueprint/blueprint-init", "Blueprint initialization") 15 | { 16 | Blueprint blueprint; 17 | REQUIRE(blueprint.name.length() == 0); 18 | REQUIRE(blueprint.description.length() == 0); 19 | REQUIRE(blueprint.metadata.size() == 0); 20 | REQUIRE(blueprint.content.elements().size() == 0); 21 | } 22 | 23 | TEST_CASE("blueprint/init", "Blueprint initialization") 24 | { 25 | Blueprint blueprint; 26 | REQUIRE(blueprint.name.length() == 0); 27 | REQUIRE(blueprint.description.length() == 0); 28 | REQUIRE(blueprint.metadata.size() == 0); 29 | REQUIRE(blueprint.content.elements().size() == 0); 30 | } 31 | -------------------------------------------------------------------------------- /test/test-BlueprintUtility.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-BlueprintUtility.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 03/03/15. 6 | // Copyright (c) 2015 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrashtest.h" 10 | #include "BlueprintUtility.h" 11 | 12 | using namespace snowcrash; 13 | using namespace snowcrashtest; 14 | 15 | TEST_CASE("Match action with same uri template and same http method", "[blueprint_utility]") 16 | { 17 | Action action1, action2; 18 | 19 | action1.method = "GET"; 20 | action2.method = "GET"; 21 | 22 | REQUIRE(MatchAction()(action1, action2)); 23 | } 24 | 25 | TEST_CASE("Match action with same uri template and different http method", "[blueprint_utility]") 26 | { 27 | Action action1, action2; 28 | 29 | action1.uriTemplate = "/test"; 30 | action1.method = "GET"; 31 | 32 | action2.uriTemplate = "/test"; 33 | action2.method = "POST"; 34 | 35 | REQUIRE_FALSE(MatchAction()(action1, action2)); 36 | } 37 | 38 | TEST_CASE("Match action with different uri template and same http method", "[blueprint_utility]") 39 | { 40 | Action action1, action2; 41 | 42 | action1.method = "GET"; 43 | 44 | action2.uriTemplate = "/test"; 45 | action2.method = "GET"; 46 | 47 | REQUIRE_FALSE(MatchAction()(action1, action2)); 48 | } 49 | -------------------------------------------------------------------------------- /test/test-ModelTable.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-ModelTable.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 6/9/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrash.h" 10 | #include "snowcrashtest.h" 11 | #include "ResourceParser.h" 12 | 13 | using namespace snowcrash; 14 | using namespace snowcrashtest; 15 | 16 | TEST_CASE("Parse object resource model", "[model_table]") 17 | { 18 | mdp::ByteBuffer source 19 | = "# /resource\n" 20 | "+ Super Model (text/plain)\n\n" 21 | " {...}\n"; 22 | 23 | // Check we will get error parsing the same symbol again with the same symbol table 24 | Models models; 25 | ModelHelper::build("Super", models); 26 | 27 | ParseResult resource; 28 | SectionParserHelper::parse(source, ModelBodySectionType, resource, 0, models); 29 | 30 | REQUIRE(resource.report.error.code != Error::OK); 31 | } 32 | -------------------------------------------------------------------------------- /test/test-RegexMatch.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-RegexMatch.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 5/2/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "catch.hpp" 10 | #include "RegexMatch.h" 11 | 12 | using namespace snowcrash; 13 | 14 | TEST_CASE("regexmatch/simple", "Simple regex test") 15 | { 16 | REQUIRE(RegexMatch("The quick brown fox jumps over the lazy dog", "fox[[:space:]]") == true); 17 | REQUIRE(RegexMatch("The quick brown fox jumps over the lazy dog", "box") == false); 18 | } 19 | 20 | TEST_CASE("regexmatch/complex", "Complex regex test") 21 | { 22 | REQUIRE(RegexMatch("The quick brown fox jumps over the lazy dog", "[[:space:]]{2}fox[[:space:]]jumps+") == true); 23 | REQUIRE(RegexMatch("The quick brown fox jumps over the lazy dog", "^The") == true); 24 | REQUIRE(RegexMatch("The quick brown fox jumps over the lazy dog", "[[:space:]]{2}fox[[:space:]]sjumps+") == false); 25 | } 26 | 27 | TEST_CASE("regexmatch/resource-header", "Match resource test") 28 | { 29 | REQUIRE(RegexMatch("HEAD /resource/{id}", "^((GET|HEAD)[[:space:]]+)?/.*$") == true); 30 | } 31 | 32 | TEST_CASE("regexmatch/request-payload", "Match request payload test") 33 | { 34 | REQUIRE(RegexMatch("Request My Id (application/json)", 35 | "^[Rr]equest([[:space:]]+([A-Za-z0-9_]|[[:space:]])*)?([[:space:]]\\([^\\)]*\\))?$") 36 | == true); 37 | } 38 | -------------------------------------------------------------------------------- /test/test-SectionParser.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-SectionParser.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 6/20/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrashtest.h" 10 | #include "SectionParser.h" 11 | 12 | using namespace snowcrash; 13 | using namespace snowcrashtest; 14 | 15 | const mdp::ByteBuffer HeaderSectionFixture 16 | = "# Signature\n" 17 | "\n" 18 | "Lorem Ipsum\n"; 19 | 20 | const mdp::ByteBuffer ListSectionFixture 21 | = "+ Signature\n" 22 | "\n" 23 | " Lorem Ipsum\n"; 24 | 25 | TEST_CASE("Header adapter with header section", "[adapter]") 26 | { 27 | mdp::MarkdownParser markdownParser; 28 | mdp::MarkdownNode markdownAST; 29 | markdownParser.parse(HeaderSectionFixture, markdownAST); 30 | 31 | SectionParserData pd(0, HeaderSectionFixture, Blueprint()); 32 | 33 | REQUIRE(!markdownAST.children().empty()); 34 | 35 | REQUIRE_NOTHROW(HeaderSectionAdapter::startingNode(markdownAST.children().begin(), pd)); 36 | MarkdownNodeIterator it = HeaderSectionAdapter::startingNode(markdownAST.children().begin(), pd); 37 | REQUIRE(it->text == "Signature"); 38 | 39 | const MarkdownNodes& collection = HeaderSectionAdapter::startingNodeSiblings(it, markdownAST.children()); 40 | REQUIRE(collection.size() == 2); 41 | 42 | REQUIRE(HeaderSectionAdapter::nextStartingNode(markdownAST.children().begin(), markdownAST.children(), it)->text 43 | == "Signature"); 44 | } 45 | 46 | TEST_CASE("Header adapter with list section", "[adapter]") 47 | { 48 | mdp::MarkdownParser markdownParser; 49 | mdp::MarkdownNode markdownAST; 50 | markdownParser.parse(ListSectionFixture, markdownAST); 51 | 52 | SectionParserData pd(0, ListSectionFixture, Blueprint()); 53 | 54 | REQUIRE(!markdownAST.children().empty()); 55 | 56 | REQUIRE_THROWS_AS(HeaderSectionAdapter::startingNode(markdownAST.children().begin(), pd), Error); 57 | } 58 | 59 | TEST_CASE("List adapter with List section", "[adapter]") 60 | { 61 | mdp::MarkdownParser markdownParser; 62 | mdp::MarkdownNode markdownAST; 63 | markdownParser.parse(ListSectionFixture, markdownAST); 64 | 65 | SectionParserData pd(0, ListSectionFixture, Blueprint()); 66 | 67 | REQUIRE(!markdownAST.children().empty()); 68 | 69 | REQUIRE_NOTHROW(ListSectionAdapter::startingNode(markdownAST.children().begin(), pd)); 70 | MarkdownNodeIterator it = ListSectionAdapter::startingNode(markdownAST.children().begin(), pd); 71 | REQUIRE(it->text == "Signature"); 72 | 73 | MarkdownNodes collection 74 | = ListSectionAdapter::startingNodeSiblings(markdownAST.children().begin(), markdownAST.children()); 75 | REQUIRE(collection.size() == 2); 76 | 77 | REQUIRE(ListSectionAdapter::nextStartingNode(markdownAST.children().begin(), markdownAST.children(), it) 78 | == markdownAST.children().end()); 79 | } 80 | 81 | TEST_CASE("List adapter with Header section", "[adapter]") 82 | { 83 | mdp::MarkdownParser markdownParser; 84 | mdp::MarkdownNode markdownAST; 85 | markdownParser.parse(HeaderSectionFixture, markdownAST); 86 | 87 | SectionParserData pd(0, HeaderSectionFixture, Blueprint()); 88 | 89 | REQUIRE(!markdownAST.children().empty()); 90 | 91 | REQUIRE_THROWS_AS(ListSectionAdapter::startingNode(markdownAST.children().begin(), pd), Error); 92 | } 93 | -------------------------------------------------------------------------------- /test/test-StringUtility.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-StringUtility.cc 3 | // snowcrash 4 | // 5 | // Created by Jiri Kratochvil on 10/15/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrashtest.h" 10 | #include "StringUtility.h" 11 | #include "BlueprintUtility.h" 12 | 13 | using namespace snowcrash; 14 | using namespace snowcrashtest; 15 | 16 | TEST_CASE("Templates for compare equality", "[utility]") 17 | { 18 | SECTION("Testing equality on same types") 19 | { 20 | REQUIRE(IsEqual()(1, 1)); 21 | REQUIRE_FALSE(IsEqual()(1, 2)); 22 | 23 | REQUIRE(IsEqual()('a', 'a')); 24 | REQUIRE_FALSE(IsEqual()('a', 'A')); 25 | } 26 | 27 | SECTION("Testing case insensitive equality on same types") 28 | { 29 | REQUIRE(IsIEqual()('a', 'a')); 30 | REQUIRE(IsIEqual()('a', 'A')); 31 | REQUIRE(IsIEqual()('A', 'a')); 32 | REQUIRE(IsIEqual()('A', 'A')); 33 | 34 | REQUIRE_FALSE(IsIEqual()('a', 'b')); 35 | } 36 | 37 | SECTION("Both version should not throw while testing different types") 38 | { 39 | REQUIRE_NOTHROW(IsEqual()('a', 1)); // allow compare different types - not throw 40 | REQUIRE_NOTHROW(IsIEqual()('a', 1)); // allow compare different types - not throw 41 | } 42 | } 43 | 44 | TEST_CASE("Container comparison", "[utility]") 45 | { 46 | REQUIRE(MatchContainers(std::string("abc"), std::string("abc"), IsEqual())); 47 | 48 | REQUIRE_FALSE(MatchContainers(std::string("ABC"), std::string("abc"), IsEqual())); 49 | 50 | REQUIRE(MatchContainers(std::string("abc"), std::string("abc"), IsIEqual())); 51 | REQUIRE(MatchContainers(std::string("abc"), std::string("ABC"), IsIEqual())); 52 | 53 | REQUIRE_FALSE(MatchContainers(std::string("def"), std::string("ABC"), IsIEqual())); 54 | } 55 | 56 | TEST_CASE("Compare string", "[utility]") 57 | { 58 | REQUIRE(Equal()(std::string("abc"), std::string("abc"))); 59 | REQUIRE_FALSE(Equal()(std::string("abcd"), std::string("abc"))); 60 | 61 | REQUIRE(IEqual()(std::string("abc"), std::string("ABC"))); 62 | REQUIRE(IEqual()(std::string("ABC"), std::string("ABC"))); 63 | REQUIRE_FALSE(IEqual()(std::string("abcd"), std::string("abc"))); 64 | } 65 | 66 | TEST_CASE("Remove markdown link") 67 | { 68 | REQUIRE(StripMarkdownLink("[Google][]") == "Google"); 69 | REQUIRE(StripMarkdownLink("[Google] [google]") == "Google"); 70 | REQUIRE(StripMarkdownLink("[ Google](https://google.com)") == "Google"); 71 | } 72 | 73 | TEST_CASE("Retrieve escaped string") 74 | { 75 | std::string subject; 76 | 77 | subject = "a```b```cd"; 78 | REQUIRE(RetrieveEscaped(subject, 1) == "```b```"); 79 | REQUIRE(subject == "cd"); 80 | 81 | subject = "*rel (custom)*"; 82 | REQUIRE(RetrieveEscaped(subject) == "*rel (custom)*"); 83 | REQUIRE(subject == ""); 84 | 85 | subject = "site_admin"; 86 | REQUIRE(RetrieveEscaped(subject, 4) == ""); 87 | REQUIRE(subject == "site_admin"); 88 | } 89 | 90 | TEST_CASE("Add two new lines", "[utility]") 91 | { 92 | std::string s1 = "abc"; 93 | std::string s2 = "abc\n"; 94 | std::string s3 = "abc\n\n"; 95 | REQUIRE(TwoNewLines(s1) == "abc\n\n"); 96 | REQUIRE(TwoNewLines(s2) == "abc\n\n"); 97 | REQUIRE(TwoNewLines(s3) == "abc\n\n"); 98 | } 99 | 100 | TEST_CASE("Get trim info", "[utility]") 101 | { 102 | TrimRange range; 103 | std::string s; 104 | 105 | s = "abc"; 106 | range = GetTrimInfo(s.begin(), s.end()); 107 | REQUIRE(std::get<0>(range) == 0); 108 | REQUIRE(std::get<1>(range) == 3); 109 | 110 | s = " abc "; 111 | range = GetTrimInfo(s.begin(), s.end()); 112 | REQUIRE(std::get<0>(range) == 3); 113 | REQUIRE(std::get<1>(range) == 3); 114 | } 115 | -------------------------------------------------------------------------------- /test/test-SymbolIdentifier.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-SymbolIdentifier.cc 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 2/7/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrash.h" 10 | #include "snowcrashtest.h" 11 | 12 | using namespace snowcrash; 13 | using namespace snowcrashtest; 14 | 15 | TEST_CASE("Punctuation in identifiers", "[symbol_identifier]") 16 | { 17 | mdp::ByteBuffer source = "# Parcel's sticker @#!$%^&*=-?><,.~`\"' [/]\n"; 18 | 19 | ParseResult blueprint; 20 | parse(source, 0, blueprint); 21 | 22 | REQUIRE(blueprint.report.error.code == Error::OK); 23 | REQUIRE(blueprint.report.warnings.empty()); 24 | 25 | REQUIRE(blueprint.node.content.elements().size() == 1); 26 | REQUIRE(blueprint.node.content.elements().at(0).element == Element::CategoryElement); 27 | REQUIRE(blueprint.node.content.elements().at(0).content.elements().size() == 1); 28 | REQUIRE(blueprint.node.content.elements().at(0).content.elements().at(0).element == Element::ResourceElement); 29 | 30 | Resource resource = blueprint.node.content.elements().at(0).content.elements().at(0).content.resource; 31 | REQUIRE(resource.name == "Parcel's sticker @#!$%^&*=-?><,.~`\"'"); 32 | REQUIRE(resource.uriTemplate == "/"); 33 | REQUIRE(resource.actions.empty()); 34 | } 35 | 36 | TEST_CASE("Non ASCII characters in identifiers", "[symbol_identifier]") 37 | { 38 | // Blueprint in question: 39 | // R"( 40 | //# Kategorii [/] 41 | //"); 42 | 43 | // "Kategorii in Russian" 44 | mdp::ByteBuffer source = "# \xD0\x9A\xD0\xB0\xD1\x82\xD0\xB5\xD0\xB3\xD0\xBE\xD1\x80\xD0\xB8\xD0\xB8 [/]\n"; 45 | 46 | ParseResult blueprint; 47 | parse(source, 0, blueprint); 48 | 49 | REQUIRE(blueprint.report.error.code == Error::OK); 50 | REQUIRE(blueprint.report.warnings.empty()); 51 | 52 | REQUIRE(blueprint.node.content.elements().size() == 1); 53 | REQUIRE(blueprint.node.content.elements().at(0).element == Element::CategoryElement); 54 | REQUIRE(blueprint.node.content.elements().at(0).content.elements().size() == 1); 55 | REQUIRE(blueprint.node.content.elements().at(0).content.elements().at(0).element == Element::ResourceElement); 56 | 57 | Resource resource = blueprint.node.content.elements().at(0).content.elements().at(0).content.resource; 58 | REQUIRE(resource.name == "\xD0\x9A\xD0\xB0\xD1\x82\xD0\xB5\xD0\xB3\xD0\xBE\xD1\x80\xD0\xB8\xD0\xB8"); 59 | REQUIRE(resource.uriTemplate == "/"); 60 | REQUIRE(resource.actions.empty()); 61 | } 62 | -------------------------------------------------------------------------------- /test/test-ValuesParser.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-ValuesParser.cc 3 | // snowcrash 4 | // 5 | // Created by Pavan Kumar Sunkara on 6/12/14. 6 | // Copyright (c) 2014 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrashtest.h" 10 | #include "ValuesParser.h" 11 | 12 | using namespace snowcrash; 13 | using namespace snowcrashtest; 14 | 15 | const mdp::ByteBuffer ValuesFixture 16 | = "+ Values\n" 17 | " + `1234`\n" 18 | " + `0000`\n" 19 | " + `beef`\n" 20 | ""; 21 | 22 | TEST_CASE("Recognize values signature", "[values]") 23 | { 24 | mdp::MarkdownParser markdownParser; 25 | mdp::MarkdownNode markdownAST; 26 | markdownParser.parse(ValuesFixture, markdownAST); 27 | 28 | REQUIRE(!markdownAST.children().empty()); 29 | SectionType sectionType = SectionProcessor::sectionType(markdownAST.children().begin()); 30 | REQUIRE(sectionType == ValuesSectionType); 31 | } 32 | 33 | TEST_CASE("Parse canonical values", "[values]") 34 | { 35 | ParseResult values; 36 | SectionParserHelper::parse(ValuesFixture, ValuesSectionType, values, ExportSourcemapOption); 37 | 38 | REQUIRE(values.report.error.code == Error::OK); 39 | CHECK(values.report.warnings.empty()); 40 | 41 | REQUIRE(values.node.size() == 3); 42 | REQUIRE(values.node[0] == "1234"); 43 | REQUIRE(values.node[1] == "0000"); 44 | REQUIRE(values.node[2] == "beef"); 45 | 46 | REQUIRE(values.sourceMap.collection.size() == 3); 47 | SourceMapHelper::check(values.sourceMap.collection[0].sourceMap, 13, 9); 48 | SourceMapHelper::check(values.sourceMap.collection[1].sourceMap, 26, 9); 49 | SourceMapHelper::check(values.sourceMap.collection[2].sourceMap, 39, 9); 50 | } 51 | 52 | TEST_CASE("Warn superfluous content in values attribute", "[values]") 53 | { 54 | mdp::ByteBuffer source 55 | = "+ Values\n\n" 56 | " extra\n\n" 57 | " + `Hello`\n"; 58 | 59 | ParseResult values; 60 | SectionParserHelper::parse(source, ValuesSectionType, values, ExportSourcemapOption); 61 | 62 | REQUIRE(values.report.error.code == Error::OK); 63 | REQUIRE(values.report.warnings.size() == 1); 64 | REQUIRE(values.report.warnings[0].code == IgnoringWarning); 65 | 66 | REQUIRE(values.node.size() == 1); 67 | REQUIRE(values.node[0] == "Hello"); 68 | 69 | REQUIRE(values.sourceMap.collection.size() == 1); 70 | SourceMapHelper::check(values.sourceMap.collection[0].sourceMap, 22, 10); 71 | } 72 | 73 | TEST_CASE("Warn about illegal entities in values attribute", "[values]") 74 | { 75 | const std::string source 76 | = "+ Values\n" 77 | " + `Hello`\n" 78 | " + illegal\n" 79 | " + `Hi`\n"; 80 | 81 | ParseResult values; 82 | SectionParserHelper::parse(source, ValuesSectionType, values, ExportSourcemapOption); 83 | 84 | REQUIRE(values.report.error.code == Error::OK); 85 | REQUIRE(values.report.warnings.size() == 1); 86 | 87 | REQUIRE(values.node.size() == 2); 88 | REQUIRE(values.node[0] == "Hello"); 89 | REQUIRE(values.node[1] == "Hi"); 90 | 91 | REQUIRE(values.sourceMap.collection.size() == 2); 92 | SourceMapHelper::check(values.sourceMap.collection[0].sourceMap, 13, 10); 93 | SourceMapHelper::check(values.sourceMap.collection[1].sourceMap, 41, 7); 94 | } 95 | -------------------------------------------------------------------------------- /test/test-Warnings.cc: -------------------------------------------------------------------------------- 1 | // 2 | // test-Warnings.c 3 | // snowcrash 4 | // 5 | // Created by Zdenek Nemec on 11/12/13. 6 | // Copyright (c) 2013 Apiary Inc. All rights reserved. 7 | // 8 | 9 | #include "snowcrash.h" 10 | #include "snowcrashtest.h" 11 | 12 | using namespace snowcrash; 13 | using namespace snowcrashtest; 14 | 15 | TEST_CASE("Warn about keywords in API name", "[warnings][31]") 16 | { 17 | mdp::ByteBuffer source 18 | = "FORMAT: 1A\n" 19 | "\n" 20 | "# POST\n"; 21 | 22 | ParseResult blueprint; 23 | parse(source, 0, blueprint); 24 | 25 | REQUIRE(blueprint.report.error.code == Error::OK); 26 | 27 | // TODO: 28 | // REQUIRE(blueprint.report.warnings.size() == 1); 29 | // REQUIRE(blueprint.report.warnings[0].code == AmbiguityWarning); 30 | 31 | // REQUIRE(blueprint.name.empty()); 32 | REQUIRE(blueprint.node.content.elements().empty()); 33 | } 34 | 35 | TEST_CASE("Warn about brackets in URI template", "[warnings][79]") 36 | { 37 | mdp::ByteBuffer source 38 | = "FORMAT: 1A\n" 39 | "\n" 40 | "# test api\n" 41 | "this is a description of the test api\n" 42 | "# Group test\n" 43 | "A test group\n" 44 | "## test [/test/{id}[2]]\n" 45 | "A test uri template\n"; 46 | 47 | ParseResult blueprint; 48 | parse(source, 0, blueprint); 49 | 50 | REQUIRE(blueprint.report.error.code == Error::OK); 51 | REQUIRE(blueprint.report.warnings.size() == 1); 52 | REQUIRE(blueprint.report.warnings[0].code == URIWarning); 53 | } 54 | 55 | TEST_CASE("Warn about unsupported uri template label expansion", "[warnings][78]") 56 | { 57 | mdp::ByteBuffer source 58 | = "FORMAT: 1A\n" 59 | "\n" 60 | "# test api\n" 61 | "this is a description of the test api\n" 62 | "# Group test\n" 63 | "A test group\n" 64 | "## test [/test/{.varone}]\n" 65 | "A test uri template\n"; 66 | 67 | ParseResult blueprint; 68 | parse(source, 0, blueprint); 69 | 70 | REQUIRE(blueprint.report.error.code == Error::OK); 71 | REQUIRE(blueprint.report.warnings.size() == 1); 72 | REQUIRE(blueprint.report.warnings[0].code == URIWarning); 73 | } 74 | 75 | TEST_CASE("Warn about unsupported uri template in abbreviated blueprint", "[warnings][78]") 76 | { 77 | mdp::ByteBuffer source 78 | = "FORMAT: 1A\n" 79 | "\n" 80 | "# test api\n" 81 | "this is a description of the test api\n" 82 | "# Group test\n" 83 | "A test group\n" 84 | "## GET /res/{id}{?a, b}\n" 85 | "+ Response 200"; 86 | 87 | ParseResult blueprint; 88 | parse(source, 0, blueprint); 89 | 90 | REQUIRE(blueprint.report.error.code == Error::OK); 91 | REQUIRE(blueprint.report.warnings.size() == 1); 92 | REQUIRE(blueprint.report.warnings[0].code == URIWarning); 93 | } 94 | -------------------------------------------------------------------------------- /tools/README.md: -------------------------------------------------------------------------------- 1 | # Snow Crash Development Tools 2 | 3 | ## Gyp 4 | Snow Crash uses (bundled) [Gyp](https://gyp.gsrc.io/) build automation tool to 5 | generate its Makefiles. 6 | 7 | ### Actual Bundled Version 8 | [**SHA d84fed246cd42612c31a56a897e2d032e254a58a**](https://chromium.googlesource.com/external/gyp/+/d84fed246cd42612c31a56a897e2d032e254a58a) 9 | 10 | ### Update 11 | 1. Download the latest stable version of Gyp from its [repository](https://chromium.googlesource.com/external/gyp/+/refs/heads/master) 12 | 13 | 2. Remove unnecessary files: 14 | 15 | ```sh 16 | $ rm -rf .svn 17 | $ rm -rf test 18 | ``` 19 | 20 | 3. Update bundled version in this README 21 | 22 | ### More info 23 | See [Gyp Hello World](https://github.com/springmeyer/hello-gyp) for a primer on 24 | using Gyp. 25 | -------------------------------------------------------------------------------- /tools/gyp/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /tools/gyp/AUTHORS: -------------------------------------------------------------------------------- 1 | # Names should be added to this file like so: 2 | # Name or Organization 3 | 4 | Google Inc. 5 | Bloomberg Finance L.P. 6 | Yandex LLC 7 | 8 | Steven Knight 9 | Ryan Norton 10 | David J. Sankel 11 | Eric N. Vander Weele 12 | Tom Freudenberg 13 | -------------------------------------------------------------------------------- /tools/gyp/DEPS: -------------------------------------------------------------------------------- 1 | # DEPS file for gclient use in buildbot execution of gyp tests. 2 | # 3 | # (You don't need to use gclient for normal GYP development work.) 4 | 5 | vars = { 6 | "chromium_git": "https://chromium.googlesource.com/", 7 | } 8 | 9 | deps = { 10 | } 11 | 12 | deps_os = { 13 | "win": { 14 | "third_party/cygwin": 15 | Var("chromium_git") + "chromium/deps/cygwin@4fbd5b9", 16 | 17 | "third_party/python_26": 18 | Var("chromium_git") + "chromium/deps/python_26@5bb4080", 19 | 20 | "src/third_party/pefile": 21 | Var("chromium_git") + "external/pefile@72c6ae4", 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /tools/gyp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Google Inc. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /tools/gyp/OWNERS: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /tools/gyp/PRESUBMIT.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | 6 | """Top-level presubmit script for GYP. 7 | 8 | See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts 9 | for more details about the presubmit API built into gcl. 10 | """ 11 | 12 | 13 | PYLINT_BLACKLIST = [ 14 | # TODO: fix me. 15 | # From SCons, not done in google style. 16 | 'test/lib/TestCmd.py', 17 | 'test/lib/TestCommon.py', 18 | 'test/lib/TestGyp.py', 19 | ] 20 | 21 | 22 | PYLINT_DISABLED_WARNINGS = [ 23 | # TODO: fix me. 24 | # Many tests include modules they don't use. 25 | 'W0611', 26 | # Possible unbalanced tuple unpacking with sequence. 27 | 'W0632', 28 | # Attempting to unpack a non-sequence. 29 | 'W0633', 30 | # Include order doesn't properly include local files? 31 | 'F0401', 32 | # Some use of built-in names. 33 | 'W0622', 34 | # Some unused variables. 35 | 'W0612', 36 | # Operator not preceded/followed by space. 37 | 'C0323', 38 | 'C0322', 39 | # Unnecessary semicolon. 40 | 'W0301', 41 | # Unused argument. 42 | 'W0613', 43 | # String has no effect (docstring in wrong place). 44 | 'W0105', 45 | # map/filter on lambda could be replaced by comprehension. 46 | 'W0110', 47 | # Use of eval. 48 | 'W0123', 49 | # Comma not followed by space. 50 | 'C0324', 51 | # Access to a protected member. 52 | 'W0212', 53 | # Bad indent. 54 | 'W0311', 55 | # Line too long. 56 | 'C0301', 57 | # Undefined variable. 58 | 'E0602', 59 | # Not exception type specified. 60 | 'W0702', 61 | # No member of that name. 62 | 'E1101', 63 | # Dangerous default {}. 64 | 'W0102', 65 | # Cyclic import. 66 | 'R0401', 67 | # Others, too many to sort. 68 | 'W0201', 'W0232', 'E1103', 'W0621', 'W0108', 'W0223', 'W0231', 69 | 'R0201', 'E0101', 'C0321', 70 | # ************* Module copy 71 | # W0104:427,12:_test.odict.__setitem__: Statement seems to have no effect 72 | 'W0104', 73 | ] 74 | 75 | 76 | def CheckChangeOnUpload(input_api, output_api): 77 | report = [] 78 | report.extend(input_api.canned_checks.PanProjectChecks( 79 | input_api, output_api)) 80 | return report 81 | 82 | 83 | def CheckChangeOnCommit(input_api, output_api): 84 | report = [] 85 | 86 | # Accept any year number from 2009 to the current year. 87 | current_year = int(input_api.time.strftime('%Y')) 88 | allowed_years = (str(s) for s in reversed(xrange(2009, current_year + 1))) 89 | years_re = '(' + '|'.join(allowed_years) + ')' 90 | 91 | # The (c) is deprecated, but tolerate it until it's removed from all files. 92 | license = ( 93 | r'.*? Copyright (\(c\) )?%(year)s Google Inc\. All rights reserved\.\n' 94 | r'.*? Use of this source code is governed by a BSD-style license that ' 95 | r'can be\n' 96 | r'.*? found in the LICENSE file\.\n' 97 | ) % { 98 | 'year': years_re, 99 | } 100 | 101 | report.extend(input_api.canned_checks.PanProjectChecks( 102 | input_api, output_api, license_header=license)) 103 | report.extend(input_api.canned_checks.CheckTreeIsOpen( 104 | input_api, output_api, 105 | 'http://gyp-status.appspot.com/status', 106 | 'http://gyp-status.appspot.com/current')) 107 | 108 | import os 109 | import sys 110 | old_sys_path = sys.path 111 | try: 112 | sys.path = ['pylib', 'test/lib'] + sys.path 113 | blacklist = PYLINT_BLACKLIST 114 | if sys.platform == 'win32': 115 | blacklist = [os.path.normpath(x).replace('\\', '\\\\') 116 | for x in PYLINT_BLACKLIST] 117 | report.extend(input_api.canned_checks.RunPylint( 118 | input_api, 119 | output_api, 120 | black_list=blacklist, 121 | disabled_warnings=PYLINT_DISABLED_WARNINGS)) 122 | finally: 123 | sys.path = old_sys_path 124 | return report 125 | 126 | 127 | TRYBOTS = [ 128 | 'linux_try', 129 | 'mac_try', 130 | 'win_try', 131 | ] 132 | 133 | 134 | def GetPreferredTryMasters(_, change): 135 | return { 136 | 'client.gyp': { t: set(['defaulttests']) for t in TRYBOTS }, 137 | } 138 | -------------------------------------------------------------------------------- /tools/gyp/README.md: -------------------------------------------------------------------------------- 1 | GYP can Generate Your Projects. 2 | =================================== 3 | 4 | Documents are available at [gyp.gsrc.io](https://gyp.gsrc.io), or you can check out ```md-pages``` branch to read those documents offline. 5 | -------------------------------------------------------------------------------- /tools/gyp/buildbot/buildbot_run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012 Google Inc. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """Argument-less script to select what to run on the buildbots.""" 7 | 8 | import os 9 | import shutil 10 | import subprocess 11 | import sys 12 | 13 | 14 | BUILDBOT_DIR = os.path.dirname(os.path.abspath(__file__)) 15 | TRUNK_DIR = os.path.dirname(BUILDBOT_DIR) 16 | ROOT_DIR = os.path.dirname(TRUNK_DIR) 17 | CMAKE_DIR = os.path.join(ROOT_DIR, 'cmake') 18 | CMAKE_BIN_DIR = os.path.join(CMAKE_DIR, 'bin') 19 | OUT_DIR = os.path.join(TRUNK_DIR, 'out') 20 | 21 | 22 | def CallSubProcess(*args, **kwargs): 23 | """Wrapper around subprocess.call which treats errors as build exceptions.""" 24 | with open(os.devnull) as devnull_fd: 25 | retcode = subprocess.call(stdin=devnull_fd, *args, **kwargs) 26 | if retcode != 0: 27 | print '@@@STEP_EXCEPTION@@@' 28 | sys.exit(1) 29 | 30 | 31 | def PrepareCmake(): 32 | """Build CMake 2.8.8 since the version in Precise is 2.8.7.""" 33 | if os.environ['BUILDBOT_CLOBBER'] == '1': 34 | print '@@@BUILD_STEP Clobber CMake checkout@@@' 35 | shutil.rmtree(CMAKE_DIR) 36 | 37 | # We always build CMake 2.8.8, so no need to do anything 38 | # if the directory already exists. 39 | if os.path.isdir(CMAKE_DIR): 40 | return 41 | 42 | print '@@@BUILD_STEP Initialize CMake checkout@@@' 43 | os.mkdir(CMAKE_DIR) 44 | 45 | print '@@@BUILD_STEP Sync CMake@@@' 46 | CallSubProcess( 47 | ['git', 'clone', 48 | '--depth', '1', 49 | '--single-branch', 50 | '--branch', 'v2.8.8', 51 | '--', 52 | 'git://cmake.org/cmake.git', 53 | CMAKE_DIR], 54 | cwd=CMAKE_DIR) 55 | 56 | print '@@@BUILD_STEP Build CMake@@@' 57 | CallSubProcess( 58 | ['/bin/bash', 'bootstrap', '--prefix=%s' % CMAKE_DIR], 59 | cwd=CMAKE_DIR) 60 | 61 | CallSubProcess( ['make', 'cmake'], cwd=CMAKE_DIR) 62 | 63 | 64 | def GypTestFormat(title, format=None, msvs_version=None, tests=[]): 65 | """Run the gyp tests for a given format, emitting annotator tags. 66 | 67 | See annotator docs at: 68 | https://sites.google.com/a/chromium.org/dev/developers/testing/chromium-build-infrastructure/buildbot-annotations 69 | Args: 70 | format: gyp format to test. 71 | Returns: 72 | 0 for sucesss, 1 for failure. 73 | """ 74 | if not format: 75 | format = title 76 | 77 | print '@@@BUILD_STEP ' + title + '@@@' 78 | sys.stdout.flush() 79 | env = os.environ.copy() 80 | if msvs_version: 81 | env['GYP_MSVS_VERSION'] = msvs_version 82 | command = ' '.join( 83 | [sys.executable, 'gyp/gyptest.py', 84 | '--all', 85 | '--passed', 86 | '--format', format, 87 | '--path', CMAKE_BIN_DIR, 88 | '--chdir', 'gyp'] + tests) 89 | retcode = subprocess.call(command, cwd=ROOT_DIR, env=env, shell=True) 90 | if retcode: 91 | # Emit failure tag, and keep going. 92 | print '@@@STEP_FAILURE@@@' 93 | return 1 94 | return 0 95 | 96 | 97 | def GypBuild(): 98 | # Dump out/ directory. 99 | print '@@@BUILD_STEP cleanup@@@' 100 | print 'Removing %s...' % OUT_DIR 101 | shutil.rmtree(OUT_DIR, ignore_errors=True) 102 | print 'Done.' 103 | 104 | retcode = 0 105 | if sys.platform.startswith('linux'): 106 | retcode += GypTestFormat('ninja') 107 | retcode += GypTestFormat('make') 108 | PrepareCmake() 109 | retcode += GypTestFormat('cmake') 110 | elif sys.platform == 'darwin': 111 | retcode += GypTestFormat('ninja') 112 | retcode += GypTestFormat('xcode') 113 | retcode += GypTestFormat('make') 114 | elif sys.platform == 'win32': 115 | retcode += GypTestFormat('ninja') 116 | if os.environ['BUILDBOT_BUILDERNAME'] == 'gyp-win64': 117 | retcode += GypTestFormat('msvs-ninja-2013', format='msvs-ninja', 118 | msvs_version='2013', 119 | tests=[ 120 | r'test\generator-output\gyptest-actions.py', 121 | r'test\generator-output\gyptest-relocate.py', 122 | r'test\generator-output\gyptest-rules.py']) 123 | retcode += GypTestFormat('msvs-2013', format='msvs', msvs_version='2013') 124 | else: 125 | raise Exception('Unknown platform') 126 | if retcode: 127 | # TODO(bradnelson): once the annotator supports a postscript (section for 128 | # after the build proper that could be used for cumulative failures), 129 | # use that instead of this. This isolates the final return value so 130 | # that it isn't misattributed to the last stage. 131 | print '@@@BUILD_STEP failures@@@' 132 | sys.exit(retcode) 133 | 134 | 135 | if __name__ == '__main__': 136 | GypBuild() 137 | -------------------------------------------------------------------------------- /tools/gyp/buildbot/commit_queue/OWNERS: -------------------------------------------------------------------------------- 1 | set noparent 2 | bradnelson@chromium.org 3 | bradnelson@google.com 4 | iannucci@chromium.org 5 | scottmg@chromium.org 6 | thakis@chromium.org 7 | -------------------------------------------------------------------------------- /tools/gyp/buildbot/commit_queue/README: -------------------------------------------------------------------------------- 1 | cq_config.json describes the trybots that must pass in order 2 | to land a change through the commit queue. 3 | Comments are here as the file is strictly JSON. 4 | -------------------------------------------------------------------------------- /tools/gyp/buildbot/commit_queue/cq_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "trybots": { 3 | "launched": { 4 | "tryserver.nacl": { 5 | "gyp-presubmit": ["defaulttests"], 6 | "gyp-linux": ["defaulttests"], 7 | "gyp-mac": ["defaulttests"], 8 | "gyp-win32": ["defaulttests"], 9 | "gyp-win64": ["defaulttests"] 10 | } 11 | }, 12 | "triggered": { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tools/gyp/codereview.settings: -------------------------------------------------------------------------------- 1 | # This file is used by gcl to get repository specific information. 2 | CODE_REVIEW_SERVER: codereview.chromium.org 3 | CC_LIST: gyp-developer@googlegroups.com 4 | VIEW_VC: https://chromium.googlesource.com/external/gyp/+/ 5 | TRY_ON_UPLOAD: False 6 | TRYSERVER_PROJECT: gyp 7 | TRYSERVER_PATCHLEVEL: 1 8 | TRYSERVER_ROOT: gyp 9 | TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try-nacl 10 | PROJECT: gyp 11 | -------------------------------------------------------------------------------- /tools/gyp/data/win/large-pdb-shim.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013 Google Inc. All rights reserved. 2 | // Use of this source code is governed by a BSD-style license that can be 3 | // found in the LICENSE file. 4 | 5 | // This file is used to generate an empty .pdb -- with a 4KB pagesize -- that is 6 | // then used during the final link for modules that have large PDBs. Otherwise, 7 | // the linker will generate a pdb with a page size of 1KB, which imposes a limit 8 | // of 1GB on the .pdb. By generating an initial empty .pdb with the compiler 9 | // (rather than the linker), this limit is avoided. With this in place PDBs may 10 | // grow to 2GB. 11 | // 12 | // This file is referenced by the msvs_large_pdb mechanism in MSVSUtil.py. 13 | -------------------------------------------------------------------------------- /tools/gyp/gyp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2013 The Chromium Authors. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | set -e 7 | base=$(dirname "$0") 8 | exec python "${base}/gyp_main.py" "$@" 9 | -------------------------------------------------------------------------------- /tools/gyp/gyp.bat: -------------------------------------------------------------------------------- 1 | @rem Copyright (c) 2009 Google Inc. All rights reserved. 2 | @rem Use of this source code is governed by a BSD-style license that can be 3 | @rem found in the LICENSE file. 4 | 5 | @python "%~dp0gyp_main.py" %* 6 | -------------------------------------------------------------------------------- /tools/gyp/gyp_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2009 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | import os 8 | import sys 9 | 10 | # Make sure we're using the version of pylib in this repo, not one installed 11 | # elsewhere on the system. 12 | sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'pylib')) 13 | import gyp 14 | 15 | if __name__ == '__main__': 16 | sys.exit(gyp.script_main()) 17 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/MSVSToolFile.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """Visual Studio project reader/writer.""" 6 | 7 | import gyp.common 8 | import gyp.easy_xml as easy_xml 9 | 10 | 11 | class Writer(object): 12 | """Visual Studio XML tool file writer.""" 13 | 14 | def __init__(self, tool_file_path, name): 15 | """Initializes the tool file. 16 | 17 | Args: 18 | tool_file_path: Path to the tool file. 19 | name: Name of the tool file. 20 | """ 21 | self.tool_file_path = tool_file_path 22 | self.name = name 23 | self.rules_section = ['Rules'] 24 | 25 | def AddCustomBuildRule(self, name, cmd, description, 26 | additional_dependencies, 27 | outputs, extensions): 28 | """Adds a rule to the tool file. 29 | 30 | Args: 31 | name: Name of the rule. 32 | description: Description of the rule. 33 | cmd: Command line of the rule. 34 | additional_dependencies: other files which may trigger the rule. 35 | outputs: outputs of the rule. 36 | extensions: extensions handled by the rule. 37 | """ 38 | rule = ['CustomBuildRule', 39 | {'Name': name, 40 | 'ExecutionDescription': description, 41 | 'CommandLine': cmd, 42 | 'Outputs': ';'.join(outputs), 43 | 'FileExtensions': ';'.join(extensions), 44 | 'AdditionalDependencies': 45 | ';'.join(additional_dependencies) 46 | }] 47 | self.rules_section.append(rule) 48 | 49 | def WriteIfChanged(self): 50 | """Writes the tool file.""" 51 | content = ['VisualStudioToolFile', 52 | {'Version': '8.00', 53 | 'Name': self.name 54 | }, 55 | self.rules_section 56 | ] 57 | easy_xml.WriteXmlIfChanged(content, self.tool_file_path, 58 | encoding="Windows-1252") 59 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/common_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2012 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """Unit tests for the common.py file.""" 8 | 9 | import gyp.common 10 | import unittest 11 | import sys 12 | 13 | 14 | class TestTopologicallySorted(unittest.TestCase): 15 | def test_Valid(self): 16 | """Test that sorting works on a valid graph with one possible order.""" 17 | graph = { 18 | 'a': ['b', 'c'], 19 | 'b': [], 20 | 'c': ['d'], 21 | 'd': ['b'], 22 | } 23 | def GetEdge(node): 24 | return tuple(graph[node]) 25 | self.assertEqual( 26 | gyp.common.TopologicallySorted(graph.keys(), GetEdge), 27 | ['a', 'c', 'd', 'b']) 28 | 29 | def test_Cycle(self): 30 | """Test that an exception is thrown on a cyclic graph.""" 31 | graph = { 32 | 'a': ['b'], 33 | 'b': ['c'], 34 | 'c': ['d'], 35 | 'd': ['a'], 36 | } 37 | def GetEdge(node): 38 | return tuple(graph[node]) 39 | self.assertRaises( 40 | gyp.common.CycleError, gyp.common.TopologicallySorted, 41 | graph.keys(), GetEdge) 42 | 43 | 44 | class TestGetFlavor(unittest.TestCase): 45 | """Test that gyp.common.GetFlavor works as intended""" 46 | original_platform = '' 47 | 48 | def setUp(self): 49 | self.original_platform = sys.platform 50 | 51 | def tearDown(self): 52 | sys.platform = self.original_platform 53 | 54 | def assertFlavor(self, expected, argument, param): 55 | sys.platform = argument 56 | self.assertEqual(expected, gyp.common.GetFlavor(param)) 57 | 58 | def test_platform_default(self): 59 | self.assertFlavor('freebsd', 'freebsd9' , {}) 60 | self.assertFlavor('freebsd', 'freebsd10', {}) 61 | self.assertFlavor('openbsd', 'openbsd5' , {}) 62 | self.assertFlavor('solaris', 'sunos5' , {}); 63 | self.assertFlavor('solaris', 'sunos' , {}); 64 | self.assertFlavor('linux' , 'linux2' , {}); 65 | self.assertFlavor('linux' , 'linux3' , {}); 66 | 67 | def test_param(self): 68 | self.assertFlavor('foobar', 'linux2' , {'flavor': 'foobar'}) 69 | 70 | 71 | if __name__ == '__main__': 72 | unittest.main() 73 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/easy_xml_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2011 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """ Unit tests for the easy_xml.py file. """ 8 | 9 | import gyp.easy_xml as easy_xml 10 | import unittest 11 | import StringIO 12 | 13 | 14 | class TestSequenceFunctions(unittest.TestCase): 15 | 16 | def setUp(self): 17 | self.stderr = StringIO.StringIO() 18 | 19 | def test_EasyXml_simple(self): 20 | self.assertEqual( 21 | easy_xml.XmlToString(['test']), 22 | '') 23 | 24 | self.assertEqual( 25 | easy_xml.XmlToString(['test'], encoding='Windows-1252'), 26 | '') 27 | 28 | def test_EasyXml_simple_with_attributes(self): 29 | self.assertEqual( 30 | easy_xml.XmlToString(['test2', {'a': 'value1', 'b': 'value2'}]), 31 | '') 32 | 33 | def test_EasyXml_escaping(self): 34 | original = '\'"\r&\nfoo' 35 | converted = '<test>\'" & foo' 36 | converted_apos = converted.replace("'", ''') 37 | self.assertEqual( 38 | easy_xml.XmlToString(['test3', {'a': original}, original]), 39 | '%s' % 40 | (converted, converted_apos)) 41 | 42 | def test_EasyXml_pretty(self): 43 | self.assertEqual( 44 | easy_xml.XmlToString( 45 | ['test3', 46 | ['GrandParent', 47 | ['Parent1', 48 | ['Child'] 49 | ], 50 | ['Parent2'] 51 | ] 52 | ], 53 | pretty=True), 54 | '\n' 55 | '\n' 56 | ' \n' 57 | ' \n' 58 | ' \n' 59 | ' \n' 60 | ' \n' 61 | ' \n' 62 | '\n') 63 | 64 | 65 | def test_EasyXml_complex(self): 66 | # We want to create: 67 | target = ( 68 | '' 69 | '' 70 | '' 71 | '{D2250C20-3A94-4FB9-AF73-11BC5B73884B}' 72 | 'Win32Proj' 73 | 'automated_ui_tests' 74 | '' 75 | '' 76 | '' 79 | 'Application' 80 | 'Unicode' 81 | '' 82 | '') 83 | 84 | xml = easy_xml.XmlToString( 85 | ['Project', 86 | ['PropertyGroup', {'Label': 'Globals'}, 87 | ['ProjectGuid', '{D2250C20-3A94-4FB9-AF73-11BC5B73884B}'], 88 | ['Keyword', 'Win32Proj'], 89 | ['RootNamespace', 'automated_ui_tests'] 90 | ], 91 | ['Import', {'Project': '$(VCTargetsPath)\\Microsoft.Cpp.props'}], 92 | ['PropertyGroup', 93 | {'Condition': "'$(Configuration)|$(Platform)'=='Debug|Win32'", 94 | 'Label': 'Configuration'}, 95 | ['ConfigurationType', 'Application'], 96 | ['CharacterSet', 'Unicode'] 97 | ] 98 | ]) 99 | self.assertEqual(xml, target) 100 | 101 | 102 | if __name__ == '__main__': 103 | unittest.main() 104 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/flock_tool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2011 Google Inc. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """These functions are executed via gyp-flock-tool when using the Makefile 7 | generator. Used on systems that don't have a built-in flock.""" 8 | 9 | import fcntl 10 | import os 11 | import struct 12 | import subprocess 13 | import sys 14 | 15 | 16 | def main(args): 17 | executor = FlockTool() 18 | executor.Dispatch(args) 19 | 20 | 21 | class FlockTool(object): 22 | """This class emulates the 'flock' command.""" 23 | def Dispatch(self, args): 24 | """Dispatches a string command to a method.""" 25 | if len(args) < 1: 26 | raise Exception("Not enough arguments") 27 | 28 | method = "Exec%s" % self._CommandifyName(args[0]) 29 | getattr(self, method)(*args[1:]) 30 | 31 | def _CommandifyName(self, name_string): 32 | """Transforms a tool name like copy-info-plist to CopyInfoPlist""" 33 | return name_string.title().replace('-', '') 34 | 35 | def ExecFlock(self, lockfile, *cmd_list): 36 | """Emulates the most basic behavior of Linux's flock(1).""" 37 | # Rely on exception handling to report errors. 38 | # Note that the stock python on SunOS has a bug 39 | # where fcntl.flock(fd, LOCK_EX) always fails 40 | # with EBADF, that's why we use this F_SETLK 41 | # hack instead. 42 | fd = os.open(lockfile, os.O_WRONLY|os.O_NOCTTY|os.O_CREAT, 0666) 43 | if sys.platform.startswith('aix'): 44 | # Python on AIX is compiled with LARGEFILE support, which changes the 45 | # struct size. 46 | op = struct.pack('hhIllqq', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0) 47 | else: 48 | op = struct.pack('hhllhhl', fcntl.F_WRLCK, 0, 0, 0, 0, 0, 0) 49 | fcntl.fcntl(fd, fcntl.F_SETLK, op) 50 | return subprocess.call(cmd_list) 51 | 52 | 53 | if __name__ == '__main__': 54 | sys.exit(main(sys.argv[1:])) 55 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apiaryio/snowcrash/b5b39faa85f88ee17459edf39fdc6fe4fc70d2e3/tools/gyp/pylib/gyp/generator/__init__.py -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/dump_dependency_json.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | import collections 6 | import os 7 | import gyp 8 | import gyp.common 9 | import gyp.msvs_emulation 10 | import json 11 | import sys 12 | 13 | generator_supports_multiple_toolsets = True 14 | 15 | generator_wants_static_library_dependencies_adjusted = False 16 | 17 | generator_filelist_paths = { 18 | } 19 | 20 | generator_default_variables = { 21 | } 22 | for dirname in ['INTERMEDIATE_DIR', 'SHARED_INTERMEDIATE_DIR', 'PRODUCT_DIR', 23 | 'LIB_DIR', 'SHARED_LIB_DIR']: 24 | # Some gyp steps fail if these are empty(!). 25 | generator_default_variables[dirname] = 'dir' 26 | for unused in ['RULE_INPUT_PATH', 'RULE_INPUT_ROOT', 'RULE_INPUT_NAME', 27 | 'RULE_INPUT_DIRNAME', 'RULE_INPUT_EXT', 28 | 'EXECUTABLE_PREFIX', 'EXECUTABLE_SUFFIX', 29 | 'STATIC_LIB_PREFIX', 'STATIC_LIB_SUFFIX', 30 | 'SHARED_LIB_PREFIX', 'SHARED_LIB_SUFFIX', 31 | 'CONFIGURATION_NAME']: 32 | generator_default_variables[unused] = '' 33 | 34 | 35 | def CalculateVariables(default_variables, params): 36 | generator_flags = params.get('generator_flags', {}) 37 | for key, val in generator_flags.items(): 38 | default_variables.setdefault(key, val) 39 | default_variables.setdefault('OS', gyp.common.GetFlavor(params)) 40 | 41 | flavor = gyp.common.GetFlavor(params) 42 | if flavor =='win': 43 | # Copy additional generator configuration data from VS, which is shared 44 | # by the Windows Ninja generator. 45 | import gyp.generator.msvs as msvs_generator 46 | generator_additional_non_configuration_keys = getattr(msvs_generator, 47 | 'generator_additional_non_configuration_keys', []) 48 | generator_additional_path_sections = getattr(msvs_generator, 49 | 'generator_additional_path_sections', []) 50 | 51 | gyp.msvs_emulation.CalculateCommonVariables(default_variables, params) 52 | 53 | 54 | def CalculateGeneratorInputInfo(params): 55 | """Calculate the generator specific info that gets fed to input (called by 56 | gyp).""" 57 | generator_flags = params.get('generator_flags', {}) 58 | if generator_flags.get('adjust_static_libraries', False): 59 | global generator_wants_static_library_dependencies_adjusted 60 | generator_wants_static_library_dependencies_adjusted = True 61 | 62 | toplevel = params['options'].toplevel_dir 63 | generator_dir = os.path.relpath(params['options'].generator_output or '.') 64 | # output_dir: relative path from generator_dir to the build directory. 65 | output_dir = generator_flags.get('output_dir', 'out') 66 | qualified_out_dir = os.path.normpath(os.path.join( 67 | toplevel, generator_dir, output_dir, 'gypfiles')) 68 | global generator_filelist_paths 69 | generator_filelist_paths = { 70 | 'toplevel': toplevel, 71 | 'qualified_out_dir': qualified_out_dir, 72 | } 73 | 74 | def GenerateOutput(target_list, target_dicts, data, params): 75 | # Map of target -> list of targets it depends on. 76 | edges = {} 77 | 78 | # Queue of targets to visit. 79 | targets_to_visit = target_list[:] 80 | 81 | while len(targets_to_visit) > 0: 82 | target = targets_to_visit.pop() 83 | if target in edges: 84 | continue 85 | edges[target] = [] 86 | 87 | for dep in target_dicts[target].get('dependencies', []): 88 | edges[target].append(dep) 89 | targets_to_visit.append(dep) 90 | 91 | try: 92 | filepath = params['generator_flags']['output_dir'] 93 | except KeyError: 94 | filepath = '.' 95 | filename = os.path.join(filepath, 'dump.json') 96 | f = open(filename, 'w') 97 | json.dump(edges, f) 98 | f.close() 99 | print 'Wrote json to %s.' % filename 100 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/gypd.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """gypd output module 6 | 7 | This module produces gyp input as its output. Output files are given the 8 | .gypd extension to avoid overwriting the .gyp files that they are generated 9 | from. Internal references to .gyp files (such as those found in 10 | "dependencies" sections) are not adjusted to point to .gypd files instead; 11 | unlike other paths, which are relative to the .gyp or .gypd file, such paths 12 | are relative to the directory from which gyp was run to create the .gypd file. 13 | 14 | This generator module is intended to be a sample and a debugging aid, hence 15 | the "d" for "debug" in .gypd. It is useful to inspect the results of the 16 | various merges, expansions, and conditional evaluations performed by gyp 17 | and to see a representation of what would be fed to a generator module. 18 | 19 | It's not advisable to rename .gypd files produced by this module to .gyp, 20 | because they will have all merges, expansions, and evaluations already 21 | performed and the relevant constructs not present in the output; paths to 22 | dependencies may be wrong; and various sections that do not belong in .gyp 23 | files such as such as "included_files" and "*_excluded" will be present. 24 | Output will also be stripped of comments. This is not intended to be a 25 | general-purpose gyp pretty-printer; for that, you probably just want to 26 | run "pprint.pprint(eval(open('source.gyp').read()))", which will still strip 27 | comments but won't do all of the other things done to this module's output. 28 | 29 | The specific formatting of the output generated by this module is subject 30 | to change. 31 | """ 32 | 33 | 34 | import gyp.common 35 | import errno 36 | import os 37 | import pprint 38 | 39 | 40 | # These variables should just be spit back out as variable references. 41 | _generator_identity_variables = [ 42 | 'CONFIGURATION_NAME', 43 | 'EXECUTABLE_PREFIX', 44 | 'EXECUTABLE_SUFFIX', 45 | 'INTERMEDIATE_DIR', 46 | 'LIB_DIR', 47 | 'PRODUCT_DIR', 48 | 'RULE_INPUT_ROOT', 49 | 'RULE_INPUT_DIRNAME', 50 | 'RULE_INPUT_EXT', 51 | 'RULE_INPUT_NAME', 52 | 'RULE_INPUT_PATH', 53 | 'SHARED_INTERMEDIATE_DIR', 54 | 'SHARED_LIB_DIR', 55 | 'SHARED_LIB_PREFIX', 56 | 'SHARED_LIB_SUFFIX', 57 | 'STATIC_LIB_PREFIX', 58 | 'STATIC_LIB_SUFFIX', 59 | ] 60 | 61 | # gypd doesn't define a default value for OS like many other generator 62 | # modules. Specify "-D OS=whatever" on the command line to provide a value. 63 | generator_default_variables = { 64 | } 65 | 66 | # gypd supports multiple toolsets 67 | generator_supports_multiple_toolsets = True 68 | 69 | # TODO(mark): This always uses <, which isn't right. The input module should 70 | # notify the generator to tell it which phase it is operating in, and this 71 | # module should use < for the early phase and then switch to > for the late 72 | # phase. Bonus points for carrying @ back into the output too. 73 | for v in _generator_identity_variables: 74 | generator_default_variables[v] = '<(%s)' % v 75 | 76 | 77 | def GenerateOutput(target_list, target_dicts, data, params): 78 | output_files = {} 79 | for qualified_target in target_list: 80 | [input_file, target] = \ 81 | gyp.common.ParseQualifiedTarget(qualified_target)[0:2] 82 | 83 | if input_file[-4:] != '.gyp': 84 | continue 85 | input_file_stem = input_file[:-4] 86 | output_file = input_file_stem + params['options'].suffix + '.gypd' 87 | 88 | if not output_file in output_files: 89 | output_files[output_file] = input_file 90 | 91 | for output_file, input_file in output_files.iteritems(): 92 | output = open(output_file, 'w') 93 | pprint.pprint(data[input_file], output) 94 | output.close() 95 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/gypsh.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """gypsh output module 6 | 7 | gypsh is a GYP shell. It's not really a generator per se. All it does is 8 | fire up an interactive Python session with a few local variables set to the 9 | variables passed to the generator. Like gypd, it's intended as a debugging 10 | aid, to facilitate the exploration of .gyp structures after being processed 11 | by the input module. 12 | 13 | The expected usage is "gyp -f gypsh -D OS=desired_os". 14 | """ 15 | 16 | 17 | import code 18 | import sys 19 | 20 | 21 | # All of this stuff about generator variables was lovingly ripped from gypd.py. 22 | # That module has a much better description of what's going on and why. 23 | _generator_identity_variables = [ 24 | 'EXECUTABLE_PREFIX', 25 | 'EXECUTABLE_SUFFIX', 26 | 'INTERMEDIATE_DIR', 27 | 'PRODUCT_DIR', 28 | 'RULE_INPUT_ROOT', 29 | 'RULE_INPUT_DIRNAME', 30 | 'RULE_INPUT_EXT', 31 | 'RULE_INPUT_NAME', 32 | 'RULE_INPUT_PATH', 33 | 'SHARED_INTERMEDIATE_DIR', 34 | ] 35 | 36 | generator_default_variables = { 37 | } 38 | 39 | for v in _generator_identity_variables: 40 | generator_default_variables[v] = '<(%s)' % v 41 | 42 | 43 | def GenerateOutput(target_list, target_dicts, data, params): 44 | locals = { 45 | 'target_list': target_list, 46 | 'target_dicts': target_dicts, 47 | 'data': data, 48 | } 49 | 50 | # Use a banner that looks like the stock Python one and like what 51 | # code.interact uses by default, but tack on something to indicate what 52 | # locals are available, and identify gypsh. 53 | banner='Python %s on %s\nlocals.keys() = %s\ngypsh' % \ 54 | (sys.version, sys.platform, repr(sorted(locals.keys()))) 55 | 56 | code.interact(banner, local=locals) 57 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/msvs_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2012 Google Inc. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | 6 | """ Unit tests for the msvs.py file. """ 7 | 8 | import gyp.generator.msvs as msvs 9 | import unittest 10 | import StringIO 11 | 12 | 13 | class TestSequenceFunctions(unittest.TestCase): 14 | 15 | def setUp(self): 16 | self.stderr = StringIO.StringIO() 17 | 18 | def test_GetLibraries(self): 19 | self.assertEqual( 20 | msvs._GetLibraries({}), 21 | []) 22 | self.assertEqual( 23 | msvs._GetLibraries({'libraries': []}), 24 | []) 25 | self.assertEqual( 26 | msvs._GetLibraries({'other':'foo', 'libraries': ['a.lib']}), 27 | ['a.lib']) 28 | self.assertEqual( 29 | msvs._GetLibraries({'libraries': ['-la']}), 30 | ['a.lib']) 31 | self.assertEqual( 32 | msvs._GetLibraries({'libraries': ['a.lib', 'b.lib', 'c.lib', '-lb.lib', 33 | '-lb.lib', 'd.lib', 'a.lib']}), 34 | ['c.lib', 'b.lib', 'd.lib', 'a.lib']) 35 | 36 | if __name__ == '__main__': 37 | unittest.main() 38 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/ninja_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2012 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """ Unit tests for the ninja.py file. """ 8 | 9 | import gyp.generator.ninja as ninja 10 | import unittest 11 | import StringIO 12 | import sys 13 | import TestCommon 14 | 15 | 16 | class TestPrefixesAndSuffixes(unittest.TestCase): 17 | def test_BinaryNamesWindows(self): 18 | # These cannot run on non-Windows as they require a VS installation to 19 | # correctly handle variable expansion. 20 | if sys.platform.startswith('win'): 21 | writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.', 22 | 'build.ninja', 'win') 23 | spec = { 'target_name': 'wee' } 24 | self.assertTrue(writer.ComputeOutputFileName(spec, 'executable'). 25 | endswith('.exe')) 26 | self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). 27 | endswith('.dll')) 28 | self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). 29 | endswith('.lib')) 30 | 31 | def test_BinaryNamesLinux(self): 32 | writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.', 33 | 'build.ninja', 'linux') 34 | spec = { 'target_name': 'wee' } 35 | self.assertTrue('.' not in writer.ComputeOutputFileName(spec, 36 | 'executable')) 37 | self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). 38 | startswith('lib')) 39 | self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). 40 | startswith('lib')) 41 | self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). 42 | endswith('.so')) 43 | self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). 44 | endswith('.a')) 45 | 46 | if __name__ == '__main__': 47 | unittest.main() 48 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/generator/xcode_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2013 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """ Unit tests for the xcode.py file. """ 8 | 9 | import gyp.generator.xcode as xcode 10 | import unittest 11 | import sys 12 | 13 | 14 | class TestEscapeXcodeDefine(unittest.TestCase): 15 | if sys.platform == 'darwin': 16 | def test_InheritedRemainsUnescaped(self): 17 | self.assertEqual(xcode.EscapeXcodeDefine('$(inherited)'), '$(inherited)') 18 | 19 | def test_Escaping(self): 20 | self.assertEqual(xcode.EscapeXcodeDefine('a b"c\\'), 'a\\ b\\"c\\\\') 21 | 22 | if __name__ == '__main__': 23 | unittest.main() 24 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/input_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2013 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """Unit tests for the input.py file.""" 8 | 9 | import gyp.input 10 | import unittest 11 | import sys 12 | 13 | 14 | class TestFindCycles(unittest.TestCase): 15 | def setUp(self): 16 | self.nodes = {} 17 | for x in ('a', 'b', 'c', 'd', 'e'): 18 | self.nodes[x] = gyp.input.DependencyGraphNode(x) 19 | 20 | def _create_dependency(self, dependent, dependency): 21 | dependent.dependencies.append(dependency) 22 | dependency.dependents.append(dependent) 23 | 24 | def test_no_cycle_empty_graph(self): 25 | for label, node in self.nodes.iteritems(): 26 | self.assertEquals([], node.FindCycles()) 27 | 28 | def test_no_cycle_line(self): 29 | self._create_dependency(self.nodes['a'], self.nodes['b']) 30 | self._create_dependency(self.nodes['b'], self.nodes['c']) 31 | self._create_dependency(self.nodes['c'], self.nodes['d']) 32 | 33 | for label, node in self.nodes.iteritems(): 34 | self.assertEquals([], node.FindCycles()) 35 | 36 | def test_no_cycle_dag(self): 37 | self._create_dependency(self.nodes['a'], self.nodes['b']) 38 | self._create_dependency(self.nodes['a'], self.nodes['c']) 39 | self._create_dependency(self.nodes['b'], self.nodes['c']) 40 | 41 | for label, node in self.nodes.iteritems(): 42 | self.assertEquals([], node.FindCycles()) 43 | 44 | def test_cycle_self_reference(self): 45 | self._create_dependency(self.nodes['a'], self.nodes['a']) 46 | 47 | self.assertEquals([[self.nodes['a'], self.nodes['a']]], 48 | self.nodes['a'].FindCycles()) 49 | 50 | def test_cycle_two_nodes(self): 51 | self._create_dependency(self.nodes['a'], self.nodes['b']) 52 | self._create_dependency(self.nodes['b'], self.nodes['a']) 53 | 54 | self.assertEquals([[self.nodes['a'], self.nodes['b'], self.nodes['a']]], 55 | self.nodes['a'].FindCycles()) 56 | self.assertEquals([[self.nodes['b'], self.nodes['a'], self.nodes['b']]], 57 | self.nodes['b'].FindCycles()) 58 | 59 | def test_two_cycles(self): 60 | self._create_dependency(self.nodes['a'], self.nodes['b']) 61 | self._create_dependency(self.nodes['b'], self.nodes['a']) 62 | 63 | self._create_dependency(self.nodes['b'], self.nodes['c']) 64 | self._create_dependency(self.nodes['c'], self.nodes['b']) 65 | 66 | cycles = self.nodes['a'].FindCycles() 67 | self.assertTrue( 68 | [self.nodes['a'], self.nodes['b'], self.nodes['a']] in cycles) 69 | self.assertTrue( 70 | [self.nodes['b'], self.nodes['c'], self.nodes['b']] in cycles) 71 | self.assertEquals(2, len(cycles)) 72 | 73 | def test_big_cycle(self): 74 | self._create_dependency(self.nodes['a'], self.nodes['b']) 75 | self._create_dependency(self.nodes['b'], self.nodes['c']) 76 | self._create_dependency(self.nodes['c'], self.nodes['d']) 77 | self._create_dependency(self.nodes['d'], self.nodes['e']) 78 | self._create_dependency(self.nodes['e'], self.nodes['a']) 79 | 80 | self.assertEquals([[self.nodes['a'], 81 | self.nodes['b'], 82 | self.nodes['c'], 83 | self.nodes['d'], 84 | self.nodes['e'], 85 | self.nodes['a']]], 86 | self.nodes['a'].FindCycles()) 87 | 88 | 89 | if __name__ == '__main__': 90 | unittest.main() 91 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/simple_copy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """A clone of the default copy.deepcopy that doesn't handle cyclic 6 | structures or complex types except for dicts and lists. This is 7 | because gyp copies so large structure that small copy overhead ends up 8 | taking seconds in a project the size of Chromium.""" 9 | 10 | class Error(Exception): 11 | pass 12 | 13 | __all__ = ["Error", "deepcopy"] 14 | 15 | def deepcopy(x): 16 | """Deep copy operation on gyp objects such as strings, ints, dicts 17 | and lists. More than twice as fast as copy.deepcopy but much less 18 | generic.""" 19 | 20 | try: 21 | return _deepcopy_dispatch[type(x)](x) 22 | except KeyError: 23 | raise Error('Unsupported type %s for deepcopy. Use copy.deepcopy ' + 24 | 'or expand simple_copy support.' % type(x)) 25 | 26 | _deepcopy_dispatch = d = {} 27 | 28 | def _deepcopy_atomic(x): 29 | return x 30 | 31 | for x in (type(None), int, long, float, 32 | bool, str, unicode, type): 33 | d[x] = _deepcopy_atomic 34 | 35 | def _deepcopy_list(x): 36 | return [deepcopy(a) for a in x] 37 | d[list] = _deepcopy_list 38 | 39 | def _deepcopy_dict(x): 40 | y = {} 41 | for key, value in x.iteritems(): 42 | y[deepcopy(key)] = deepcopy(value) 43 | return y 44 | d[dict] = _deepcopy_dict 45 | 46 | del d 47 | -------------------------------------------------------------------------------- /tools/gyp/pylib/gyp/xml_fix.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2011 Google Inc. All rights reserved. 2 | # Use of this source code is governed by a BSD-style license that can be 3 | # found in the LICENSE file. 4 | 5 | """Applies a fix to CR LF TAB handling in xml.dom. 6 | 7 | Fixes this: http://code.google.com/p/chromium/issues/detail?id=76293 8 | Working around this: http://bugs.python.org/issue5752 9 | TODO(bradnelson): Consider dropping this when we drop XP support. 10 | """ 11 | 12 | 13 | import xml.dom.minidom 14 | 15 | 16 | def _Replacement_write_data(writer, data, is_attrib=False): 17 | """Writes datachars to writer.""" 18 | data = data.replace("&", "&").replace("<", "<") 19 | data = data.replace("\"", """).replace(">", ">") 20 | if is_attrib: 21 | data = data.replace( 22 | "\r", " ").replace( 23 | "\n", " ").replace( 24 | "\t", " ") 25 | writer.write(data) 26 | 27 | 28 | def _Replacement_writexml(self, writer, indent="", addindent="", newl=""): 29 | # indent = current indentation 30 | # addindent = indentation to add to higher levels 31 | # newl = newline string 32 | writer.write(indent+"<" + self.tagName) 33 | 34 | attrs = self._get_attributes() 35 | a_names = attrs.keys() 36 | a_names.sort() 37 | 38 | for a_name in a_names: 39 | writer.write(" %s=\"" % a_name) 40 | _Replacement_write_data(writer, attrs[a_name].value, is_attrib=True) 41 | writer.write("\"") 42 | if self.childNodes: 43 | writer.write(">%s" % newl) 44 | for node in self.childNodes: 45 | node.writexml(writer, indent + addindent, addindent, newl) 46 | writer.write("%s%s" % (indent, self.tagName, newl)) 47 | else: 48 | writer.write("/>%s" % newl) 49 | 50 | 51 | class XmlFix(object): 52 | """Object to manage temporary patching of xml.dom.minidom.""" 53 | 54 | def __init__(self): 55 | # Preserve current xml.dom.minidom functions. 56 | self.write_data = xml.dom.minidom._write_data 57 | self.writexml = xml.dom.minidom.Element.writexml 58 | # Inject replacement versions of a function and a method. 59 | xml.dom.minidom._write_data = _Replacement_write_data 60 | xml.dom.minidom.Element.writexml = _Replacement_writexml 61 | 62 | def Cleanup(self): 63 | if self.write_data: 64 | xml.dom.minidom._write_data = self.write_data 65 | xml.dom.minidom.Element.writexml = self.writexml 66 | self.write_data = None 67 | 68 | def __del__(self): 69 | self.Cleanup() 70 | -------------------------------------------------------------------------------- /tools/gyp/samples/samples: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # Copyright (c) 2009 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | import os.path 8 | import shutil 9 | import sys 10 | 11 | 12 | gyps = [ 13 | 'app/app.gyp', 14 | 'base/base.gyp', 15 | 'build/temp_gyp/googleurl.gyp', 16 | 'build/all.gyp', 17 | 'build/common.gypi', 18 | 'build/external_code.gypi', 19 | 'chrome/test/security_tests/security_tests.gyp', 20 | 'chrome/third_party/hunspell/hunspell.gyp', 21 | 'chrome/chrome.gyp', 22 | 'media/media.gyp', 23 | 'net/net.gyp', 24 | 'printing/printing.gyp', 25 | 'sdch/sdch.gyp', 26 | 'skia/skia.gyp', 27 | 'testing/gmock.gyp', 28 | 'testing/gtest.gyp', 29 | 'third_party/bzip2/bzip2.gyp', 30 | 'third_party/icu38/icu38.gyp', 31 | 'third_party/libevent/libevent.gyp', 32 | 'third_party/libjpeg/libjpeg.gyp', 33 | 'third_party/libpng/libpng.gyp', 34 | 'third_party/libxml/libxml.gyp', 35 | 'third_party/libxslt/libxslt.gyp', 36 | 'third_party/lzma_sdk/lzma_sdk.gyp', 37 | 'third_party/modp_b64/modp_b64.gyp', 38 | 'third_party/npapi/npapi.gyp', 39 | 'third_party/sqlite/sqlite.gyp', 40 | 'third_party/zlib/zlib.gyp', 41 | 'v8/tools/gyp/v8.gyp', 42 | 'webkit/activex_shim/activex_shim.gyp', 43 | 'webkit/activex_shim_dll/activex_shim_dll.gyp', 44 | 'webkit/build/action_csspropertynames.py', 45 | 'webkit/build/action_cssvaluekeywords.py', 46 | 'webkit/build/action_jsconfig.py', 47 | 'webkit/build/action_makenames.py', 48 | 'webkit/build/action_maketokenizer.py', 49 | 'webkit/build/action_useragentstylesheets.py', 50 | 'webkit/build/rule_binding.py', 51 | 'webkit/build/rule_bison.py', 52 | 'webkit/build/rule_gperf.py', 53 | 'webkit/tools/test_shell/test_shell.gyp', 54 | 'webkit/webkit.gyp', 55 | ] 56 | 57 | 58 | def Main(argv): 59 | if len(argv) != 3 or argv[1] not in ['push', 'pull']: 60 | print 'Usage: %s push/pull PATH_TO_CHROME' % argv[0] 61 | return 1 62 | 63 | path_to_chrome = argv[2] 64 | 65 | for g in gyps: 66 | chrome_file = os.path.join(path_to_chrome, g) 67 | local_file = os.path.join(os.path.dirname(argv[0]), os.path.split(g)[1]) 68 | if argv[1] == 'push': 69 | print 'Copying %s to %s' % (local_file, chrome_file) 70 | shutil.copyfile(local_file, chrome_file) 71 | elif argv[1] == 'pull': 72 | print 'Copying %s to %s' % (chrome_file, local_file) 73 | shutil.copyfile(chrome_file, local_file) 74 | else: 75 | assert False 76 | 77 | return 0 78 | 79 | 80 | if __name__ == '__main__': 81 | sys.exit(Main(sys.argv)) 82 | -------------------------------------------------------------------------------- /tools/gyp/samples/samples.bat: -------------------------------------------------------------------------------- 1 | @rem Copyright (c) 2009 Google Inc. All rights reserved. 2 | @rem Use of this source code is governed by a BSD-style license that can be 3 | @rem found in the LICENSE file. 4 | 5 | @python %~dp0/samples %* 6 | -------------------------------------------------------------------------------- /tools/gyp/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2009 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | from setuptools import setup 8 | 9 | setup( 10 | name='gyp', 11 | version='0.1', 12 | description='Generate Your Projects', 13 | author='Chromium Authors', 14 | author_email='chromium-dev@googlegroups.com', 15 | url='http://code.google.com/p/gyp', 16 | package_dir = {'': 'pylib'}, 17 | packages=['gyp', 'gyp.generator'], 18 | entry_points = {'console_scripts': ['gyp=gyp:script_main'] } 19 | ) 20 | -------------------------------------------------------------------------------- /tools/gyp/tools/README: -------------------------------------------------------------------------------- 1 | pretty_vcproj: 2 | Usage: pretty_vcproj.py "c:\path\to\vcproj.vcproj" [key1=value1] [key2=value2] 3 | 4 | They key/value pair are used to resolve vsprops name. 5 | 6 | For example, if I want to diff the base.vcproj project: 7 | 8 | pretty_vcproj.py z:\dev\src-chrome\src\base\build\base.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > orignal.txt 9 | pretty_vcproj.py z:\dev\src-chrome\src\base\base_gyp.vcproj "$(SolutionDir)=z:\dev\src-chrome\src\chrome\\" "$(CHROMIUM_BUILD)=" "$(CHROME_BUILD_TYPE)=" > gyp.txt 10 | 11 | And you can use your favorite diff tool to see the changes. 12 | 13 | Note: In the case of base.vcproj, the original vcproj is one level up the generated one. 14 | I suggest you do a search and replace for '"..\' and replace it with '"' in original.txt 15 | before you perform the diff. -------------------------------------------------------------------------------- /tools/gyp/tools/Xcode/README: -------------------------------------------------------------------------------- 1 | Specifications contains syntax formatters for Xcode 3. These do not appear to be supported yet on Xcode 4. To use these with Xcode 3 please install both the gyp.pbfilespec and gyp.xclangspec files in 2 | 3 | ~/Library/Application Support/Developer/Shared/Xcode/Specifications/ 4 | 5 | and restart Xcode. -------------------------------------------------------------------------------- /tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec: -------------------------------------------------------------------------------- 1 | /* 2 | gyp.pbfilespec 3 | GYP source file spec for Xcode 3 4 | 5 | There is not much documentation available regarding the format 6 | of .pbfilespec files. As a starting point, see for instance the 7 | outdated documentation at: 8 | http://maxao.free.fr/xcode-plugin-interface/specifications.html 9 | and the files in: 10 | /Developer/Library/PrivateFrameworks/XcodeEdit.framework/Versions/A/Resources/ 11 | 12 | Place this file in directory: 13 | ~/Library/Application Support/Developer/Shared/Xcode/Specifications/ 14 | */ 15 | 16 | ( 17 | { 18 | Identifier = sourcecode.gyp; 19 | BasedOn = sourcecode; 20 | Name = "GYP Files"; 21 | Extensions = ("gyp", "gypi"); 22 | MIMETypes = ("text/gyp"); 23 | Language = "xcode.lang.gyp"; 24 | IsTextFile = YES; 25 | IsSourceFile = YES; 26 | } 27 | ) 28 | -------------------------------------------------------------------------------- /tools/gyp/tools/emacs/README: -------------------------------------------------------------------------------- 1 | How to install gyp-mode for emacs: 2 | 3 | Add the following to your ~/.emacs (replace ... with the path to your gyp 4 | checkout). 5 | 6 | (setq load-path (cons ".../tools/emacs" load-path)) 7 | (require 'gyp) 8 | 9 | Restart emacs (or eval-region the added lines) and you should be all set. 10 | 11 | Please note that ert is required for running the tests, which is included in 12 | Emacs 24, or available separately from https://github.com/ohler/ert 13 | -------------------------------------------------------------------------------- /tools/gyp/tools/emacs/gyp-tests.el: -------------------------------------------------------------------------------- 1 | ;;; gyp-tests.el - unit tests for gyp-mode. 2 | 3 | ;; Copyright (c) 2012 Google Inc. All rights reserved. 4 | ;; Use of this source code is governed by a BSD-style license that can be 5 | ;; found in the LICENSE file. 6 | 7 | ;; The recommended way to run these tests is to run them from the command-line, 8 | ;; with the run-unit-tests.sh script. 9 | 10 | (require 'cl) 11 | (require 'ert) 12 | (require 'gyp) 13 | 14 | (defconst samples (directory-files "testdata" t ".gyp$") 15 | "List of golden samples to check") 16 | 17 | (defun fontify (filename) 18 | (with-temp-buffer 19 | (insert-file-contents-literally filename) 20 | (gyp-mode) 21 | (font-lock-fontify-buffer) 22 | (buffer-string))) 23 | 24 | (defun read-golden-sample (filename) 25 | (with-temp-buffer 26 | (insert-file-contents-literally (concat filename ".fontified")) 27 | (read (current-buffer)))) 28 | 29 | (defun equivalent-face (face) 30 | "For the purposes of face comparison, we're not interested in the 31 | differences between certain faces. For example, the difference between 32 | font-lock-comment-delimiter and font-lock-comment-face." 33 | (case face 34 | ((font-lock-comment-delimiter-face) font-lock-comment-face) 35 | (t face))) 36 | 37 | (defun text-face-properties (s) 38 | "Extract the text properties from s" 39 | (let ((result (list t))) 40 | (dotimes (i (length s)) 41 | (setq result (cons (equivalent-face (get-text-property i 'face s)) 42 | result))) 43 | (nreverse result))) 44 | 45 | (ert-deftest test-golden-samples () 46 | "Check that fontification produces the same results as the golden samples" 47 | (dolist (sample samples) 48 | (let ((golden (read-golden-sample sample)) 49 | (fontified (fontify sample))) 50 | (should (equal golden fontified)) 51 | (should (equal (text-face-properties golden) 52 | (text-face-properties fontified)))))) 53 | 54 | (defun create-golden-sample (filename) 55 | "Create a golden sample by fontifying filename and writing out the printable 56 | representation of the fontified buffer (with text properties) to the 57 | FILENAME.fontified" 58 | (with-temp-file (concat filename ".fontified") 59 | (print (fontify filename) (current-buffer)))) 60 | 61 | (defun create-golden-samples () 62 | "Recreate the golden samples" 63 | (dolist (sample samples) (create-golden-sample sample))) 64 | -------------------------------------------------------------------------------- /tools/gyp/tools/emacs/run-unit-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright (c) 2012 Google Inc. All rights reserved. 3 | # Use of this source code is governed by a BSD-style license that can be 4 | # found in the LICENSE file. 5 | emacs --no-site-file --no-init-file --batch \ 6 | --load ert.el --load gyp.el --load gyp-tests.el \ 7 | -f ert-run-tests-batch-and-exit 8 | -------------------------------------------------------------------------------- /tools/gyp/tools/graphviz.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright (c) 2011 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """Using the JSON dumped by the dump-dependency-json generator, 8 | generate input suitable for graphviz to render a dependency graph of 9 | targets.""" 10 | 11 | import collections 12 | import json 13 | import sys 14 | 15 | 16 | def ParseTarget(target): 17 | target, _, suffix = target.partition('#') 18 | filename, _, target = target.partition(':') 19 | return filename, target, suffix 20 | 21 | 22 | def LoadEdges(filename, targets): 23 | """Load the edges map from the dump file, and filter it to only 24 | show targets in |targets| and their depedendents.""" 25 | 26 | file = open('dump.json') 27 | edges = json.load(file) 28 | file.close() 29 | 30 | # Copy out only the edges we're interested in from the full edge list. 31 | target_edges = {} 32 | to_visit = targets[:] 33 | while to_visit: 34 | src = to_visit.pop() 35 | if src in target_edges: 36 | continue 37 | target_edges[src] = edges[src] 38 | to_visit.extend(edges[src]) 39 | 40 | return target_edges 41 | 42 | 43 | def WriteGraph(edges): 44 | """Print a graphviz graph to stdout. 45 | |edges| is a map of target to a list of other targets it depends on.""" 46 | 47 | # Bucket targets by file. 48 | files = collections.defaultdict(list) 49 | for src, dst in edges.items(): 50 | build_file, target_name, toolset = ParseTarget(src) 51 | files[build_file].append(src) 52 | 53 | print 'digraph D {' 54 | print ' fontsize=8' # Used by subgraphs. 55 | print ' node [fontsize=8]' 56 | 57 | # Output nodes by file. We must first write out each node within 58 | # its file grouping before writing out any edges that may refer 59 | # to those nodes. 60 | for filename, targets in files.items(): 61 | if len(targets) == 1: 62 | # If there's only one node for this file, simplify 63 | # the display by making it a box without an internal node. 64 | target = targets[0] 65 | build_file, target_name, toolset = ParseTarget(target) 66 | print ' "%s" [shape=box, label="%s\\n%s"]' % (target, filename, 67 | target_name) 68 | else: 69 | # Group multiple nodes together in a subgraph. 70 | print ' subgraph "cluster_%s" {' % filename 71 | print ' label = "%s"' % filename 72 | for target in targets: 73 | build_file, target_name, toolset = ParseTarget(target) 74 | print ' "%s" [label="%s"]' % (target, target_name) 75 | print ' }' 76 | 77 | # Now that we've placed all the nodes within subgraphs, output all 78 | # the edges between nodes. 79 | for src, dsts in edges.items(): 80 | for dst in dsts: 81 | print ' "%s" -> "%s"' % (src, dst) 82 | 83 | print '}' 84 | 85 | 86 | def main(): 87 | if len(sys.argv) < 2: 88 | print >>sys.stderr, __doc__ 89 | print >>sys.stderr 90 | print >>sys.stderr, 'usage: %s target1 target2...' % (sys.argv[0]) 91 | return 1 92 | 93 | edges = LoadEdges('dump.json', sys.argv[1:]) 94 | 95 | WriteGraph(edges) 96 | return 0 97 | 98 | 99 | if __name__ == '__main__': 100 | sys.exit(main()) 101 | --------------------------------------------------------------------------------