1 // =================================
  2 // Copyright (c) 2020 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <sng2html/sng2html/Project.hpp>
  7 #include <sng2html/sng2html/LinkerVisitor.hpp>
  8 #include <sng2html/sng2html/GrammarHtmlGenerator.hpp>
  9 #include <sngxml/xpath/XPathEvaluate.hpp>
 10 #include <sngxml/dom/Element.hpp>
 11 #include <soulng/util/Path.hpp>
 12 #include <soulng/util/Unicode.hpp>
 13 #include <sng2html/sng2html/LexerFileLexer.hpp>
 14 #include <sng2html/sng2html/LexerFileParser.hpp>
 15 #include <sng2html/sng2html/ParserFileLexer.hpp>
 16 #include <sng2html/sng2html/ParserFileParser.hpp>
 17 #include <boost/filesystem.hpp>
 18 #include <iostream>
 19 
 20 namespace sng2html { namespace sng2html {
 21 
 22 using namespace soulng::unicode;
 23 
 24 Project::Project(bool verbose_const std::string& xmlFilePath_) :
 25     verbose(verbose_)xmlFilePath(xmlFilePath_)rootDir(Path::GetDirectoryName(GetFullPath(xmlFilePath)))xmlDoc(sngxml::dom::ReadDocument(xmlFilePath))
 26     lexerContext(IdentifierClassKind::unicode)
 27 {
 28 }
 29 
 30 void Project::Process()
 31 {
 32     ReadLexerFiles();
 33     ReadParserFiles();
 34     ReadOutDir();
 35     ReadStyleFilePath();
 36     Link();
 37     ParseLexerFiles();
 38     ReadGrammarFiles();
 39     GenerateHtml();
 40 }
 41 
 42 void Project::ReadLexerFiles()
 43 {
 44     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/lexer"xmlDoc.get());
 45     if (result)
 46     {
 47         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 48         {
 49             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 50             int n = nodeSet->Length();
 51             for (int i = 0; i < n; ++i)
 52             {
 53                 sngxml::dom::Node* node = (*nodeSet)[i];
 54                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 55                 {
 56                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 57                     std::u32string fileAttribute = element->GetAttribute(U"file");
 58                     if (!fileAttribute.empty())
 59                     {
 60                         std::string lexerFilePath = GetFullPath(Path::Combine(rootDirPath::MakeCanonical(ToUtf8(fileAttribute))));
 61                         if (verbose)
 62                         {
 63                             std::cout << "> " << lexerFilePath << std::endl;
 64                         }
 65                         LexerFileLexer lexerFileLexer(ToUtf32(ReadFile(lexerFilePath))lexerFilePath0);
 66                         std::unique_ptr<LexerFile> lexerFile = LexerFileParser::Parse(lexerFileLexer);
 67                         lexerMap[lexerFile->GetLexer()->Name()] = lexerFile.get();
 68                         lexerFiles.push_back(std::move(lexerFile));
 69                     }
 70                 }
 71             }
 72         }
 73     }
 74 }
 75 
 76 void Project::ReadParserFiles()
 77 {
 78     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/parser"xmlDoc.get());
 79     if (result)
 80     {
 81         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
 82         {
 83             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
 84             int n = nodeSet->Length();
 85             for (int i = 0; i < n; ++i)
 86             {
 87                 sngxml::dom::Node* node = (*nodeSet)[i];
 88                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
 89                 {
 90                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
 91                     std::u32string fileAttribute = element->GetAttribute(U"file");
 92                     if (!fileAttribute.empty())
 93                     {
 94                         std::string parserFilePath = GetFullPath(Path::Combine(rootDirPath::MakeCanonical(ToUtf8(fileAttribute))));
 95                         if (verbose)
 96                         {
 97                             std::cout << "> " << parserFilePath << std::endl;
 98                         }
 99                         ParserFileLexer parserFileLexer(ToUtf32(ReadFile(parserFilePath))parserFilePath0);
100                         std::unique_ptr<ParserFile> parserFile = ParserFileParser::Parse(parserFileLexer);
101                         domain.AddParserFile(parserFile.get());
102                         parserFiles.push_back(std::move(parserFile));
103                     }
104                 }
105             }
106         }
107     }
108 }
109 
110 void Project::ReadOutDir()
111 {
112     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/out"xmlDoc.get());
113     if (result)
114     {
115         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
116         {
117             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
118             int n = nodeSet->Length();
119             for (int i = 0; i < n; ++i)
120             {
121                 sngxml::dom::Node* node = (*nodeSet)[i];
122                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
123                 {
124                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
125                     std::u32string dirAttribute = element->GetAttribute(U"dir");
126                     if (!dirAttribute.empty())
127                     {
128                         if (outDir.empty())
129                         {
130                             outDir = GetFullPath(Path::Combine(rootDirPath::MakeCanonical(ToUtf8(dirAttribute))));
131                         }
132                         else
133                         {
134                             throw std::runtime_error("output directory already specified");
135                         }
136                     }
137                 }
138             }
139         }
140     }
141     if (outDir.empty())
142     {
143         throw std::runtime_error("output directory not specified");
144     }
145     boost::filesystem::create_directories(outDir);
146 }
147 
148 void Project::ReadStyleFilePath()
149 {
150     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/style"xmlDoc.get());
151     if (result)
152     {
153         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
154         {
155             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
156             int n = nodeSet->Length();
157             for (int i = 0; i < n; ++i)
158             {
159                 sngxml::dom::Node* node = (*nodeSet)[i];
160                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
161                 {
162                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
163                     std::u32string fileAttribute = element->GetAttribute(U"file");
164                     if (!fileAttribute.empty())
165                     {
166                         if (styleFilePath.empty())
167                         {
168                             styleFilePath = fileAttribute;
169                         }
170                         else
171                         {
172                             throw std::runtime_error("style file path already specified");
173                         }
174                     }
175                     else
176                     {
177                         throw std::runtime_error("style file path is empty");
178                     }
179                 }
180             }
181         }
182     }
183     if (styleFilePath.empty())
184     {
185         throw std::runtime_error("style file path not specified");
186     }
187 }
188 
189 void Project::ReadGrammarFiles()
190 {
191     std::unique_ptr<sngxml::xpath::XPathObject> result = sngxml::xpath::Evaluate(U"/project/grammar"xmlDoc.get());
192     if (result)
193     {
194         if (result->Type() == sngxml::xpath::XPathObjectType::nodeSet)
195         {
196             sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(result.get());
197             int n = nodeSet->Length();
198             for (int i = 0; i < n; ++i)
199             {
200                 sngxml::dom::Node* node = (*nodeSet)[i];
201                 if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
202                 {
203                     sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
204                     std::u32string fileAttribute = element->GetAttribute(U"file");
205                     if (!fileAttribute.empty())
206                     {
207                         std::u32string grammarName = ToUtf32(Path::GetFileNameWithoutExtension(ToUtf8(fileAttribute)));
208                         std::string grammarFilePath = GetFullPath(Path::Combine(rootDirPath::MakeCanonical(ToUtf8(fileAttribute))));
209                         std::unique_ptr<sngxml::dom::Document> grammarDoc = sngxml::dom::ReadDocument(grammarFilePath);
210                         std::u32string kindAttribute = grammarDoc->DocumentElement()->GetAttribute(U"kind");
211                         std::u32string sourceAttribute = grammarDoc->DocumentElement()->GetAttribute(U"source");
212                         std::u32string titleAttribute = grammarDoc->DocumentElement()->GetAttribute(U"title");
213                         if (sourceAttribute.empty())
214                         {
215                             throw std::runtime_error("grammar source not specified");
216                         }
217                         if (titleAttribute.empty())
218                         {
219                             throw std::runtime_error("grammar title not specified");
220                         }
221                         std::unique_ptr<Grammar> grammar;
222                         GrammarParser* grammarParser = nullptr;
223                         LexerFile* lexerFile = nullptr;
224                         if (kindAttribute == U"lexical")
225                         {
226                             auto it = lexerMap.find(sourceAttribute);
227                             if (it != lexerMap.cend())
228                             {
229                                 lexerFile = it->second;
230                                 grammar.reset(new Grammar(grammarNametitleAttributeGetFullPath(Path::Combine(outDirToUtf8(grammarName) + ".html"))lexerFile));
231                                 lexerGrammarMap[lexerFile] = grammar.get();
232                             }
233                             else
234                             {
235                                 throw std::runtime_error("lexer '" + ToUtf8(sourceAttribute) + "' not found");
236                             }
237                         }
238                         else
239                         {
240                             grammarParser = domain.GetParser(sourceAttribute);
241                             grammar.reset(new Grammar(grammarNametitleAttributeGetFullPath(Path::Combine(outDirToUtf8(grammarName) + ".html"))grammarParser));
242                             parserGrammarMap[grammarParser] = grammar.get();
243                         }
244                         std::unique_ptr<sngxml::xpath::XPathObject> ruleResult = sngxml::xpath::Evaluate(U"/grammar/rule"grammarDoc.get());
245                         if (ruleResult)
246                         {
247                             if (ruleResult->Type() == sngxml::xpath::XPathObjectType::nodeSet)
248                             {
249                                 sngxml::xpath::XPathNodeSet* nodeSet = static_cast<sngxml::xpath::XPathNodeSet*>(ruleResult.get());
250                                 int n = nodeSet->Length();
251                                 for (int i = 0; i < n; ++i)
252                                 {
253                                     sngxml::dom::Node* node = (*nodeSet)[i];
254                                     if (node->GetNodeType() == sngxml::dom::NodeType::elementNode)
255                                     {
256                                         sngxml::dom::Element* element = static_cast<sngxml::dom::Element*>(node);
257                                         std::u32string sourceAttribute = element->GetAttribute(U"source");
258                                         std::u32string nameAttribute = element->GetAttribute(U"name");
259                                         if (sourceAttribute.empty())
260                                         {
261                                             throw std::runtime_error("rule source not specified");
262                                         }
263                                         if (nameAttribute.empty())
264                                         {
265                                             throw std::runtime_error("rule name not specified");
266                                         }
267                                         std::unique_ptr<Rule> rule;
268                                         if (kindAttribute == U"lexical")
269                                         {
270                                             Expression* expression = lexerFile->GetExpressions()->Get(sourceAttribute);
271                                             rule.reset(new Rule(nameAttributeexpression));
272                                             lexerRuleMap[expression] = rule.get();
273                                         }
274                                         else
275                                         {
276                                             RuleParser* ruleParser = grammarParser->GetRule(sourceAttribute);
277                                             rule.reset(new Rule(nameAttributeruleParser));
278                                             parserRuleMap[ruleParser] = rule.get();
279                                         }
280                                         grammar->AddRule(rule.release());
281                                     }
282                                 }
283                             }
284                         }
285                         grammars.push_back(std::move(grammar));
286                     }
287                 }
288             }
289         }
290     }
291 }
292 
293 void Project::Link()
294 {
295     LinkerVisitor visitor;
296     domain.Accept(visitor);
297 }
298 
299 void Project::ParseLexerFiles()
300 {
301     for (auto& lexerFile : lexerFiles)
302     {
303         lexerFile->Parse(lexerContext);
304     }
305 }
306 
307 void Project::GenerateHtml()
308 {
309     GrammarHtmlGeneratorVisitor domainVisitor(verboseparserGrammarMapparserRuleMaplexerGrammarMaplexerRuleMaplexerMapstyleFilePath);
310     domain.Accept(domainVisitor);
311     for (auto& lexerFile : lexerFiles)
312     {
313         auto it = lexerGrammarMap.find(lexerFile.get());
314         if (it != lexerGrammarMap.cend())
315         {
316             Grammar* grammar = it->second;
317             Expression* idStartExpression = lexerFile->GetExpressions()->Get(U"idstart");
318             Rule* idStartRule = new Rule(U"idstart"idStartExpression);
319             grammar->AddRule(idStartRule);
320             lexerRuleMap[idStartExpression] = idStartRule;
321             Expression* idContExpression = lexerFile->GetExpressions()->Get(U"idcont");
322             Rule* idContRule = new Rule(U"idcont"idContExpression);
323             grammar->AddRule(idContRule);
324             lexerRuleMap[idContExpression] = idContRule;
325         }
326         GrammarHtmlGeneratorVisitor lexerFileVisitor(verboseparserGrammarMapparserRuleMaplexerGrammarMaplexerRuleMaplexerMapstyleFilePath);
327         lexerFile->Accept(lexerFileVisitor);
328     }
329 }
330 
331 } } // namespace sng2html::sng2html