Description of the Frag class

1

Description of the Frag class

2
@Add(input prereqs)
class Frag;

struct Write_State {
std::string source_name = {};
bool in_macro = false;
bool c_style;

Write_State(const std::string &f);
};
@End(input prereqs)
3
@Add(input prereqs)
@Put(frag prereqs)
@put(define frag)
@end(input prereqs)
4
@def(define frag)
class Frag_Ref {
public:
const std::string path;
const std::string name;
const bool local;
Frag_Ref(const std::string &p, const std::string &n, bool l):
path { p }, name { n }, local { l }
{ }
};
class Frag_Entry {
std::string str_;
std::string file_;
int first_line_ { -1 };
int last_line_;
Frag_Ref sub_ = { std::string {}, std::string {}, true };
public:
@put(entry methods)
const Frag_Ref &sub() const {
return sub_;
}
};
@end(define frag)
5
@def(entry methods)
Frag_Entry() { }
Frag_Entry(Frag_Ref sub):
sub_ { sub }
{ }
@end(entry methods)
6
@add(entry methods)
void update_state(Write_State &state) const {
@put(update state)
}
@end(entry methods)
7
@def(update state)
auto c { str_.end() };
auto b { str_.begin() };
bool some_nl { false };
while (b != c) {
--c;
@put(update state checks)
break;
}
if (b != c && *c > ' ') {
state.in_macro = false;
}
@end(update state)
8
@def(update state checks)
if (*c == '\n' || *c == '\r') {
some_nl = true;
continue;
}
if (*c <= ' ') { continue; }
if (*c == '\\') {
if (some_nl) {
state.in_macro = true;
return;
}
}
@end(update state checks)
9
@add(entry methods)
std::string str(Write_State &state) const {
@put(check c-like)
std::ostringstream oss;
oss << "\n#line " << first_line_ << " \"" << file_ << "\"\n" << str_;
return oss.str();
}
@end(entry methods)
10
@def(check c-like)
bool old { state.in_macro };
update_state(state);
if (old) { return str_; }
if (! state.c_style) { return str_; }
if (first_line_ < 1) { return str_; }
if (str_.empty()) { return str_; };
@end(check c-like)
11
@add(entry methods)
void add(char ch, const std::string &file, int line) {
@mul(copy file and line)
str_ += ch;
}
@end(entry methods)
12
@def(copy file and line)
if (file_.empty() || first_line_ <= 0) {
file_ = file;
first_line_ = line;
}
last_line_ = line;
@end(copy file and line)
13
@add(entry methods)
void add(const std::string &value, const std::string &file, int line) {
@mul(copy file and line)
str_ += value;
}
@end(entry methods)
14
@add(entry methods)
bool canAdd(const std::string &file, int line) {
@put(can add)
return false;
}
@end(entry methods)
15
@def(can add)
if (! file_.empty() && file != file_) {
return false;
}
@end(can add)
16
@add(can add)
if (last_line_ > 0 && last_line_ != line && last_line_ + 1 != line) {
return false;
}
@end(can add)
17
@add(can add)
return true;
@end(can add)
18
@Add(includes)
#include <vector>
@end(includes)
19
@add(define frag)
class Frag {
std::vector<Frag_Entry> entries_;
int expands_;
int multiples_;
Frag *prefix_;
const bool is_meta_;
public:
const std::string name;
@put(frag methods)
};
@end(define frag)

add a Frag

20

add a Frag

