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.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/.gitmodules +3 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/Rakefile +36 -0
- data/Vagrantfile +20 -0
- data/ext/snowcrash/Makefile +64 -0
- data/ext/snowcrash/Vagrantfile +20 -0
- data/ext/snowcrash/bin/snowcrash +0 -0
- data/ext/snowcrash/common.gypi +163 -0
- data/ext/snowcrash/config.gypi +10 -0
- data/ext/snowcrash/config.mk +5 -0
- data/ext/snowcrash/configure +213 -0
- data/ext/snowcrash/provisioning.sh +15 -0
- data/ext/snowcrash/snowcrash.gyp +141 -0
- data/ext/snowcrash/src/ActionParser.h +503 -0
- data/ext/snowcrash/src/AssetParser.h +215 -0
- data/ext/snowcrash/src/BlockUtility.h +186 -0
- data/ext/snowcrash/src/Blueprint.h +283 -0
- data/ext/snowcrash/src/BlueprintParser.h +347 -0
- data/ext/snowcrash/src/BlueprintParserCore.h +190 -0
- data/ext/snowcrash/src/BlueprintSection.h +140 -0
- data/ext/snowcrash/src/BlueprintUtility.h +126 -0
- data/ext/snowcrash/src/CBlueprint.cc +600 -0
- data/ext/snowcrash/src/CBlueprint.h +354 -0
- data/ext/snowcrash/src/CSourceAnnotation.cc +140 -0
- data/ext/snowcrash/src/CSourceAnnotation.h +106 -0
- data/ext/snowcrash/src/CodeBlockUtility.h +189 -0
- data/ext/snowcrash/src/DescriptionSectionUtility.h +156 -0
- data/ext/snowcrash/src/HTTP.cc +46 -0
- data/ext/snowcrash/src/HTTP.h +105 -0
- data/ext/snowcrash/src/HeaderParser.h +289 -0
- data/ext/snowcrash/src/ListBlockUtility.h +273 -0
- data/ext/snowcrash/src/ListUtility.h +95 -0
- data/ext/snowcrash/src/MarkdownBlock.cc +176 -0
- data/ext/snowcrash/src/MarkdownBlock.h +93 -0
- data/ext/snowcrash/src/MarkdownParser.cc +266 -0
- data/ext/snowcrash/src/MarkdownParser.h +88 -0
- data/ext/snowcrash/src/ParameterDefinitonParser.h +570 -0
- data/ext/snowcrash/src/ParametersParser.h +252 -0
- data/ext/snowcrash/src/Parser.cc +71 -0
- data/ext/snowcrash/src/Parser.h +29 -0
- data/ext/snowcrash/src/ParserCore.cc +120 -0
- data/ext/snowcrash/src/ParserCore.h +82 -0
- data/ext/snowcrash/src/PayloadParser.h +672 -0
- data/ext/snowcrash/src/Platform.h +54 -0
- data/ext/snowcrash/src/RegexMatch.h +32 -0
- data/ext/snowcrash/src/ResourceGroupParser.h +195 -0
- data/ext/snowcrash/src/ResourceParser.h +584 -0
- data/ext/snowcrash/src/SectionUtility.h +142 -0
- data/ext/snowcrash/src/Serialize.cc +52 -0
- data/ext/snowcrash/src/Serialize.h +69 -0
- data/ext/snowcrash/src/SerializeJSON.cc +601 -0
- data/ext/snowcrash/src/SerializeJSON.h +21 -0
- data/ext/snowcrash/src/SerializeYAML.cc +336 -0
- data/ext/snowcrash/src/SerializeYAML.h +21 -0
- data/ext/snowcrash/src/SourceAnnotation.h +177 -0
- data/ext/snowcrash/src/StringUtility.h +109 -0
- data/ext/snowcrash/src/SymbolTable.h +83 -0
- data/ext/snowcrash/src/UriTemplateParser.cc +195 -0
- data/ext/snowcrash/src/UriTemplateParser.h +243 -0
- data/ext/snowcrash/src/Version.h +39 -0
- data/ext/snowcrash/src/csnowcrash.cc +23 -0
- data/ext/snowcrash/src/csnowcrash.h +38 -0
- data/ext/snowcrash/src/posix/RegexMatch.cc +99 -0
- data/ext/snowcrash/src/snowcrash.cc +18 -0
- data/ext/snowcrash/src/snowcrash.h +41 -0
- data/ext/snowcrash/src/snowcrash/snowcrash.cc +170 -0
- data/ext/snowcrash/src/win/RegexMatch.cc +78 -0
- data/ext/snowcrash/sundown/CONTRIBUTING.md +10 -0
- data/ext/snowcrash/sundown/Makefile +83 -0
- data/ext/snowcrash/sundown/Makefile.win +33 -0
- data/ext/snowcrash/sundown/examples/smartypants.c +72 -0
- data/ext/snowcrash/sundown/examples/sundown.c +80 -0
- data/ext/snowcrash/sundown/html/houdini.h +37 -0
- data/ext/snowcrash/sundown/html/houdini_href_e.c +108 -0
- data/ext/snowcrash/sundown/html/houdini_html_e.c +84 -0
- data/ext/snowcrash/sundown/html/html.c +647 -0
- data/ext/snowcrash/sundown/html/html.h +77 -0
- data/ext/snowcrash/sundown/html/html_smartypants.c +389 -0
- data/ext/snowcrash/sundown/html_block_names.txt +25 -0
- data/ext/snowcrash/sundown/src/autolink.c +297 -0
- data/ext/snowcrash/sundown/src/autolink.h +51 -0
- data/ext/snowcrash/sundown/src/buffer.c +225 -0
- data/ext/snowcrash/sundown/src/buffer.h +96 -0
- data/ext/snowcrash/sundown/src/html_blocks.h +206 -0
- data/ext/snowcrash/sundown/src/markdown.c +2701 -0
- data/ext/snowcrash/sundown/src/markdown.h +147 -0
- data/ext/snowcrash/sundown/src/src_map.c +200 -0
- data/ext/snowcrash/sundown/src/src_map.h +58 -0
- data/ext/snowcrash/sundown/src/stack.c +81 -0
- data/ext/snowcrash/sundown/src/stack.h +29 -0
- data/ext/snowcrash/sundown/sundown.def +20 -0
- data/ext/snowcrash/tools/gyp/AUTHORS +11 -0
- data/ext/snowcrash/tools/gyp/DEPS +24 -0
- data/ext/snowcrash/tools/gyp/OWNERS +1 -0
- data/ext/snowcrash/tools/gyp/PRESUBMIT.py +120 -0
- data/ext/snowcrash/tools/gyp/buildbot/buildbot_run.py +190 -0
- data/ext/snowcrash/tools/gyp/codereview.settings +10 -0
- data/ext/snowcrash/tools/gyp/data/win/large-pdb-shim.cc +12 -0
- data/ext/snowcrash/tools/gyp/gyp +8 -0
- data/ext/snowcrash/tools/gyp/gyp.bat +5 -0
- data/ext/snowcrash/tools/gyp/gyp_main.py +18 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSNew.py +340 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSProject.py +208 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSSettings.py +1063 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSToolFile.py +58 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUserFile.py +147 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSUtil.py +267 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/MSVSVersion.py +409 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.py +537 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/__init__.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/common.py +521 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/common.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/easy_xml.py +157 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/flock_tool.py +49 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.py +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/__init__.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/android.py +1069 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/cmake.py +1143 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/dump_dependency_json.py +81 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/eclipse.py +335 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypd.py +87 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/gypsh.py +56 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.py +2181 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/make.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/msvs.py +3335 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/ninja.py +2156 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.py +1224 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/generator/xcode.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/input.py +2809 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/input.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/mac_tool.py +510 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/msvs_emulation.py +972 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/ninja_syntax.py +160 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/ordered_dict.py +289 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/win_tool.py +292 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.py +1440 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcode_emulation.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.py +2889 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xcodeproj_file.pyc +0 -0
- data/ext/snowcrash/tools/gyp/pylib/gyp/xml_fix.py +69 -0
- data/ext/snowcrash/tools/gyp/pylintrc +307 -0
- data/ext/snowcrash/tools/gyp/samples/samples +81 -0
- data/ext/snowcrash/tools/gyp/samples/samples.bat +5 -0
- data/ext/snowcrash/tools/gyp/setup.py +19 -0
- data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.pbfilespec +27 -0
- data/ext/snowcrash/tools/gyp/tools/Xcode/Specifications/gyp.xclangspec +226 -0
- data/ext/snowcrash/tools/gyp/tools/emacs/gyp.el +252 -0
- data/ext/snowcrash/tools/gyp/tools/graphviz.py +100 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_gyp.py +155 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_sln.py +168 -0
- data/ext/snowcrash/tools/gyp/tools/pretty_vcproj.py +329 -0
- data/ext/snowcrash/tools/homebrew/snowcrash.rb +11 -0
- data/ext/snowcrash/vcbuild.bat +184 -0
- data/lib/redsnow.rb +31 -0
- data/lib/redsnow/binding.rb +132 -0
- data/lib/redsnow/blueprint.rb +365 -0
- data/lib/redsnow/object.rb +18 -0
- data/lib/redsnow/parseresult.rb +107 -0
- data/lib/redsnow/version.rb +4 -0
- data/provisioning.sh +20 -0
- data/redsnow.gemspec +35 -0
- data/test/_helper.rb +15 -0
- data/test/fixtures/sample-api-ast.json +97 -0
- data/test/fixtures/sample-api.apib +20 -0
- data/test/redsnow_binding_test.rb +35 -0
- data/test/redsnow_parseresult_test.rb +50 -0
- data/test/redsnow_test.rb +285 -0
- metadata +358 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
//
|
|
2
|
+
// MarkdownParser.h
|
|
3
|
+
// snowcrash
|
|
4
|
+
//
|
|
5
|
+
// Created by Zdenek Nemec on 4/15/13.
|
|
6
|
+
// Copyright (c) 2013 Apiary Inc. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#ifndef SNOWCRASH_MARKDOWNPARSER_H
|
|
10
|
+
#define SNOWCRASH_MARKDOWNPARSER_H
|
|
11
|
+
|
|
12
|
+
#include "ParserCore.h"
|
|
13
|
+
#include "SourceAnnotation.h"
|
|
14
|
+
#include "MarkdownBlock.h"
|
|
15
|
+
#include "markdown.h"
|
|
16
|
+
|
|
17
|
+
namespace snowcrash {
|
|
18
|
+
|
|
19
|
+
//
|
|
20
|
+
// Markdown Parser
|
|
21
|
+
//
|
|
22
|
+
class MarkdownParser {
|
|
23
|
+
public:
|
|
24
|
+
// Default sundown parser configuration
|
|
25
|
+
static const size_t OutputUnitSize; // = 64;
|
|
26
|
+
static const size_t MaxNesting;// = 16;
|
|
27
|
+
static const int ParserExtensions;// = MKDEXT_FENCED_CODE | MKDEXT_NO_INTRA_EMPHASIS /*| MKDEXT_TABLES */;
|
|
28
|
+
|
|
29
|
+
// Parse source Markdown into Markdown AST
|
|
30
|
+
void parse(const SourceData& source, Result& result, MarkdownBlock::Stack& markdown);
|
|
31
|
+
|
|
32
|
+
private:
|
|
33
|
+
typedef sd_callbacks RenderCallbacks;
|
|
34
|
+
typedef void * RenderCallbackData;
|
|
35
|
+
|
|
36
|
+
RenderCallbacks renderCallbacks();
|
|
37
|
+
RenderCallbackData renderCallbackData();
|
|
38
|
+
|
|
39
|
+
MarkdownBlock::Stack m_renderStack;
|
|
40
|
+
|
|
41
|
+
// Header
|
|
42
|
+
static void renderHeader(struct buf *ob, const struct buf *text, int level, void *opaque);
|
|
43
|
+
void renderHeader(const std::string& text, int level);
|
|
44
|
+
|
|
45
|
+
// List
|
|
46
|
+
static void beginList(int flags, void *opaque);
|
|
47
|
+
void beginList(int flags);
|
|
48
|
+
|
|
49
|
+
static void renderList(struct buf *ob, const struct buf *text, int flags, void *opaque);
|
|
50
|
+
void renderList(const std::string& text, int flags);
|
|
51
|
+
|
|
52
|
+
// List item
|
|
53
|
+
static void beginListItem(int flags, void *opaque);
|
|
54
|
+
void beginListItem(int flags);
|
|
55
|
+
|
|
56
|
+
static void renderListItem(struct buf *ob, const struct buf *text, int flags, void *opaque);
|
|
57
|
+
void renderListItem(const std::string& text, int flags);
|
|
58
|
+
|
|
59
|
+
// Code block
|
|
60
|
+
static void renderBlockCode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque);
|
|
61
|
+
void renderBlockCode(const std::string& text, const std::string& language);
|
|
62
|
+
|
|
63
|
+
// Paragraph
|
|
64
|
+
static void renderParagraph(struct buf *ob, const struct buf *text, void *opaque);
|
|
65
|
+
void renderParagraph(const std::string& text);
|
|
66
|
+
|
|
67
|
+
// Horizontal Rule
|
|
68
|
+
static void renderHorizontalRule(struct buf *ob, void *opaque);
|
|
69
|
+
void renderHorizontalRule();
|
|
70
|
+
|
|
71
|
+
// HTML
|
|
72
|
+
static void renderHTML(struct buf *ob, const struct buf *text, void *opaque);
|
|
73
|
+
void renderHTML(const std::string& text);
|
|
74
|
+
|
|
75
|
+
// Quote
|
|
76
|
+
static void beginQuote(void *opaque);
|
|
77
|
+
void beginQuote();
|
|
78
|
+
|
|
79
|
+
static void renderQuote(struct buf *ob, const struct buf *text, void *opaque);
|
|
80
|
+
void renderQuote(const std::string& text);
|
|
81
|
+
|
|
82
|
+
// Source maps
|
|
83
|
+
static void blockDidParse(const src_map* map, const uint8_t *txt_data, size_t size, void *opaque);
|
|
84
|
+
void blockDidParse(const SourceDataBlock& sourceMap);
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
#endif
|
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ParameterDefinitonParser.h
|
|
3
|
+
// snowcrash
|
|
4
|
+
//
|
|
5
|
+
// Created by Zdenek Nemec on 9/1/13.
|
|
6
|
+
// Copyright (c) 2013 Apiary Inc. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#ifndef SNOWCRASH_PARAMETERDEFINITIONPARSER_H
|
|
10
|
+
#define SNOWCRASH_PARAMETERDEFINITIONPARSER_H
|
|
11
|
+
|
|
12
|
+
#include <sstream>
|
|
13
|
+
#include "BlueprintParserCore.h"
|
|
14
|
+
#include "Blueprint.h"
|
|
15
|
+
#include "RegexMatch.h"
|
|
16
|
+
#include "StringUtility.h"
|
|
17
|
+
#include "ListBlockUtility.h"
|
|
18
|
+
#include "SectionUtility.h"
|
|
19
|
+
#include "DescriptionSectionUtility.h"
|
|
20
|
+
|
|
21
|
+
/** Parameter Value regex */
|
|
22
|
+
#define PARAMETER_VALUE "`([^`]+)`"
|
|
23
|
+
|
|
24
|
+
/** Parameter Identifier */
|
|
25
|
+
#define PARAMETER_IDENTIFIER "([[:alnum:]_.-]+)"
|
|
26
|
+
|
|
27
|
+
/** Lead in and out for comma separated values regex */
|
|
28
|
+
#define CSV_LEADINOUT "[[:blank:]]*,?[[:blank:]]*"
|
|
29
|
+
|
|
30
|
+
namespace snowcrashconst {
|
|
31
|
+
|
|
32
|
+
/** Parameter Abbreviated definition matching regex */
|
|
33
|
+
const char* const ParameterAbbrevDefinitionRegex = "^" PARAMETER_IDENTIFIER \
|
|
34
|
+
"([[:blank:]]*=[[:blank:]]*`([^`]*)`[[:blank:]]*)?([[:blank:]]*\\(([^)]*)\\)[[:blank:]]*)?([[:blank:]]*\\.\\.\\.[[:blank:]]*(.*))?$";
|
|
35
|
+
|
|
36
|
+
/** Parameter Required matching regex */
|
|
37
|
+
const char* const ParameterRequiredRegex = "^[[:blank:]]*[Rr]equired[[:blank:]]*$";
|
|
38
|
+
|
|
39
|
+
/** Parameter Optional matching regex */
|
|
40
|
+
const char* const ParameterOptionalRegex = "^[[:blank:]]*[Oo]ptional[[:blank:]]*$";
|
|
41
|
+
|
|
42
|
+
/** Additonal Parameter Traits Example matching regex */
|
|
43
|
+
const char* const AdditionalTraitsExampleRegex = CSV_LEADINOUT "`([^`]*)`" CSV_LEADINOUT;
|
|
44
|
+
|
|
45
|
+
/** Additonal Parameter Traits Use matching regex */
|
|
46
|
+
const char* const AdditionalTraitsUseRegex = CSV_LEADINOUT "([Oo]ptional|[Rr]equired)" CSV_LEADINOUT;
|
|
47
|
+
|
|
48
|
+
/** Additonal Parameter Traits Type matching regex */
|
|
49
|
+
const char* const AdditionalTraitsTypeRegex = CSV_LEADINOUT "([^,]*)" CSV_LEADINOUT;
|
|
50
|
+
|
|
51
|
+
/** Parameter Values matching regex */
|
|
52
|
+
const char* const ParameterValuesRegex = "^[[:blank:]]*[Vv]alues[[:blank:]]*$";
|
|
53
|
+
|
|
54
|
+
/** Values expected content */
|
|
55
|
+
const char* const ExpectedValuesContent = "nested list of possible parameter values, one element per list item e.g. '`value`'";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
namespace snowcrash {
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Classifier of internal list items, ParameterCollection context.
|
|
62
|
+
*/
|
|
63
|
+
template <>
|
|
64
|
+
FORCEINLINE SectionType ClassifyInternaListBlock<Parameter>(const BlockIterator& begin,
|
|
65
|
+
const BlockIterator& end) {
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if (begin->type != ListBlockBeginType &&
|
|
69
|
+
begin->type != ListItemBlockBeginType)
|
|
70
|
+
return UndefinedSectionType;
|
|
71
|
+
|
|
72
|
+
SourceData remainingContent;
|
|
73
|
+
SourceData content = GetListItemSignature(begin, end, remainingContent);
|
|
74
|
+
|
|
75
|
+
content = TrimString(content);
|
|
76
|
+
|
|
77
|
+
if (RegexMatch(content, snowcrashconst::ParameterValuesRegex))
|
|
78
|
+
return ParameterValuesSectionType;
|
|
79
|
+
|
|
80
|
+
return UndefinedSectionType;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Children blocks classifier */
|
|
84
|
+
template <>
|
|
85
|
+
FORCEINLINE SectionType ClassifyChildrenListBlock<Parameter>(const BlockIterator& begin,
|
|
86
|
+
const BlockIterator& end) {
|
|
87
|
+
|
|
88
|
+
SectionType type = ClassifyInternaListBlock<Parameter>(begin, end);
|
|
89
|
+
if (type != UndefinedSectionType)
|
|
90
|
+
return type;
|
|
91
|
+
|
|
92
|
+
return UndefinedSectionType;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Returns true if given block has a parameter definition signature, false otherwise.
|
|
97
|
+
*/
|
|
98
|
+
FORCEINLINE bool HasParameterDefinitionSignature(const BlockIterator& begin,
|
|
99
|
+
const BlockIterator& end) {
|
|
100
|
+
|
|
101
|
+
if (begin->type != ListBlockBeginType &&
|
|
102
|
+
begin->type != ListItemBlockBeginType)
|
|
103
|
+
return false;
|
|
104
|
+
|
|
105
|
+
// Since we are too generic make sure the signature is not inner list
|
|
106
|
+
SectionType listSection = ClassifyInternaListBlock<Parameter>(begin, end);
|
|
107
|
+
if (listSection != UndefinedSectionType)
|
|
108
|
+
return false;
|
|
109
|
+
|
|
110
|
+
// Or any other reserved keyword
|
|
111
|
+
if (HasParametersSignature(begin, end))
|
|
112
|
+
return false;
|
|
113
|
+
|
|
114
|
+
SourceData remainingContent;
|
|
115
|
+
SourceData content = GetListItemSignature(begin, end, remainingContent);
|
|
116
|
+
content = TrimString(content);
|
|
117
|
+
return RegexMatch(content, snowcrashconst::ParameterAbbrevDefinitionRegex);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Block Classifier, Parameter context.
|
|
122
|
+
*/
|
|
123
|
+
template <>
|
|
124
|
+
FORCEINLINE SectionType ClassifyBlock<Parameter>(const BlockIterator& begin,
|
|
125
|
+
const BlockIterator& end,
|
|
126
|
+
const SectionType& context) {
|
|
127
|
+
|
|
128
|
+
if (context == UndefinedSectionType) {
|
|
129
|
+
if (HasParameterDefinitionSignature(begin, end))
|
|
130
|
+
return ParameterDefinitionSectionType;
|
|
131
|
+
}
|
|
132
|
+
else if (context == ParameterDefinitionSectionType) {
|
|
133
|
+
|
|
134
|
+
if (begin->type == ListItemBlockEndType ||
|
|
135
|
+
begin->type == ListBlockEndType)
|
|
136
|
+
return UndefinedSectionType;
|
|
137
|
+
|
|
138
|
+
SectionType listSection = ClassifyInternaListBlock<Parameter>(begin, end);
|
|
139
|
+
if (listSection != UndefinedSectionType)
|
|
140
|
+
return listSection;
|
|
141
|
+
|
|
142
|
+
if (begin->type == ListBlockBeginType)
|
|
143
|
+
return ForeignSectionType; // Foreign nested list-item
|
|
144
|
+
|
|
145
|
+
if (begin->type == ListItemBlockBeginType)
|
|
146
|
+
return UndefinedSectionType;
|
|
147
|
+
}
|
|
148
|
+
else if (context == ParameterValuesSectionType ||
|
|
149
|
+
context == ForeignSectionType) {
|
|
150
|
+
|
|
151
|
+
if (begin->type == ListItemBlockEndType ||
|
|
152
|
+
begin->type == ListBlockEndType)
|
|
153
|
+
return UndefinedSectionType;
|
|
154
|
+
|
|
155
|
+
SectionType listSection = ClassifyInternaListBlock<Parameter>(begin, end);
|
|
156
|
+
if (listSection != UndefinedSectionType)
|
|
157
|
+
return listSection;
|
|
158
|
+
|
|
159
|
+
return ForeignSectionType;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (context == ParameterDefinitionSectionType) ? context : UndefinedSectionType;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Parameter section parser.
|
|
167
|
+
*/
|
|
168
|
+
template<>
|
|
169
|
+
struct SectionParser<Parameter> {
|
|
170
|
+
|
|
171
|
+
static ParseSectionResult ParseSection(const BlueprintSection& section,
|
|
172
|
+
const BlockIterator& cur,
|
|
173
|
+
BlueprintParserCore& parser,
|
|
174
|
+
Parameter& parameter) {
|
|
175
|
+
|
|
176
|
+
ParseSectionResult result = std::make_pair(Result(), cur);
|
|
177
|
+
switch (section.type) {
|
|
178
|
+
|
|
179
|
+
case ParameterDefinitionSectionType:
|
|
180
|
+
result = HandleParmeterDefinitionSection(section, cur, parser, parameter);
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case ParameterValuesSectionType:
|
|
184
|
+
result = HandleValuesSection(section, cur, parser, parameter);
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case ForeignSectionType:
|
|
188
|
+
result = HandleForeignSection<Parameter>(section, cur, parser.sourceData);
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
case UndefinedSectionType:
|
|
192
|
+
result.second = CloseList(cur, section.bounds.second);
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
default:
|
|
196
|
+
result.first.error = UnexpectedBlockError(section, cur, parser.sourceData);
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
static void Finalize(const SectionBounds& bounds,
|
|
205
|
+
BlueprintParserCore& parser,
|
|
206
|
+
Parameter& parameter,
|
|
207
|
+
Result& result) {}
|
|
208
|
+
|
|
209
|
+
/** Parse a parameter definition top-level section blocks. */
|
|
210
|
+
static ParseSectionResult HandleParmeterDefinitionSection(const BlueprintSection& section,
|
|
211
|
+
const BlockIterator& cur,
|
|
212
|
+
BlueprintParserCore& parser,
|
|
213
|
+
Parameter& parameter) {
|
|
214
|
+
|
|
215
|
+
ParseSectionResult result = std::make_pair(Result(), cur);
|
|
216
|
+
BlockIterator sectionCur = cur;
|
|
217
|
+
|
|
218
|
+
// Signature
|
|
219
|
+
if (sectionCur == section.bounds.first) {
|
|
220
|
+
ProcessSignature(section, sectionCur, parser.sourceData, result.first, parameter);
|
|
221
|
+
result.second = SkipSignatureBlock(sectionCur, section.bounds.second);
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Description
|
|
226
|
+
result = ParseDescriptionBlock<Parameter>(section,
|
|
227
|
+
sectionCur,
|
|
228
|
+
parser.sourceData,
|
|
229
|
+
parameter);
|
|
230
|
+
return result;
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Retrieve and process parameter definition signature.
|
|
236
|
+
*/
|
|
237
|
+
static void ProcessSignature(const BlueprintSection& section,
|
|
238
|
+
const BlockIterator& cur,
|
|
239
|
+
const SourceData& sourceData,
|
|
240
|
+
Result& result,
|
|
241
|
+
Parameter& parameter) {
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
// Set default values
|
|
245
|
+
parameter.use = UndefinedParameterUse;
|
|
246
|
+
|
|
247
|
+
// Process signature
|
|
248
|
+
SourceData remainingContent;
|
|
249
|
+
SourceData signature = GetListItemSignature(cur, section.bounds.second, remainingContent);
|
|
250
|
+
|
|
251
|
+
TrimString(signature);
|
|
252
|
+
CaptureGroups captureGroups;
|
|
253
|
+
if (RegexCapture(signature, snowcrashconst::ParameterAbbrevDefinitionRegex, captureGroups) &&
|
|
254
|
+
captureGroups.size() == 8) {
|
|
255
|
+
|
|
256
|
+
// Name
|
|
257
|
+
parameter.name = captureGroups[1];
|
|
258
|
+
TrimString(parameter.name);
|
|
259
|
+
|
|
260
|
+
// Default value
|
|
261
|
+
if (!captureGroups[3].empty())
|
|
262
|
+
parameter.defaultValue = captureGroups[3];
|
|
263
|
+
|
|
264
|
+
// Additional Attributes
|
|
265
|
+
if (!captureGroups[5].empty())
|
|
266
|
+
ProcessSignatureAdditionalTraits(section, cur, captureGroups[5], sourceData, result, parameter);
|
|
267
|
+
|
|
268
|
+
// Description
|
|
269
|
+
if (!captureGroups[7].empty())
|
|
270
|
+
parameter.description = captureGroups[7];
|
|
271
|
+
|
|
272
|
+
if (!remainingContent.empty()) {
|
|
273
|
+
parameter.description += "\n";
|
|
274
|
+
parameter.description += remainingContent;
|
|
275
|
+
parameter.description += "\n";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Check possible required vs default clash
|
|
279
|
+
if (parameter.use != OptionalParameterUse &&
|
|
280
|
+
!parameter.defaultValue.empty()) {
|
|
281
|
+
|
|
282
|
+
// WARN: Required vs default clash
|
|
283
|
+
std::stringstream ss;
|
|
284
|
+
ss << "specifying parameter '" << parameter.name << "' as required supersedes its default value"\
|
|
285
|
+
", declare the parameter as 'optional' to specify its default value";
|
|
286
|
+
|
|
287
|
+
BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
|
|
288
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, sourceData);
|
|
289
|
+
result.warnings.push_back(Warning(ss.str(),
|
|
290
|
+
LogicalErrorWarning,
|
|
291
|
+
sourceBlock));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// ERR: unable to parse
|
|
297
|
+
BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
|
|
298
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, sourceData);
|
|
299
|
+
result.error = (Error("unable to parse parameter specification",
|
|
300
|
+
BusinessError,
|
|
301
|
+
sourceBlock));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** Parse additional parameter attributes from abbrev definition bracket */
|
|
306
|
+
static void ProcessSignatureAdditionalTraits(const BlueprintSection& section,
|
|
307
|
+
const BlockIterator& cur,
|
|
308
|
+
const SourceData& additionalTraits,
|
|
309
|
+
const SourceData& sourceData,
|
|
310
|
+
Result& result,
|
|
311
|
+
Parameter& parameter)
|
|
312
|
+
{
|
|
313
|
+
|
|
314
|
+
// Cherry pick example value, if any
|
|
315
|
+
std::string source = additionalTraits;
|
|
316
|
+
TrimString(source);
|
|
317
|
+
CaptureGroups captureGroups;
|
|
318
|
+
if (RegexCapture(source, snowcrashconst::AdditionalTraitsExampleRegex, captureGroups) &&
|
|
319
|
+
captureGroups.size() > 1) {
|
|
320
|
+
|
|
321
|
+
parameter.exampleValue = captureGroups[1];
|
|
322
|
+
std::string::size_type pos = source.find(captureGroups[0]);
|
|
323
|
+
if (pos != std::string::npos)
|
|
324
|
+
source.replace(pos, captureGroups[0].length(), std::string());
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Cherry pick use attribute, if any
|
|
328
|
+
captureGroups.clear();
|
|
329
|
+
if (RegexCapture(source, snowcrashconst::AdditionalTraitsUseRegex, captureGroups) &&
|
|
330
|
+
captureGroups.size() > 1) {
|
|
331
|
+
|
|
332
|
+
parameter.use = (RegexMatch(captureGroups[1], snowcrashconst::ParameterOptionalRegex)) ? OptionalParameterUse : RequiredParameterUse;
|
|
333
|
+
|
|
334
|
+
std::string::size_type pos = source.find(captureGroups[0]);
|
|
335
|
+
if (pos != std::string::npos)
|
|
336
|
+
source.replace(pos, captureGroups[0].length(), std::string());
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Finish with type
|
|
340
|
+
captureGroups.clear();
|
|
341
|
+
if (RegexCapture(source, snowcrashconst::AdditionalTraitsTypeRegex, captureGroups) &&
|
|
342
|
+
captureGroups.size() > 1) {
|
|
343
|
+
|
|
344
|
+
parameter.type = captureGroups[1];
|
|
345
|
+
|
|
346
|
+
std::string::size_type pos = source.find(captureGroups[0]);
|
|
347
|
+
if (pos != std::string::npos)
|
|
348
|
+
source.replace(pos, captureGroups[0].length(), std::string());
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Check whats left
|
|
352
|
+
TrimString(source);
|
|
353
|
+
if (!source.empty()) {
|
|
354
|
+
// WARN: Additional parameters traits warning
|
|
355
|
+
std::stringstream ss;
|
|
356
|
+
ss << "unable to parse additional parameter traits";
|
|
357
|
+
ss << ", expected '([required | optional], [<type>], [`<example value>`])'";
|
|
358
|
+
ss << ", e.g. '(optional, string, `Hello World`)'";
|
|
359
|
+
|
|
360
|
+
BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
|
|
361
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, sourceData);
|
|
362
|
+
result.warnings.push_back(Warning(ss.str(),
|
|
363
|
+
FormattingWarning,
|
|
364
|
+
sourceBlock));
|
|
365
|
+
|
|
366
|
+
parameter.type.clear();
|
|
367
|
+
parameter.exampleValue.clear();
|
|
368
|
+
parameter.use = UndefinedParameterUse;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/** Parse possible values enumeration section blocks. */
|
|
373
|
+
static ParseSectionResult HandleValuesSection(const BlueprintSection& section,
|
|
374
|
+
const BlockIterator& cur,
|
|
375
|
+
BlueprintParserCore& parser,
|
|
376
|
+
Parameter& parameter) {
|
|
377
|
+
|
|
378
|
+
ParseSectionResult result = std::make_pair(Result(), cur);
|
|
379
|
+
|
|
380
|
+
// Check redefinition
|
|
381
|
+
if (!parameter.values.empty()) {
|
|
382
|
+
// WARN: parameter values are already defined
|
|
383
|
+
std::stringstream ss;
|
|
384
|
+
ss << "overshadowing previous 'values' definition";
|
|
385
|
+
ss << " for parameter '" << parameter.name << "'";
|
|
386
|
+
|
|
387
|
+
BlockIterator nameBlock = ListItemNameBlock(cur, section.bounds.second);
|
|
388
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(nameBlock, cur, section.bounds, parser.sourceData);
|
|
389
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
390
|
+
RedefinitionWarning,
|
|
391
|
+
sourceBlock));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Clear any previous content
|
|
395
|
+
parameter.values.clear();
|
|
396
|
+
|
|
397
|
+
// Check additional content in signature
|
|
398
|
+
CheckSignatureAdditionalContent(section,
|
|
399
|
+
cur,
|
|
400
|
+
parser.sourceData,
|
|
401
|
+
"'values:' keyword",
|
|
402
|
+
snowcrashconst::ExpectedValuesContent,
|
|
403
|
+
result.first);
|
|
404
|
+
|
|
405
|
+
// Parse inner list of entities
|
|
406
|
+
BlockIterator sectionCur = SkipSignatureBlock(cur, section.bounds.second);
|
|
407
|
+
BlockIterator endCur = cur;
|
|
408
|
+
if (endCur->type == ListBlockBeginType)
|
|
409
|
+
++endCur;
|
|
410
|
+
endCur = SkipToClosingBlock(endCur, section.bounds.second, ListItemBlockBeginType, ListItemBlockEndType);
|
|
411
|
+
|
|
412
|
+
if (sectionCur != endCur) {
|
|
413
|
+
|
|
414
|
+
// Iterate over list blocks, try to parse any nested lists of possible elements
|
|
415
|
+
for (; sectionCur != endCur; ++sectionCur) {
|
|
416
|
+
|
|
417
|
+
if (sectionCur->type == QuoteBlockBeginType)
|
|
418
|
+
sectionCur = SkipToClosingBlock(sectionCur, endCur, QuoteBlockBeginType, QuoteBlockEndType);
|
|
419
|
+
|
|
420
|
+
bool entitiesParsed = false;
|
|
421
|
+
if (sectionCur->type == ListBlockBeginType) {
|
|
422
|
+
if (parameter.values.empty()) {
|
|
423
|
+
|
|
424
|
+
// Try to parse some values
|
|
425
|
+
ParseSectionResult valuesResult = ParseValuesEntities(sectionCur,
|
|
426
|
+
section.bounds,
|
|
427
|
+
parser,
|
|
428
|
+
parameter.values);
|
|
429
|
+
result.first += valuesResult.first;
|
|
430
|
+
sectionCur = valuesResult.second;
|
|
431
|
+
if (result.first.error.code != Error::OK)
|
|
432
|
+
return result;
|
|
433
|
+
|
|
434
|
+
entitiesParsed = true;
|
|
435
|
+
}
|
|
436
|
+
else {
|
|
437
|
+
sectionCur = SkipToClosingBlock(sectionCur, endCur, ListBlockBeginType, ListBlockEndType);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
if (!entitiesParsed) {
|
|
442
|
+
// WARN: ignoring extraneous content
|
|
443
|
+
std::stringstream ss;
|
|
444
|
+
ss << "ignoring additional content in the 'values' attribute of the '";
|
|
445
|
+
ss << parameter.name << "' parameter";
|
|
446
|
+
ss << ", " << snowcrashconst::ExpectedValuesContent;
|
|
447
|
+
|
|
448
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(sectionCur, cur, section.bounds, parser.sourceData);
|
|
449
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
450
|
+
IgnoringWarning,
|
|
451
|
+
sourceBlock));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (parameter.values.empty()) {
|
|
457
|
+
// WARN: empty definition
|
|
458
|
+
std::stringstream ss;
|
|
459
|
+
ss << "no possible values specified for parameter '" << parameter.name << "'";
|
|
460
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(sectionCur, cur, section.bounds, parser.sourceData);
|
|
461
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
462
|
+
EmptyDefinitionWarning,
|
|
463
|
+
sourceBlock));
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if((!parameter.exampleValue.empty() || !parameter.defaultValue.empty()) && !parameter.values.empty()) {
|
|
467
|
+
CheckExampleAndDefaultValue(section, sectionCur, parser, parameter, result);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
endCur = CloseList(sectionCur, section.bounds.second);
|
|
471
|
+
result.second = endCur;
|
|
472
|
+
return result;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/** Parse entities in values attribute */
|
|
476
|
+
static ParseSectionResult ParseValuesEntities(const BlockIterator& cur,
|
|
477
|
+
const SectionBounds& bounds,
|
|
478
|
+
BlueprintParserCore& parser,
|
|
479
|
+
Collection<Value>::type& values) {
|
|
480
|
+
|
|
481
|
+
ParseSectionResult result = std::make_pair(Result(), cur);
|
|
482
|
+
|
|
483
|
+
if (cur->type != ListBlockBeginType)
|
|
484
|
+
return result;
|
|
485
|
+
|
|
486
|
+
BlockIterator sectionCur = ContentBlock(cur, bounds.second);
|
|
487
|
+
|
|
488
|
+
while (sectionCur != bounds.second &&
|
|
489
|
+
sectionCur->type == ListItemBlockBeginType) {
|
|
490
|
+
|
|
491
|
+
sectionCur = SkipToClosingBlock(sectionCur, bounds.second, ListItemBlockBeginType, ListItemBlockEndType);
|
|
492
|
+
|
|
493
|
+
CaptureGroups captureGroups;
|
|
494
|
+
std::string content = sectionCur->content;
|
|
495
|
+
if (content.empty()) {
|
|
496
|
+
// Not inline list, map from source
|
|
497
|
+
content = MapSourceData(parser.sourceData, sectionCur->sourceMap);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
RegexCapture(content, PARAMETER_VALUE, captureGroups);
|
|
501
|
+
if (captureGroups.size() > 1) {
|
|
502
|
+
values.push_back(captureGroups[1]);
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
// WARN: Ignoring unexpected content
|
|
506
|
+
TrimString(content);
|
|
507
|
+
std::stringstream ss;
|
|
508
|
+
ss << "ignoring the '" << content << "' element";
|
|
509
|
+
ss << ", expected '`" << content << "`'";
|
|
510
|
+
|
|
511
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(sectionCur, cur, bounds, parser.sourceData);
|
|
512
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
513
|
+
IgnoringWarning,
|
|
514
|
+
sourceBlock));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
++sectionCur;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
result.second = sectionCur;
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
static void CheckExampleAndDefaultValue(const BlueprintSection& section,
|
|
525
|
+
const BlockIterator& cur,
|
|
526
|
+
const BlueprintParserCore& parser,
|
|
527
|
+
const Parameter& parameter,
|
|
528
|
+
ParseSectionResult& result) {
|
|
529
|
+
|
|
530
|
+
bool isExampleFound = false;
|
|
531
|
+
bool isDefaultFound = false;
|
|
532
|
+
|
|
533
|
+
for (Collection<Value>::const_iterator it = parameter.values.begin(); it != parameter.values.end(); ++it){
|
|
534
|
+
if(parameter.exampleValue == *it) {
|
|
535
|
+
isExampleFound = true;
|
|
536
|
+
}
|
|
537
|
+
if(parameter.defaultValue == *it) {
|
|
538
|
+
isDefaultFound = true;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if(!parameter.exampleValue.empty() && !isExampleFound) {
|
|
543
|
+
// WARN: missing example in values.
|
|
544
|
+
std::stringstream ss;
|
|
545
|
+
ss << "the example value '" << parameter.exampleValue << "' of parameter '"<< parameter.name <<"' is not in its list of expected values";
|
|
546
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur, section.bounds.second, section.bounds, parser.sourceData);
|
|
547
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
548
|
+
LogicalErrorWarning,
|
|
549
|
+
sourceBlock));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if(!parameter.defaultValue.empty() && !isDefaultFound) {
|
|
553
|
+
// WARN: missing default in values.
|
|
554
|
+
std::stringstream ss;
|
|
555
|
+
ss << "the default value '" << parameter.defaultValue << "' of parameter '"<< parameter.name <<"' is not in its list of expected values";
|
|
556
|
+
SourceCharactersBlock sourceBlock = CharacterMapForBlock(cur, section.bounds.second, section.bounds, parser.sourceData);
|
|
557
|
+
result.first.warnings.push_back(Warning(ss.str(),
|
|
558
|
+
LogicalErrorWarning,
|
|
559
|
+
sourceBlock));
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
typedef BlockParser<Parameter, SectionParser<Parameter> > ParameterDefinitionParser;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
#endif
|