src/ir/skeleton/control_flow.cc \
src/ir/skeleton/generate_code.cc \
src/ir/skeleton/generate_data.cc \
- src/ir/skeleton/maxlen.cc \
+ src/ir/skeleton/maxpath.cc \
src/ir/skeleton/skeleton.cc \
src/ir/skeleton/unreachable_nullable.cc \
src/main.cc \
-/* Generated by re2c 0.16 on Mon Mar 14 22:45:09 2016 */
+/* Generated by re2c 0.16 on Tue Mar 15 17:24:03 2016 */
#line 1 "../src/parse/lex.re"
#include "src/util/c99_stdint.h"
#include <stddef.h>
.wline_info (in->get_cline (), in->get_fname ().c_str ());
if (opts->target == opt_t::SKELETON)
{
- Skeleton::emit_prolog (o.source);
+ emit_prolog(o.source);
}
Enc encodingOld = opts->encoding;
if (opts->target == opt_t::SKELETON)
{
- Skeleton::emit_epilog (o.source, o.skeletons);
+ emit_epilog(o.source, o.skeletons);
}
parse_cleanup();
if (opts->target == opt_t::SKELETON)
{
- skeleton->emit_action (o, ind, rule->rank);
+ emit_action(*skeleton, o, ind, rule->rank);
}
else
{
head->action.set_initial (initial_label, head->action.type == Action::SAVE);
- skeleton->warn_undefined_control_flow ();
- skeleton->warn_unreachable_nullable_rules ();
+ warn_undefined_control_flow(*skeleton);
+ warn_unreachable_nullable_rules(*skeleton);
if (opts->target == opt_t::SKELETON)
{
if (output.skeletons.insert (name).second)
{
- skeleton->emit_data (o.file_name);
- skeleton->emit_start (o, max_fill, need_backup, need_backupctx, need_accept);
+ emit_data(*skeleton, o.file_name);
+ emit_start(*skeleton, o, max_fill, need_backup, need_backupctx, need_accept);
uint32_t i = 2;
emit_body (o, i, used_labels, initial_label);
- skeleton->emit_end (o, need_backup, need_backupctx);
+ emit_end(*skeleton, o, need_backup, need_backupctx);
}
}
else
}
}
-void Warn::undefined_control_flow (uint32_t line, const std::string & cond, std::vector<path_t> & paths, bool overflow)
+void Warn::undefined_control_flow (const Skeleton &skel, std::vector<path_t> & paths, bool overflow)
{
if (mask[UNDEFINED_CONTROL_FLOW] & WARNING)
{
error_accuml |= e;
// report shorter patterns first
- std::sort (paths.begin (), paths.end (), compare_default_paths);
+ std::sort (paths.begin (), paths.end ());
- warning_start (line, e);
- fprintf (stderr, "control flow %sis undefined for strings that match ", incond (cond).c_str ());
+ warning_start (skel.line, e);
+ fprintf (stderr, "control flow %sis undefined for strings that match ", incond (skel.cond).c_str ());
const size_t count = paths.size ();
if (count == 1)
{
- fprint_default_path (stderr, paths[0]);
+ fprint_default_path (stderr, skel, paths[0]);
}
else
{
for (size_t i = 0; i < count; ++i)
{
fprintf (stderr, "\n\t");
- fprint_default_path (stderr, paths[i]);
+ fprint_default_path (stderr, skel, paths[i]);
}
fprintf (stderr, "\n");
}
namespace re2c {
struct path_t;
+struct Skeleton;
#define RE2C_WARNING_TYPES \
W (CONDITION_ORDER, "condition-order"), \
void empty_class (uint32_t line);
void match_empty_string (uint32_t line);
void swapped_range (uint32_t line, uint32_t l, uint32_t u);
- void undefined_control_flow (uint32_t line, const std::string & cond, std::vector<path_t> & paths, bool overflow);
+ void undefined_control_flow (const Skeleton &skel, std::vector<path_t> & paths, bool overflow);
void unreachable_rule (const std::string & cond, const RuleInfo *rule);
void useless_escape (uint32_t line, uint32_t col, char c);
};
-#include <map>
-#include <utility>
#include <vector>
#include "src/conf/warn.h"
namespace re2c
{
+// See note [counting skeleton edges].
+// Type for counting arcs in paths that cause undefined behaviour.
+// These paths are stored on heap, so the limit should be low.
+// Most real-world cases have only a few short paths.
+// We don't need all paths anyway, just some examples.
+typedef u32lim_t<1024> nakeds_t; // ~1Kb
+
// We don't need all patterns that cause undefined behaviour.
// We only need some examples, the shorter the better.
-// See also note [counting skeleton edges].
-void Node::naked_paths(
- path_t &prefix,
+static void naked_paths(
+ Skeleton &skel,
std::vector<path_t> &paths,
- nakeds_t &size)
+ path_t &prefix,
+ nakeds_t &size,
+ size_t i)
{
- if (rule) {
+ const Node &node = skel.nodes[i];
+ uint8_t &loop = skel.loops[i];
+
+ if (node.rule) {
return;
- } else if (end()) {
+ } else if (node.end()) {
paths.push_back(prefix);
size = size + nakeds_t::from64(prefix.len());
} else if (loop < 2) {
local_inc _(loop);
- for (arcsets_t::iterator i = arcsets.begin();
- i != arcsets.end() && !size.overflow(); ++i) {
- prefix.push(i->first);
- i->first->naked_paths(prefix, paths, size);
+ Node::arcsets_t::const_iterator
+ arc = node.arcsets.begin(),
+ end = node.arcsets.end();
+ for (; arc != end && !size.overflow(); ++arc) {
+ const size_t j = arc->first;
+ prefix.push(j);
+ naked_paths(skel, paths, prefix, size, j);
prefix.pop();
}
}
}
-void Skeleton::warn_undefined_control_flow()
+void warn_undefined_control_flow(Skeleton &skel)
{
- path_t prefix(&nodes[0]);
+ path_t prefix(0);
std::vector<path_t> paths;
- Node::nakeds_t size = Node::nakeds_t::from32(0u);
+ nakeds_t size = nakeds_t::from32(0u);
- nodes->naked_paths(prefix, paths, size);
+ naked_paths(skel, paths, prefix, size, 0);
if (!paths.empty()) {
- warn.undefined_control_flow(line, cond, paths, size.overflow());
+ warn.undefined_control_flow(skel, paths, size.overflow());
} else if (size.overflow()) {
- warn.fail(Warn::UNDEFINED_CONTROL_FLOW, line,
+ warn.fail(Warn::UNDEFINED_CONTROL_FLOW, skel.line,
"DFA is too large to check undefined control flow");
}
}
-// define strict weak ordering on patterns:
-// 1st criterion is length (short patterns go first)
-// 2nd criterion is lexicographical order (applies to patterns of equal length)
-bool compare_default_paths(const path_t &p1, const path_t &p2)
-{
- const size_t l1 = p1.len();
- const size_t l2 = p2.len();
- if (l1 == l2) {
- for (size_t i = 0; i < l1; ++i) {
- const Node::arcset_t
- &a1 = p1.arcset(i),
- &a2 = p2.arcset(i);
- const size_t s1 = a1.size();
- const size_t s2 = a2.size();
- for (size_t j = 0; j < s1; ++j) {
- if (j == s2 || a2[j] < a1[j]) {
- return false;
- } else if (a1[j] < a2[j]) {
- return true;
- }
- }
- }
- return false;
- } else {
- return l1 < l2;
- }
-}
-
static void fprint_default_arc(FILE *f, const Node::arcset_t &arc)
{
const size_t ranges = arc.size();
}
}
-void fprint_default_path(FILE *f, const path_t &p)
+void fprint_default_path(
+ FILE *f,
+ const Skeleton &skel,
+ const path_t &p)
{
fprintf(f, "'");
const size_t len = p.len();
if (i > 0) {
fprintf(f, " ");
}
- const Node::arcset_t &arc = p.arcset(i);
+ const Node::arcset_t &arc = p.arcset(skel, i);
fprint_default_arc(stderr, arc);
}
fprintf(f, "'");
namespace re2c
{
-static void exact_uint (OutputFile & o, size_t width)
+static void exact_uint(OutputFile &o, size_t width)
{
- if (width == sizeof (char))
- {
+ if (width == sizeof(char)) {
o.ws("unsigned char");
- }
- else if (width == sizeof (short))
- {
+ } else if (width == sizeof(short)) {
o.ws("unsigned short");
- }
- else if (width == sizeof (int))
- {
+ } else if (width == sizeof(int)) {
o.ws("unsigned int");
- }
- else if (width == sizeof (long))
- {
+ } else if (width == sizeof(long)) {
o.ws("unsigned long");
- }
- else
- {
- o.ws("uint").wu64 (width * 8).ws("_t");
+ } else {
+ o.ws("uint").wu64(width * 8).ws("_t");
}
}
o.ws("\n").wind(ind).ws("/* from little-endian to host-endian */");
o.ws("\n").wind(ind).ws("unsigned char *p = (unsigned char*)&").ws(expr).ws(";");
o.ws("\n").wind(ind).ws(expr).ws(" = p[0]");
- for (uint32_t i = 1; i < size; ++i)
- {
+ for (uint32_t i = 1; i < size; ++i) {
o.ws(" + (p[").wu32(i).ws("] << ").wu32(i * 8).ws("u)");
}
o.ws(";");
}
-void Skeleton::emit_prolog (OutputFile & o)
+void emit_prolog(OutputFile &o)
{
o.ws("\n#include <stdio.h>");
o.ws("\n#include <stdlib.h> /* malloc, free */");
o.ws("\n");
}
-void Skeleton::emit_start
- ( OutputFile & o
- , size_t maxfill
- , bool backup
- , bool backupctx
- , bool accept
- ) const
+void emit_start(const Skeleton &skel, OutputFile &o, size_t maxfill,
+ bool backup, bool backupctx, bool accept)
{
- const size_t sizeof_cunit = opts->encoding.szCodeUnit();
- const uint32_t default_rule = rule2key (rule_rank_t::none ());
+ const size_t
+ sizeof_cunit = opts->encoding.szCodeUnit(),
+ sizeof_key = skel.sizeof_key;
+ const uint32_t default_rule = skel.rule2key(rule_rank_t::none());
+ const std::string &name = skel.name;
o.ws("\n#define YYCTYPE ");
exact_uint (o, sizeof_cunit);
exact_uint (o, sizeof_key);
o.ws("\n#define YYPEEK() *cursor");
o.ws("\n#define YYSKIP() ++cursor");
- if (backup)
- {
+ if (backup) {
o.ws("\n#define YYBACKUP() marker = cursor");
o.ws("\n#define YYRESTORE() cursor = marker");
}
- if (backupctx)
- {
+ if (backupctx) {
o.ws("\n#define YYBACKUPCTX() ctxmarker = cursor");
o.ws("\n#define YYRESTORECTX() cursor = ctxmarker");
}
o.ws("\n").wind(2).ws("goto end;");
o.ws("\n").wind(1).ws("}");
o.ws("\n");
- if (sizeof_cunit > 1)
- {
+ if (sizeof_cunit > 1) {
o.ws("\n").wind(1).ws("for (i = 0; i < input_len; ++i) {");
from_le(o, 2, sizeof_cunit, "input[i]");
o.ws("\n").wind(1).ws("}");
o.ws("\n");
o.ws("\n").wind(1).ws("for (i = 0; status == 0 && i < keys_count; ++i) {");
o.ws("\n").wind(2).ws("token = cursor;");
- if (backup)
- {
+ if (backup) {
o.ws("\n").wind(2).ws("const YYCTYPE *marker = NULL;");
}
- if (backupctx)
- {
+ if (backupctx) {
o.ws("\n").wind(2).ws("const YYCTYPE *ctxmarker = NULL;");
}
o.ws("\n").wind(2).ws("YYCTYPE yych;");
- if (accept)
- {
+ if (accept) {
o.ws("\n").wind(2).ws("unsigned int yyaccept = 0;");
}
o.ws("\n");
- if (opts->bFlag && BitMap::first)
- {
- BitMap::gen (o, 2, 0, std::min (0x100u, opts->encoding.nCodeUnits ()));
+ if (opts->bFlag && BitMap::first) {
+ BitMap::gen(o, 2, 0, std::min(0x100u, opts->encoding.nCodeUnits()));
}
o.ws("\n");
}
-void Skeleton::emit_end
- ( OutputFile & o
- , bool backup
- , bool backupctx
- ) const
+void emit_end(const Skeleton &skel, OutputFile &o, bool backup, bool backupctx)
{
+ const std::string &name = skel.name;
+
o.ws("\n").wind(1).ws("}");
o.ws("\n").wind(1).ws("if (status == 0) {");
o.ws("\n").wind(2).ws("if (cursor != eof) {");
o.ws("\n#undef YYKEYTYPE");
o.ws("\n#undef YYPEEK");
o.ws("\n#undef YYSKIP");
- if (backup)
- {
+ if (backup) {
o.ws("\n#undef YYBACKUP");
o.ws("\n#undef YYRESTORE");
}
- if (backupctx)
- {
+ if (backupctx) {
o.ws("\n#undef YYBACKUPCTX");
o.ws("\n#undef YYRESTORECTX");
}
o.ws("\n");
}
-void Skeleton::emit_epilog (OutputFile & o, const std::set<std::string> & names)
+void emit_epilog(OutputFile &o, const std::set<std::string> &names)
{
o.ws("\n").ws("int main()");
o.ws("\n").ws("{");
- for (std::set<std::string>::const_iterator i = names.begin (); i != names.end (); ++i)
- {
+ for (std::set<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
o.ws("\n").wind(1).ws("if(lex_").wstring(*i).ws("() != 0) {");
o.ws("\n").wind(2).ws("return 1;");
o.ws("\n").wind(1).ws("}");
o.ws("\n");
}
-void Skeleton::emit_action (OutputFile & o, uint32_t ind, rule_rank_t rank) const
+void emit_action(const Skeleton &skel, OutputFile &o, uint32_t ind, rule_rank_t rank)
{
- o.wind(ind).ws("status = action_").wstring(name).ws("(i, keys, input, token, &cursor, ").wu32(rule2key (rank)).ws(");\n");
+ o.wind(ind).ws("status = action_").wstring(skel.name).ws("(i, keys, input, token, &cursor, ").wu32(skel.rule2key(rank)).ws(");\n");
o.wind(ind).ws("continue;\n");
}
namespace re2c
{
-template <typename cunit_t, typename key_t>
- static Node::covers_t cover_one (FILE * input, FILE * keys, const path_t & path);
+/*
+ * note [counting skeleton edges]
+ *
+ * To avoid any possible overflows all size calculations are wrapped in
+ * a special truncated unsigned 32-bit integer type that checks overflow
+ * on each binary operation or conversion from another type.
+ *
+ * Two things contribute to size calculation: path length and the number
+ * of outgoing arcs in each node. Some considerations on why these values
+ * will not overflow before they are converted to truncated type:
+ *
+ * - Maximal number of outgoing arcs in each node cannot exceed 32 bits:
+ * it is bounded by the number of code units in current encoding, and
+ * re2c doesn't support any encoding with more than 2^32 code units.
+ * Conversion is safe.
+ *
+ * - Maximal path length cannot exceed 32 bits: we estimate it right
+ * after skeleton construction and check for overflow. If path length
+ * does overflow, an error is reported and re2c aborts.
+ */
+
+// See note [counting skeleton edges].
+// Type for calculating the size of path cover.
+// Paths are dumped to file as soon as generated and don't eat
+// heap space. The total size of path cover (measured in edges)
+// is O(N^2) where N is the number of edges in skeleton.
+typedef u32lim_t<1024 * 1024 * 1024> covers_t; // ~1Gb
+
+template<typename uintn_t> static uintn_t to_le(uintn_t n)
+{
+ uintn_t m;
+ uint8_t *p = reinterpret_cast<uint8_t*>(&m);
+ for (size_t i = 0; i < sizeof(uintn_t); ++i) {
+ p[i] = static_cast<uint8_t>(n >> (i * 8));
+ }
+ return m;
+}
+
+template<typename key_t> static void keygen(FILE *f, size_t count,
+ size_t len, size_t len_match, rule_rank_t match)
+{
+ const key_t m = Skeleton::rule2key<key_t>(match);
+
+ const size_t keys_size = 3 * count;
+ key_t * keys = new key_t[keys_size];
+ for (uint32_t i = 0; i < keys_size;) {
+ keys[i++] = to_le<key_t>(static_cast<key_t>(len));
+ keys[i++] = to_le<key_t>(static_cast<key_t>(len_match));
+ keys[i++] = to_le<key_t>(m);
+ }
+ fwrite(keys, sizeof(key_t), keys_size, f);
+ delete[] keys;
+}
+
+template<typename cunit_t, typename key_t> static covers_t cover_one(
+ Skeleton &skel, FILE *input, FILE * keys, const path_t & path)
+{
+ const size_t len = path.len();
+
+ size_t count = 0;
+ for (size_t i = 0; i < len; ++i) {
+ count = std::max (count, path.arc(skel, i).size());
+ }
+
+ const covers_t size = covers_t::from64(len)
+ * covers_t::from64(count);
+ if (!size.overflow()) {
+ // input
+ const size_t buffer_size = size.uint32();
+ cunit_t *buffer = new cunit_t [buffer_size];
+ for (size_t i = 0; i < len; ++i) {
+ const std::vector<uint32_t> &arc = path.arc(skel, i);
+ const size_t width = arc.size();
+ for (size_t j = 0; j < count; ++j) {
+ const size_t k = j % width;
+ buffer[j * len + i] = to_le<cunit_t>(static_cast<cunit_t>(arc[k]));
+ }
+ }
+ fwrite (buffer, sizeof(cunit_t), buffer_size, input);
+ delete[] buffer;
+
+ // keys
+ keygen<key_t>(keys, count, len, path.len_matching(skel), path.match(skel));
+ }
+
+ return size;
+}
/*
* note [generating skeleton path cover]
* See also note [counting skeleton edges].
*
*/
-template <typename cunit_t, typename key_t>
- void Node::cover (path_t & prefix, FILE * input, FILE * keys, covers_t &size)
+template <typename cunit_t, typename key_t> static void cover(
+ Skeleton &skel,
+ FILE *input,
+ FILE *keys,
+ path_t &prefix,
+ covers_t &size,
+ size_t i)
{
- if (end () && suffix == NULL)
- {
- suffix = new path_t(this);
+ const Node &node = skel.nodes[i];
+ uint8_t &loop = skel.loops[i];
+ path_t *&suffix = skel.suffixes[i];
+
+ if (node.end() && suffix == NULL) {
+ suffix = new path_t(i);
}
+
if (suffix != NULL)
{
- prefix.append (suffix);
- size = size + cover_one<cunit_t, key_t> (input, keys, prefix);
- }
- else if (loop < 2)
- {
- local_inc _ (loop);
- for (arcs_t::iterator i = arcs.begin ();
- i != arcs.end () && !size.overflow(); ++i)
- {
+ prefix.append(suffix);
+ size = size + cover_one<cunit_t, key_t>(skel, input, keys, prefix);
+ } else if (loop < 2) {
+ local_inc _(loop);
+ Node::arcs_t::const_iterator
+ arc = node.arcs.begin(),
+ end = node.arcs.end();
+ for (; arc != end && !size.overflow(); ++arc) {
+ const size_t j = arc->first;
+
path_t new_prefix = prefix;
- new_prefix.push (i->first);
- i->first->cover<cunit_t, key_t> (new_prefix, input, keys, size);
- if (i->first->suffix != NULL && suffix == NULL)
- {
- suffix = new path_t(this);
- suffix->push (i->first);
- suffix->append (i->first->suffix);
+ new_prefix.push(j);
+ cover<cunit_t, key_t>(skel, input, keys, new_prefix, size, j);
+
+ const path_t *sfx = skel.suffixes[j];
+ if (sfx != NULL && suffix == NULL) {
+ suffix = new path_t(i);
+ suffix->push(j);
+ suffix->append(sfx);
}
}
}
}
-template <typename cunit_t, typename key_t>
- void Skeleton::generate_paths_cunit_key (FILE * input, FILE * keys)
+template<typename cunit_t, typename key_t> static void generate_paths_cunit_key(
+ Skeleton &skel, FILE *input, FILE *keys)
{
- path_t prefix (nodes);
- Node::covers_t size = Node::covers_t::from32(0u);
+ path_t prefix(0);
+ covers_t size = covers_t::from32(0u);
- nodes->cover<cunit_t, key_t> (prefix, input, keys, size);
+ cover<cunit_t, key_t>(skel, input, keys, prefix, size, 0);
- if (size.overflow ())
- {
- warning
- ( NULL
- , line
- , false
- , "DFA %sis too large: can only generate partial path cover"
- , incond (cond).c_str ()
- );
+ if (size.overflow()) {
+ warning(NULL, skel.line, false,
+ "DFA %sis too large: can only generate partial path cover",
+ incond(skel.cond).c_str());
}
}
-template <typename cunit_t>
- void Skeleton::generate_paths_cunit (FILE * input, FILE * keys)
+template<typename cunit_t> static void generate_paths_cunit(
+ Skeleton &skel, FILE *input, FILE *keys)
{
- switch (sizeof_key)
- {
- case 4: generate_paths_cunit_key<cunit_t, uint32_t> (input, keys); break;
- case 2: generate_paths_cunit_key<cunit_t, uint16_t> (input, keys); break;
- case 1: generate_paths_cunit_key<cunit_t, uint8_t> (input, keys); break;
+ switch (skel.sizeof_key) {
+ case 4: generate_paths_cunit_key<cunit_t, uint32_t>(skel, input, keys); break;
+ case 2: generate_paths_cunit_key<cunit_t, uint16_t>(skel, input, keys); break;
+ case 1: generate_paths_cunit_key<cunit_t, uint8_t>(skel, input, keys); break;
}
}
-void Skeleton::generate_paths (FILE * input, FILE * keys)
+static void generate_paths(Skeleton &skel, FILE *input, FILE *keys)
{
- switch (opts->encoding.szCodeUnit ())
- {
- case 4: generate_paths_cunit<uint32_t> (input, keys); break;
- case 2: generate_paths_cunit<uint16_t> (input, keys); break;
- case 1: generate_paths_cunit<uint8_t> (input, keys); break;
- }
-}
-
-void Skeleton::emit_data (const char * fname)
-{
- const std::string input_name = std::string (fname) + "." + name + ".input";
- FILE * input = fopen (input_name.c_str (), "wb");
- if (!input)
- {
- error ("cannot open file: %s", input_name.c_str ());
- exit (1);
- }
- const std::string keys_name = std::string (fname) + "." + name + ".keys";
- FILE * keys = fopen (keys_name.c_str (), "wb");
- if (!keys)
- {
- error ("cannot open file: %s", keys_name.c_str ());
- exit (1);
- }
-
- generate_paths (input, keys);
-
- fclose (input);
- fclose (keys);
-}
-
-template <typename uintn_t> static uintn_t to_le(uintn_t n)
-{
- uintn_t m;
- uint8_t *p = reinterpret_cast<uint8_t*>(&m);
- for (size_t i = 0; i < sizeof(uintn_t); ++i)
- {
- p[i] = static_cast<uint8_t>(n >> (i * 8));
+ switch (opts->encoding.szCodeUnit()) {
+ case 4: generate_paths_cunit<uint32_t>(skel, input, keys); break;
+ case 2: generate_paths_cunit<uint16_t>(skel, input, keys); break;
+ case 1: generate_paths_cunit<uint8_t>(skel, input, keys); break;
}
- return m;
}
-template <typename key_t>
- static void keygen (FILE * f, size_t count, size_t len, size_t len_match, rule_rank_t match)
+void emit_data(Skeleton &skel, const char *fname)
{
- const key_t m = Skeleton::rule2key<key_t> (match);
-
- const size_t keys_size = 3 * count;
- key_t * keys = new key_t [keys_size];
- for (uint32_t i = 0; i < keys_size;)
- {
- keys[i++] = to_le<key_t>(static_cast<key_t> (len));
- keys[i++] = to_le<key_t>(static_cast<key_t> (len_match));
- keys[i++] = to_le<key_t>(m);
+ const std::string input_name = std::string(fname) + "." + skel.name + ".input";
+ FILE *input = fopen(input_name.c_str(), "wb");
+ if (!input) {
+ error("cannot open file: %s", input_name.c_str());
+ exit(1);
}
- fwrite (keys, sizeof (key_t), keys_size, f);
- delete [] keys;
-}
-
-template <typename cunit_t, typename key_t>
- static Node::covers_t cover_one (FILE * input, FILE * keys, const path_t & path)
-{
- const size_t len = path.len ();
-
- size_t count = 0;
- for (size_t i = 0; i < len; ++i)
- {
- count = std::max (count, path.arc(i).size ());
+ const std::string keys_name = std::string(fname) + "." + skel.name + ".keys";
+ FILE *keys = fopen (keys_name.c_str(), "wb");
+ if (!keys) {
+ error("cannot open file: %s", keys_name.c_str());
+ exit(1);
}
- const Node::covers_t size = Node::covers_t::from64(len) * Node::covers_t::from64(count);
- if (!size.overflow ())
- {
- // input
- const size_t buffer_size = size.uint32 ();
- cunit_t * buffer = new cunit_t [buffer_size];
- for (size_t i = 0; i < len; ++i)
- {
- const std::vector<uint32_t> & arc = path.arc(i);
- const size_t width = arc.size ();
- for (size_t j = 0; j < count; ++j)
- {
- const size_t k = j % width;
- buffer[j * len + i] = to_le<cunit_t>(static_cast<cunit_t> (arc[k]));
- }
- }
- fwrite (buffer, sizeof (cunit_t), buffer_size, input);
- delete [] buffer;
-
- // keys
- keygen<key_t> (keys, count, len, path.len_matching (), path.match ());
- }
+ generate_paths(skel, input, keys);
- return size;
+ fclose(input);
+ fclose(keys);
}
} // namespace re2c
+++ /dev/null
-#include "src/util/c99_stdint.h"
-#include <algorithm>
-#include <limits>
-#include <map>
-#include <utility>
-
-#include "src/ir/skeleton/skeleton.h"
-
-namespace re2c
-{
-
-// 0 < DIST_MAX < DIST_ERROR <= std::numeric_limits<uint32_t>::max()
-const uint32_t Node::DIST_ERROR = std::numeric_limits<uint32_t>::max();
-const uint32_t Node::DIST_MAX = DIST_ERROR - 1;
-
-// different from YYMAXFILL calculation
-// in the way it handles loops and empty regexp
-void Node::calc_dist ()
-{
- if (dist != DIST_ERROR)
- {
- return;
- }
- else if (end ())
- {
- dist = 0;
- }
- else if (loop < 2)
- {
- local_inc _ (loop);
- for (arcs_t::iterator i = arcs.begin (); i != arcs.end (); ++i)
- {
- i->first->calc_dist ();
- if (i->first->dist != DIST_ERROR)
- {
- if (dist == DIST_ERROR)
- {
- dist = i->first->dist;
- }
- else
- {
- dist = std::max (dist, i->first->dist);
- }
- }
- }
- dist = std::min (dist + 1, DIST_MAX);
- }
-}
-
-} // namespace re2c
--- /dev/null
+#include "src/util/c99_stdint.h"
+#include <algorithm>
+#include <limits>
+
+#include "src/conf/msg.h"
+#include "src/ir/skeleton/skeleton.h"
+
+namespace re2c
+{
+
+// 0 < DIST_MAX < DIST_ERROR <= std::numeric_limits<uint32_t>::max()
+static const uint32_t DIST_ERROR = std::numeric_limits<uint32_t>::max();
+static const uint32_t DIST_MAX = DIST_ERROR - 1;
+
+// maximal distance to end node (assuming one iteration per loop)
+// different from YYMAXFILL calculation
+// in the way it handles loops and empty regexp
+static void calc_dist(
+ Skeleton &skel,
+ std::vector<uint32_t> &dists,
+ size_t i)
+{
+ const Node &node = skel.nodes[i];
+ uint8_t &loop = skel.loops[i];
+ uint32_t &dist = dists[i];
+
+ if (dist != DIST_ERROR) {
+ return;
+ } else if (node.end()) {
+ dist = 0;
+ } else if (loop < 2) {
+ local_inc _(loop);
+ Node::arcs_t::const_iterator
+ arc = node.arcs.begin(),
+ end = node.arcs.end();
+ for (; arc != end; ++arc) {
+ const size_t j = arc->first;
+ calc_dist(skel, dists, j);
+ uint32_t &d = dists[j];
+ if (d != DIST_ERROR) {
+ if (dist == DIST_ERROR) {
+ dist = d;
+ } else {
+ dist = std::max(dist, d);
+ }
+ }
+ }
+ dist = std::min(dist + 1, DIST_MAX);
+ }
+}
+
+// calculate maximal path length, check overflow
+uint32_t maxpath(Skeleton &skel)
+{
+ std::vector<uint32_t> dists(skel.nodes_count);
+ calc_dist(skel, dists, 0);
+ const uint32_t maxlen = dists[0];
+ if (maxlen == DIST_MAX) {
+ error("DFA path %sis too long", incond(skel.cond).c_str());
+ exit(1);
+ }
+ return maxlen;
+}
+
+} // namespace re2c
class path_t
{
- std::vector<Node*> arcs;
+ std::vector<size_t> arcs;
public:
- explicit path_t(Node *n) : arcs()
+ explicit path_t(size_t i) : arcs()
{
- arcs.push_back(n);
- }
- path_t(const path_t &p) : arcs(p.arcs) {}
- path_t &operator=(const path_t &p)
- {
- new (this) path_t(p);
- return *this;
+ arcs.push_back(i);
}
size_t len() const
{
return arcs.size() - 1;
}
- size_t len_matching () const
+ size_t len_matching(const Skeleton &skel) const
{
- std::vector<Node*>::const_reverse_iterator
+ std::vector<size_t>::const_reverse_iterator
tail = arcs.rbegin(),
head = arcs.rend();
for (; tail != head; ++tail) {
- RuleInfo *rule = (*tail)->rule;
+ RuleInfo *rule = skel.nodes[*tail].rule;
if (rule == NULL) {
continue;
}
return len;
case ~0u:
for (; tail != head; ++tail) {
- if ((*tail)->ctx) {
+ if (skel.nodes[*tail].ctx) {
return static_cast<size_t>(head - tail) - 1;
}
}
}
return 0;
}
- rule_rank_t match() const
+ rule_rank_t match(const Skeleton &skel) const
{
- std::vector<Node*>::const_reverse_iterator
+ std::vector<size_t>::const_reverse_iterator
tail = arcs.rbegin(),
head = arcs.rend();
for (; tail != head; ++tail) {
- RuleInfo *rule = (*tail)->rule;
+ RuleInfo *rule = skel.nodes[*tail].rule;
if (rule != NULL) {
return rule->rank;
}
}
return rule_rank_t::none();
}
- const Node::arc_t& arc(size_t i) const
+ const Node::arc_t& arc(const Skeleton &skel, size_t i) const
{
- return arcs[i]->arcs[arcs[i + 1]];
+ return skel.nodes[arcs[i]].arcs[arcs[i + 1]];
}
- const Node::arcset_t& arcset(size_t i) const
+ const Node::arcset_t& arcset(const Skeleton &skel, size_t i) const
{
- return arcs[i]->arcsets[arcs[i + 1]];
+ return skel.nodes[arcs[i]].arcsets[arcs[i + 1]];
}
- void push(Node *n)
+ void push(size_t n)
{
arcs.push_back(n);
}
assert(arcs.back() == p->arcs.front());
arcs.insert(arcs.end(), p->arcs.begin() + 1, p->arcs.end());
}
+ bool operator<(const path_t &p) const
+ {
+ const size_t
+ s1 = arcs.size(),
+ s2 = p.arcs.size();
+ return (s1 == s2 && arcs < p.arcs)
+ || s1 < s2;
+ }
};
} // namespace re2c
-#include <stdlib.h>
+#include <string.h>
#include <algorithm>
-#include <utility>
-#include "src/codegen/go.h"
-#include "src/conf/msg.h"
#include "src/ir/dfa/dfa.h"
-#include "src/ir/regexp/regexp.h"
#include "src/ir/skeleton/path.h"
#include "src/ir/skeleton/skeleton.h"
namespace re2c
{
-Node::Node ()
- : arcs ()
- , arcsets ()
- , loop (0)
- , rule (NULL)
- , ctx (false)
- , dist (DIST_ERROR)
- , reachable ()
- , suffix (NULL)
+Node::Node() :
+ arcs(),
+ arcsets(),
+ rule(NULL),
+ ctx(false)
{}
-void Node::init(bool c, RuleInfo *r, const std::vector<std::pair<Node*, uint32_t> > &a)
+void Node::init(
+ bool c,
+ RuleInfo *r,
+ const std::vector<std::pair<size_t, uint32_t> > &a)
{
rule = r;
ctx = c;
uint32_t lb = 0;
- std::vector<std::pair<Node*, uint32_t> >::const_iterator
+ std::vector<std::pair<size_t, uint32_t> >::const_iterator
i = a.begin(),
e = a.end();
- for (; i != e; ++i)
- {
- Node *n = i->first;
+ for (; i != e; ++i) {
+ const size_t n = i->first;
const uint32_t ub = i->second - 1;
// pick at most 0x100 unique edges from this range
// - values should be evenly distributed
// - values should be deterministic
const uint32_t step = 1 + (ub - lb) / 0x100;
- for (uint32_t c = lb; c < ub; c += step)
- {
- arcs[n].push_back (c);
+ for (uint32_t c = lb; c < ub; c += step) {
+ arcs[n].push_back(c);
}
- arcs[n].push_back (ub);
+ arcs[n].push_back(ub);
- arcsets[n].push_back (std::make_pair (lb, ub));
+ arcsets[n].push_back(std::make_pair(lb, ub));
lb = ub + 1;
}
}
-Node::~Node ()
+bool Node::end() const
{
- delete suffix;
+ return arcs.size() == 0;
}
-bool Node::end () const
+Skeleton::Skeleton(
+ const dfa_t &dfa,
+ const charset_t &cs,
+ const rules_t &rs,
+ const std::string &dfa_name,
+ const std::string &dfa_cond,
+ uint32_t dfa_line) :
+ name(dfa_name),
+ cond(dfa_cond),
+ line(dfa_line),
+ nodes_count(dfa.states.size() + 1), // +1 for default state
+ nodes(new Node[nodes_count]),
+ loops(new uint8_t[nodes_count]),
+ suffixes(new path_t*[nodes_count]),
+ sizeof_key(4),
+ rules(rs)
{
- return arcs.size () == 0;
-}
+ memset(loops, 0, sizeof(uint8_t) * nodes_count);
+ memset(suffixes, 0, sizeof(path_t*) * nodes_count);
-Skeleton::Skeleton
- ( const dfa_t &dfa
- , const charset_t &cs
- , const rules_t &rs
- , const std::string &dfa_name
- , const std::string &dfa_cond
- , uint32_t dfa_line
- )
- : name (dfa_name)
- , cond (dfa_cond)
- , line (dfa_line)
- , nodes_count (dfa.states.size())
- , nodes (new Node [nodes_count + 1]) // +1 for default state
- , sizeof_key (4)
- , rules (rs)
-{
const size_t nc = cs.size() - 1;
// initialize skeleton nodes
- Node *nil = &nodes[nodes_count];
- for (size_t i = 0; i < nodes_count; ++i)
- {
+ for (size_t i = 0; i < nodes_count - 1; ++i) {
dfa_state_t *s = dfa.states[i];
- std::vector<std::pair<Node*, uint32_t> > arcs;
- for (size_t c = 0; c < nc;)
- {
+ std::vector<std::pair<size_t, uint32_t> > arcs;
+ for (size_t c = 0; c < nc;) {
const size_t j = s->arcs[c];
for (;++c < nc && s->arcs[c] == j;);
- Node *to = j == dfa_t::NIL
- ? nil
- : &nodes[j];
+ const size_t to = j == dfa_t::NIL
+ ? nodes_count - 1
+ : j;
arcs.push_back(std::make_pair(to, cs[c]));
}
// all arcs go to default node => this node is final, drop arcs
- if (arcs.size() == 1 && arcs[0].first == nil)
- {
+ if (arcs.size() == 1 && arcs[0].first == nodes_count - 1) {
arcs.clear();
}
nodes[i].init(s->ctx, s->rule, arcs);
}
- // calculate maximal path length, check overflow
- nodes->calc_dist ();
- const uint32_t maxlen = nodes->dist;
- if (maxlen == Node::DIST_MAX)
- {
- error ("DFA path %sis too long", incond (cond).c_str ());
- exit (1);
- }
+ const uint32_t maxlen = maxpath(*this);
// calculate maximal rule rank (disregarding default and none rules)
uint32_t maxrule = 0;
- for (uint32_t i = 0; i < nodes_count; ++i)
- {
+ for (uint32_t i = 0; i < nodes_count; ++i) {
const RuleInfo *r = nodes[i].rule;
- if (r && !r->rank.is_def())
- {
- maxrule = std::max (maxrule, r->rank.uint32 ());
+ if (r && !r->rank.is_def()) {
+ maxrule = std::max(maxrule, r->rank.uint32());
}
}
// two upper values reserved for default and none rules)
maxrule += 2;
// initialize size of key
- const uint32_t max = std::max (maxlen, maxrule);
- if (max <= std::numeric_limits<uint8_t>::max())
- {
+ const uint32_t max = std::max(maxlen, maxrule);
+ if (max <= std::numeric_limits<uint8_t>::max()) {
sizeof_key = 1;
- }
- else if (max <= std::numeric_limits<uint16_t>::max())
- {
+ } else if (max <= std::numeric_limits<uint16_t>::max()) {
sizeof_key = 2;
}
}
-Skeleton::~Skeleton ()
+Skeleton::~Skeleton()
{
- delete [] nodes;
+ delete[] nodes;
+ delete[] loops;
+ for (size_t i = 0; i < nodes_count; ++i) {
+ delete suffixes[i];
+ }
+ delete[] suffixes;
}
-uint32_t Skeleton::rule2key (rule_rank_t r) const
+uint32_t Skeleton::rule2key(rule_rank_t r) const
{
- switch (sizeof_key)
- {
+ switch (sizeof_key) {
default: // shouldn't happen
- case 4: return rule2key<uint32_t> (r);
- case 2: return rule2key<uint16_t> (r);
- case 1: return rule2key<uint8_t> (r);
+ case 4: return rule2key<uint32_t>(r);
+ case 2: return rule2key<uint16_t>(r);
+ case 1: return rule2key<uint8_t>(r);
}
}
#define _RE2C_IR_SKELETON_SKELETON_
#include "src/util/c99_stdint.h"
-#include <stddef.h>
#include <stdio.h>
#include <limits>
#include <map>
#include <utility>
#include "src/ir/regexp/regexp.h"
-#include "src/ir/rule_rank.h"
#include "src/parse/rules.h"
#include "src/util/local_increment.h"
#include "src/util/forbid_copy.h"
-#include "src/util/u32lim.h"
namespace re2c
{
struct dfa_t;
struct OutputFile;
-class RuleInfo;
struct path_t;
+typedef local_increment_t<uint8_t> local_inc;
+
struct Node
{
- /*
- * note [counting skeleton edges]
- *
- * To avoid any possible overflows all size calculations are wrapped in
- * a special truncated unsigned 32-bit integer type that checks overflow
- * on each binary operation or conversion from another type.
- *
- * Two things contribute to size calculation: path length and the number
- * of outgoing arcs in each node. Some considerations on why these values
- * will not overflow before they are converted to truncated type:
- *
- * - Maximal number of outgoing arcs in each node cannot exceed 32 bits:
- * it is bounded by the number of code units in current encoding, and
- * re2c doesn't support any encoding with more than 2^32 code units.
- * Conversion is safe.
- *
- * - Maximal path length cannot exceed 32 bits: we estimate it right
- * after skeleton construction and check for overflow. If path length
- * does overflow, an error is reported and re2c aborts.
- */
-
- // Type for calculating the size of path cover.
- // Paths are dumped to file as soon as generated and don't eat
- // heap space. The total size of path cover (measured in edges)
- // is O(N^2) where N is the number of edges in skeleton.
- typedef u32lim_t<1024 * 1024 * 1024> covers_t; // ~1Gb
-
- // Type for counting arcs in paths that cause undefined behaviour.
- // These paths are stored on heap, so the limit should be low.
- // Most real-world cases have only a few short paths.
- // We don't need all paths anyway, just some examples.
- typedef u32lim_t<1024> nakeds_t; // ~1Kb
-
typedef std::vector<std::pair<uint32_t, uint32_t> > arcset_t;
- typedef std::map<Node *, arcset_t> arcsets_t;
+ typedef std::map<size_t, arcset_t> arcsets_t;
typedef std::vector<uint32_t> arc_t;
- typedef std::map<Node *, arc_t> arcs_t;
+ typedef std::map<size_t, arc_t> arcs_t;
- typedef local_increment_t<uint8_t> local_inc;
-
- // outgoing arcs
arcs_t arcs;
arcsets_t arcsets;
-
- // how many times this node has been visited
- // (controls looping in graph traversals)
- uint8_t loop;
-
- // rule for corresponding DFA state (if any)
RuleInfo *rule;
-
- // start of trailing context
bool ctx;
- // maximal distance to end node (assuming one iteration per loop)
- static const uint32_t DIST_ERROR;
- static const uint32_t DIST_MAX;
- uint32_t dist;
-
- // rules reachable from this node (including absent rule)
- std::set<RuleInfo*> reachable;
+ Node();
+ void init(bool b, RuleInfo *r,
+ const std::vector<std::pair<size_t, uint32_t> > &arcs);
+ bool end() const;
- // path to end node (for constructing path cover)
- path_t * suffix;
-
- Node ();
- void init(bool b, RuleInfo *r, const std::vector<std::pair<Node*, uint32_t> > &arcs);
- ~Node ();
- bool end () const;
- void calc_dist ();
- void calc_reachable ();
- template <typename cunit_t, typename key_t>
- void cover (path_t & prefix, FILE * input, FILE * keys, covers_t &size);
- void naked_paths(path_t &prefix, std::vector<path_t> &paths, nakeds_t &size);
-
- FORBID_COPY (Node);
+ FORBID_COPY(Node);
};
struct Skeleton
const uint32_t line;
const size_t nodes_count;
- Node * nodes;
+ Node *nodes;
+
+ // visit counters (for graph traversal)
+ uint8_t *loops;
+
+ // paths to end node (for constructing path cover)
+ path_t **suffixes;
+
size_t sizeof_key;
rules_t rules;
- Skeleton
- ( const dfa_t &dfa
- , const charset_t &cs
- , const rules_t & rs
- , const std::string &dfa_name
- , const std::string &dfa_cond
- , uint32_t dfa_line
- );
+ Skeleton(const dfa_t &dfa, const charset_t &cs, const rules_t &rs,
+ const std::string &dfa_name, const std::string &dfa_cond,
+ uint32_t dfa_line);
~Skeleton ();
- void warn_undefined_control_flow ();
- void warn_unreachable_nullable_rules ();
- void emit_data (const char * fname);
- static void emit_prolog (OutputFile & o);
- void emit_start
- ( OutputFile & o
- , size_t maxfill
- , bool backup
- , bool backupctx
- , bool accept
- ) const;
- void emit_end
- ( OutputFile & o
- , bool backup
- , bool backupctx
- ) const;
- static void emit_epilog (OutputFile & o, const std::set<std::string> & names);
- void emit_action (OutputFile & o, uint32_t ind, rule_rank_t rank) const;
-
- template <typename key_t> static key_t rule2key (rule_rank_t r);
- uint32_t rule2key (rule_rank_t r) const;
-
-private:
- template <typename cunit_t, typename key_t>
- void generate_paths_cunit_key (FILE * input, FILE * keys);
- template <typename cunit_t>
- void generate_paths_cunit (FILE * input, FILE * keys);
- void generate_paths (FILE * input, FILE * keys);
-
- FORBID_COPY (Skeleton);
+ uint32_t rule2key(rule_rank_t r) const;
+ template<typename key_t> static key_t rule2key(rule_rank_t r);
+
+ FORBID_COPY(Skeleton);
};
-template<typename key_t> key_t Skeleton::rule2key (rule_rank_t r)
+template<typename key_t> key_t Skeleton::rule2key(rule_rank_t r)
{
if (r.is_none()) {
return std::numeric_limits<key_t>::max();
}
}
-bool compare_default_paths(const path_t &p1, const path_t &p2);
-void fprint_default_path(FILE *f, const path_t &p);
+uint32_t maxpath(Skeleton &skel);
+void warn_undefined_control_flow(Skeleton &skel);
+void fprint_default_path(FILE *f, const Skeleton &skel, const path_t &p);
+void warn_unreachable_nullable_rules(Skeleton &skel);
+void emit_data(Skeleton &skel, const char *fname);
+void emit_prolog(OutputFile & o);
+void emit_start(const Skeleton &skel, OutputFile &o, size_t maxfill,
+ bool backup, bool backupctx, bool accept);
+void emit_end(const Skeleton &skel, OutputFile &o, bool backup, bool backupctx);
+void emit_epilog(OutputFile &o, const std::set<std::string> &names);
+void emit_action(const Skeleton &skel, OutputFile &o, uint32_t ind,
+ rule_rank_t rank);
} // namespace re2c
#include "src/util/c99_stdint.h"
-#include <map>
#include <set>
-#include <utility>
#include "src/conf/warn.h"
#include "src/globals.h"
namespace re2c
{
-void Node::calc_reachable ()
+static void calc_reachable(
+ Skeleton &skel,
+ std::vector<std::set<RuleInfo*> > &reachs,
+ size_t i)
{
- if (!reachable.empty ())
- {
+ const Node &node = skel.nodes[i];
+ uint8_t &loop = skel.loops[i];
+ std::set<RuleInfo*> &reach = reachs[i];
+
+ if (!reach.empty()) {
return;
- }
- else if (end ())
- {
- reachable.insert (rule);
- }
- else if (loop < 2)
- {
- local_inc _ (loop);
- for (arcs_t::iterator i = arcs.begin (); i != arcs.end (); ++i)
- {
- i->first->calc_reachable ();
- reachable.insert (i->first->reachable.begin (), i->first->reachable.end ());
+ } else if (node.end()) {
+ reach.insert(node.rule);
+ } else if (loop < 2) {
+ local_inc _(loop);
+ Node::arcs_t::const_iterator
+ arc = node.arcs.begin(),
+ end = node.arcs.end();
+ for (; arc != end; ++arc) {
+ const size_t j = arc->first;
+ calc_reachable(skel, reachs, j);
+ reach.insert(reachs[j].begin(), reachs[j].end());
}
}
}
-void Skeleton::warn_unreachable_nullable_rules ()
+void warn_unreachable_nullable_rules(Skeleton &skel)
{
// calculate reachable rules
- nodes->calc_reachable();
- for (uint32_t i = 0; i < nodes_count; ++i)
- {
- RuleInfo *r1 = nodes[i].rule;
+ std::vector<std::set<RuleInfo*> > reachs(skel.nodes_count);
+ calc_reachable(skel, reachs, 0);
+
+ for (uint32_t i = 0; i < skel.nodes_count; ++i) {
+ RuleInfo *r1 = skel.nodes[i].rule;
if (!r1) {
continue;
}
- const std::set<RuleInfo*> & rs = nodes[i].reachable;
- for (std::set<RuleInfo*>::const_iterator j = rs.begin(); j != rs.end(); ++j)
- {
- RuleInfo* r2 = *j;
- if (!r2 || r1->rank == r2->rank)
- {
+ std::set<RuleInfo*>::const_iterator
+ rule = reachs[i].begin(),
+ end = reachs[i].end();
+ for (; rule != end; ++rule) {
+ RuleInfo* r2 = *rule;
+ if (!r2 || r1->rank == r2->rank) {
r1->reachable = true;
- }
- else
- {
+ } else {
r1->shadow.insert(r2->loc.line);
}
}
// - infinite rules that consume infinitely many characters and fail on YYFILL, e.g. '[^]*'
// - rules that contain never-matching link, e.g. '[]' with option '--empty-class match-none'
// default rule '*' should not be reported
- for (rules_t::const_iterator i = rules.begin(); i != rules.end(); ++i)
- {
+ for (rules_t::const_iterator i = skel.rules.begin(); i != skel.rules.end(); ++i) {
const RuleInfo *r = *i;
- if (!r->rank.is_def() && !r->reachable)
- {
- warn.unreachable_rule(cond, r);
+ if (!r->rank.is_def() && !r->reachable) {
+ warn.unreachable_rule(skel.cond, r);
}
}
// - rules that match empty strins with nonempty trailing context
// false positives on partially shadowed (yet reachable) rules, e.g.:
// [^]?
- for (rules_t::const_iterator i = rules.begin(); i != rules.end(); ++i)
- {
+ for (rules_t::const_iterator i = skel.rules.begin(); i != skel.rules.end(); ++i) {
const RuleInfo *r = *i;
- if (r->nullable && r->reachable)
- {
+ if (r->nullable && r->reachable) {
warn.match_empty_string(r->loc.line);
}
}
.wline_info (in->get_cline (), in->get_fname ().c_str ());
if (opts->target == opt_t::SKELETON)
{
- Skeleton::emit_prolog (o.source);
+ emit_prolog(o.source);
}
Enc encodingOld = opts->encoding;
if (opts->target == opt_t::SKELETON)
{
- Skeleton::emit_epilog (o.source, o.skeletons);
+ emit_epilog(o.source, o.skeletons);
}
parse_cleanup();