21
@def(frag methods)
static bool isFile(const std::string &name) {
static const std::string prefix { "file: " };
std::string p { name.substr(0, prefix.size()) };
return p == prefix;
}
@end(frag methods)
22
@add(frag methods)
static std::string cmd(const std::string &name) {
static const std::string prefix { "| " };
std::string p { name.substr(0, prefix.size()) };
return p == prefix ? name.substr(prefix.size()) : std::string {};
}
@end(frag methods)
23
@add(frag methods)
Frag(const std::string &name, Frag *prefix):
entries_ {}, expands_ { 0 }, multiples_ { 0 }, prefix_ { prefix },
is_meta_ { name.find('@') != std::string::npos }, name { name }
{
if (isFile(name)) { ++expands_; }
if (cmd(name).size()) { ++expands_; }
}
@end(frag methods)
24
@add(frag methods)
const Frag *prefix() const {
return prefix_;
}
Frag *prefix() {
return prefix_;
}
@end(frag methods)
25
@add(frag methods)
bool is_meta() {
return is_meta_;
}
@end(frag methods)
26
@add(frag methods)
void clear() {
if (prefix_) {
prefix_->clear();
}
entries_.clear();
}
@end(frag methods)
27
@add(frag methods)
bool empty() const {
if (prefix_ && ! prefix_->empty()) {
return false;
}
return entries_.empty();
}
@end(frag methods)
28
@add(define frag)
Write_State::Write_State(const std::string &f):
c_style { Frag::is_c_style(f) }
{ }
@end(define frag)
29
@Def(perform unit-tests)
@put(unit-tests)
@end(perform unit-tests)
30
@add(define frag)
void test_frag_name(const std::string &name) {
Frag f(name, nullptr);
ASSERT(f.name == name);
}
@end(define frag)
31
@def(unit-tests)
test_frag_name("abc");
test_frag_name("");
test_frag_name("A c");
@end(unit-tests)
32
@add(unit-tests) {
Frag f { "ab", nullptr };
ASSERT(f.empty());
} @end(unit-tests)
33
@add(define frag)
bool isPopulatedFrag(const Frag *f) {
return f && ! f->empty();
}
@end(define frag)
34
@add(unit-tests) {
Frag_Entry entry;
} @end(unit-tests)
35
@add(unit-tests) {
Frag f { "", nullptr };
Write_State s { "" };
Frag_Entry entry;
ASSERT(entry.str(s).empty());
} @end(unit-tests)

Add entries to Frags

36

Add entries to Frags

37
@add(frag methods)
void add(const std::string &value, const std::string &file, int line) {
if (value.empty()) { return; }
@mul(assure frag entry)
entries_.back().add(value, file, line);
}
@end(frag methods)
38
@def(assure frag entry)
if (entries_.empty()) {
entries_.emplace_back();
} else if (! entries_.back().canAdd(file, line)) {
entries_.emplace_back();
}
@end(assure frag entry)
39
@add(frag methods)
void add(char ch, const std::string &file, int line) {
@mul(assure frag entry)
entries_.back().add(ch, file, line);
}
@end(frag methods)
40
@add(frag methods)
Frag &add(const Frag_Ref &sub);
@end(frag methods)
41
@add(define frag)
@put(define cycle check)
Frag &Frag::add(const Frag_Ref &sub) {
@put(avoid frag cycles)
@put(add frag entry)
return *this;
}
@end(define frag)
42
@def(add frag entry)
entries_.push_back(Frag_Entry { sub });
@end(add frag entry)
43
@add(frag methods)
auto begin() const {
return entries_.cbegin();
}
@end(frag methods)
44
@add(frag methods)
auto end() const {
return entries_.cend();
}
@end(frag methods)
45
@add(frag methods)
int expands() const {
return expands_ + (prefix_ ? prefix_->expands() : 0);
}
@end(frag methods)
46
@add(frag methods)
void addExpand() {
++expands_;
}
@end(frag methods)
47
@add(frag methods)
int multiples() const {
return multiples_ + (prefix_ ? prefix_->multiples() : 0);
}
@end(frag methods)
48
@add(frag methods)
void addMultiple() {
++multiples_;
}
@end(frag methods)
49
@add(frag methods)
static bool is_c_style(const std::string &name) {
@put(is c-style)
return false;
}
@end(frag methods)
50
@def(is c-style)
static const std::string exts[] = { ".c", ".h", ".cpp" };
const std::string *end = exts + sizeof(exts)/sizeof(*exts);
@end(is c-style)
51
@add(is c-style)
for (auto i = exts; i != end; ++i) {
if (name.length() > i->length()) {
if (name.substr(name.length() - i->length()) == *i) {
return true;
}
}
}
@end(is c-style)

Serialize Frags

52

Serialize Frags

