Skip to content

Commit 72bc6cf

Browse files
authored
Introduce a better symbol management mechanism (#10)
Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent 49b4b6c commit 72bc6cf

File tree

40 files changed

+2027
-1365
lines changed

40 files changed

+2027
-1365
lines changed

src/generator/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
sourcemeta_library(NAMESPACE sourcemeta PROJECT codegen NAME generator
22
FOLDER "Codegen/Generator"
33
PRIVATE_HEADERS typescript.h
4-
SOURCES typescript.cc)
4+
SOURCES typescript.cc mangle.cc)
55

66
if(CODEGEN_INSTALL)
77
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT codegen NAME generator)

src/generator/include/sourcemeta/codegen/generator.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,31 @@
1313

1414
#include <sourcemeta/core/jsonpointer.h>
1515

16+
#include <map> // std::map
1617
#include <ostream> // std::ostream
18+
#include <string> // std::string
1719
#include <string_view> // std::string_view
1820
#include <variant> // std::visit
21+
#include <vector> // std::vector
1922

2023
/// @defgroup generator Generator
2124
/// @brief The codegen JSON Schema code generation package
2225

2326
namespace sourcemeta::codegen {
2427

28+
/// @ingroup generator
29+
SOURCEMETA_CODEGEN_GENERATOR_EXPORT
30+
auto mangle(const std::string_view prefix,
31+
const sourcemeta::core::Pointer &pointer,
32+
const std::vector<std::string> &symbol,
33+
std::map<std::string, sourcemeta::core::Pointer> &cache)
34+
-> const std::string &;
35+
2536
/// @ingroup generator
2637
template <typename T>
2738
auto generate(std::ostream &output, const IRResult &result,
2839
const std::string_view prefix = "Schema") -> void {
29-
const T visitor{output, prefix};
40+
T visitor{output, prefix};
3041
const char *separator{""};
3142
for (const auto &entity : result) {
3243
output << separator;

src/generator/include/sourcemeta/codegen/generator_typescript.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@
77

88
#include <sourcemeta/codegen/ir.h>
99

10+
#include <sourcemeta/core/jsonpointer.h>
11+
12+
#include <map> // std::map
1013
#include <ostream> // std::ostream
14+
#include <string> // std::string
1115
#include <string_view> // std::string_view
1216

1317
namespace sourcemeta::codegen {
@@ -16,14 +20,14 @@ namespace sourcemeta::codegen {
1620
class SOURCEMETA_CODEGEN_GENERATOR_EXPORT TypeScript {
1721
public:
1822
TypeScript(std::ostream &stream, std::string_view type_prefix);
19-
auto operator()(const IRScalar &entry) const -> void;
20-
auto operator()(const IREnumeration &entry) const -> void;
21-
auto operator()(const IRObject &entry) const -> void;
22-
auto operator()(const IRImpossible &entry) const -> void;
23-
auto operator()(const IRArray &entry) const -> void;
24-
auto operator()(const IRReference &entry) const -> void;
25-
auto operator()(const IRTuple &entry) const -> void;
26-
auto operator()(const IRUnion &entry) const -> void;
23+
auto operator()(const IRScalar &entry) -> void;
24+
auto operator()(const IREnumeration &entry) -> void;
25+
auto operator()(const IRObject &entry) -> void;
26+
auto operator()(const IRImpossible &entry) -> void;
27+
auto operator()(const IRArray &entry) -> void;
28+
auto operator()(const IRReference &entry) -> void;
29+
auto operator()(const IRTuple &entry) -> void;
30+
auto operator()(const IRUnion &entry) -> void;
2731

2832
private:
2933
// Exporting symbols that depends on the standard C++ library is considered
@@ -35,6 +39,7 @@ class SOURCEMETA_CODEGEN_GENERATOR_EXPORT TypeScript {
3539
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
3640
std::ostream &output;
3741
std::string_view prefix;
42+
std::map<std::string, sourcemeta::core::Pointer> cache;
3843
#if defined(_MSC_VER)
3944
#pragma warning(default : 4251)
4045
#endif

src/generator/mangle.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#include <sourcemeta/codegen/generator.h>
2+
3+
namespace {
4+
5+
auto is_alpha(char character) -> bool {
6+
return (character >= 'a' && character <= 'z') ||
7+
(character >= 'A' && character <= 'Z');
8+
}
9+
10+
auto is_digit(char character) -> bool {
11+
return character >= '0' && character <= '9';
12+
}
13+
14+
auto to_upper(char character) -> char {
15+
if (character >= 'a' && character <= 'z') {
16+
return static_cast<char>(character - 'a' + 'A');
17+
}
18+
return character;
19+
}
20+
21+
auto symbol_to_identifier(const std::string_view prefix,
22+
const std::vector<std::string> &symbol)
23+
-> std::string {
24+
std::string result{prefix};
25+
26+
for (const auto &segment : symbol) {
27+
if (segment.empty()) {
28+
continue;
29+
}
30+
31+
bool first_in_segment{true};
32+
for (const auto character : segment) {
33+
if (is_alpha(character)) {
34+
if (first_in_segment) {
35+
result += to_upper(character);
36+
first_in_segment = false;
37+
} else {
38+
result += character;
39+
}
40+
} else if (is_digit(character)) {
41+
if (first_in_segment) {
42+
result += '_';
43+
}
44+
result += character;
45+
first_in_segment = false;
46+
} else if (character == '_' || character == '$') {
47+
result += character;
48+
first_in_segment = false;
49+
}
50+
}
51+
}
52+
53+
if (result.empty()) {
54+
return "_";
55+
}
56+
57+
if (is_digit(result[0])) {
58+
result.insert(0, "_");
59+
}
60+
61+
return result;
62+
}
63+
64+
} // namespace
65+
66+
namespace sourcemeta::codegen {
67+
68+
auto mangle(const std::string_view prefix,
69+
const sourcemeta::core::Pointer &pointer,
70+
const std::vector<std::string> &symbol,
71+
std::map<std::string, sourcemeta::core::Pointer> &cache)
72+
-> const std::string & {
73+
auto name{symbol_to_identifier(prefix, symbol)};
74+
75+
while (true) {
76+
auto iterator{cache.find(name)};
77+
if (iterator != cache.end()) {
78+
if (iterator->second == pointer) {
79+
return iterator->first;
80+
}
81+
82+
name.insert(0, "_");
83+
} else {
84+
auto result{cache.insert({std::move(name), pointer})};
85+
return result.first->first;
86+
}
87+
}
88+
}
89+
90+
} // namespace sourcemeta::codegen

src/generator/typescript.cc

Lines changed: 38 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ namespace sourcemeta::codegen {
4040
TypeScript::TypeScript(std::ostream &stream, const std::string_view type_prefix)
4141
: output{stream}, prefix{type_prefix} {}
4242

43-
auto TypeScript::operator()(const IRScalar &entry) const -> void {
43+
auto TypeScript::operator()(const IRScalar &entry) -> void {
4444
this->output << "export type "
45-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
45+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
4646
<< " = ";
4747

4848
switch (entry.value) {
@@ -64,9 +64,9 @@ auto TypeScript::operator()(const IRScalar &entry) const -> void {
6464
this->output << ";\n";
6565
}
6666

67-
auto TypeScript::operator()(const IREnumeration &entry) const -> void {
67+
auto TypeScript::operator()(const IREnumeration &entry) -> void {
6868
this->output << "export type "
69-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
69+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
7070
<< " = ";
7171

7272
const char *separator{""};
@@ -79,19 +79,20 @@ auto TypeScript::operator()(const IREnumeration &entry) const -> void {
7979
this->output << ";\n";
8080
}
8181

82-
auto TypeScript::operator()(const IRObject &entry) const -> void {
83-
const auto type_name{sourcemeta::core::mangle(entry.pointer, this->prefix)};
82+
auto TypeScript::operator()(const IRObject &entry) -> void {
83+
const auto type_name{
84+
mangle(this->prefix, entry.pointer, entry.symbol, this->cache)};
8485
const auto has_typed_additional{
8586
std::holds_alternative<IRType>(entry.additional)};
8687
const auto allows_any_additional{
8788
std::holds_alternative<bool>(entry.additional) &&
8889
std::get<bool>(entry.additional)};
8990

9091
if (has_typed_additional && entry.members.empty()) {
92+
const auto &additional_type{std::get<IRType>(entry.additional)};
9193
this->output << "export type " << type_name << " = Record<string, "
92-
<< sourcemeta::core::mangle(
93-
std::get<IRType>(entry.additional).pointer,
94-
this->prefix)
94+
<< mangle(this->prefix, additional_type.pointer,
95+
additional_type.symbol, this->cache)
9596
<< ">;\n";
9697
return;
9798
}
@@ -117,7 +118,8 @@ auto TypeScript::operator()(const IRObject &entry) const -> void {
117118
this->output << " " << readonly_marker << "\""
118119
<< escape_string(member_name) << "\"" << optional_marker
119120
<< ": "
120-
<< sourcemeta::core::mangle(member_value.pointer, this->prefix)
121+
<< mangle(this->prefix, member_value.pointer,
122+
member_value.symbol, this->cache)
121123
<< ";\n";
122124
}
123125

@@ -135,35 +137,36 @@ auto TypeScript::operator()(const IRObject &entry) const -> void {
135137
this->output << " // match a superset of what JSON Schema allows\n";
136138
for (const auto &[member_name, member_value] : entry.members) {
137139
this->output << " "
138-
<< sourcemeta::core::mangle(member_value.pointer,
139-
this->prefix)
140+
<< mangle(this->prefix, member_value.pointer,
141+
member_value.symbol, this->cache)
140142
<< " |\n";
141143
}
142144

145+
const auto &additional_type{std::get<IRType>(entry.additional)};
143146
this->output << " "
144-
<< sourcemeta::core::mangle(
145-
std::get<IRType>(entry.additional).pointer,
146-
this->prefix)
147+
<< mangle(this->prefix, additional_type.pointer,
148+
additional_type.symbol, this->cache)
147149
<< " |\n";
148150
this->output << " undefined;\n";
149151
}
150152

151153
this->output << "}\n";
152154
}
153155

154-
auto TypeScript::operator()(const IRImpossible &entry) const -> void {
156+
auto TypeScript::operator()(const IRImpossible &entry) -> void {
155157
this->output << "export type "
156-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
158+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
157159
<< " = never;\n";
158160
}
159161

160-
auto TypeScript::operator()(const IRArray &entry) const -> void {
162+
auto TypeScript::operator()(const IRArray &entry) -> void {
161163
this->output << "export type "
162-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
164+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
163165
<< " = ";
164166

165167
if (entry.items.has_value()) {
166-
this->output << sourcemeta::core::mangle(entry.items->pointer, this->prefix)
168+
this->output << mangle(this->prefix, entry.items->pointer,
169+
entry.items->symbol, this->cache)
167170
<< "[]";
168171
} else {
169172
this->output << "unknown[]";
@@ -172,44 +175,48 @@ auto TypeScript::operator()(const IRArray &entry) const -> void {
172175
this->output << ";\n";
173176
}
174177

175-
auto TypeScript::operator()(const IRReference &entry) const -> void {
178+
auto TypeScript::operator()(const IRReference &entry) -> void {
176179
this->output << "export type "
177-
<< sourcemeta::core::mangle(entry.pointer, this->prefix) << " = "
178-
<< sourcemeta::core::mangle(entry.target.pointer, this->prefix)
180+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
181+
<< " = "
182+
<< mangle(this->prefix, entry.target.pointer,
183+
entry.target.symbol, this->cache)
179184
<< ";\n";
180185
}
181186

182-
auto TypeScript::operator()(const IRTuple &entry) const -> void {
187+
auto TypeScript::operator()(const IRTuple &entry) -> void {
183188
this->output << "export type "
184-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
189+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
185190
<< " = [";
186191

187192
const char *separator{""};
188193
for (const auto &item : entry.items) {
189194
this->output << separator
190-
<< sourcemeta::core::mangle(item.pointer, this->prefix);
195+
<< mangle(this->prefix, item.pointer, item.symbol,
196+
this->cache);
191197
separator = ", ";
192198
}
193199

194200
if (entry.additional.has_value()) {
195201
this->output << separator << "..."
196-
<< sourcemeta::core::mangle(entry.additional->pointer,
197-
this->prefix)
202+
<< mangle(this->prefix, entry.additional->pointer,
203+
entry.additional->symbol, this->cache)
198204
<< "[]";
199205
}
200206

201207
this->output << "];\n";
202208
}
203209

204-
auto TypeScript::operator()(const IRUnion &entry) const -> void {
210+
auto TypeScript::operator()(const IRUnion &entry) -> void {
205211
this->output << "export type "
206-
<< sourcemeta::core::mangle(entry.pointer, this->prefix)
212+
<< mangle(this->prefix, entry.pointer, entry.symbol, this->cache)
207213
<< " =\n";
208214

209215
const char *separator{""};
210216
for (const auto &value : entry.values) {
211217
this->output << separator << " "
212-
<< sourcemeta::core::mangle(value.pointer, this->prefix);
218+
<< mangle(this->prefix, value.pointer, value.symbol,
219+
this->cache);
213220
separator = " |\n";
214221
}
215222

src/ir/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
sourcemeta_library(NAMESPACE sourcemeta PROJECT codegen NAME ir
22
FOLDER "Codegen/IR"
33
PRIVATE_HEADERS error.h
4-
SOURCES ir.cc ir_default_compiler.h)
4+
SOURCES ir.cc ir_symbol.cc ir_default_compiler.h)
55

66
if(CODEGEN_INSTALL)
77
sourcemeta_library_install(NAMESPACE sourcemeta PROJECT codegen NAME ir)

src/ir/include/sourcemeta/codegen/ir.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <cstdint> // std::uint8_t
1717
#include <functional> // std::function
1818
#include <optional> // std::optional, std::nullopt
19+
#include <string> // std::string
1920
#include <utility> // std::pair
2021
#include <variant> // std::variant
2122
#include <vector> // std::vector
@@ -37,6 +38,7 @@ enum class IRScalarType : std::uint8_t {
3738
/// @ingroup ir
3839
struct IRType {
3940
sourcemeta::core::Pointer pointer;
41+
std::vector<std::string> symbol;
4042
};
4143

4244
/// @ingroup ir
@@ -116,6 +118,12 @@ auto compile(const sourcemeta::core::JSON &schema,
116118
const std::string_view default_dialect = "",
117119
const std::string_view default_id = "") -> IRResult;
118120

121+
/// @ingroup ir
122+
SOURCEMETA_CODEGEN_IR_EXPORT
123+
auto symbol(const sourcemeta::core::SchemaFrame &frame,
124+
const sourcemeta::core::SchemaFrame::Location &location)
125+
-> std::vector<std::string>;
126+
119127
} // namespace sourcemeta::codegen
120128

121129
#endif

0 commit comments

Comments
 (0)