5#ifndef LINE_LANG_LQN_SRVN_WRITER_H
6#define LINE_LANG_LQN_SRVN_WRITER_H
62inline const std::string*
get(
const Element* el,
const std::string& key) {
63 for (
const auto& kv : el->
attrs)
64 if (kv.first == key)
return &kv.second;
69inline std::string
get(
const Element* el,
const std::string& key,
const std::string& dflt) {
70 const std::string* v =
get(el, key);
75inline std::string
show(
const Element* el,
const std::string& key) {
76 const std::string* v =
get(el, key);
77 return v ? *v : std::string(
"None");
80inline std::string
lower(std::string s) {
82 if (c >=
'A' && c <=
'Z') c =
static_cast<char>(c -
'A' +
'a');
86inline std::string
upper(std::string s) {
88 if (c >=
'a' && c <=
'z') c =
static_cast<char>(c -
'a' +
'A');
92inline bool starts_with(
const std::string& s,
const char* p) {
return s.compare(0, std::string(p).size(), p) == 0; }
94inline void utf8(std::string& out,
unsigned long cp) {
95 if (cp < 0x80) out +=
static_cast<char>(cp);
96 else if (cp < 0x800) {
97 out +=
static_cast<char>(0xC0 | (cp >> 6));
98 out +=
static_cast<char>(0x80 | (cp & 0x3F));
99 }
else if (cp < 0x10000) {
100 out +=
static_cast<char>(0xE0 | (cp >> 12));
101 out +=
static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
102 out +=
static_cast<char>(0x80 | (cp & 0x3F));
104 out +=
static_cast<char>(0xF0 | (cp >> 18));
105 out +=
static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
106 out +=
static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
107 out +=
static_cast<char>(0x80 | (cp & 0x3F));
117 ws.reserve(v.size());
118 for (std::size_t i = 0; i < v.size(); ++i) {
122 if (i + 1 < v.size() && v[i + 1] ==
'\n') ++i;
123 }
else if (c ==
'\n' || c ==
'\t') {
130 out.reserve(
ws.size());
131 for (std::size_t i = 0; i <
ws.size(); ++i) {
132 if (
ws[i] ==
'&' && i + 2 <
ws.size() &&
ws[i + 1] ==
'#') {
133 const std::size_t semi =
ws.find(
';', i);
134 if (semi != std::string::npos) {
135 const bool hex =
ws[i + 2] ==
'x';
136 const std::string digits =
ws.substr(i + (hex ? 3 : 2), semi - i - (hex ? 3 : 2));
137 bool ok = !digits.empty();
138 for (
char d : digits)
141 utf8(out, std::strtoul(digits.c_str(), NULL, hex ? 16 : 10));
159 return starts_with(tag,
"result-") || tag ==
"service-time-distribution" || tag ==
"mva-info" ||
160 tag ==
"convergence-collection";
165 std::vector<const Element*> out;
174 if (c->name == tag)
return c.get();
178inline std::vector<const Element*>
findall(
const Element* el,
const std::string& tag) {
182inline bool in(
const std::string& s, std::initializer_list<const char*> set) {
183 for (
const char* x : set)
184 if (s == x)
return true;
188inline void check(
const Element* el, std::initializer_list<const char*> allowed,
189 std::initializer_list<const char*> kidtags = {}) {
190 const std::string* nm =
get(el,
"name");
191 const std::string where = (nm && !nm->empty()) ?
" name=\"" + *nm +
"\"" : std::string();
192 for (
const auto& kv : el->
attrs)
193 if (!
in(kv.first, allowed))
194 throw unsup(
"attribute \"" + kv.first +
"\" of <" + el->
name + where +
"> has no SRVN spelling");
196 if (!
in(k->name, kidtags))
197 throw unsup(
"<" + k->name +
"> inside <" + el->
name + where +
"> has no SRVN spelling");
201inline bool py_float(
const std::string& text,
double& x) {
202 std::size_t a = 0, b = text.size();
205 std::string s = text.substr(a, b - a);
208 if (i < s.size() && (s[i] ==
'+' || s[i] ==
'-')) sign = s[i++];
209 const std::string rest =
lower(s.substr(i));
210 if (rest ==
"inf" || rest ==
"infinity") {
211 x = sign ==
"-" ? -HUGE_VAL : HUGE_VAL;
219 std::string clean = sign;
220 auto digitpart = [&](std::size_t& k) ->
bool {
223 while (k < s.size()) {
230 bool mant = digitpart(i);
231 if (i < s.size() && s[i] ==
'.') {
234 const bool frac = digitpart(i);
237 if (!mant)
return false;
238 if (i < s.size() && (s[i] ==
'e' || s[i] ==
'E')) {
241 if (i < s.size() && (s[i] ==
'+' || s[i] ==
'-')) clean += s[i++];
242 if (!digitpart(i))
return false;
244 if (i != s.size())
return false;
245 x = std::strtod(clean.c_str(), NULL);
250inline const std::string*
num(
const Element* el,
const std::string& key, std::string& buf,
251 const char* dflt = NULL) {
252 const std::string* text =
get(el, key);
254 if (!dflt)
return NULL;
260 throw unsup(key +
"=\"" + *text +
"\" of <" + el->
name +
261 "> is not a number (SPEX and LQX expressions have no SRVN spelling here)");
262 if (!std::isfinite(x))
throw unsup(key +
"=\"" + *text +
"\" of <" + el->
name +
"> is not finite");
268inline std::string
numd(
const Element* el,
const std::string& key,
const char* dflt) {
270 return *
num(el, key, buf, dflt);
275 std::size_t n = nm.size();
276 if (n > 0 && nm[n - 1] ==
'\n') --n;
277 if (n < 2)
return false;
278 auto alpha = [](
char c) {
return (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z') || c ==
'_'; };
279 if (!alpha(nm[0]))
return false;
280 for (std::size_t i = 1; i < n; ++i)
285inline std::string
name(
const Element* el,
const std::string& key =
"name") {
286 const std::string* nm =
get(el, key);
287 if (!nm)
throw xerr(
"<" + el->
name +
"> has no " + key);
289 throw unsup(
"the name \"" + *nm +
290 "\" is not an LQNS identifier (a letter or \"_\", then at least one letter, digit or \"_\"); "
291 "write the model with use_abstract_names=True");
295inline std::string
join(
const std::vector<std::string>& v,
const std::string& sep) {
297 for (std::size_t i = 0; i < v.size(); ++i) s += (i ? sep : std::string()) + v[i];
308 std::vector<std::pair<std::string, std::vector<std::string>>>& lines,
309 std::vector<CallRow>& calls) {
310 const int K = acts.rbegin()->first;
311 static const char* fields[4][3] = {{
"s",
"host-demand-mean",
"0"},
312 {
"c",
"host-demand-cvsq",
"1"},
313 {
"Z",
"think-time",
"0"},
314 {
"M",
"max-service-time",
"0"}};
315 for (
int fi = 0; fi < 4; ++fi) {
316 const std::string key = fields[fi][0];
317 std::vector<std::string> vals;
319 for (
int k = 1; k <= K; ++k) {
320 std::map<int, const Element*>::const_iterator it = acts.find(k);
322 const std::string* v = it == acts.end() ? NULL :
num(it->second, fields[fi][1], buf);
324 vals.push_back(v ? *v : std::string(fields[fi][2]));
326 if (key ==
"s" || any) lines.emplace_back(key, vals);
328 std::vector<std::string> det;
330 for (
int k = 1; k <= K; ++k) {
331 std::map<int, const Element*>::const_iterator it = acts.find(k);
333 it != acts.end() &&
upper(
get(it->second,
"call-order",
"STOCHASTIC")) ==
"DETERMINISTIC";
334 anydet = anydet || d;
335 det.push_back(d ?
"1" :
"0");
337 if (anydet) lines.emplace_back(
"f", det);
340 for (
int k = 1; k <= K; ++k) {
341 std::map<int, const Element*>::const_iterator it = acts.find(k);
342 if (it == acts.end())
continue;
343 std::set<std::pair<std::string, std::string>> seen;
345 if (c->name !=
"synch-call" && c->name !=
"asynch-call")
continue;
346 const std::pair<std::string, std::string> key(c->name ==
"synch-call" ?
"y" :
"z",
name(c,
"dest"));
348 throw unsup(
"phase " + std::to_string(k) +
" of entry \"" +
show(it->second,
"name") +
"\" calls \"" +
349 key.second +
"\" twice; SRVN carries one count per phase");
353 if (r.kind == key.first && r.dst == key.second) row = &r;
355 calls.push_back(
CallRow{key.first, key.second, std::vector<std::string>(K,
"0")});
358 row->vals[k - 1] =
numd(c,
"calls-mean",
"0");
364 static const std::map<std::string, const char*> t = {{
"fcfs",
"f"}, {
"hol",
"h"}, {
"pri",
"p"}, {
"pp",
"p"},
365 {
"rand",
"r"}, {
"siro",
"r"}, {
"inf",
"i"}, {
"cfs",
"c"},
366 {
"gps",
"c"}, {
"ps",
"s"}};
367 std::map<std::string, const char*>::const_iterator it = t.find(s);
368 return it == t.end() ? NULL : it->second;
372 static const std::map<std::string, const char*> t = {
373 {
"ref",
"r"}, {
"fcfs",
"n"}, {
"hol",
"h"}, {
"pri",
"p"}, {
"pp",
"p"}, {
"inf",
"i"},
374 {
"poll",
"P"}, {
"burst",
"b"}, {
"uniform",
"u"}, {
"semaphore",
"S"}, {
"rwlock",
"W"}};
375 std::map<std::string, const char*>::const_iterator it = t.find(s);
376 return it == t.end() ? NULL : it->second;
380 if (s ==
"r-lock")
return "R";
381 if (s ==
"r-unlock")
return "U";
382 if (s ==
"w-lock")
return "W";
383 if (s ==
"w-unlock")
return "X";
388 if (s ==
"signal")
return "P";
389 if (s ==
"wait")
return "V";
393#define LINE_SRVN_ACT_ATTRS "name", "phase", "host-demand-mean", "host-demand-cvsq", "call-order", "think-time", \
404 check(ta, {}, {
"activity",
"precedence",
"reply-entry"});
406 out.push_back(
"A " +
show(t,
"name"));
409 const std::string an =
name(a);
410 out.push_back(
" s " + an +
" " +
numd(a,
"host-demand-mean",
"0"));
411 if (
get(a,
"host-demand-cvsq")) {
413 out.push_back(
" c " + an +
" " + *
num(a,
"host-demand-cvsq", buf));
415 if (
upper(
get(a,
"call-order",
"STOCHASTIC")) ==
"DETERMINISTIC") out.push_back(
" f " + an +
" 1");
416 static const char* zm[2][2] = {{
"Z",
"think-time"}, {
"M",
"max-service-time"}};
417 for (
int q = 0; q < 2; ++q) {
419 const std::string* v =
num(a, zm[q][1], buf);
420 if (v && *v !=
"0") out.push_back(
" " + std::string(zm[q][0]) +
" " + an +
" " + *v);
423 check(c, {
"dest",
"calls-mean"});
424 const std::string letter = c->name ==
"synch-call" ?
"y" :
"z";
425 const std::string dst =
name(c,
"dest");
426 out.push_back(
" " + letter +
" " + an +
" " + dst +
" " +
numd(c,
"calls-mean",
"0"));
430 std::vector<std::pair<std::string, std::vector<std::string>>> replies;
432 check(re, {
"name"}, {
"reply-activity"});
435 const std::string an =
show(ra,
"name");
436 std::pair<std::string, std::vector<std::string>>* row = NULL;
437 for (
auto& r : replies)
438 if (r.first == an) row = &r;
440 replies.emplace_back(an, std::vector<std::string>());
441 row = &replies.back();
443 const std::string en =
show(re,
"name");
445 for (
const std::string& x : row->second) have = have || x == en;
446 if (!have) row->second.push_back(en);
449 std::map<std::string, std::vector<std::string>> pending(replies.begin(), replies.end());
451 auto ref = [&](
const std::string& nm) -> std::string {
452 std::map<std::string, std::vector<std::string>>::iterator it = pending.find(nm);
453 if (it == pending.end())
return nm;
454 const std::string s = nm +
"[" +
join(it->second,
", ") +
"]";
458 const std::string tn =
show(t,
"name");
459 std::vector<std::string> conns;
461 const std::vector<const Element*> parts =
kids(pr);
462 std::vector<const Element*> pre, post;
463 for (
const Element* k : parts) {
464 if (
in(k->name, {
"pre",
"pre-AND",
"pre-OR"})) pre.push_back(k);
465 if (
starts_with(k->name,
"post")) post.push_back(k);
467 if (pre.size() != 1 || post.size() > 1 || pre.size() + post.size() != parts.size())
468 throw unsup(
"a <precedence> of task \"" + tn +
"\" is not one pre list and at most one post list");
470 if (p0->
name ==
"pre-AND")
check(p0, {
"quorum"}, {
"activity"});
471 else check(p0, {}, {
"activity"});
472 std::vector<std::string> names;
475 if (names.empty() || (p0->
name ==
"pre" && names.size() != 1))
476 throw xerr(
"a <" + p0->
name +
"> of task \"" + tn +
"\" lists " + std::to_string(names.size()) +
478 bool anypending =
false;
479 for (
const std::string& x : names) anypending = anypending || pending.count(x);
480 if (post.empty() && !anypending)
continue;
481 std::vector<std::string> refs;
482 for (
const std::string& x : names) refs.push_back(ref(x));
483 std::string s =
join(refs, p0->
name ==
"pre-AND" ?
" & " :
" + ");
484 if (
get(p0,
"quorum")) {
486 s +=
" (" + *
num(p0,
"quorum", buf) +
")";
490 const std::vector<const Element*> items =
findall(q,
"activity");
492 if (q->
name ==
"post-OR") {
493 check(q, {}, {
"activity"});
494 for (
const Element* x : items)
check(x, {
"name",
"prob"});
495 std::vector<std::string> bs;
496 for (
const Element* x : items) {
497 const std::string pv =
numd(x,
"prob",
"0");
498 bs.push_back(
"(" + pv +
") " +
name(x));
500 rhs =
join(bs,
" + ");
501 }
else if (q->
name ==
"post-LOOP") {
502 check(q, {
"end"}, {
"activity"});
503 if (items.size() != 1)
504 throw unsup(
"the loop in task \"" + tn +
"\" has " + std::to_string(items.size()) +
505 " bodies; SRVN gives a loop one body");
506 check(items[0], {
"name",
"count"});
507 const std::string cnt =
numd(items[0],
"count",
"1");
508 rhs = cnt +
" * " +
name(items[0]);
509 if (
get(q,
"end")) rhs +=
", " +
name(q,
"end");
510 }
else if (q->
name ==
"post" || q->
name ==
"post-AND") {
511 check(q, {}, {
"activity"});
513 if (q->
name ==
"post" && items.size() != 1)
514 throw xerr(
"a <post> of task \"" + tn +
"\" lists " + std::to_string(items.size()) +
516 std::vector<std::string> ns;
517 for (
const Element* x : items) ns.push_back(
name(x));
518 rhs =
join(ns,
" & ");
520 throw unsup(
"<" + q->
name +
"> in task \"" + tn +
"\" has no SRVN spelling");
522 if (items.empty())
throw xerr(
"a <" + q->
name +
"> of task \"" + tn +
"\" lists no activities");
527 for (
const auto& r : replies)
528 if (pending.count(r.first)) conns.push_back(ref(r.first));
529 if (!conns.empty()) {
531 for (std::size_t i = 0; i < conns.size(); ++i)
532 out.push_back(
" " + conns[i] + (i + 1 < conns.size() ?
";" :
""));
549inline std::string
lqnx2srvn(
const std::string& xml_text,
const std::string& name = std::string()) {
551 std::unique_ptr<Element> doc;
555 throw xerr(std::string(
"the LQN XML is malformed: ") + e.what());
557 normalize_tree(doc.get());
558 const Element* root = doc.get();
559 if (root->
name !=
"lqn-model")
throw xerr(
"the root element is <" + root->
name +
">, not <lqn-model>");
560 const std::string model_name = name.empty() ? get(root,
"name",
"model") : name;
561 for (
const Element* k : kids(root))
562 if (k->name !=
"solver-params" && k->name !=
"processor")
throw unsup(
"<" + k->name +
"> has no SRVN spelling");
563 std::vector<std::string> out;
564 out.push_back(
"# SRVN model " + model_name +
", written by LINE");
565 const Element* sp = find(root,
"solver-params");
567 std::vector<std::string> solver = {
"1e-05",
"50"};
569 check(sp, {
"comment",
"conv_val",
"it_limit",
"print_int",
"underrelax_coeff"}, {
"pragma"});
570 for (
const Element* pr : findall(sp,
"pragma")) {
571 check(pr, {
"param",
"value"});
572 out.push_back(
"#pragma " + get(pr,
"param",
"") +
"=" + get(pr,
"value",
""));
574 comment = get(sp,
"comment",
"");
575 solver = {numd(sp,
"conv_val",
"1e-05"), numd(sp,
"it_limit",
"50")};
577 const std::string* pi = num(sp,
"print_int", pb);
578 const std::string* ur = num(sp,
"underrelax_coeff", ub);
579 if (pi || ur) solver.push_back(pi ? *pi : std::string(
"0"));
580 if (ur) solver.push_back(*ur);
582 if (comment.find(
'"') != std::string::npos)
583 throw unsup(
"the solver-params comment contains a double quote, which an SRVN string cannot hold");
585 std::vector<std::string> words;
587 while (i < comment.size()) {
591 if (j > i) words.push_back(comment.substr(i, j - i));
594 out.push_back(
"G \"" + join(words,
" ") +
"\" " + join(solver,
" ") +
" -1");
597 std::vector<const Element*> procs;
598 std::vector<std::pair<const Element*, const Element*>> groups;
599 std::vector<TaskRef> tasks;
600 for (
const Element* p : findall(root,
"processor")) {
601 check(p, {
"name",
"scheduling",
"quantum",
"multiplicity",
"replication",
"speed-factor"}, {
"task",
"group"});
603 for (
const Element* k : kids(p)) {
604 if (k->name ==
"group") {
605 check(k, {
"name",
"share",
"cap"}, {
"task"});
606 groups.emplace_back(k, p);
607 for (
const Element* t : findall(k,
"task")) tasks.push_back(TaskRef{t, p, k});
609 tasks.push_back(TaskRef{k, p, NULL});
615 out.push_back(
"P " + std::to_string(procs.size()));
616 for (
const Element* p : procs) {
617 const std::string sched = lower(get(p,
"scheduling",
"fcfs"));
618 const char* letter = proc_letter(sched);
620 throw unsup(
"processor \"" + show(p,
"name") +
"\" uses scheduling \"" + sched +
621 "\", which SRVN cannot express");
623 if (std::string(letter) ==
"c" || std::string(letter) ==
"s") {
624 s +=
" " + numd(p,
"quantum",
"0");
625 }
else if (get(p,
"quantum")) {
627 if (std::strtod(num(p,
"quantum", buf)->c_str(), NULL) != 0)
628 throw unsup(
"processor \"" + show(p,
"name") +
"\" has a quantum, which SRVN gives to \"ps\" and \"cfs\" only");
630 std::string b1, b2, b3;
631 const std::string* mult = num(p,
"multiplicity", b1);
632 if (sched !=
"inf" && mult && *mult !=
"1") s +=
" m " + *mult;
633 const std::string* repl = num(p,
"replication", b2);
634 if (repl && *repl !=
"1") s +=
" r " + *repl;
635 const std::string* speed = num(p,
"speed-factor", b3);
636 if (speed && *speed !=
"1") s +=
" R " + *speed;
641 if (!groups.empty()) {
643 out.push_back(
"U " + std::to_string(groups.size()));
644 for (
const auto& gp : groups) {
645 const std::string cap = lower(get(gp.first,
"cap",
"false"));
647 const std::string share = numd(gp.first,
"share",
"0");
648 out.push_back(
" g " + gn +
" " + share + (cap ==
"true" ?
" c" :
"") +
" " + show(gp.second,
"name"));
653 std::vector<std::pair<const Element*, const Element*>> entries;
654 std::vector<std::string> fans;
656 out.push_back(
"T " + std::to_string(tasks.size()));
657 for (
const TaskRef&
tr : tasks) {
659 check(t, {
"name",
"scheduling",
"multiplicity",
"replication",
"think-time",
"priority",
"queue-length"},
660 {
"entry",
"task-activities",
"fan-in",
"fan-out"});
661 const std::string sched = lower(get(t,
"scheduling",
"fcfs"));
662 const char* letter = task_letter(sched);
664 throw unsup(
"task \"" + show(t,
"name") +
"\" uses scheduling \"" + sched +
"\", which SRVN cannot express");
665 const std::vector<const Element*> ents = findall(t,
"entry");
666 if (ents.empty())
throw xerr(
"task \"" + show(t,
"name") +
"\" has no entries");
668 std::vector<std::string> ens;
670 std::string s =
" t " + tn +
" " + letter +
" " + join(ens,
" ") +
" -1 " + show(
tr.p,
"name");
671 std::string b1, b2, b3, b4, b5;
672 const std::string* prio = num(t,
"priority", b1);
673 if (prio) s +=
" " + *prio;
674 const std::string* think = num(t,
"think-time", b2);
675 if (think && *think !=
"0") s +=
" z " + *think;
676 const std::string* mult = num(t,
"multiplicity", b3);
677 if (sched !=
"inf" && mult && *mult !=
"1") s +=
" m " + *mult;
678 const std::string* repl = num(t,
"replication", b4);
679 if (repl && *repl !=
"1") s +=
" r " + *repl;
680 if (
tr.g) s +=
" g " + show(
tr.g,
"name");
681 const std::string* qlen = num(t,
"queue-length", b5);
682 if (qlen) s +=
" q " + *qlen;
684 for (
const Element* f : findall(t,
"fan-in")) {
685 check(f, {
"source",
"value"});
687 fans.push_back(
" I " + src +
" " + show(t,
"name") +
" " + numd(f,
"value",
"1"));
689 for (
const Element* f : findall(t,
"fan-out")) {
690 check(f, {
"dest",
"value"});
692 fans.push_back(
" O " + show(t,
"name") +
" " + dst +
" " + numd(f,
"value",
"1"));
694 for (
const Element* e : ents) entries.emplace_back(e, t);
696 out.insert(out.end(), fans.begin(), fans.end());
699 std::map<std::string, std::string> bound;
700 for (
const TaskRef&
tr : tasks) {
701 const Element* ta = find(
tr.t,
"task-activities");
703 for (
const Element* a : findall(ta,
"activity")) {
704 const std::string* be = get(a,
"bound-to-entry");
705 if (be && !bound.count(*be)) bound[*be] = show(a,
"name");
709 out.push_back(
"E " + std::to_string(entries.size()));
710 for (
const auto& et : entries) {
712 check(e, {
"name",
"type",
"open-arrival-rate",
"priority",
"semaphore",
"rwlock"},
713 {
"forwarding",
"entry-phase-activities"});
714 const std::string en = show(e,
"name");
716 const std::string* rate = num(e,
"open-arrival-rate", b1);
717 if (rate && *rate !=
"0") out.push_back(
" a " + en +
" " + *rate);
718 const std::string* prio = num(e,
"priority", b2);
719 if (prio) out.push_back(
" p " + en +
" " + *prio);
720 if (
const std::string* sem = get(e,
"semaphore")) {
721 if (!sem_letter(*sem))
throw unsup(
"entry \"" + en +
"\" has semaphore=\"" + *sem +
"\"");
722 out.push_back(std::string(
" ") + sem_letter(*sem) +
" " + en);
724 if (
const std::string* rw = get(e,
"rwlock")) {
725 if (!rwlock_letter(*rw))
throw unsup(
"entry \"" + en +
"\" has rwlock=\"" + *rw +
"\"");
726 out.push_back(std::string(
" ") + rwlock_letter(*rw) +
" " + en);
728 const Element* epa = find(e,
"entry-phase-activities");
729 std::map<int, const Element*> acts;
731 check(epa, {}, {
"activity"});
732 for (
const Element* a : findall(epa,
"activity")) {
734 const int k =
static_cast<int>(std::strtod(numd(a,
"phase",
"1").c_str(), NULL));
735 if (k < 1 || acts.count(k))
736 throw xerr(
"entry \"" + en +
"\" has two activities for phase " + std::to_string(k));
740 std::map<std::string, std::string>::const_iterator b = bound.find(en);
741 if (b != bound.end()) {
743 throw unsup(
"entry \"" + en +
"\" has both phases and a start activity, which SRVN cannot express");
744 out.push_back(
" A " + en +
" " + b->second);
747 std::vector<std::pair<std::string, std::vector<std::string>>> lines;
748 std::vector<CallRow> calls;
749 phase_lists(acts, lines, calls);
750 for (
const auto& l : lines) out.push_back(
" " + l.first +
" " + en +
" " + join(l.second,
" ") +
" -1");
751 for (
const CallRow& c : calls)
752 out.push_back(
" " + c.kind +
" " + en +
" " + c.dst +
" " + join(c.vals,
" ") +
" -1");
754 for (
const Element* f : findall(e,
"forwarding")) {
755 check(f, {
"dest",
"prob"});
757 out.push_back(
" F " + en +
" " + dst +
" " + numd(f,
"prob",
"0") +
" -1");
762 for (
const TaskRef&
tr : tasks) {
763 const Element* ta = find(
tr.t,
"task-activities");
764 if (ta) activity_section(
tr.t, ta, out);
767 for (
const std::string& l : out) {
774#undef LINE_SRVN_ACT_ATTRS
786 const std::string xml_text =
lqnx_to_string(m, m.
name.empty() ? std::string(
"LQN") : m.
name, use_abstract_names);
788 std::ofstream f(path.c_str(), std::ios::binary);
A refusal: the feature, method or analysis is not covered: LINE:unsupported:<kind>.
UnsupportedError(const std::string &what)
The exception types the port throws.
LqnModel -> .lqnx, a port of matlab/src/lang/layered/@LayeredNetwork/writeXML.m.
bool is_space(char c)
Python's str.isspace over ASCII, which also counts the four separators 0x1c-0x1f.
bool in(const std::string &s, std::initializer_list< const char * > set)
std::vector< const Element * > findall(const Element *el, const std::string &tag)
void normalize_tree(Element *el)
bool srvn_name_ok(const std::string &nm)
[A-Za-z_][A-Za-z0-9_]+$ under re.match, where $ also matches before a final newline.
const char * task_letter(const std::string &s)
const char * proc_letter(const std::string &s)
const char * rwlock_letter(const std::string &s)
std::string normalize_attr(const std::string &v)
An attribute value as expat delivers it: literal tab, CR and LF become spaces (a CR LF pair one space...
bool is_output_tag(const std::string &tag)
Output requests: read and dropped, as the reader drops H, R and observations.
std::string numd(const Element *el, const std::string &key, const char *dflt)
_num with a default, which never returns None.
void check(const Element *el, std::initializer_list< const char * > allowed, std::initializer_list< const char * > kidtags={})
void phase_lists(const std::map< int, const Element * > &acts, std::vector< std::pair< std::string, std::vector< std::string > > > &lines, std::vector< CallRow > &calls)
acts maps phase -> <activity>; the E-section lines of one entry's phases.
std::string join(const std::vector< std::string > &v, const std::string &sep)
void activity_section(const Element *t, const Element *ta, std::vector< std::string > &out)
One task's A section, appended to out.
InputError xerr(const std::string &msg)
bool py_float(const std::string &text, double &x)
Python's float(text): surrounding whitespace, underscores between digits, inf/nan.
std::string show(const Element *el, const std::string &key)
's' % el.get(key): None prints as "None".
std::string lower(std::string s)
std::vector< const Element * > kids(const Element *el)
Model children of el: results and output requests are skipped.
const std::string * num(const Element *el, const std::string &key, std::string &buf, const char *dflt=NULL)
_num: the attribute as format_number(float(text)), or dflt (null = None) when absent.
const std::string * get(const Element *el, const std::string &key)
el.get(key), with Python's None as a null pointer.
std::string upper(std::string s)
bool starts_with(const std::string &s, const char *p)
UnsupportedError unsup(const std::string &msg)
const Element * find(const Element *el, const std::string &tag)
el.find(tag): the first direct child with that tag, or null.
const char * sem_letter(const std::string &s)
std::string name(const Element *el, const std::string &key="name")
void utf8(std::string &out, unsigned long cp)
std::string lqnx2srvn(const std::string &xml_text, const std::string &name=std::string())
Translate an LQN XML document into SRVN text.
std::string format_number(double x)
Shortest C g spelling that round-trips x; the four codebases share this rule.
std::string lqnx_to_string(const LqnModel< T > &m, const std::string &model_name=std::string("LQN"), bool use_abstract_names=false)
The .lqnx document as a STRING, for a caller with no file to write to – the CLI's model-generation mo...
void write_srvn(const LqnModel< T > &m, const std::string &path, bool use_abstract_names=false)
Write a layered model as SRVN text: write_lqnx, then lqnx2srvn, with LF line endings.
std::unique_ptr< Element > parse(const std::string &text)
Parse a whole XML document and return its root element.
Conservation laws of a layered queueing network, enumerated from its structure.
SRVN input format (the LQNS .in / .lqn text grammar) for layered models, the C++ twin of python/line_...
#define LINE_SRVN_ACT_ATTRS
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
The intermediate model, and the second stage that flattens it.
std::string name
LayeredNetwork.getName(); empty when unnamed.
std::vector< std::string > vals
std::vector< std::pair< std::string, std::string > > attrs
std::vector< std::unique_ptr< Element > > children
std::vector< const Element * > child_tags(const std::string &tag) const
Direct children with the given tag, in document order.
A minimal XML DOM: read for the .lqnx interchange format, write for the JMT .jsimg and ....