53
@add(define frag)
void serializeFrag(const Frag &frag, std::ostream &out,
Write_State &state, const std::string &path
) {
@put(iterate entries)
}
@end(define frag)
54
@add(define frag)
void serializeFrag(const std::string &name, const Frag &f,
std::ostream &out, const std::string &path
) {
Write_State state { name };
return serializeFrag(f, out, state, path);
}
@end(define frag)
55
@def(iterate entries)
if (frag.prefix()) {
serializeFrag(*frag.prefix(), out, state, path);
}
for (const auto &entry : frag) {
if (! entry.sub().name.empty()) {
std::string new_path = path;
const Frag *f { find_frag(entry.sub(), &new_path) };
if (f) {
serializeFrag(*f, out, state, new_path);
} else {
std::cerr << "no frag [" << entry.sub().name << "], " <<
(entry.sub().local ? "local" : "global" ) << ", [" <<
path << "]\n";
}
}
out << entry.str(state);
}
@end(iterate entries)
56
@add(define frag)
bool check_frag(const Frag &f, std::istream &in,
Write_State &state, const std::string &path
) {
@put(check entries)
return true;
}
@end(define frag)
57
@add(define frag)
bool check_frag(const std::string &name, const Frag &f,
std::istream &in, const std::string &path
) {
Write_State state { name };
return check_frag(f, in, state, path);
}
@end(define frag)
58
@def(check entries)
if (f.prefix()) {
if (! check_frag(*f.prefix(), in, state, path)) {
return false;
}
}
for (const auto &entry : f) {
if (! entry.sub().name.empty()) {
std::string new_path = path;
const Frag *f { find_frag(entry.sub(), &new_path) };
if (f) {
if (! check_frag(*f, in, state, new_path)) {
return false;
}
}
}
@put(check entry str)
}
@end(check entries)
59
@def(check entry str)
for (const auto &i : entry.str(state)) {
if (in.get() != i) {
return false;
}
}
@end(check entry str)
60
@add(define frag)
void testFrag(const std::string &name,
const Frag &frag, const std::string &expected
) {
@put(serialize test frag)
}
@end(define frag)
61
@Add(includes)
#include <sstream>
@end(includes)
62
@def(serialize test frag)
std::ostringstream buffer;
serializeFrag(name, frag, buffer, "");
ASSERT(buffer.str() == expected);
@end(serialize test frag)
63
@add(define frag)
void addStringToFrag(Frag *frag, const std::string &str) {
frag->add(str, std::string {}, 0);
}
@end(define frag)
64
@add(unit-tests) {
clear_frags();
Frag frag { "a", nullptr };
addStringToFrag(&frag, "abc");
addStringToFrag(&frag, "def");
testFrag("a", frag, "abcdef");
} @end(unit-tests)
65
@add(unit-tests) {
clear_frags();
Frag &a { get_frag("", "a", true) };
Frag &b { get_frag("", "b", true) };
addStringToFrag(&a, "abc");
b.add(Frag_Ref { "", "a", true });
addStringToFrag(&b, "def");
b.add(Frag_Ref { "", "a", true });
testFrag("b", b, "abcdefabc");
} @end(unit-tests)
66
@add(unit-tests) {
clear_frags();
Frag &a { get_frag("", "a", false) };
Frag &b { get_frag("", "b", true) };
addStringToFrag(&a, "abc");
b.add(Frag_Ref { "", "a", true });
addStringToFrag(&b, "def");
b.add(Frag_Ref { "", "a", false });
testFrag("b", b, "abcdefabc");
} @end(unit-tests)

Cycle detection

67

Cycle detection

68
@def(define cycle check)
bool isFragInFrag(const std::string &path,
const Frag *needle, const Frag *haystack
) {
ASSERT(needle);
ASSERT(haystack);
@put(check cycle frag)
@put(check cycle entries)
return false;
}
@end(define cycle check)
69
@def(avoid frag cycles)
Frag &f { get_frag(sub) };
ASSERT(! isFragInFrag(sub.path, this, &f));
@end(avoid frag cycles)
70
@def(check cycle frag)
if (needle == haystack) {
return true;
}
@end(check cycle frag)
71
@def(check cycle entries)
if (haystack->prefix() &&
isFragInFrag(path, needle, haystack->prefix())
) {
return true;
}
for (const auto &i : *haystack) {
if (i.sub().name.empty()) { continue; }
std::string new_path { path };
Frag *f { find_frag(i.sub(), &new_path) };
if (! f) { continue; }
if (isFragInFrag(new_path, needle, f)) {
return true;
}
}
@end(check cycle entries)