#!/usr/bin/env rdmd import std.algorithm; import std.ascii; import std.file; import std.regex; import std.stdio; import std.string; struct ConstantCompletion { string[] identifiers; string ddoc; } struct SingleConstantCompletion { string identifier; string ddoc; string toString() const { return "ConstantCompletion(\"" ~ identifier ~ "\", `" ~ ddoc.replaceAll(ctRegex!"`+", "` ~ \"$0\" ~ `") ~ "`)"; } } unittest { ConstantCompletion completion; completion.ddoc = "'abc``def'"; assert(completion.toString == q{ConstantCompletion("", `'abc` ~ "``" ~ `def'`)}); } ConstantCompletion[] parsePragmas(string ddoc) { ConstantCompletion[] completions; bool foundTerminator; ConstantCompletion current; bool inInlineCode; bool seekingToFirst = true; string indent; void addCurrent() { if (seekingToFirst) { current = ConstantCompletion.init; return; } // ret still has `$(DD content)` around it, strip that if (current.ddoc.startsWith("$(DD")) { current.ddoc = current.ddoc["$(DD".length .. $].strip; if (current.ddoc.endsWith(")")) current.ddoc = current.ddoc[0 .. $ - 1].stripRight; } completions ~= current; current = ConstantCompletion.init; } foreach (line; ddoc.lineSplitter!(KeepTerminator.yes)) { auto strippedLine = line.stripLeft; if (strippedLine.startsWith("$(SPEC_SUBNAV_PREV_NEXT")) { addCurrent(); // end of macros foundTerminator = true; break; } else if (strippedLine.startsWith("$(DT $(LNAME2 ")) { addCurrent(); seekingToFirst = false; indent = line[0 .. $ - strippedLine.length]; string identifierLine = strippedLine.stripRight; // fully stripped auto closing = identifierLine.indexOfAny("),"); if (closing == -1) closing = identifierLine.length; current.identifiers = [identifierLine["$(DT $(LNAME2".length .. closing].strip]; } else if (!seekingToFirst) { if (line.startsWith("---")) // code blocks aren't indented inInlineCode = !inInlineCode; if (inInlineCode) current.ddoc ~= line; else { if (line.startsWith(indent)) // strip indentation equal to DT (section header) current.ddoc ~= line[indent.length .. $]; else current.ddoc ~= line; } } } if (!foundTerminator) throw new Exception("Could not find '$(SPEC_SUBNAV_PREV_NEXT' line in pragma.dd, format of the file has changed and code needs to be adjusted."); return completions; } ConstantCompletion[] parseTraits(string ddoc) { ConstantCompletion[] completions; bool foundTerminator; ConstantCompletion current; bool inInlineCode; bool seekingToFirst = true; string indent; void addCurrent() { current.ddoc = current.ddoc.strip; if (seekingToFirst || current == ConstantCompletion.init) { current = ConstantCompletion.init; return; } completions ~= current; current = ConstantCompletion.init; } foreach (line; ddoc.lineSplitter!(KeepTerminator.yes)) { if (line.stripLeft.startsWith("$(SPEC_SUBNAV_PREV_NEXT")) { addCurrent(); foundTerminator = true; break; } else if (line.canFind("$(GNAME ")) { addCurrent(); ptrdiff_t i = line.indexOf("$(GNAME "); while (i != -1) { auto closing = line.indexOfAny("),", i); current.identifiers ~= line[i + "$(GNAME ".length .. closing].strip; i = line.indexOf("$(GNAME ", closing); } seekingToFirst = false; if (current.identifiers.length == 1 && current.identifiers[0][0].isUpper) seekingToFirst = true; // not considering capitalized identifiers traits (TraitsKeyword, TraitsExpression, etc.) } else if (!seekingToFirst) { if (line.startsWith("---")) inInlineCode = !inInlineCode; if (inInlineCode) current.ddoc ~= line; else { if (!current.ddoc.length) indent = line[0 .. $ - line.stripLeft.length]; if (line.startsWith(indent)) current.ddoc ~= line[indent.length .. $]; else current.ddoc ~= line; } } } if (!foundTerminator) throw new Exception("Could not find '$(SPEC_SUBNAV_PREV_NEXT' line in traits.dd, format of the file has changed and code needs to be adjusted."); return completions; } void main() { immutable pragmaDDoc = readText("pragma.dd"); immutable traitsDDoc = readText("traits.dd"); auto pragmas = parsePragmas(pragmaDDoc); auto traits = parseTraits(traitsDDoc); string part1 = `// // // this file is auto generated by constants-gen/generator.d, do not edit manually. // // module dcd.common.constants2; import dcd.common.constants : ConstantCompletion; /** * Pragma arguments */ immutable ConstantCompletion[] pragmas = [ // generated from pragma.dd`; string part2 = `]; /** * Traits arguments */ immutable ConstantCompletion[] traits = [ // generated from traits.dd`; string part3 = "];"; auto file = File("../src/dcd/common/constants2.d", "w"); file.writeln(part1); foreach (pragma_; pragmas.sorted) file.writeln('\t', pragma_, ","); file.writeln(part2); foreach (trait; traits.sorted) file.writeln('\t', trait, ","); file.writeln(part3); } auto sorted(T)(T range) { return range.map!(a => a.identifiers.map!(identifier => SingleConstantCompletion(identifier, a.ddoc))).join.sort!"a.identifier < b.identifier"; }