redsnow 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +34 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +20 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +21 -0
  8. data/README.md +62 -0
  9. data/Rakefile +36 -0
  10. data/Vagrantfile +20 -0
  11. data/ext/snowcrash/Makefile +64 -0
  12. data/ext/snowcrash/Vagrantfile +20 -0
  13. data/ext/snowcrash/bin/snowcrash +0 -0
  14. data/ext/snowcrash/common.gypi +163 -0
  15. data/ext/snowcrash/config.gypi +10 -0
  16. data/ext/snowcrash/config.mk +5 -0
  17. data/ext/snowcrash/configure +213 -0
  18. data/ext/snowcrash/provisioning.sh +15 -0
  19. data/ext/snowcrash/snowcrash.gyp +141 -0
  20. data/ext/snowcrash/src/ActionParser.h +503 -0
  21. data/ext/snowcrash/src/AssetParser.h +215 -0
  22. data/ext/snowcrash/src/BlockUtility.h +186 -0
  23. data/ext/snowcrash/src/Blueprint.h +283 -0
  24. data/ext/snowcrash/src/BlueprintParser.h +347 -0
  25. data/ext/snowcrash/src/BlueprintParserCore.h +190 -0
  26. data/ext/snowcrash/src/BlueprintSection.h +140 -0
  27. data/ext/snowcrash/src/BlueprintUtility.h +126 -0
  28. data/ext/snowcrash/src/CBlueprint.cc +600 -0
  29. data/ext/snowcrash/src/CBlueprint.h +354 -0
  30. data/ext/snowcrash/src/CSourceAnnotation.cc +140 -0
  31. data/ext/snowcrash/src/CSourceAnnotation.h +106 -0
  32. data/ext/snowcrash/src/CodeBlockUtility.h +189 -0
  33. data/ext/snowcrash/src/DescriptionSectionUtility.h +156 -0
  34. data/ext/snowcrash/src/HTTP.cc +46 -0
  35. data/ext/snowcrash/src/HTTP.h +105 -0
  36. data/ext/snowcrash/src/HeaderParser.h +289 -0
  37. data/ext/snowcrash/src/ListBlockUtility.h +273 -0
  38. data/ext/snowcrash/src/ListUtility.h +95 -0
  39. data/ext/snowcrash/src/MarkdownBlock.cc +176 -0
  40. data/ext/snowcrash/src/MarkdownBlock.h +93 -0
  41. data/ext/snowcrash/src/MarkdownParser.cc +266 -0
  42. data/ext/snowcrash/src/MarkdownParser.h +88 -0
  43. data/ext/snowcrash/src/ParameterDefinitonParser.h +570 -0
  44. data/ext/snowcrash/src/ParametersParser.h +252 -0
  45. data/ext/snowcrash/src/Parser.cc +71 -0
  46. data/ext/snowcrash/src/Parser.h +29 -0
  47. data/ext/snowcrash/src/ParserCore.cc +120 -0
  48. data/ext/snowcrash/src/ParserCore.h +82 -0
  49. data/ext/snowcrash/src/PayloadParser.h +672 -0
  50. data/ext/snowcrash/src/Platform.h +54 -0
  51. data/ext/snowcrash/src/RegexMatch.h +32 -0
  52. data/ext/snowcrash/src/ResourceGroupParser.h +195 -0
  53. data/ext/snowcrash/src/ResourceParser.h +584 -0
  54. data/ext/snowcrash/src/SectionUtility.h +142 -0
  55. data/ext/snowcrash/src/Serialize.cc +52 -0
  56. data/ext/snowcrash/src/Serialize.h +69 -0
  57. data/ext/snowcrash/src/SerializeJSON.cc +601 -0
  58. data/ext/snowcrash/src/SerializeJSON.h +21 -0
  59. data/ext/snowcrash/src/SerializeYAML.cc +336 -0
  60. data/ext/snowcrash/src/SerializeYAML.h +21 -0
  61. data/ext/snowcrash/src/SourceAnnotation.h +177 -0
  62. data/ext/snowcrash/src/StringUtility.h +109 -0
  63. data/ext/snowcrash/src/SymbolTable.h +83 -0
  64. data/ext/snowcrash/src/UriTemplateParser.cc +195 -0
  65. data/ext/snowcrash/src/UriTemplateParser.h +243 -0
  66. data/ext/snowcrash/src/Version.h +39 -0
  67. data/ext/snowcrash/src/csnowcrash.cc +23 -0
  68. data/ext/snowcrash/src/csnowcrash.h +38 -0
  69. data/ext/snowcrash/src/posix/RegexMatch.cc +99 -0
  70. data/ext/snowcrash/src/snowcrash.cc +18 -0
  71. data/ext/snowcrash/src/snowcrash.h +41 -0
  72. data/ext/snowcrash/src/snowcrash/snowcrash.cc +170 -0
  73. data/ext/snowcrash/src/win/RegexMatch.cc +78 -0
  74. data/ext/snowcrash/sundown/CONTRIBUTING.md +10 -0
  75. data/ext/snowcrash/sundown/Makefile +83 -0
  76. data/ext/snowcrash/sundown/Makefile.win +33 -0
  77. data/ext/snowcrash/sundown/examples/smartypants.c +72 -0
  78. data/ext/snowcrash/sundown/examples/sundown.c +80 -0
  79. data/ext/snowcrash/sundown/html/houdini.h +37 -0
  80. data/ext/snowcrash/sundown/html/houdini_href_e.c +108 -0
  81. data/ext/snowcrash/sundown/html/houdini_html_e.c +84 -0
  82. data/ext/snowcrash/sundown/html/html.c +647 -0
  83. data/ext/snowcrash/sundown/html/html.h +77 -0
  84. data/ext/snowcrash/sundown/html/html_smartypants.c +389 -0
  85. data/ext/snowcrash/sundown/html_block_names.txt +25 -0
  86. data/ext/snowcrash/sundown/src/autolink.c +297 -0
  87. data/ext/snowcrash/sundown/src/autolink.h +51 -0
  88. data/ext/snowcrash/sundown/src/buffer.c +225 -0
  89. data/ext/snowcrash/sundown/src/buffer.h +96 -0
  90. data/ext/snowcrash/sundown/src/html_blocks.h +206 -0
  91. data/ext/snowcrash/sundown/src/markdown.c +2701 -0
  92. data/ext/snowcrash/sundown/src/markdown.h +147 -0
  93. data/ext/snowcrash/sundown/src/src_map.c +200 -0
  94. data/ext/snowcrash/sundown/src/src_map.h +58 -0
  95. data/ext/snowcrash/sundown/src/stack.c +81 -0
  96. data/ext/snowcrash/sundown/src/stack.h +29 -0
  97. data/ext/snowcrash/sundown/sundown.def +20 -0
  98. data/ext/snowcrash/tools/gyp/AUTHORS +11 -0
  99. data/ext/snowcrash/tools/gyp/DEPS +24 -0
  100. data/ext/snowcrash/tools/gyp/OWNERS +1 -0
  101. data/ext/snowcrash/tools/gyp/PRESUBMIT.py +120 -0
  102. data/ext/snowcrash/tools/gyp/buildbot/buildbot_run.py +190 -0
  103. data/ext/snowcrash/tools/gyp/codereview.settings +10 -0
  104. data/ext/snowcrash/tools/gyp/data/win/large-pdb-shim.cc +12 -0
  105. data/ext/snowcrash/tools/gyp/gyp +8 -0
  106. data/ext/snowcrash/tools/gyp/gyp.bat +5 -0
  107. data/ext/snowcrash/tools/gyp/gyp_main.py +18 -0
  108. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSNew.py +340 -0
  109. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSProject.py +208 -0
  110. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSSettings.py +1063 -0
  111. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSToolFile.py +58 -0
  112. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUserFile.py +147 -0
  113. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.py +267 -0
  114. data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.py +409 -0
  115. data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.py +537 -0
  116. data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.pyc +0 -0
  117. data/ext/snowcrash/tools/gyp/pylib/gyp/common.py +521 -0
  118. data/ext/snowcrash/tools/gyp/pylib/gyp/common.pyc +0 -0
  119. data/ext/snowcrash/tools/gyp/pylib/gyp/easy_xml.py +157 -0
  120. data/ext/snowcrash/tools/gyp/pylib/gyp/flock_tool.py +49 -0
  121. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.py +0 -0
  122. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.pyc +0 -0
  123. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/android.py +1069 -0
  124. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/cmake.py +1143 -0
  125. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +81 -0
  126. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/eclipse.py +335 -0
  127. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypd.py +87 -0
  128. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypsh.py +56 -0
  129. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.py +2181 -0
  130. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.pyc +0 -0
  131. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/msvs.py +3335 -0
  132. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.py +2156 -0
  133. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.py +1224 -0
  134. data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.pyc +0 -0
  135. data/ext/snowcrash/tools/gyp/pylib/gyp/input.py +2809 -0
  136. data/ext/snowcrash/tools/gyp/pylib/gyp/input.pyc +0 -0
  137. data/ext/snowcrash/tools/gyp/pylib/gyp/mac_tool.py +510 -0
  138. data/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.py +972 -0
  139. data/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.py +160 -0
  140. data/ext/snowcrash/tools/gyp/pylib/gyp/ordered_dict.py +289 -0
  141. data/ext/snowcrash/tools/gyp/pylib/gyp/win_tool.py +292 -0
  142. data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.py +1440 -0
  143. data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.pyc +0 -0
  144. data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.py +2889 -0
  145. data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.pyc +0 -0
  146. data/ext/snowcrash/tools/gyp/pylib/gyp/xml_fix.py +69 -0
  147. data/ext/snowcrash/tools/gyp/pylintrc +307 -0
  148. data/ext/snowcrash/tools/gyp/samples/samples +81 -0
  149. data/ext/snowcrash/tools/gyp/samples/samples.bat +5 -0
  150. data/ext/snowcrash/tools/gyp/setup.py +19 -0
  151. data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec +27 -0
  152. data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.xclangspec +226 -0
  153. data/ext/snowcrash/tools/gyp/tools/emacs/gyp.el +252 -0
  154. data/ext/snowcrash/tools/gyp/tools/graphviz.py +100 -0
  155. data/ext/snowcrash/tools/gyp/tools/pretty_gyp.py +155 -0
  156. data/ext/snowcrash/tools/gyp/tools/pretty_sln.py +168 -0
  157. data/ext/snowcrash/tools/gyp/tools/pretty_vcproj.py +329 -0
  158. data/ext/snowcrash/tools/homebrew/snowcrash.rb +11 -0
  159. data/ext/snowcrash/vcbuild.bat +184 -0
  160. data/lib/redsnow.rb +31 -0
  161. data/lib/redsnow/binding.rb +132 -0
  162. data/lib/redsnow/blueprint.rb +365 -0
  163. data/lib/redsnow/object.rb +18 -0
  164. data/lib/redsnow/parseresult.rb +107 -0
  165. data/lib/redsnow/version.rb +4 -0
  166. data/provisioning.sh +20 -0
  167. data/redsnow.gemspec +35 -0
  168. data/test/_helper.rb +15 -0
  169. data/test/fixtures/sample-api-ast.json +97 -0
  170. data/test/fixtures/sample-api.apib +20 -0
  171. data/test/redsnow_binding_test.rb +35 -0
  172. data/test/redsnow_parseresult_test.rb +50 -0
  173. data/test/redsnow_test.rb +285 -0
  174. metadata +358 -0
