redsnow 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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