1 // Copyright 2015 Tony Wasserka
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 //       notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above copyright
10 //       notice, this list of conditions and the following disclaimer in the
11 //       documentation and/or other materials provided with the distribution.
12 //     * Neither the name of the owner nor the names of its contributors may
13 //       be used to endorse or promote products derived from this software
14 //       without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 #include <nihstro/parser_assembly_private.h>
29 #include <nihstro/preprocessor.h>
30 #include <nihstro/source_tree.h>
31 
32 #include <boost/spirit/include/qi.hpp>
33 
34 #include <fstream>
35 
36 namespace nihstro {
37 
38 template<typename Iterator>
39 struct IncludeParser : qi::grammar<Iterator, std::string(), AssemblySkipper<Iterator>> {
40     using Skipper = AssemblySkipper<Iterator>;
41 
IncludeParsernihstro::IncludeParser42     IncludeParser() : IncludeParser::base_type(include) {
43         include = qi::lexeme[qi::lit(".include") >> &qi::ascii::space]
44                   > qi::lexeme[qi::lit("\"") > +qi::char_("a-zA-Z0-9./_\\-") > qi::lit("\"")]
45                   > qi::omit[qi::eol | qi::eoi];
46     }
47 
48     qi::rule<Iterator, std::string(), Skipper> include;
49 };
50 
51 
PreprocessAssemblyFile(const std::string & filename)52 SourceTree PreprocessAssemblyFile(const std::string& filename) {
53     SourceTree tree;
54     tree.file_info.filename = filename;
55 
56     std::ifstream input_file(filename);
57     if (!input_file) {
58         throw std::runtime_error("Could not open input file " + filename);
59     }
60 
61     std::string prefix;
62     {
63         auto last_slash = filename.find_last_of("/");
64         if (last_slash != std::string::npos)
65             prefix = filename.substr(0, last_slash + 1);
66     }
67 
68     input_file.seekg(0, std::ios::end);
69     tree.code.resize(input_file.tellg());
70 
71     input_file.seekg(0, std::ios::beg);
72     input_file.read(&tree.code[0], tree.code.size());
73     input_file.close();
74 
75     auto cursor = tree.code.begin();
76 
77     IncludeParser<decltype(cursor)> include_parser;
78     AssemblySkipper<decltype(cursor)> skipper;
79 
80     while (cursor != tree.code.end()) {
81         std::string parsed_filename;
82         auto cursor_prev = cursor;
83         if (qi::phrase_parse(cursor, tree.code.end(), include_parser, skipper, parsed_filename)) {
84             if (parsed_filename[0] == '/')
85                 throw std::runtime_error("Given filename must be relative to the path of the including file");
86 
87             // TODO: Protect against circular inclusions
88             auto newtree = PreprocessAssemblyFile(prefix + parsed_filename);
89             tree.Attach(newtree, cursor_prev - tree.code.begin());
90             cursor = tree.code.erase(cursor_prev, cursor);
91             cursor = tree.code.insert(cursor, '\n');
92         } else {
93             // Skip this line
94             qi::parse(cursor, tree.code.end(), *(qi::char_ - (qi::eol | qi::eoi)) >> (qi::eol | qi::eoi));
95         }
96     }
97     return tree;
98 }
99 
100 } // namespace
101