dioptic.profileparser 0.2__tar.gz → 0.3.0__tar.gz

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 (39) hide show
  1. {dioptic.profileparser-0.2 → dioptic_profileparser-0.3.0}/.github/workflows/pypi-packages.yml +11 -10
  2. dioptic_profileparser-0.3.0/.github/workflows/web-package.yml +28 -0
  3. dioptic_profileparser-0.3.0/PKG-INFO +11 -0
  4. dioptic_profileparser-0.3.0/cpp/CMakeLists.txt +33 -0
  5. dioptic_profileparser-0.3.0/cpp/include/profileparser/types.hpp +143 -0
  6. dioptic_profileparser-0.3.0/cpp/include/profileparser/utils.hpp +194 -0
  7. dioptic_profileparser-0.3.0/cpp/include/profileparser.hpp +58 -0
  8. dioptic_profileparser-0.3.0/cpp/lineinfo.cpp +21 -0
  9. dioptic_profileparser-0.3.0/cpp/parser.cpp +505 -0
  10. dioptic_profileparser-0.3.0/cpp/parser.hpp +12 -0
  11. dioptic_profileparser-0.3.0/cpp/printers.cpp +325 -0
  12. dioptic_profileparser-0.3.0/cpp/validation.cpp +128 -0
  13. dioptic_profileparser-0.3.0/pyproject.toml +24 -0
  14. dioptic_profileparser-0.3.0/python/CMakeLists.txt +58 -0
  15. dioptic_profileparser-0.3.0/python/__init__.py +2 -0
  16. dioptic_profileparser-0.3.0/python/pyprofileparser.cpp +175 -0
  17. {dioptic.profileparser-0.2/js → dioptic_profileparser-0.3.0/web}/CMakeLists.txt +20 -14
  18. dioptic_profileparser-0.3.0/web/README.md +11 -0
  19. dioptic_profileparser-0.3.0/web/binding_api.hpp +201 -0
  20. dioptic_profileparser-0.3.0/web/binding_ast.hpp +242 -0
  21. dioptic_profileparser-0.3.0/web/binding_helpers.hpp +41 -0
  22. dioptic_profileparser-0.3.0/web/profile_parser_js.cpp +20 -0
  23. dioptic.profileparser-0.2/.gitlab-ci.yml +0 -76
  24. dioptic.profileparser-0.2/PKG-INFO +0 -11
  25. dioptic.profileparser-0.2/cpp/processprofile.hpp +0 -926
  26. dioptic.profileparser-0.2/dioptic.profileparser.egg-info/PKG-INFO +0 -11
  27. dioptic.profileparser-0.2/dioptic.profileparser.egg-info/SOURCES.txt +0 -17
  28. dioptic.profileparser-0.2/dioptic.profileparser.egg-info/dependency_links.txt +0 -1
  29. dioptic.profileparser-0.2/dioptic.profileparser.egg-info/top_level.txt +0 -1
  30. dioptic.profileparser-0.2/js/profile_parser_js.cpp +0 -255
  31. dioptic.profileparser-0.2/pyproject.toml +0 -3
  32. dioptic.profileparser-0.2/python/CMakeLists.txt +0 -37
  33. dioptic.profileparser-0.2/python/pyprofileparser.cpp +0 -150
  34. dioptic.profileparser-0.2/setup.cfg +0 -4
  35. dioptic.profileparser-0.2/setup.py +0 -25
  36. {dioptic.profileparser-0.2 → dioptic_profileparser-0.3.0}/.gitignore +0 -0
  37. {dioptic.profileparser-0.2 → dioptic_profileparser-0.3.0}/LICENSE +0 -0
  38. {dioptic.profileparser-0.2 → dioptic_profileparser-0.3.0}/README.md +0 -0
  39. {dioptic.profileparser-0.2 → dioptic_profileparser-0.3.0}/cpp/peglib.h +0 -0
@@ -11,34 +11,34 @@ jobs:
11
11
  build-sdist:
