diff --git a/date-time/spec/KBFuncs.nlp b/date-time/spec/KBFuncs.nlp index d6bb5cb..58da36f 100644 --- a/date-time/spec/KBFuncs.nlp +++ b/date-time/spec/KBFuncs.nlp @@ -184,13 +184,14 @@ CountName(L("con"),L("root"),L("pad")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } @@ -403,65 +404,89 @@ JsonKB(L("file"),L("con")) { } JsonKBRecurse(L("file"),L("parent"),L("level")) { + # The separator is written before each child, once it is known what the + # child is. Writing it after put the comma inside an array that a plain + # sibling then closed, which was not JSON. A counted child with a new + # base name (item2 then thing1) starts its own array. L("con") = down(L("parent")); L("first") = 1; - L("has concount") = 0; + L("in array") = 0; + L("array base") = 0; while (L("con")) { L("name") = conceptname(L("con")); L("concount") = SeparateConCount(L("name")); L("connum") = G("connum"); + L("continues") = 0; + if (L("concount") && L("in array")) { + if (L("concount") == L("array base")) + L("continues") = 1; + } + + if (!L("first")) { + if (L("continues")) { + L("file") << ",\n"; + } else if (L("in array")) { + L("file") << "\n" << SpacesStr(L("level")+1) << "],\n"; + L("in array") = 0; + } else if (L("concount")) { + L("file") << ","; + } else { + L("file") << ",\n"; + } + } if (L("concount")) { - "hier.txt" << SpacesStr(L("level")+1) << " ====> " << L("connum") << "\n"; - if (!L("has concount")) { + if (!L("continues")) { L("file") << SpacesStr(L("level")+1) << JsonStr(L("concount")) << ": [\n"; - } else { - L("file") << "\n"; + L("in array") = 1; + L("array base") = L("concount"); } L("file") << SpacesStr(L("level")+2) << "{\n"; - L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\",\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+2); - L("has concount") = 1; - + L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\""; + L("has attrs") = JsonHasAttributes(L("con")); + if (L("has attrs")) { + L("file") << ",\n"; + JsonAttributes(L("file"),L("con"),L("level")+2); + } if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+2); } else { L("file") << "\n" << SpacesStr(L("level")+2) << "}"; } } else { - if (L("has concount")) { - L("file") << "\n" << SpacesStr(L("level")+2) << "}"; - L("file") << "\n" << SpacesStr(L("level")+1) << "]"; - L("has concount") = 0; - } - if (!L("first")) - L("file") << "\n"; L("file") << SpacesStr(L("level")+1) << JsonStr(L("name")) << ": {\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+1); - + L("has attrs") = JsonAttributes(L("file"),L("con"),L("level")+1); if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + if (L("has attrs")) L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+1); - } - else if (!L("has concount")) { + } else { L("file") << "\n" << SpacesStr(L("level")+1) << "}"; } } L("con") = next(L("con")); - if (L("con")) { - L("file") << ","; - } L("first") = 0; } - if (L("has concount")) { + if (L("in array")) { L("file") << "\n" << SpacesStr(L("level")+1) << "]"; } L("file") << "\n" << SpacesStr(L("level")) << "}"; } +JsonHasAttributes(L("con")) { + # Would JsonAttributes write anything? An array element writes its "id" + # first and needs to know whether a comma follows it. + L("attrs") = findattrs(L("con")); + while (L("attrs")) { + L("count con") = IsCountCon(attrname(L("attrs")),L("con")); + if (!L("count con")) return 1; + L("attrs") = nextattr(L("attrs")); + } + return 0; +} + JsonAttributes(L("file"),L("con"),L("level")) { L("attrs") = findattrs(L("con")); if (!L("attrs")) { @@ -527,26 +552,21 @@ JsonStr(L("str")) { } SeparateConCount(L("conname")) { + # "item12" -> "item", with G("connum") = "12". Only the digits at the + # end count: "a1b2" is "a1b" and "2". Returns 0 when the name does not + # end in digits or is all digits. + G("connum") = 0; if (strisdigit(L("conname"))) return 0; - L("i") = strlength(L("conname")) - 1; - G("connum") = 0; - L("stop") = 0; - while (!L("stop") && L("i") > 0) { - L("c") = strpiece(L("conname"),L("i"),L("i")); - if (strisdigit(L("c"))) { - L("num") = L("c") + L("num"); - } else { - L("stop"); - } + L("last") = strlength(L("conname")) - 1; + L("i") = L("last"); + while (L("i") > 0 && strisdigit(strpiece(L("conname"),L("i"),L("i")))) { L("i")--; } - if (L("num")) { - G("connum") = L("num"); - L("base") = strpiece(L("conname"),0,strlength(L("conname"))-strlength(L("num"))-1); - return L("base"); - } - return 0; + if (L("i") == L("last")) return 0; + + G("connum") = strpiece(L("conname"),L("i")+1,L("last")); + return strpiece(L("conname"),0,L("i")); } IsCountCon(L("str"),L("con")) { diff --git a/formatting/spec/KBFuncs.nlp b/formatting/spec/KBFuncs.nlp index 451a5ec..eb85dfb 100644 --- a/formatting/spec/KBFuncs.nlp +++ b/formatting/spec/KBFuncs.nlp @@ -184,13 +184,14 @@ CountName(L("con"),L("root"),L("pad")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } @@ -403,65 +404,89 @@ JsonKB(L("file"),L("con")) { } JsonKBRecurse(L("file"),L("parent"),L("level")) { + # The separator is written before each child, once it is known what the + # child is. Writing it after put the comma inside an array that a plain + # sibling then closed, which was not JSON. A counted child with a new + # base name (item2 then thing1) starts its own array. L("con") = down(L("parent")); L("first") = 1; - L("has concount") = 0; + L("in array") = 0; + L("array base") = 0; while (L("con")) { L("name") = conceptname(L("con")); L("concount") = SeparateConCount(L("name")); L("connum") = G("connum"); + L("continues") = 0; + if (L("concount") && L("in array")) { + if (L("concount") == L("array base")) + L("continues") = 1; + } + + if (!L("first")) { + if (L("continues")) { + L("file") << ",\n"; + } else if (L("in array")) { + L("file") << "\n" << SpacesStr(L("level")+1) << "],\n"; + L("in array") = 0; + } else if (L("concount")) { + L("file") << ","; + } else { + L("file") << ",\n"; + } + } if (L("concount")) { - "hier.txt" << SpacesStr(L("level")+1) << " ====> " << L("connum") << "\n"; - if (!L("has concount")) { + if (!L("continues")) { L("file") << SpacesStr(L("level")+1) << JsonStr(L("concount")) << ": [\n"; - } else { - L("file") << "\n"; + L("in array") = 1; + L("array base") = L("concount"); } L("file") << SpacesStr(L("level")+2) << "{\n"; - L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\",\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+2); - L("has concount") = 1; - + L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\""; + L("has attrs") = JsonHasAttributes(L("con")); + if (L("has attrs")) { + L("file") << ",\n"; + JsonAttributes(L("file"),L("con"),L("level")+2); + } if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+2); } else { L("file") << "\n" << SpacesStr(L("level")+2) << "}"; } } else { - if (L("has concount")) { - L("file") << "\n" << SpacesStr(L("level")+2) << "}"; - L("file") << "\n" << SpacesStr(L("level")+1) << "]"; - L("has concount") = 0; - } - if (!L("first")) - L("file") << "\n"; L("file") << SpacesStr(L("level")+1) << JsonStr(L("name")) << ": {\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+1); - + L("has attrs") = JsonAttributes(L("file"),L("con"),L("level")+1); if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + if (L("has attrs")) L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+1); - } - else if (!L("has concount")) { + } else { L("file") << "\n" << SpacesStr(L("level")+1) << "}"; } } L("con") = next(L("con")); - if (L("con")) { - L("file") << ","; - } L("first") = 0; } - if (L("has concount")) { + if (L("in array")) { L("file") << "\n" << SpacesStr(L("level")+1) << "]"; } L("file") << "\n" << SpacesStr(L("level")) << "}"; } +JsonHasAttributes(L("con")) { + # Would JsonAttributes write anything? An array element writes its "id" + # first and needs to know whether a comma follows it. + L("attrs") = findattrs(L("con")); + while (L("attrs")) { + L("count con") = IsCountCon(attrname(L("attrs")),L("con")); + if (!L("count con")) return 1; + L("attrs") = nextattr(L("attrs")); + } + return 0; +} + JsonAttributes(L("file"),L("con"),L("level")) { L("attrs") = findattrs(L("con")); if (!L("attrs")) { @@ -527,26 +552,21 @@ JsonStr(L("str")) { } SeparateConCount(L("conname")) { + # "item12" -> "item", with G("connum") = "12". Only the digits at the + # end count: "a1b2" is "a1b" and "2". Returns 0 when the name does not + # end in digits or is all digits. + G("connum") = 0; if (strisdigit(L("conname"))) return 0; - L("i") = strlength(L("conname")) - 1; - G("connum") = 0; - L("stop") = 0; - while (!L("stop") && L("i") > 0) { - L("c") = strpiece(L("conname"),L("i"),L("i")); - if (strisdigit(L("c"))) { - L("num") = L("c") + L("num"); - } else { - L("stop"); - } + L("last") = strlength(L("conname")) - 1; + L("i") = L("last"); + while (L("i") > 0 && strisdigit(strpiece(L("conname"),L("i"),L("i")))) { L("i")--; } - if (L("num")) { - G("connum") = L("num"); - L("base") = strpiece(L("conname"),0,strlength(L("conname"))-strlength(L("num"))-1); - return L("base"); - } - return 0; + if (L("i") == L("last")) return 0; + + G("connum") = strpiece(L("conname"),L("i")+1,L("last")); + return strpiece(L("conname"),0,L("i")); } IsCountCon(L("str"),L("con")) { diff --git a/nlp/spec/KBFuncs.nlp b/nlp/spec/KBFuncs.nlp index b7bb38e..8956e00 100644 --- a/nlp/spec/KBFuncs.nlp +++ b/nlp/spec/KBFuncs.nlp @@ -187,13 +187,14 @@ CountName(L("con"),L("root"),L("pad")) { } StripEndDigits(L("name")) { + # "item12" -> "item". A name with no trailing digits comes back whole, + # an all-digit name gives 0. Positions only count up from 0, so a short + # name never reaches a negative strpiece position (which counts from the + # end since engine 4.2.15). if (strisdigit(L("name"))) return 0; - L("len") = strlength(L("name")) - 1; - L("i") = L("len") - 1; - L("str") = strpiece(L("name"),L("i"),L("len")); - while (strisdigit(L("str")) && L("i")) { + L("i") = strlength(L("name")) - 1; + while (L("i") > 0 && strisdigit(strpiece(L("name"),L("i"),L("i")))) { L("i")--; - L("str") = strpiece(L("name"),L("i"),L("len")); } return strpiece(L("name"),0,L("i")); } @@ -406,65 +407,89 @@ JsonKB(L("file"),L("con")) { } JsonKBRecurse(L("file"),L("parent"),L("level")) { + # The separator is written before each child, once it is known what the + # child is. Writing it after put the comma inside an array that a plain + # sibling then closed, which was not JSON. A counted child with a new + # base name (item2 then thing1) starts its own array. L("con") = down(L("parent")); L("first") = 1; - L("has concount") = 0; + L("in array") = 0; + L("array base") = 0; while (L("con")) { L("name") = conceptname(L("con")); L("concount") = SeparateConCount(L("name")); L("connum") = G("connum"); + L("continues") = 0; + if (L("concount") && L("in array")) { + if (L("concount") == L("array base")) + L("continues") = 1; + } + + if (!L("first")) { + if (L("continues")) { + L("file") << ",\n"; + } else if (L("in array")) { + L("file") << "\n" << SpacesStr(L("level")+1) << "],\n"; + L("in array") = 0; + } else if (L("concount")) { + L("file") << ","; + } else { + L("file") << ",\n"; + } + } if (L("concount")) { - "hier.txt" << SpacesStr(L("level")+1) << " ====> " << L("connum") << "\n"; - if (!L("has concount")) { + if (!L("continues")) { L("file") << SpacesStr(L("level")+1) << JsonStr(L("concount")) << ": [\n"; - } else { - L("file") << "\n"; + L("in array") = 1; + L("array base") = L("concount"); } L("file") << SpacesStr(L("level")+2) << "{\n"; - L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\",\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+2); - L("has concount") = 1; - + L("file") << SpacesStr(L("level")+3) << "\"id\": \"" << L("connum") << "\""; + L("has attrs") = JsonHasAttributes(L("con")); + if (L("has attrs")) { + L("file") << ",\n"; + JsonAttributes(L("file"),L("con"),L("level")+2); + } if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+2); } else { L("file") << "\n" << SpacesStr(L("level")+2) << "}"; } } else { - if (L("has concount")) { - L("file") << "\n" << SpacesStr(L("level")+2) << "}"; - L("file") << "\n" << SpacesStr(L("level")+1) << "]"; - L("has concount") = 0; - } - if (!L("first")) - L("file") << "\n"; L("file") << SpacesStr(L("level")+1) << JsonStr(L("name")) << ": {\n"; - L("first") = !JsonAttributes(L("file"),L("con"),L("level")+1); - + L("has attrs") = JsonAttributes(L("file"),L("con"),L("level")+1); if (down(L("con"))) { - if (!L("first")) L("file") << ",\n"; + if (L("has attrs")) L("file") << ",\n"; JsonKBRecurse(L("file"),L("con"),L("level")+1); - } - else if (!L("has concount")) { + } else { L("file") << "\n" << SpacesStr(L("level")+1) << "}"; } } L("con") = next(L("con")); - if (L("con")) { - L("file") << ","; - } L("first") = 0; } - if (L("has concount")) { + if (L("in array")) { L("file") << "\n" << SpacesStr(L("level")+1) << "]"; } L("file") << "\n" << SpacesStr(L("level")) << "}"; } +JsonHasAttributes(L("con")) { + # Would JsonAttributes write anything? An array element writes its "id" + # first and needs to know whether a comma follows it. + L("attrs") = findattrs(L("con")); + while (L("attrs")) { + L("count con") = IsCountCon(attrname(L("attrs")),L("con")); + if (!L("count con")) return 1; + L("attrs") = nextattr(L("attrs")); + } + return 0; +} + JsonAttributes(L("file"),L("con"),L("level")) { L("attrs") = findattrs(L("con")); if (!L("attrs")) { @@ -530,26 +555,21 @@ JsonStr(L("str")) { } SeparateConCount(L("conname")) { + # "item12" -> "item", with G("connum") = "12". Only the digits at the + # end count: "a1b2" is "a1b" and "2". Returns 0 when the name does not + # end in digits or is all digits. + G("connum") = 0; if (strisdigit(L("conname"))) return 0; - L("i") = strlength(L("conname")) - 1; - G("connum") = 0; - L("stop") = 0; - while (!L("stop") && L("i") > 0) { - L("c") = strpiece(L("conname"),L("i"),L("i")); - if (strisdigit(L("c"))) { - L("num") = L("c") + L("num"); - } else { - L("stop"); - } + L("last") = strlength(L("conname")) - 1; + L("i") = L("last"); + while (L("i") > 0 && strisdigit(strpiece(L("conname"),L("i"),L("i")))) { L("i")--; } - if (L("num")) { - G("connum") = L("num"); - L("base") = strpiece(L("conname"),0,strlength(L("conname"))-strlength(L("num"))-1); - return L("base"); - } - return 0; + if (L("i") == L("last")) return 0; + + G("connum") = strpiece(L("conname"),L("i")+1,L("last")); + return strpiece(L("conname"),0,L("i")); } IsCountCon(L("str"),L("con")) {