172 lines
5.1 KiB
C++
172 lines
5.1 KiB
C++
/*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
|
*/
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "cpp.hpp"
|
|
|
|
#include "io.hpp"
|
|
#include "cast.hpp"
|
|
|
|
using util::cpp::include;
|
|
using util::cpp::passthrough;
|
|
using util::cpp::processor;
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
processor::processor ()
|
|
{
|
|
m_directives.insert ({
|
|
"include",
|
|
std::make_unique<include> (*this)
|
|
});
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
processor::add (std::string token, std::unique_ptr<directive> handler)
|
|
{
|
|
m_directives.emplace (std::pair {std::move (token), std::move (handler)});
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
processor::process (std::ostream &os, const std::experimental::filesystem::path &src) const
|
|
{
|
|
const auto data = util::slurp<char> (src);
|
|
context ctx;
|
|
ctx.source.push (src);
|
|
|
|
util::tokeniser<const char*> tok (data, '\n');
|
|
process (os, ctx, util::view (tok.begin (), tok.end ()));
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
util::tokeniser<const char*>::iterator
|
|
processor::process (
|
|
std::ostream &os,
|
|
context &ctx,
|
|
util::view<util::tokeniser<const char*>::iterator> lines) const
|
|
{
|
|
for (auto l = lines.begin (), last = lines.end (); l != last; ++l) {
|
|
if (l->empty () || (*l)[0] != '#') {
|
|
os << *l << '\n';
|
|
continue;
|
|
}
|
|
|
|
auto tokens = util::tokeniser (*l, ' ');
|
|
auto head = tokens.begin ();
|
|
auto head_string = std::string { head->begin () + 1, head->size () - 1 };
|
|
|
|
if (*head == "#endif")
|
|
return l;
|
|
|
|
|
|
if (*head == "#define") {
|
|
auto key = head;
|
|
++key;
|
|
|
|
if (key == tokens.end ())
|
|
throw std::runtime_error ("expected token for define");
|
|
|
|
std::string key_string { key->begin (), key->size () };
|
|
|
|
auto val = key;
|
|
++val;
|
|
ctx.defines[key_string] = val == tokens.end () ? "" : std::string (val->begin (), val->size ());
|
|
continue;
|
|
}
|
|
|
|
if (*head == "#ifndef") {
|
|
auto tail = head + 1;
|
|
if (tail == tokens.end ())
|
|
throw std::runtime_error ("expected token for define");
|
|
|
|
// recurse and parse the block...
|
|
std::string name { tail->begin (), tail->end () };
|
|
if (ctx.defines.find (name) == ctx.defines.cend ()) {
|
|
l = process (os, ctx, {++l, lines.end ()});
|
|
// ... or skip until and endif
|
|
} else {
|
|
for (++l; l != lines.end () && *l != "#endif"; ++l)
|
|
;
|
|
}
|
|
|
|
// check if we've got the expected endif
|
|
if (l == lines.cend () || *l != "#endif")
|
|
throw std::runtime_error ("expected #endif");
|
|
|
|
continue;
|
|
}
|
|
|
|
auto handler = m_directives.find (head_string);
|
|
if (handler == m_directives.end ())
|
|
throw unknown_directive (head_string);
|
|
|
|
lines.consume (handler->second->process (os, ctx, {l, lines.end()}));
|
|
}
|
|
|
|
return lines.end ();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
passthrough::passthrough (const std::string &name):
|
|
m_name (name)
|
|
{ ; }
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
util::tokeniser<const char*>::iterator
|
|
passthrough::process (std::ostream &os,
|
|
context&,
|
|
util::view<util::tokeniser<const char*>::iterator> lines) const
|
|
{
|
|
os << *lines.begin () << '\n';
|
|
return lines.begin ()++;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
include::include (processor &_parent):
|
|
m_parent (_parent)
|
|
{ ; }
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
util::tokeniser<const char*>::iterator
|
|
include::process (std::ostream &os,
|
|
context &ctx,
|
|
util::view<util::tokeniser<const char*>::iterator> lines) const
|
|
{
|
|
const auto name = lines.begin ()->slice (strlen("#include '"), -2);
|
|
std::experimental::filesystem::path fragment { name.begin (), name.end () };
|
|
|
|
const auto target = ctx.source.top ().parent_path () / fragment;
|
|
const auto data = util::slurp<char> (target);
|
|
|
|
ctx.source.push (target);
|
|
util::tokeniser<const char*> tok (data, '\n');
|
|
m_parent.process (os, ctx, util::view (tok.begin (), tok.end ()));
|
|
|
|
ctx.source.pop ();
|
|
|
|
return lines.begin ()++;
|
|
}
|