12
12
  runs-on: ubuntu-latest
13
13
  steps:
14
- - uses: actions/checkout@v3
14
+ - uses: actions/checkout@v4
15
15
  - name: Build sdist package
16
16
  run: |
17
17
  python -m pip install pip build --upgrade
18
18
  python -m build --sdist
19
- - uses: actions/upload-artifact@v3
19
+ - uses: actions/upload-artifact@v4
20
20
  with:
21
- name: packages
21
+ name: packages-src
22
22
  path: "dist/*.tar.gz"
23
23
 
24
24
  build-win64-wheels:
25
25
  runs-on: windows-latest
26
26
  strategy:
27
27
  matrix:
28
- python-version: ["3.9", "3.10", "3.11"]
28
+ python-version: ["3.9", "3.10", "3.11", "3.12"]
29
29
  steps:
30
- - uses: actions/checkout@v3
30
+ - uses: actions/checkout@v4
31
31
  - uses: ilammy/msvc-dev-cmd@v1
32
- - uses: actions/setup-python@v4
32
+ - uses: actions/setup-python@v5
33
33
  with:
34
34
  python-version: ${{ matrix.python-version }}
35
35
  - name: Build wheel for py${{ matrix.python-version }}
36
36
  run: |
37
37
  python -m pip install pip build --upgrade
38
38
  python -m build --wheel
39
- - uses: actions/upload-artifact@v3
39
+ - uses: actions/upload-artifact@v4
40
40
  with:
41
- name: packages
41
+ name: packages-win-py${{ matrix.python-version }}
42
42
  path: "dist/*.whl"
43
43
 
44
44
  pypi-publish:
@@ -49,10 +49,11 @@ jobs:
49
49
  # IMPORTANT: this permission is mandatory for trusted publishing
50
50
  id-token: write
51
51
  steps:
52
- - uses: actions/download-artifact@v3
52
+ - uses: actions/download-artifact@v4
53
53
  with:
54
- name: packages
55
54
  path: dist
55
+ pattern: packages-*
56
+ merge-multiple: true
56
57
  - name: Test PyPI publish
57
58
  uses: pypa/gh-action-pypi-publish@release/v1
58
59
  with:
