TextMateLib 1.0
Modern C++ implementation of the TextMate syntax highlighting engine
Loading...
Searching...
No Matches
parseRawGrammar.cpp
1#include "parseRawGrammar.h"
2#include "rapidjson/document.h"
3#include "rapidjson/error/en.h"
4#include <stdexcept>
5
6using namespace rapidjson;
7
8namespace tml {
9
10// Helper function to get string from JSON value
11static std::string* getStringPtr(const Value& val) {
12 if (val.IsString()) {
13 return new std::string(val.GetString(), val.GetStringLength());
14 }
15 return nullptr;
16}
17
18// Forward declarations
19static IRawRule* parseRule(const Value& ruleObj);
20static std::vector<IRawRule*>* parsePatterns(const Value& patternsArray);
21
22// Helper function to parse captures
23static IRawCaptures* parseCaptures(const Value& capturesObj) {
24 if (!capturesObj.IsObject()) {
25 return nullptr;
26 }
27
28 IRawCaptures* captures = new IRawCaptures();
29 for (auto it = capturesObj.MemberBegin(); it != capturesObj.MemberEnd(); ++it) {
30 std::string key(it->name.GetString(), it->name.GetStringLength());
31 IRawRule* rule = new IRawRule();
32
33 if (it->value.IsObject()) {
34 const Value& ruleObj = it->value;
35
36 if (ruleObj.HasMember("name") && ruleObj["name"].IsString()) {
37 rule->name = getStringPtr(ruleObj["name"]);
38 }
39 if (ruleObj.HasMember("contentName") && ruleObj["contentName"].IsString()) {
40 rule->contentName = getStringPtr(ruleObj["contentName"]);
41 }
42 if (ruleObj.HasMember("patterns") && ruleObj["patterns"].IsArray()) {
43 // Parse patterns for this capture
44 rule->patterns = parsePatterns(ruleObj["patterns"]);
45 }
46 }
47
48 captures->captures[key] = rule;
49 }
50
51 return captures;
52}
53
54// Helper function to parse patterns array
55static std::vector<IRawRule*>* parsePatterns(const Value& patternsArray) {
56 if (!patternsArray.IsArray()) {
57 return nullptr;
58 }
59
60 std::vector<IRawRule*>* patterns = new std::vector<IRawRule*>();
61 for (SizeType i = 0; i < patternsArray.Size(); i++) {
62 const Value& patternObj = patternsArray[i];
63 if (patternObj.IsObject()) {
64 patterns->push_back(parseRule(patternObj));
65 }
66 }
67
68 return patterns;
69}
70
71// Helper function to parse repository
72static IRawRepository* parseRepository(const Value& repoObj) {
73 if (!repoObj.IsObject()) {
74 return nullptr;
75 }
76
77 IRawRepository* repository = new IRawRepository();
78
79 for (auto it = repoObj.MemberBegin(); it != repoObj.MemberEnd(); ++it) {
80 std::string key(it->name.GetString(), it->name.GetStringLength());
81
82 if (key == "$self") {
83 if (it->value.IsObject()) {
84 repository->selfRule = parseRule(it->value);
85 }
86 } else if (key == "$base") {
87 if (it->value.IsObject()) {
88 repository->baseRule = parseRule(it->value);
89 }
90 } else {
91 if (it->value.IsObject()) {
92 repository->rules[key] = parseRule(it->value);
93 }
94 }
95 }
96
97 return repository;
98}
99
100// Parse a single rule
101static IRawRule* parseRule(const Value& ruleObj) {
102 if (!ruleObj.IsObject()) {
103 return nullptr;
104 }
105
106 IRawRule* rule = new IRawRule();
107
108 // Parse include
109 if (ruleObj.HasMember("include") && ruleObj["include"].IsString()) {
110 rule->include = getStringPtr(ruleObj["include"]);
111 }
112
113 // Parse name
114 if (ruleObj.HasMember("name") && ruleObj["name"].IsString()) {
115 rule->name = getStringPtr(ruleObj["name"]);
116 }
117
118 // Parse contentName
119 if (ruleObj.HasMember("contentName") && ruleObj["contentName"].IsString()) {
120 rule->contentName = getStringPtr(ruleObj["contentName"]);
121 }
122
123 // Parse match
124 if (ruleObj.HasMember("match") && ruleObj["match"].IsString()) {
125 rule->match = getStringPtr(ruleObj["match"]);
126 }
127
128 // Parse captures
129 if (ruleObj.HasMember("captures") && ruleObj["captures"].IsObject()) {
130 rule->captures = parseCaptures(ruleObj["captures"]);
131 }
132
133 // Parse begin
134 if (ruleObj.HasMember("begin") && ruleObj["begin"].IsString()) {
135 rule->begin = getStringPtr(ruleObj["begin"]);
136 }
137
138 // Parse beginCaptures
139 if (ruleObj.HasMember("beginCaptures") && ruleObj["beginCaptures"].IsObject()) {
140 rule->beginCaptures = parseCaptures(ruleObj["beginCaptures"]);
141 }
142
143 // Parse end
144 if (ruleObj.HasMember("end") && ruleObj["end"].IsString()) {
145 rule->end = getStringPtr(ruleObj["end"]);
146 }
147
148 // Parse endCaptures
149 if (ruleObj.HasMember("endCaptures") && ruleObj["endCaptures"].IsObject()) {
150 rule->endCaptures = parseCaptures(ruleObj["endCaptures"]);
151 }
152
153 // Parse while
154 if (ruleObj.HasMember("while") && ruleObj["while"].IsString()) {
155 rule->whilePattern = getStringPtr(ruleObj["while"]);
156 }
157
158 // Parse whileCaptures
159 if (ruleObj.HasMember("whileCaptures") && ruleObj["whileCaptures"].IsObject()) {
160 rule->whileCaptures = parseCaptures(ruleObj["whileCaptures"]);
161 }
162
163 // Parse patterns
164 if (ruleObj.HasMember("patterns") && ruleObj["patterns"].IsArray()) {
165 rule->patterns = parsePatterns(ruleObj["patterns"]);
166 }
167
168 // Parse repository
169 if (ruleObj.HasMember("repository") && ruleObj["repository"].IsObject()) {
170 rule->repository = parseRepository(ruleObj["repository"]);
171 }
172
173 // Parse applyEndPatternLast
174 if (ruleObj.HasMember("applyEndPatternLast") && ruleObj["applyEndPatternLast"].IsBool()) {
175 rule->applyEndPatternLast = new bool(ruleObj["applyEndPatternLast"].GetBool());
176 }
177
178 return rule;
179}
180
181// Parse JSON grammar
182IRawGrammar* parseJSONGrammar(const std::string& content, const std::string* filename) {
183 Document doc;
184
185 // Parse JSON document
186 ParseResult result = doc.Parse(content.c_str());
187 if (!result) {
188 std::string error = "JSON parse error: ";
189 error += GetParseError_En(result.Code());
190 error += " at offset " + std::to_string(result.Offset());
191 throw std::runtime_error(error);
192 }
193
194 if (!doc.IsObject()) {
195 throw std::runtime_error("Grammar JSON must be an object");
196 }
197
198 IRawGrammar* grammar = new IRawGrammar();
199
200 // Parse scopeName (required)
201 if (doc.HasMember("scopeName") && doc["scopeName"].IsString()) {
202 grammar->scopeName = std::string(doc["scopeName"].GetString(),
203 doc["scopeName"].GetStringLength());
204 } else {
205 delete grammar;
206 throw std::runtime_error("Grammar must have a scopeName");
207 }
208
209 // Parse patterns (required)
210 if (doc.HasMember("patterns") && doc["patterns"].IsArray()) {
211 const Value& patternsArray = doc["patterns"];
212 // IRawGrammar now inherits from IRawRule, so patterns is a pointer
213 grammar->patterns = new std::vector<IRawRule*>();
214 for (SizeType i = 0; i < patternsArray.Size(); i++) {
215 if (patternsArray[i].IsObject()) {
216 grammar->patterns->push_back(parseRule(patternsArray[i]));
217 }
218 }
219 }
220
221 // Parse repository
222 if (doc.HasMember("repository") && doc["repository"].IsObject()) {
223 grammar->repository = parseRepository(doc["repository"]);
224 }
225
226 // Parse injections
227 if (doc.HasMember("injections") && doc["injections"].IsObject()) {
228 grammar->injections = new std::map<std::string, IRawRule*>();
229 const Value& injectionsObj = doc["injections"];
230
231 for (auto it = injectionsObj.MemberBegin(); it != injectionsObj.MemberEnd(); ++it) {
232 std::string key(it->name.GetString(), it->name.GetStringLength());
233 if (it->value.IsObject()) {
234 (*grammar->injections)[key] = parseRule(it->value);
235 }
236 }
237 }
238
239 // Parse injectionSelector
240 if (doc.HasMember("injectionSelector") && doc["injectionSelector"].IsString()) {
241 grammar->injectionSelector = getStringPtr(doc["injectionSelector"]);
242 }
243
244 // Parse fileTypes
245 if (doc.HasMember("fileTypes") && doc["fileTypes"].IsArray()) {
246 grammar->fileTypes = new std::vector<std::string>();
247 const Value& fileTypesArray = doc["fileTypes"];
248 for (SizeType i = 0; i < fileTypesArray.Size(); i++) {
249 if (fileTypesArray[i].IsString()) {
250 grammar->fileTypes->push_back(
251 std::string(fileTypesArray[i].GetString(),
252 fileTypesArray[i].GetStringLength())
253 );
254 }
255 }
256 }
257
258 // Parse name
259 if (doc.HasMember("name") && doc["name"].IsString()) {
260 grammar->name = getStringPtr(doc["name"]);
261 }
262
263 // Parse firstLineMatch
264 if (doc.HasMember("firstLineMatch") && doc["firstLineMatch"].IsString()) {
265 grammar->firstLineMatch = getStringPtr(doc["firstLineMatch"]);
266 }
267
268 return grammar;
269}
270
271// Parse raw grammar (determines format and calls appropriate parser)
272IRawGrammar* parseRawGrammar(const std::string& content, const std::string* filePath) {
273 // For now, we only support JSON format
274 // PLIST support could be added later if needed
275
276 bool isJSON = true;
277 if (filePath != nullptr) {
278 // Check file extension
279 size_t dotPos = filePath->find_last_of('.');
280 if (dotPos != std::string::npos) {
281 std::string ext = filePath->substr(dotPos);
282 isJSON = (ext == ".json");
283 }
284 }
285
286 if (isJSON) {
287 return parseJSONGrammar(content, filePath);
288 } else {
289 throw std::runtime_error("Only JSON grammar format is currently supported");
290 }
291}
292
293} // namespace tml