@@ -0,0 +1,10 @@
1
+ # Do not edit. Generated by the configure script.
2
+ { 'target_defaults': { 'cflags': [],
3
+ 'default_configuration': 'Release',
4
+ 'defines': [],
5
+ 'include_dirs': [],
6
+ 'libraries': []},
7
+ 'variables': { 'host_arch': 'x64',
8
+ 'libsnowcrash_type': 'shared_library',
9
+ 'python': '/usr/bin/python',
10
+ 'target_arch': 'x64'}}
@@ -0,0 +1,5 @@
1
+ # Do not edit. Generated by the configure script.
2
+ PYTHON=/usr/bin/python
3
+ BUILDTYPE=Release
4
+ BUILD_DIR=./build
5
+ INTEGRATION_TESTS=
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env python
2
+ #
3
+ # Attribution Notice
4
+ # ------------------
5
+ # This file uses parts of Node.js `configure`.
6
+ # Please refer to https://github.com/joyent/node.
7
+ #
8
+ import optparse
9
+ import os
10
+ import pprint
11
+ import re
12
+ import shlex
13
+ import subprocess
14
+ import sys
15
+ import platform
16
+
17
+ CC = os.environ.get('CC', 'cc')
18
+
19
+ root_dir = os.path.dirname(__file__)
20
+ build_dir = './build'
21
+
22
+ # Parse options
23
+ parser = optparse.OptionParser()
24
+ parser.add_option("--debug",
25
+ action="store_true",
26
+ dest="debug",
27
+ help="Also build the debug build.")
28
+
29
+ parser.add_option("--dest-cpu",
30
+ action="store",
31
+ dest="dest_cpu",
32
+ help="CPU architecture to build for. Valid values are: ia32, x64.")
33
+
34
+ parser.add_option("--shared",
35
+ action="store_true",
36
+ dest="shared",
37
+ help="Build and use shared libsnowcrash instead of static one.")
38
+
39
+ parser.add_option("--include-integration-tests",
40
+ action="store_true",
41
+ dest="include_integration_tests",
42
+ help="Configure for integration testing using Cucumber")
43
+
44
+ (options, args) = parser.parse_args()
45
+
46
+ def write(filename, data):
47
+ filename = os.path.join(root_dir, filename)
48
+ print "creating ", filename
49
+ f = open(filename, 'w+')
50
+ f.write(data)
51
+
52
+ def cc_macros():
53
+ """Checks predefined macros using the CC command."""
54
+
55
+ try:
56
+ p = subprocess.Popen(shlex.split(CC) + ['-dM', '-E', '-'],
57
+ stdin=subprocess.PIPE,
58
+ stdout=subprocess.PIPE,
59
+ stderr=subprocess.PIPE)
60
+ except OSError:
61
+ print '''Configure error: No acceptable C compiler found!
62
+
63
+ Please make sure you have a C compiler installed on your system and/or
64
+ consider adjusting the CC environment variable if you installed
65
+ it in a non-standard prefix.
66
+ '''
67
+ sys.exit()
68
+
69
+ p.stdin.write('\n')
70
+ out = p.communicate()[0]
71
+
72
+ out = str(out).split('\n')
73
+
74
+ k = {}
75
+ for line in out:
76
+ lst = shlex.split(line)
77
+ if len(lst) > 2:
78
+ key = lst[1]
79
+ val = lst[2]
80
+ k[key] = val
81
+ return k
82
+
83
+ def host_arch_cc():
84
+ """Host architecture check using the CC command."""
85
+
86
+ k = cc_macros()
87
+
88
+ matchup = {
89
+ '__x86_64__' : 'x64',
90
+ '__i386__' : 'ia32',
91
+ '__arm__' : 'arm',
92
+ }
93
+
94
+ rtn = 'ia32' # default
95
+
96
+ for i in matchup:
97
+ if i in k and k[i] != '0':
98
+ rtn = matchup[i]
99
+ break
100
+
101
+ return rtn
102
+
103
+ def host_arch_win():
104
+ """Host architecture check using environ vars (better way to do this?)"""
105
+
106
+ arch = os.environ.get('PROCESSOR_ARCHITECTURE', 'x86')
107
+
108
+ matchup = {
109
+ 'AMD64' : 'x64',
110
+ 'x86' : 'ia32',
111
+ 'arm' : 'arm',
112
+ }
113
+
114
+ return matchup.get(arch, 'ia32')
115
+
116
+ def configure_snowcrash(o):
117
+ # Build configuration
118
+ o['default_configuration'] = 'Debug' if options.debug else 'Release'
119
+
120
+ # Architecture
121
+ host_arch = host_arch_win() if os.name == 'nt' else host_arch_cc()
122
+ target_arch = options.dest_cpu or host_arch
123
+ o['variables']['host_arch'] = host_arch
124
+ o['variables']['target_arch'] = target_arch
125
+ o['variables']['libsnowcrash_type'] = 'shared_library' if options.shared else 'static_library'
126
+
127
+ #
128
+ # Cucumber testing environment
129
+ #
130
+ # TODO: use bundle check
131
+ if options.include_integration_tests:
132
+ print "Installing dependencies for integration tests..."
133
+ try:
134
+ if sys.platform == 'win32':
135
+ subprocess.call(["bundle.bat", "install"])
136
+ else:
137
+ subprocess.call(["bundle", "install"])
138
+ except OSError as e:
139
+ if e.errno == os.errno.ENOENT:
140
+ raise RuntimeError(
141
+ "Integration test dependecies rely on Ruby Bundler "
142
+ "but it cannot be find in the system $PATH. "
143
+ "Please make sure to install Bundler or/and to "
144
+ "add it to the $PATH."
145
+ )
146
+ else:
147
+ raise
148
+
149
+ #
150
+ # config.gypi
151
+ #
152
+ output = {
153
+ 'variables': { 'python': sys.executable },
154
+ 'include_dirs': [],
155
+ 'libraries': [],
156
+ 'defines': [],
157
+ 'cflags': [],
158
+ }
159
+
160
+ configure_snowcrash(output)
161
+
162
+ # variables should be a root level element,
163
+ # move everything else to target_defaults
164
+ variables = output['variables']
165
+ del output['variables']
166
+ output = {
167
+ 'variables': variables,
168
+ 'target_defaults': output
169
+ }
170
+
171
+ write('config.gypi', "# Do not edit. Generated by the configure script.\n" +
172
+ pprint.pformat(output, indent=2) + "\n")
173
+
174
+ #
175
+ # config.mk
176
+ #
177
+ config = {
178
+ 'BUILDTYPE': 'Debug' if options.debug else 'Release',
179
+ 'PYTHON': sys.executable,
180
+ 'BUILD_DIR': build_dir,
181
+ 'INTEGRATION_TESTS': '1' if options.include_integration_tests else ''
182
+ }
183
+ config = '\n'.join(map('='.join, config.iteritems())) + '\n'
184
+
185
+ write('config.mk',
186
+ '# Do not edit. Generated by the configure script.\n' + config)
187
+
188
+ #
189
+ # Gyp call
190
+ #
191
+ print "creating makefiles"
192
+
193
+ gyp_args = ['--generator-output', build_dir, '--depth', '.']
194
+ if sys.platform == 'win32':
195
+ # Windows
196
+ gyp_args.extend(['-f', 'msvs'])
197
+ else:
198
+ # Posix systems
199
+ gyp_args.extend(['-f', 'make'])
200
+
201
+ # Include common.gypi and config.gypi
202
+ if sys.platform == 'win32':
203
+ options_fn = os.path.join(root_dir, 'config.gypi')
204
+ else:
205
+ options_fn = os.path.join(os.path.abspath(root_dir), 'config.gypi')
206
+
207
+ if os.path.exists(options_fn):
208
+ gyp_args.extend(['-I', options_fn])
209
+
210
+ subprocess.call([sys.executable, 'tools/gyp/gyp_main.py'] + gyp_args)
211
+
212
+ # All done
213
+ print "All OK."
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ # GCC 4.7
4
+ sudo apt-get update -y
5
+ sudo apt-get install -y python-software-properties
6
+ sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
7
+ sudo apt-get update -y
8
+ sudo apt-get install -y gcc-4.7 g++-4.7 gdb build-essential git-core
9
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-4.6
10
+ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-4.7
11
+
12
+ #sudo update-alternatives --config gcc
13
+
14
+ # Ruby
15
+ gem install bundler
@@ -0,0 +1,141 @@
1
+ {
2
+ "includes": [
3
+ "common.gypi"
4
+ ],
5
+
6
+ 'targets' : [
7
+ {
8
+ 'target_name': 'sundown',
9
+ 'type': 'static_library',
10
+ 'include_dirs': [
11
+ 'sundown/src',
12
+ 'sundown/html',
13
+ ],
14
+ 'sources': [
15
+ 'sundown/src/autolink.c',
16
+ 'sundown/src/buffer.c',
17
+ 'sundown/html/houdini_href_e.c',
18
+ 'sundown/html/houdini_html_e.c',
19
+ 'sundown/html/html.c',
20
+ 'sundown/html/html_smartypants.c',
21
+ 'sundown/src/markdown.c',
22
+ 'sundown/src/stack.c',
23
+ 'sundown/src/src_map.c'
24
+ ]
25
+ },
26
+ {
27
+ 'target_name': 'libsnowcrash',
28
+ 'type': '<(libsnowcrash_type)',
29
+ 'include_dirs': [
30
+ 'src',
31
+ 'sundown/src',
32
+ 'sundown/src/html'
33
+ ],
34
+ 'sources': [
35
+ 'src/HTTP.cc',
36
+ 'src/MarkdownBlock.cc',
37
+ 'src/MarkdownParser.cc',
38
+ 'src/Parser.cc',
39
+ 'src/ParserCore.cc',
40
+ 'src/RegexMatch.h',
41
+ 'src/Serialize.cc',
42
+ 'src/Serialize.h',
43
+ 'src/SerializeJSON.cc',
44
+ 'src/SerializeYAML.cc',
45
+ 'src/UriTemplateParser.cc',
46
+ 'src/snowcrash.cc',
47
+ 'src/csnowcrash.cc',
48
+ 'src/CBlueprint.cc',
49
+ 'src/CSourceAnnotation.cc'
50
+ ],
51
+ 'conditions': [
52
+ [ 'OS=="win"',
53
+ { 'sources': [ 'src/win/RegexMatch.cc' ] },
54
+ { 'sources': [ 'src/posix/RegexMatch.cc' ] } # OS != Windows
55
+ ]
56
+ ],
57
+ 'dependencies': [
58
+ 'sundown'
59
+ ]
60
+ },
61
+ {
62
+ 'target_name': 'test-libsnowcrash',
63
+ 'type': 'executable',
64
+ 'include_dirs': [
65
+ 'src',
66
+ 'test',
67
+ 'test/vendor/Catch/include',
68
+ 'sundown/src',
69
+ 'sundown/src/html'
70
+ ],
71
+ 'sources': [
72
+ 'test/test-ActionParser.cc',
73
+ 'test/test-AssetParser.cc',
74
+ 'test/test-Blueprint.cc',
75
+ 'test/test-BlueprintParser.cc',
76
+ 'test/test-HeaderParser.cc',
77
+ 'test/test-Indentation.cc',
78
+ 'test/test-ListUtility.cc',
79
+ 'test/test-MarkdownBlock.cc',
80
+ 'test/test-MarkdownParser.cc',
81
+ 'test/test-ParameterDefinitonParser.cc',
82
+ 'test/test-ParametersParser.cc',
83
+ 'test/test-Parser.cc',
84
+ 'test/test-PayloadParser.cc',
85
+ 'test/test-RegexMatch.cc',
86
+ 'test/test-ResouceGroupParser.cc',
87
+ 'test/test-ResourceParser.cc',
88
+ 'test/test-SymbolIdentifier.cc',
89
+ 'test/test-SymbolTable.cc',
90
+ 'test/test-Warnings.cc',
91
+ 'test/test-csnowcrash.cc',
92
+ 'test/test-UriTemplateParser.cc',
93
+ 'test/test-snowcrash.cc'
94
+ ],
95
+ 'dependencies': [
96
+ 'libsnowcrash',
97
+ 'sundown'
98
+ ]
99
+ },
100
+
101
+ {
102
+ 'target_name': 'snowcrash',
103
+ 'type': 'executable',
104
+ 'include_dirs': [
105
+ 'src',
106
+ 'src/snowcrash',
107
+ 'cmdline'
108
+ ],
109
+ 'sources': [
110
+ 'src/snowcrash/snowcrash.cc'
111
+ ],
112
+ 'dependencies': [
113
+ 'libsnowcrash',
114
+ 'sundown'
115
+ ]
116
+ }
117
+ ],
118
+ 'conditions': [
119
+ ['OS in "mac linux"', {
120
+ 'targets': [
121
+ {
122
+ 'target_name': 'perf-libsnowcrash',
123
+ 'type': 'executable',
124
+ 'include_dirs': [
125
+ 'src',
126
+ 'cmdline',
127
+ 'test',
128
+ 'test/performance',
129
+ ],
130
+ 'sources': [
131
+ 'test/performance/perf-snowcrash.cc'
132
+ ],
133
+ 'dependencies': [
134
+ 'libsnowcrash',
135
+ 'sundown'
136
+ ]
137
+ }
138
+ ]
139
+ }]
140
+ ]
141
+ }
@@ -0,0 +1,503 @@
1
+ //
2
+ // ActionParser.h
3
+ // snowcrash
4
+ //
5
+ // Created by Zdenek Nemec on 5/4/13.
6
+ // Copyright (c) 2013 Apiary Inc. All rights reserved.
7
+ //
8
+
9
+ #ifndef SNOWCRASH_ACTIONPARSER_H
10
+ #define SNOWCRASH_ACTIONPARSER_H
11
+
12
+ #include "BlueprintParserCore.h"
13
+ #include "Blueprint.h"
14
+ #include "RegexMatch.h"
15
+ #include "PayloadParser.h"
16
+ #include "HeaderParser.h"
17
+ #include "ParametersParser.h"
18
+ #include "HTTP.h"
19
+ #include "DescriptionSectionUtility.h"
20
+
21
+ namespace snowcrashconst {
22
+
23
+ /** Nameless action matching regex */
24
+ const char* const ActionHeaderRegex = "^[[:blank:]]*" HTTP_REQUEST_METHOD "[[:blank:]]*" URI_TEMPLATE "?$";
25
+
26
+ /** Named action matching regex */
27
+ const char* const NamedActionHeaderRegex = "^[[:blank:]]*" SYMBOL_IDENTIFIER "\\[" HTTP_REQUEST_METHOD "]$";
28
+ }
29
+
30
+ namespace snowcrash {
31
+
32
+ // Method signature
33
+ enum ActionSignature {
34
+ UndefinedActionSignature,
35
+ NoActionSignature,
36
+ MethodActionSignature, // # GET
37
+ MethodURIActionSignature, // # GET /uri
38
+ NamedActionSignature // # My Method [GET]
39
+ };
40
+
41
+ // Query method signature
42
+ FORCEINLINE ActionSignature GetActionSignature(const MarkdownBlock& block,
43
+ Name& name,
44
+ HTTPMethod& method) {
45
+ if (block.type != HeaderBlockType ||
46
+ block.content.empty())
47
+ return NoActionSignature;
48
+
49
+ CaptureGroups captureGroups;
50
+ if (RegexCapture(block.content, snowcrashconst::ActionHeaderRegex, captureGroups, 3)) {
51
+ // Nameless action
52
+ method = captureGroups[1];
53
+ URITemplate uri = captureGroups[2];
54
+ return (uri.empty()) ? MethodActionSignature : MethodURIActionSignature;
55
+ }
56
+ else if (RegexCapture(block.content, snowcrashconst::NamedActionHeaderRegex, captureGroups, 3)) {
57
+ // Named action
58
+ name = captureGroups[1];
59
+ TrimString(name);
60
+ method = captureGroups[2];
61
+ return NamedActionSignature;
62
+ }
63
+
64
+ return NoActionSignature;
65
+ }
66
+
67
+ // Returns true if block has HTTP Method signature, false otherwise
68
+ FORCEINLINE bool HasActionSignature(const MarkdownBlock& block) {
69
+
70
+ if (block.type != HeaderBlockType ||
71
+ block.content.empty())
72
+ return false;
73
+
74
+ Name name;
75
+ HTTPMethod method;
76
+ return GetActionSignature(block, name, method) != NoActionSignature;
77
+ }
78
+
79
+ // Finds an action inside resource
80
+ FORCEINLINE Collection<Action>::iterator FindAction(Resource& resource,
81
+ const Action& action) {
82
+ return std::find_if(resource.actions.begin(),
83
+ resource.actions.end(),
84
+ std::bind2nd(MatchAction<Action>(), action));
85
+ }
86
+
87
+ //
88
+ // Classifier of internal list items, Payload context
89
+ //
90
+ template <>
91
+ FORCEINLINE SectionType ClassifyInternaListBlock<Action>(const BlockIterator& begin,
92
+ const BlockIterator& end) {
93
+ if (HasHeaderSignature(begin, end))
94
+ return HeadersSectionType;
95
+
96
+ if (HasParametersSignature(begin, end))
97
+ return ParametersSectionType;
98
+
99
+ Name name;
100
+ SourceData mediaType;
101
+ PayloadSignature payload = GetPayloadSignature(begin, end, name, mediaType);
102
+ if (payload == RequestPayloadSignature)
103
+ return RequestSectionType;
104
+ else if (payload == ResponsePayloadSignature)
105
+ return ResponseSectionType;
106
+ else if (payload == ObjectPayloadSignature)
107
+ return ObjectSectionType;
108
+ else if (payload == ModelPayloadSignature)
109
+ return ModelSectionType;
110
+
111
+ return UndefinedSectionType;
112
+ }
113
+
114
+ /** Children blocks classifier */
115
+ template <>
116
+ FORCEINLINE SectionType ClassifyChildrenListBlock<Action>(const BlockIterator& begin,
117
+ const BlockIterator& end) {
118
+
119
+ SectionType type = ClassifyInternaListBlock<Action>(begin, end);
120
+ if (type != UndefinedSectionType)
121
+ return type;
122
+
123
+ type = ClassifyChildrenListBlock<HeaderCollection>(begin, end);
124
+ if (type != UndefinedSectionType)
125
+ return type;
126
+
127
+ type = ClassifyChildrenListBlock<ParameterCollection>(begin, end);
128
+ if (type != UndefinedSectionType)
129
+ return type;
130
+
131
+ type = ClassifyChildrenListBlock<Payload>(begin, end);
132
+ if (type != UndefinedSectionType)
133
+ return type;
134
+
135
+ return UndefinedSectionType;
136
+ }
137
+
138
+ //
139
+ // Block Classifier, Action Context
140
+ //
141
+ template <>
142
+ FORCEINLINE SectionType ClassifyBlock<Action>(const BlockIterator& begin,
143
+ const BlockIterator& end,
144
+ const SectionType& context) {
145
+
146
+ if (HasActionSignature(*begin))
147
+ return (context == UndefinedSectionType) ? ActionSectionType : UndefinedSectionType;
148
+
149
+ if (HasResourceSignature(*begin) ||
150
+ HasResourceGroupSignature(*begin))
151
+ return UndefinedSectionType;
152
+
153
+ SectionType listSection = ClassifyInternaListBlock<Action>(begin, end);
154
+ if (listSection != UndefinedSectionType)
155
+ return listSection;
156
+
157
+ // Unrecognized list item at this level
158
+ if (begin->type == ListItemBlockBeginType)
159
+ return ForeignSectionType;
160
+
161
+ return (context == ActionSectionType) ? ActionSectionType : UndefinedSectionType;
162
+ }
163
+
164
+ //
165
+ // Action SectionType Parser
166
+ //
167
+ template<>
168
+ struct SectionParser<Action> {
169
+
170
+ static ParseSectionResult ParseSection(const BlueprintSection& section,
171
+ const BlockIterator& cur,
172
+ BlueprintParserCore& parser,
173
+ Action& action) {
174
+
175
+ ParseSectionResult result = std::make_pair(Result(), cur);
176
+
177
+ switch (section.type) {
178
+ case ActionSectionType:
179
+ result = HandleActionDescriptionBlock(section, cur, parser, action);
180
+ break;
181
+
182
+ case ParametersSectionType:
183
+ result = HandleParameters(section, cur, parser, action);
184
+ break;
185
+
186
+ case HeadersSectionType:
187
+ result = HandleDeprecatedHeaders(section, cur, parser, action);
188
+ break;
189
+
190
+ case RequestSectionType:
191
+ case ResponseSectionType:
192
+ result = HandlePayload(section, cur, parser, action);
193
+ break;
194
+
195
+ case ForeignSectionType:
196
+ result = HandleForeignSection<Action>(section, cur, parser.sourceData);
197
+ break;
198
+
199
+ case UndefinedSectionType:
200
+ result.second = CloseList(cur, section.bounds.second);
201
+ break;
202
+
203
+ case ModelSectionType:
204
+ case ObjectSectionType:
205
+ {
206
+ // ERR: Unexpected model definition
207
+ SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur,
208
+ section.bounds.second,
209
+ section.bounds,
210
+ parser.sourceData);
211
+ result.first.error = Error("unexpected model definiton, a model can be only defined in the resource section",
212
+ SymbolError,
213
+ sourceBlock);
214
+ }
215
+ break;
216
+
217
+ default:
218
+ result.first.error = UnexpectedBlockError(section, cur, parser.sourceData);
219
+ break;
220
+ }
221
+
222
+ return result;
223
+ }
224
+
225
+ static void Finalize(const SectionBounds& bounds,
226
+ BlueprintParserCore& parser,
227
+ Action& action,
228
+ Result& result)
229
+ {
230
+ // Consolidate deprecated headers into subsequent payloads
231
+ if (!action.headers.empty()) {
232
+ InjectDeprecatedHeaders(action.headers, action.examples);
233
+ action.headers.clear();
234
+ }
235
+
236
+ // Check whether transaction example request is followed by a response
237
+ if (action.examples.size() > 1 &&
238
+ !action.examples.back().requests.empty() &&
239
+ action.examples.back().responses.empty()) {
240
+ // WARN: No response for request
241
+
242
+ std::stringstream ss;
243
+ ss << "action is missing a response for ";
244
+ if (action.examples.back().requests.back().name.empty())
245
+ ss << "a request";
246
+ else
247
+ ss << "the '" << action.examples.back().requests.back().name << "' request";
248
+
249
+ SourceCharactersBlock sourceBlock = CharacterMapForBlock(bounds.first, bounds.first, bounds, parser.sourceData);
250
+ result.warnings.push_back(Warning(ss.str(),
251
+ EmptyDefinitionWarning,
252
+ sourceBlock));
253
+ }
254
+ }
255
+
256
+ static ParseSectionResult HandleActionDescriptionBlock(const BlueprintSection& section,
257
+ const BlockIterator& cur,
258
+ BlueprintParserCore& parser,
259
+ Action& action) {
260
+
261
+ ParseSectionResult result = std::make_pair(Result(), cur);
262
+ BlockIterator sectionCur(cur);
263
+
264
+ if (cur->type == HeaderBlockType &&
265
+ cur == section.bounds.first) {
266
+
267
+ GetActionSignature(*cur, action.name, action.method);
268
+ result.second = ++sectionCur;
269
+ return result;
270
+ }
271
+
272
+ result = ParseDescriptionBlock<Action>(section,
273
+ sectionCur,
274
+ parser.sourceData,
275
+ action);
276
+ return result;
277
+ }
278
+
279
+ /** Parse Parameters section */
280
+ static ParseSectionResult HandleParameters(const BlueprintSection& section,
281
+ const BlockIterator& cur,
282
+ BlueprintParserCore& parser,
283
+ Action& action) {
284
+ ParameterCollection parameters;
285
+ ParseSectionResult result = ParametersParser::Parse(cur,
286
+ section.bounds.second,
287
+ section,
288
+ parser,
289
+ parameters);
290
+ if (result.first.error.code != Error::OK)
291
+ return result;
292
+
293
+ if (parameters.empty()) {
294
+ BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
295
+ SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, parser.sourceData);
296
+ result.first.warnings.push_back(Warning(snowcrashconst::NoParametersMessage,
297
+ FormattingWarning,
298
+ sourceBlock));
299
+ }
300
+ else {
301
+ action.parameters.insert(action.parameters.end(), parameters.begin(), parameters.end());
302
+ }
303
+
304
+ return result;
305
+ }
306
+
307
+ /**
308
+ * \brief Parse action payload
309
+ * \param section Actual section being parsed.
310
+ * \param cur Cursor within the section boundaries.
311
+ * \param parser A parser's instance.
312
+ * \param action An output buffer to store parsed payload into.
313
+ * \return A block parser section result.
314
+ */
315
+ static ParseSectionResult HandlePayload(const BlueprintSection& section,
316
+ const BlockIterator& cur,
317
+ BlueprintParserCore& parser,
318
+ Action& action)
319
+ {
320
+ Payload payload;
321
+ ParseSectionResult result = PayloadParser::Parse(cur,
322
+ section.bounds.second,
323
+ section,
324
+ parser,
325
+ payload);
326
+ if (result.first.error.code != Error::OK)
327
+ return result;
328
+
329
+ // Create transaction example if needed
330
+ if (action.examples.empty()) {
331
+ action.examples.push_back(TransactionExample());
332
+ }
333
+ else if (section.type == RequestSectionType) {
334
+ // Automatic request response pairing:
335
+ // If a request follows a response create a new transaction example
336
+ if (!action.examples.back().responses.empty()) {
337
+ action.examples.push_back(TransactionExample());
338
+ }
339
+ }
340
+
341
+ BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
342
+
343
+ // Check for duplicate
344
+ if (IsPayloadDuplicate(section.type, payload, action.examples.back())) {
345
+ // WARN: duplicate payload
346
+ std::stringstream ss;
347
+ ss << SectionName(section.type) << " payload `" << payload.name << "`";
348
+ ss << " already defined for `" << action.method << "` method";
349
+
350
+ SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, parser.sourceData);
351
+ result.first.warnings.push_back(Warning(ss.str(),
352
+ DuplicateWarning,
353
+ sourceBlock));
354
+ }
355
+
356
+ // Check payload integrity
357
+ CheckPayload(section.type, payload, action.method, nameBlock->sourceMap, parser.sourceData, result.first);
358
+
359
+ // Inject parsed payload into the action
360
+ if (section.type == RequestSectionType) {
361
+ action.examples.back().requests.push_back(payload);
362
+ }
363
+ else if (section.type == ResponseSectionType) {
364
+ action.examples.back().responses.push_back(payload);
365
+ }
366
+
367
+ // Check header duplicates
368
+ CheckHeaderDuplicates(action, payload, nameBlock->sourceMap, parser.sourceData, result.first);
369
+
370
+ return result;
371
+ }
372
+
373
+
374
+ /**
375
+ * \brief Check & report payload validity.
376
+ * \param section A section of the payload.
377
+ * \param sourceMap Payload signature source map.
378
+ * \param payload The payload to be checked.
379
+ */
380
+ static void CheckPayload(const SectionType& section,
381
+ const Payload& payload,
382
+ const HTTPMethod method,
383
+ const SourceDataBlock& sourceMap,
384
+ const SourceData& sourceData,
385
+ Result& result) {
386
+
387
+ bool warnEmptyBody = false;
388
+
389
+ std::string contentLength;
390
+ std::string transferEncoding;
391
+
392
+ for (Collection<Header>::const_iterator it = payload.headers.begin();
393
+ it != payload.headers.end();
394
+ ++it) {
395
+
396
+ if (it->first == HTTPHeaderName::ContentLength) {
397
+ contentLength = it->second;
398
+ }
399
+
400
+ if (it->first == HTTPHeaderName::TransferEncoding) {
401
+ transferEncoding = it->second;
402
+ }
403
+ }
404
+
405
+ if (section == RequestSectionType) {
406
+
407
+ if (payload.body.empty()) {
408
+
409
+ // Warn when content-length or transfer-encoding is specified or both headers and body are empty
410
+ if (payload.headers.empty()) {
411
+ warnEmptyBody = true;
412
+ }
413
+ else {
414
+ warnEmptyBody = !contentLength.empty() ||
415
+ !transferEncoding.empty();
416
+ }
417
+
418
+ if (warnEmptyBody) {
419
+ // WARN: empty body
420
+ std::stringstream ss;
421
+ ss << "empty " << SectionName(section) << " " << SectionName(BodySectionType);
422
+
423
+ if (!contentLength.empty()) {
424
+ ss << ", expected " << SectionName(BodySectionType) << " for '" << contentLength << "' Content-Length";
425
+ }
426
+ else if (!transferEncoding.empty()) {
427
+ ss << ", expected " << SectionName(BodySectionType) << " for '" << transferEncoding << "' Transfer-Encoding";
428
+ }
429
+
430
+ result.warnings.push_back(Warning(ss.str(),
431
+ EmptyDefinitionWarning,
432
+ MapSourceDataBlock(sourceMap, sourceData)));
433
+ }
434
+
435
+ }
436
+ }
437
+ else if (section == ResponseSectionType) {
438
+ // Check status code
439
+ HTTPStatusCode code = 0;
440
+
441
+ if (!payload.name.empty()) {
442
+ std::stringstream(payload.name) >> code;
443
+ }
444
+
445
+ StatusCodeTraits statusCodeTraits = GetStatusCodeTrait(code);
446
+ HTTPMethodTraits methodTraits = GetMethodTrait(method);
447
+
448
+ if ((!statusCodeTraits.allowBody || !methodTraits.allowBody) && !payload.body.empty()) {
449
+ // WARN: not empty body
450
+
451
+ if (!statusCodeTraits.allowBody) {
452
+ std::stringstream ss;
453
+ ss << "the " << code << " response MUST NOT include a " << SectionName(BodySectionType);
454
+ result.warnings.push_back(Warning(ss.str(),
455
+ EmptyDefinitionWarning,
456
+ MapSourceDataBlock(sourceMap, sourceData)));
457
+ }
458
+
459
+ // WARN: Edge case for 2xx CONNECT
460
+ if (method == HTTPMethodName::Connect && code/100 == 2) {
461
+ std::stringstream ss;
462
+ ss << "the response for " << code << " " << method << " request MUST NOT include a " << SectionName(BodySectionType);
463
+ result.warnings.push_back(Warning(ss.str(),
464
+ EmptyDefinitionWarning,
465
+ MapSourceDataBlock(sourceMap, sourceData)));
466
+
467
+ }
468
+ else if (method != HTTPMethodName::Connect && !methodTraits.allowBody) {
469
+ std::stringstream ss;
470
+ ss << "the response for " << method << " request MUST NOT include a " << SectionName(BodySectionType);
471
+ result.warnings.push_back(Warning(ss.str(),
472
+ EmptyDefinitionWarning,
473
+ MapSourceDataBlock(sourceMap, sourceData)));
474
+ }
475
+
476
+ return;
477
+ }
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Checks whether given section payload has duplicate within its transaction examples
483
+ * \return True when a duplicate is found, false otherwise.
484
+ */
485
+ static bool IsPayloadDuplicate(const SectionType& section, const Payload& payload, TransactionExample& example) {
486
+
487
+ if (section == RequestSectionType) {
488
+ Collection<Request>::const_iterator duplicate = FindRequest(example, payload);
489
+ return duplicate != example.requests.end();
490
+ }
491
+ else if (section == ResponseSectionType) {
492
+ Collection<Response>::const_iterator duplicate = FindResponse(example, payload);
493
+ return duplicate != example.responses.end();
494
+ }
495
+
496
+ return false;
497
+ }
498
+ };
499
+
500
+ typedef BlockParser<Action, SectionParser<Action> > ActionParser;
501
+ }
502
+
503
+ #endif