@@ -0,0 +1,28 @@
1
+ name: Build WASM/web port
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - master
7
+ tags:
8
+ - v[0-9]+.**
9
+
10
+ jobs:
11
+ build-web:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: mymindstorm/setup-emsdk@v14
16
+ with:
17
+ version: 3.1.57
18
+ actions-cache-folder: 'emsdk-cache'
19
+ - name: Build WASM port
20
+ run: |
21
+ cd web
22
+ mkdir build && cd build
23
+ emcmake cmake -DCMAKE_BUILD_TYPE="Release" ..
24
+ cmake --build .
25
+ - uses: actions/upload-artifact@v4
26
+ with:
27
+ name: web
28
+ path: "web/build/ProfileParserJs.*"
@@ -0,0 +1,11 @@
1
+ Metadata-Version: 2.1
2
+ Name: dioptic.profileparser
3
+ Version: 0.3.0
4
+ Summary: Parser library for ARGOS declarative profiles
5
+ Author-Email: =?utf-8?q?Peter_W=C3=BCrtz?= <pwuertz@gmail.com>
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Project-URL: Homepage, https://github.com/dioptic/profileparser
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+
11
+ # Dioptic declarative profile parser
@@ -0,0 +1,33 @@
1
+ cmake_minimum_required(VERSION 3.16)
2
+ project(libProfileParser CXX)
3
+
4
+ add_library(libProfileParser STATIC)
5
+ add_library(libProfileParser::libProfileParser ALIAS libProfileParser)
6
+
7
+ target_include_directories(libProfileParser PUBLIC
8
+ "${CMAKE_CURRENT_LIST_DIR}/include"
9
+ )
10
+
11
+ set_target_properties(libProfileParser PROPERTIES
12
+ CXX_STANDARD 20
13
+ CXX_STANDARD_REQUIRED ON
14
+ POSITION_INDEPENDENT_CODE ON
15
+ )
16
+
17
+ if(MSVC)
18
+ target_compile_options(libProfileParser PUBLIC /W3 /permissive- /Zc:__cplusplus)
19
+ else()
20
+ target_compile_options(libProfileParser PRIVATE -Wall -Wextra -Wpedantic)
21
+ endif()
22
+
23
+ target_sources(libProfileParser PRIVATE
24
+ "include/profileparser.hpp"
25
+ "include/profileparser/types.hpp"
26
+ "include/profileparser/utils.hpp"
27
+ "lineinfo.cpp"
28
+ "parser.cpp"
29
+ "parser.hpp"
30
+ "printers.cpp"
31
+ "validation.cpp"
32
+ "peglib.h" # Use FetchContent?
33
+ )
@@ -0,0 +1,143 @@
1
+ #pragma once
2
+
3
+ #include <memory>
4
+ #include <variant>
5
+ #include <vector>
6
+ #include <string>
7
+ #include <tuple>
8
+ #include <functional>
9
+ #include <unordered_map>
10
+ #include <stdexcept>
11
+
12
+ namespace Profile {
13
+
14
+ // AST elements
15
+
16
+ struct SourceInfo {
17
+ size_t offset;
18
+ size_t length;
19
+ };
20
+
21
+ using Boolean = bool;
22
+ using Integer = long;
23
+ using Float = double;
24
+ using String = std::string;
25
+
26
+ struct ListNode;
27
+ using ListNodePtr = std::shared_ptr<ListNode>;
28
+ struct DictNode;
29
+ using DictNodePtr = std::shared_ptr<DictNode>;
30
+
31
+ struct NumericExpressionNode;
32
+ using NumericExpressionNodePtr = std::shared_ptr<NumericExpressionNode>;
33
+ struct StringExpressionNode;
34
+ using StringExpressionNodePtr = std::shared_ptr<StringExpressionNode>;
35
+
36
+ struct ObjectNode;
37
+ using ObjectNodePtr = std::shared_ptr<ObjectNode>;
38
+ struct ReferenceNode;
39
+ using ReferenceNodePtr = std::shared_ptr<ReferenceNode>;
40
+ struct AttributeNode;
41
+ using AttributeNodePtr = std::shared_ptr<AttributeNode>;
42
+
43
+ struct ReferenceNode {
44
+ SourceInfo source_info;
45
+ std::string reference;
46
+ };
47
+
48
+ using Value = std::variant<ObjectNodePtr, ReferenceNodePtr, NumericExpressionNodePtr, StringExpressionNodePtr, ListNodePtr, DictNodePtr, Boolean, Integer, Float, String>;
49
+
50
+ struct KeyVal {
51
+ std::string key;
52
+ Value value;
53
+ };
54
+
55
+ struct DocString {
56
+ SourceInfo source_info;
57
+ std::vector<std::string> lines;
58
+ };
59
+
60
+ struct AttributeNode {
61
+ SourceInfo source_info;
62
+ SourceInfo value_source_info;
63
+ std::string name;
64
+ Value value;
65
+ };
66
+
67
+ struct ObjectNode {
68
+ using Item = std::variant<AttributeNodePtr, ObjectNodePtr>;
69
+
70
+ SourceInfo source_info;
71
+ DocString docstring;
72
+ std::string classname;
73
+ std::string id;
74
+ std::vector<ObjectNodePtr> children;
75
+ std::vector<AttributeNodePtr> attributes;
76
+ };
77
+
78
+ struct ListNode {
79
+ SourceInfo source_info;
80
+ std::string id;
81
+ std::vector<Value> items;
82
+ };
83
+
84
+ struct DictNode {
85
+ SourceInfo source_info;
86
+ std::string id;
87
+ std::vector<KeyVal> items;
88
+ };
89
+
90
+ struct NumericExpressionNode {
91
+ SourceInfo source_info;
92
+ std::string value;
93
+ std::vector<ReferenceNodePtr> references;
94
+ };
95
+
96
+ using StringFragment = std::variant<String, ReferenceNodePtr>;
97
+
98
+ struct StringExpressionNode {
99
+ SourceInfo source_info;
100
+ std::vector<StringFragment> fragments;
101
+ };
102
+
103
+ // Parser API
104
+
105
+ using CheckRefCallback = std::function<bool(const std::string&)>;
106
+ static inline const CheckRefCallback default_ref_callback = [](const std::string&) { return false; };
107
+
108
+ struct IdCollection {
109
+ std::unordered_map<std::string, const ObjectNode*> object_ids;
110
+ std::unordered_map<std::string, const AttributeNode*> value_ids;
111
+ };
112
+
113
+ struct ParsedProfile {
114
+ std::string source;
115
+ ObjectNodePtr root;
116
+ IdCollection ids;
117
+ };
118
+
119
+ struct LineInfo {
120
+ size_t line;
121
+ size_t column;
122
+ };
123
+
124
+ struct LineRange {
125
+ LineInfo begin;
126
+ LineInfo end;
127
+ };
128
+
129
+ struct ErrorLine {
130
+ LineInfo where;
131
+ std::string message;
132
+ };
133
+
134
+ struct ProfileError : public std::runtime_error {
135
+ ProfileError(std::string message, std::vector<ErrorLine> errors)
136
+ : std::runtime_error{std::move(message)}
137
+ , errors{std::move(errors)}
138
+ {}
139
+
140
+ std::vector<ErrorLine> errors;
141
+ };
142
+
143
+ }
@@ -0,0 +1,194 @@
1
+ #pragma once
2
+ #include <algorithm>
3
+ #include <sstream>
4
+ #include <profileparser/types.hpp>
5
+
6
+ namespace Profile {
7
+
8
+ /**
9
+ * @brief Locate line & column number of given source info.
10
+ */
11
+ LineInfo get_line_info(const std::string_view& source, const size_t offset);
12
+
13
+ /**
14
+ * @brief Locate line & column number of given source info.
15
+ */
16
+ inline LineInfo get_line_info(const std::string_view& source, const SourceInfo& info)
17
+ {
18
+ return get_line_info(source, info.offset);
19
+ }
20
+
21
+ /**
22
+ * @brief Locate line & column range of given source info.
23
+ */
24
+ inline LineRange get_line_range(const std::string_view& source, const SourceInfo& info)
25
+ {
26
+ return LineRange{
27
+ .begin=get_line_info(source, info.offset),
28
+ .end=get_line_info(source, info.offset + info.length),
29
+ };
30
+ }
31
+
32
+ /**
33
+ * @brief Find named attribute field in parsed object node.
34
+ */
35
+ inline AttributeNodePtr find_attribute(const ObjectNode& obj, const std::string& name)
36
+ {
37
+ auto it = std::find_if(obj.attributes.begin(), obj.attributes.end(), [&](const auto& a) {
38
+ return a->name == name;
39
+ });
40
+ if (it == obj.attributes.end()) {
41
+ return nullptr;
42
+ }
43
+ return (*it);
44
+ }
45
+
46
+ /**
47
+ * @brief visit_values Visit profile value objects.
48
+ * @param profile Profile object.
49
+ * @param visitor Value object visitor.
50
+ */
51
+ inline void visit_values(ObjectNodePtr& profile, auto&& visitor)
52
+ {
53
+ const auto isValue = [](const ObjectNodePtr& obj) -> bool {
54
+ return obj->classname == "Value";
55
+ };
56
+ for (auto& obj : profile->children) {
57
+ if (!isValue(obj)) { continue; }
58
+ visitor(obj);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * @brief visit_values Visit profile value objects.
64
+ * @param profile Profile object.
65
+ * @param visitor Value object visitor.
66
+ */
67
+ inline void visit_values(const ObjectNodePtr& profile, auto&& visitor)
68
+ {
69
+ const auto isValue = [](const ObjectNodePtr& obj) -> bool {
70
+ return obj->classname == "Value";
71
+ };
72
+ for (const auto& obj : profile->children) {
73
+ if (!isValue(obj)) { continue; }
74
+ visitor(obj);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @brief Template for implementing Profile AST visitors.
80
+ */
81
+ template<typename VisitorImpl>
82
+ struct Visitor
83
+ {
84
+ template<typename T>
85
+ void operator()([[maybe_unused]] const T& v) { }
86
+
87
+ template<typename T>
88
+ void operator()(const std::shared_ptr<T>& v) {
89
+ return self()(static_cast<const T&>(*v));
90
+ }
91
+
92
+ template<typename... Ts>
93
+ void operator()(const std::variant<Ts...>& v) {
94
+ std::visit([&](const auto& v_) { return self()(v_); }, v);
95
+ }
96
+
97
+ void operator()(const ListNode& r) {
98
+ for(const auto& v: r.items) { self()(v); };
99
+ }
100
+
101
+ void operator()(const DictNode& r) {
102
+ for(const auto& v: r.items) { self()(v); };
103
+ }
104
+
105
+ void operator()(const ObjectNode& obj) {
106
+ for (const ObjectNodePtr& child: obj.children) { self()(child); }
107
+ for (const AttributeNodePtr& attr: obj.attributes) { self()(attr->value); }
108
+ }
109
+
110
+ private:
111
+ inline VisitorImpl& self() { return static_cast<VisitorImpl&>(*this); }
112
+ };
113
+
114
+ /**
115
+ * @brief Visitor for collecting Objects and Values with an identifier
116
+ */
117
+ struct IdCollector : protected Visitor<IdCollector>
118
+ {
119
+ IdCollection& collection;
120
+ std::string_view source;
121
+ std::vector<ErrorLine>& errors;
122
+
123
+ IdCollector(IdCollection& collection, std::string_view source, std::vector<ErrorLine>& errors)
124
+ : Visitor<IdCollector>{}
125
+ , collection{collection}
126
+ , source{source}
127
+ , errors{errors}
128
+ { }
129
+
130
+ void operator()(const ObjectNode& obj)
131
+ {
132
+ // Object with id, check for duplicates
133
+ if (!obj.id.empty()) {
134
+ if (!collection.object_ids.emplace(obj.id, &obj).second) {
135
+ add_error("Object id '" + obj.id + "' was declared multiple times", obj.source_info);
136
+ }
137
+ }
138
+ // If object is 'Value', store reference to value attribute
139
+ if (obj.classname == "Value") {
140
+ if (obj.id.empty()) {
141
+ add_error("Value object is missing id", obj.source_info);
142
+ }
143
+ auto value_attr = find_attribute(obj, "value");
144
+ if (value_attr == nullptr) {
145
+ add_error("Value object '" + obj.id + "' is missing 'value' attribute", obj.source_info);
146
+ } else
147
+ collection.value_ids.emplace(obj.id, &(*value_attr));
148
+ }
149
+ // Continue search for objects in children and attribute list
150
+ Visitor<IdCollector>::operator()(obj);
151
+ }
152
+
153
+ inline void add_error(auto&& message, SourceInfo where)
154
+ {
155
+ errors.emplace_back(ErrorLine{
156
+ .where=get_line_info(source, where),
157
+ .message=std::forward<decltype(message)>(message),
158
+ });
159
+ }
160
+
161
+ protected:
162
+ friend struct Visitor<IdCollector>;
163
+ using Visitor::operator();
164
+ };
165
+
166
+
167
+ inline ProfileError format_error(const std::string_view& message, std::vector<ErrorLine>&& errors)
168
+ {
169
+ std::stringstream ss;
170
+ ss << std::move(message);
171
+ for (auto& line : errors) {
172
+ ss << '\n' << line.where.column << ':' << line.where.column << ' ' << line.message;
173
+ }
174
+ return ProfileError { ss.str(), std::move(errors) };
175
+ }
176
+
177
+ /**
178
+ * @brief Create ID collection from parsed profile.
179
+ *
180
+ * @throws ProfileError Thrown if profile contains invalid ID information.
181
+ */
182
+ inline IdCollection collect_ids(const ObjectNodePtr& obj, std::string_view source)
183
+ {
184
+ IdCollection collection;
185
+ std::vector<ErrorLine> errors;
186
+ IdCollector collect(collection, source, errors);
187
+ collect(*obj);
188
+ if (!errors.empty()) {
189
+ throw format_error("Error assembling object ID information", std::move(errors));
190
+ }
191
+ return collection;
192
+ }
193
+
194
+ } // namespace Profile
@@ -0,0 +1,58 @@
1
+ #pragma once
2
+ #include <profileparser/types.hpp>
3
+
4
+ namespace Profile {
5
+
6
+ /**
7
+ * @brief Parse profile from source string.
8
+ *
9
+ * @param source Profile source.
10
+ * @throws ProfileError Thrown if parsing profile from source failed.
11
+ * @return Parsed profile.
12
+ */
13
+ ParsedProfile parse(std::string source);
14
+
15
+ /**
16
+ * @brief Get source information from node source reference
17
+ * @param profile Parsed profile.
18
+ * @param info Source reference from node.
19
+ * @return Corresponding line/column source range.
20
+ */
21
+ LineRange get_source_range(const ParsedProfile& profile, SourceInfo info);
22
+
23
+ /**
24
+ * @brief Serialize profile object to profile source.
25
+ * @param profile Parsed profile.
26
+ * @return Profile source serialization.
27
+ */
28
+ std::string to_profile_source(const ParsedProfile& profile);
29
+
30
+ /**
31
+ * @brief Re-emit profile source with given value substitutions.
32
+ *
33
+ * @param profile Parsed profile.
34
+ * @param values Value substitutions.
35
+ * @return Updated profile source.
36
+ */
37
+ std::string to_updated_source(const ParsedProfile& profile, const std::unordered_map<std::string, Value>& values);
38
+ std::string to_updated_source(const ParsedProfile& profile, const std::unordered_map<const AttributeNode*, Value>& values);
39
+
40
+ /**
41
+ * @brief Serialize profile object to JSON representation.
42
+ * @param profile Parsed profile.
43
+ * @param eval_expressions If true, serialize with expressions evaluated to constants.
44
+ * @return JSON serialization.
45
+ */
46
+ std::string to_json_ast(const ParsedProfile& profile, const bool eval_expressions);
47
+
48
+ /**
49
+ * @brief Validate profile for use in `argosprocess`.
50
+ *
51
+ * @param profile Parsed profile.
52
+ * @param is_allowed_ref Callback for validating external references.
53
+ *
54
+ * @throws ProfileError Thrown if semantic validation failed.
55
+ */
56
+ void validate_profile(const ParsedProfile& profile, const CheckRefCallback& is_allowed_ref = default_ref_callback);
57
+
58
+ } // namespace Profile
@@ -0,0 +1,21 @@
1
+ #include <profileparser.hpp>
2
+ #include <profileparser/utils.hpp>
3
+ #include "peglib.h"
4
+
5
+ namespace Profile
6
+ {
7
+
8
+ LineInfo get_line_info(const std::string_view& source, const size_t offset)
9
+ {
10
+ // TODO: `peg::line_info` correctly counts unicode characters, but lacks line-cache like `vs.line_info`.
11
+ const auto* sourceStart = source.data();
12
+ auto [line, column] = peg::line_info(sourceStart, sourceStart + offset);
13
+ return LineInfo{ .line=line, .column=column };
14
+ }
15
+
16
+ LineRange get_source_range(const ParsedProfile& profile, SourceInfo info)
17
+ {
18
+ return get_line_range(profile.source, info);
19
+ }
20
+
21
+ } // namespace Profile