5#ifndef LINE_LANG_LQN_SRVN_READER_H
6#define LINE_LANG_LQN_SRVN_READER_H
58 return c ==
' ' || (c >=
'\t' && c <=
'\r') || (c >=
'\x1c' && c <=
'\x1f');
61inline bool is_digit(
char c) {
return c >=
'0' && c <=
'9'; }
64 return is_digit(c) || (c >=
'A' && c <=
'Z') || (c >=
'a' && c <=
'z');
68inline bool is_ident(
char c) {
return is_alnum(c) || c ==
'_' || c ==
'.' || c ==
'$' || c ==
'@'; }
70inline std::string
strip(
const std::string& s) {
71 std::size_t a = 0, b = s.size();
73 while (b > a &&
is_space(s[b - 1])) --b;
74 return s.substr(a, b - a);
78inline std::string
py_repr(
const std::string& s) {
79 const bool dq = s.find(
'\'') != std::string::npos && s.find(
'"') == std::string::npos;
80 const char q = dq ?
'"' :
'\'';
82 for (
unsigned char c : s) {
83 if (c ==
'\\') r +=
"\\\\";
84 else if (c ==
static_cast<unsigned char>(q)) { r +=
'\\'; r +=
static_cast<char>(c); }
85 else if (c ==
'\n') r +=
"\\n";
86 else if (c ==
'\r') r +=
"\\r";
87 else if (c ==
'\t') r +=
"\\t";
88 else if (c < 0x20 || c == 0x7f) {
90 std::snprintf(buf,
sizeof(buf),
"\\x%02x", c);
92 }
else r +=
static_cast<char>(c);
98 std::string where =
line < 0 ? std::string() :
" (line " + std::to_string(
line) +
")";
102inline std::string
esc(
const std::string& s) {
106 if (c ==
'&') r +=
"&";
107 else if (c ==
'<') r +=
"<";
108 else if (c ==
'>') r +=
">";
109 else if (c ==
'"') r +=
""";
136 while (a < s.size() && s[a] ==
'+') ++a;
140inline double to_double(
const std::string& s) {
return std::strtod(s.c_str(), NULL); }
143inline std::size_t
match_number(
const std::string& src, std::size_t i) {
144 const std::size_t n = src.size();
146 if (k < n && (src[k] ==
'-' || src[k] ==
'+')) ++k;
148 while (k < n &&
is_digit(src[k])) ++k;
149 if (k < n && src[k] ==
'.') ++k;
150 while (k < n &&
is_digit(src[k])) ++k;
151 }
else if (k + 1 < n && src[k] ==
'.' &&
is_digit(src[k + 1])) {
153 while (k < n &&
is_digit(src[k])) ++k;
155 return std::string::npos;
157 if (k < n && (src[k] ==
'e' || src[k] ==
'E')) {
158 std::size_t e = k + 1;
159 if (e < n && (src[e] ==
'-' || src[e] ==
'+')) ++e;
161 while (e < n &&
is_digit(src[e])) ++e;
170 if (
line.empty() ||
line[0] !=
'#')
return false;
173 if (
line.compare(k, 6,
"pragma") != 0)
return false;
181inline void tokenize(
const std::string& src, std::vector<Tok>& toks,
182 std::vector<std::pair<std::string, std::string>>& pragmas) {
184 const std::size_t n = src.size();
187 const char c = src[i];
193 }
else if (c ==
'#') {
194 std::size_t j = src.find(
'\n', i);
195 if (j == std::string::npos) j = n;
199 while (k < body.size()) {
200 while (k < body.size() &&
is_space(body[k])) ++k;
201 if (k >= body.size())
break;
203 while (e < body.size() && !
is_space(body[e])) ++e;
204 const std::string item = body.substr(k, e - k);
205 const std::size_t eq = item.find(
'=');
206 if (eq == std::string::npos) pragmas.emplace_back(item, std::string());
207 else pragmas.emplace_back(item.substr(0, eq), item.substr(eq + 1));
212 }
else if (c ==
'"') {
213 const std::size_t j = src.find(
'"', i + 1);
214 if (j == std::string::npos)
throw err(
"unterminated string",
line);
216 for (std::size_t k = i; k < j; ++k)
217 if (src[k] ==
'\n') ++
line;
219 }
else if (src.compare(i, 2,
"->") == 0) {
222 }
else if (c ==
'%') {
223 std::size_t j = i + 1;
224 while (j < n && (
is_alnum(src[j]) || src[j] ==
'_')) ++j;
227 }
else if (
is_digit(c) || (c ==
'.' && i + 1 < n &&
is_digit(src[i + 1])) ||
228 ((c ==
'-' || c ==
'+') && i + 1 < n &&
229 (
is_digit(src[i + 1]) || (src[i + 1] ==
'.' && i + 2 < n &&
is_digit(src[i + 2]))))) {
231 if (j < n &&
is_ident(src[j]) && c !=
'-' && c !=
'+') {
232 while (j < n &&
is_ident(src[j])) ++j;
238 }
else if (std::string(
"&;:()[],*=/^{}+-").find(c) != std::string::npos) {
243 while (j < n &&
is_ident(src[j])) ++j;
244 std::string text = src.substr(i, j - i);
246 toks.push_back(
Tok{k, std::move(text),
line});
249 throw err(
"unexpected character " +
py_repr(std::string(1, c)),
line);
307 std::vector<std::pair<std::string, Val>>
fwd;
312 std::vector<std::string>
pre;
316 std::vector<std::pair<std::string, Val>>
items;
323 std::map<std::string, std::size_t>
by;
324 std::vector<std::vector<std::string>>
prec;
325 std::vector<std::pair<std::string, std::string>>
replies;
331 std::vector<std::pair<std::string, Val>>
solver;
339 std::map<std::string, Acts>
acts;
343 static const std::map<std::string, const char*> t = {
344 {
"f",
"fcfs"}, {
"h",
"hol"}, {
"p",
"pri"}, {
"r",
"rand"}, {
"i",
"inf"}, {
"c",
"cfs"}, {
"s",
"ps"}};
345 std::map<std::string, const char*>::const_iterator it = t.find(k);
346 return it == t.end() ? NULL : it->second;
350 static const std::map<std::string, const char*> t = {
351 {
"r",
"ref"}, {
"n",
"fcfs"}, {
"f",
"fcfs"}, {
"h",
"hol"}, {
"p",
"pri"}, {
"i",
"inf"},
352 {
"P",
"poll"}, {
"b",
"burst"}, {
"u",
"uniform"}, {
"S",
"semaphore"}, {
"W",
"rwlock"}};
353 std::map<std::string, const char*>::const_iterator it = t.find(k);
354 return it == t.end() ? NULL : it->second;
358 if (k ==
"R")
return "r-lock";
359 if (k ==
"U")
return "r-unlock";
360 if (k ==
"W")
return "w-lock";
361 if (k ==
"X")
return "w-unlock";
376 if (!std::isfinite(x))
throw InputError(
ErrorKind{
"Format"},
"format_number: the value is not finite");
378 if (x == std::trunc(x) && std::fabs(x) < 1e15) {
379 std::snprintf(buf,
sizeof(buf),
"%lld",
static_cast<long long>(x));
382 for (
int p = 1; p < 18; ++p) {
383 std::snprintf(buf,
sizeof(buf),
"%.*g", p, x);
384 if (std::strtod(buf, NULL) == x)
return buf;
386 std::snprintf(buf,
sizeof(buf),
"%.17g", x);
390namespace srvn_detail {
397 std::vector<std::pair<std::string, std::string>>
pragmas;
404 if (!at(
"P"))
throw err(
"expected the processor section \"P\"",
line());
427 const Tok& t = next();
428 if (t.
text ==
"[" || t.
text ==
"(") ++depth;
429 else if (t.
text ==
"]" || t.
text ==
")") --depth;
435 if (pos <
toks.size()) {
439 if (!used_sweeps.empty()) {
441 for (std::size_t i = 0; i < used_sweeps.size(); ++i) names += (i ?
", " :
"") + used_sweeps[i];
443 "SRVN: the model uses the SPEX sweep parameter(s) " + names +
444 "; LINE reads one value per parameter, so give each a scalar value (e.g. \"" +
445 used_sweeps[0] +
" = 1\")");
452 std::map<std::string, Val> params;
453 std::map<std::string, int> sweeps;
454 std::vector<std::string> used_sweeps;
464 const Tok* peek(
int k = 0) {
475 return p <
toks.size() ? &
toks[p] : NULL;
479 const Tok* t = peek();
480 if (!t)
throw err(
"unexpected end of input");
486 const Tok* t = peek();
487 return t ? t->line : (
toks.empty() ? 1 :
toks.back().line);
490 bool at(
const char* text) {
491 const Tok* t = peek();
492 return t && t->text == text;
496 const Tok* t = peek();
497 return t && t->kind ==
TK::NUM && t->text ==
"-1";
500 void expect(
const char* text) {
501 const Tok& t = next();
502 if (t.text != text)
throw err(
"expected " +
py_repr(text) +
", found " +
py_repr(t.text), t.line);
505 void expect_end_list() {
506 const Tok& t = next();
507 if (!(t.kind ==
TK::NUM && t.text ==
"-1"))
508 throw err(
"expected end of list \"-1\", found " +
py_repr(t.text), t.line);
511 std::string ident(
const std::string& what) {
512 const Tok& t = next();
514 throw err(
"expected " + what +
" name, found " +
py_repr(t.text), t.line);
519 Val value(
const std::string& what =
"value") {
520 const Tok& t = next();
522 if (t.kind ==
TK::VAR)
return lookup(t);
523 throw err(
"expected a number for " + what +
", found " +
py_repr(t.text), t.line);
526 bool is_value(
int k = 0) {
527 const Tok* t = peek(k);
531 Val lookup(
const Tok& t) {
532 const std::string&
name = t.text;
533 if (sweeps.count(name)) {
535 for (
const std::string& u : used_sweeps) seen = seen || u ==
name;
536 if (!seen) used_sweeps.push_back(name);
537 return Val(
"0", 0.0);
539 std::map<std::string, Val>::const_iterator it = params.find(name);
540 if (it == params.end())
throw err(
"parameter " + name +
" is used but not defined", t.line);
544 std::vector<Val> value_list(
const std::string& what) {
545 std::vector<Val> vals;
546 while (!at_end_list()) vals.push_back(value(what));
554 const Tok* a = peek();
555 if (!a || a->kind !=
TK::VAR)
break;
556 const Tok* b = peek(1);
557 if (!b || b->text !=
"=")
break;
558 const Tok vt = next();
561 sweeps[vt.text] = vt.line;
564 const Tok& t = next();
565 if (t.text ==
"[") ++depth;
566 else if (t.text ==
"]") {
568 if (depth == 0)
break;
573 const std::size_t start = pos;
574 const double v = expr();
576 if (!std::isfinite(v))
throw err(
"parameter " + vt.text +
" evaluates to a non-finite value", vt.line);
577 const bool single = pos - start == 1 &&
toks[start].kind ==
TK::NUM;
585 const Tok* t = peek();
586 if (t && t->kind ==
TK::P && (t->text ==
"+" || t->text ==
"-")) {
587 const bool plus = next().text ==
"+";
588 const double r = term();
589 v = plus ? v + r : v - r;
590 }
else if (t && t->kind ==
TK::NUM && (t->text[0] ==
'+' || t->text[0] ==
'-')) {
604 while (at(
"*") || at(
"/")) {
605 const Tok& ot = next();
606 const bool mul = ot.text ==
"*";
607 const double r = factor();
608 if (!mul && r == 0)
throw err(
"division by zero in a parameter expression", ot.line);
609 v = mul ? v * r : v / r;
615 const double b = unary();
618 return std::pow(b, factor());
634 const double v = expr();
638 const Tok& t = next();
641 if (sweeps.count(t.text))
643 "SRVN (line " + std::to_string(t.line) +
"): a parameter expression uses the SPEX sweep " +
644 t.text +
"; LINE reads one value per parameter, so give it a scalar value");
645 return lookup(t).value;
647 throw err(
"malformed parameter expression at " +
py_repr(t.text), t.line);
651 void general(Model& m) {
652 const Tok* t = peek();
653 if (t && t->kind ==
TK::STR) m.comment = next().text;
654 static const char* names[] = {
"conv_val",
"it_limit",
"print_int",
"underrelax_coeff"};
656 while (!at_end_list()) {
657 if (k >= 4)
throw err(
"too many values in the general section", line());
658 m.solver.emplace_back(names[k], value(names[k]));
664 void processors(Model& m) {
665 while (!at_end_list()) {
666 const int ln = line();
668 const std::string
name = ident(
"processor");
669 if (m.proc_by.count(name))
throw err(
"processor \"" + name +
"\" is declared twice", ln);
670 const Tok& t = next();
673 throw err(
"unknown processor scheduling flag " +
py_repr(t.text) +
" for \"" + name +
"\"", t.line);
677 if (t.text ==
"c" || t.text ==
"s") p.quantum = value(
"quantum");
681 p.mult = value(
"multiplicity");
682 }
else if (at(
"i")) {
685 }
else if (at(
"r")) {
687 p.repl = value(
"replication");
688 }
else if (at(
"R")) {
690 p.speed = value(
"speed factor");
695 m.proc_by[
name] = m.procs.size();
696 m.procs.push_back(p);
701 void groups(Model& m) {
702 while (!at_end_list()) {
703 const int ln = line();
705 const std::string
name = ident(
"group");
706 const Val share = value(
"share");
712 const std::string proc = ident(
"processor");
713 if (!m.proc_by.count(proc))
714 throw err(
"group \"" + name +
"\" names the undeclared processor \"" + proc +
"\"", ln);
719 m.procs[m.proc_by[proc]].groups.push_back(m.groups.size());
720 m.groups.push_back(g);
726 std::string kind, src, dst;
731 void tasks(Model& m) {
732 std::vector<Fan> fans;
733 while (!at_end_list()) {
734 const int ln = line();
735 const Tok& t = next();
736 if (t.text ==
"I" || t.text ==
"O") {
740 f.src = ident(
"task");
741 f.dst = ident(
"task");
742 f.v = value(
"fan value");
746 if (t.text !=
"t")
throw err(
"expected a task declaration \"t\", found " +
py_repr(t.text), t.line);
747 const std::string
name = ident(
"task");
748 if (m.task_by.count(name))
throw err(
"task \"" + name +
"\" is declared twice", ln);
749 const Tok& st = next();
752 throw err(
"unknown task scheduling flag " +
py_repr(st.text) +
" for \"" + name +
"\"", st.line);
756 while (!at_end_list()) task.entries.push_back(ident(
"entry"));
758 task.proc = ident(
"processor");
759 if (!m.proc_by.count(task.proc))
760 throw err(
"task \"" + name +
"\" runs on the undeclared processor \"" + task.proc +
"\"", ln);
763 task.prio = value(
"priority");
764 }
else if (at(
"z")) {
766 task.think = value(
"think time");
767 }
else if (at(
"m")) {
769 task.mult = value(
"multiplicity");
770 }
else if (at(
"i")) {
773 }
else if (at(
"r")) {
775 task.repl = value(
"replication");
776 }
else if (at(
"g")) {
778 task.has_group =
true;
779 task.group = ident(
"group");
780 }
else if (at(
"q")) {
782 task.qlen = value(
"queue length");
787 for (
const std::string& e : task.entries) {
788 if (m.entries.count(e))
throw err(
"entry \"" + e +
"\" is declared twice", ln);
793 m.entry_order.push_back(e);
795 const std::size_t ti = m.tasks.size();
796 m.tasks.push_back(task);
797 m.task_by[
name] = ti;
798 Proc& proc = m.procs[m.proc_by[task.proc]];
799 if (task.has_group) {
801 for (std::size_t gi : proc.groups) {
802 if (m.groups[gi].name == task.group) {
803 m.groups[gi].tasks.push_back(ti);
809 throw err(
"task \"" + name +
"\" names group \"" + task.group +
"\", which processor \"" +
810 proc.name +
"\" does not declare", ln);
812 proc.tasks.push_back(ti);
816 for (
const Fan& f : fans) {
817 for (
const std::string* nm : {&f.src, &f.dst})
818 if (!m.task_by.count(*nm))
819 throw err(std::string(
"fan-") + (f.kind ==
"I" ?
"in" :
"out") +
" names the undeclared task \"" +
821 if (f.kind ==
"O") m.tasks[m.task_by[f.src]].fanout.emplace_back(f.dst, f.v);
822 else m.tasks[m.task_by[f.dst]].fanin.emplace_back(f.src, f.v);
826 Entry& entry(Model& m,
const std::string& name,
int ln) {
827 std::map<std::string, Entry>::iterator it = m.entries.find(name);
828 if (it == m.entries.end())
throw err(
"entry \"" + name +
"\" is not declared by any task", ln);
832 static Work& phase(Entry& e,
int k) {
return e.phases[k]; }
834 void entries(Model& m) {
835 while (!at_end_list()) {
836 const Tok t = next();
837 const int ln = t.line;
838 const std::string& k = t.text;
839 if (k ==
"s" || k ==
"c" || k ==
"f" || k ==
"Z" || k ==
"M") {
840 Entry& e = entry(m, ident(
"entry"), ln);
841 const std::vector<Val> vals = value_list(
"phase value");
842 for (std::size_t ph = 0; ph < vals.size(); ++ph) phase(e,
static_cast<int>(ph + 1)).field(k[0]) = vals[ph];
843 }
else if (k ==
"y" || k ==
"z") {
844 Entry& e = entry(m, ident(
"entry"), ln);
845 const std::string dst = ident(
"entry");
847 const std::vector<Val> vals = value_list(
"call count");
848 for (std::size_t ph = 0; ph < vals.size(); ++ph)
849 phase(e,
static_cast<int>(ph + 1))
850 .calls.push_back(Call{k ==
"y" ?
"synch-call" :
"asynch-call", dst, vals[ph]});
851 }
else if (k ==
"F") {
852 Entry& e = entry(m, ident(
"entry"), ln);
853 const std::string dst = ident(
"entry");
855 e.fwd.emplace_back(dst, value(
"forwarding probability"));
856 if (at_end_list()) next();
857 }
else if (k ==
"a") {
858 Entry& e = entry(m, ident(
"entry"), ln);
859 e.arrival = value(
"arrival rate");
860 }
else if (k ==
"A") {
861 Entry& e = entry(m, ident(
"entry"), ln);
863 e.start = ident(
"activity");
864 }
else if (k ==
"p") {
865 Entry& e = entry(m, ident(
"entry"), ln);
866 e.prio = value(
"priority");
867 }
else if (k ==
"P" || k ==
"V") {
868 Entry& e = entry(m, ident(
"entry"), ln);
869 e.sem = k ==
"P" ?
"signal" :
"wait";
871 Entry& e = entry(m, ident(
"entry"), ln);
873 }
else if (k ==
"H") {
874 entry(m, ident(
"entry"), ln);
878 throw err(
"unknown entry declaration " +
py_repr(k), ln);
886 value(
"histogram minimum");
888 value(
"histogram maximum");
889 value(
"histogram bins");
890 if (at(
"log") || at(
"sqrt") || at(
"linear")) next();
893 void activities(Model& m) {
894 const int ln = line();
895 const std::string tname = ident(
"task");
896 if (!m.task_by.count(tname))
throw err(
"activity section for the undeclared task \"" + tname +
"\"", ln);
897 if (m.acts.count(tname))
throw err(
"task \"" + tname +
"\" has two activity sections", ln);
898 Acts& A = m.acts[tname];
899 while (!(at_end_list() || at(
":"))) {
900 const Tok t = next();
901 const std::string& k = t.text;
902 if (!(k ==
"s" || k ==
"c" || k ==
"f" || k ==
"Z" || k ==
"M" || k ==
"y" || k ==
"z" || k ==
"H"))
903 throw err(
"unknown activity declaration " +
py_repr(k) +
" in task \"" + tname +
"\"", t.line);
904 const std::size_t ai = act(A, ident(
"activity"));
905 if (k ==
"y" || k ==
"z") {
906 const std::string dst = ident(
"entry");
907 entry(m, dst, t.line);
908 const Val v = value(
"call count");
909 A.defs[ai].calls.push_back(Call{k ==
"y" ?
"synch-call" :
"asynch-call", dst, v});
910 }
else if (k ==
"H") {
913 const Val v = value(
"activity value");
914 A.defs[ai].field(k[0]) = v;
920 connection(A, m, tname);
929 for (
const std::vector<std::string>& names : A.prec)
930 for (
const std::string& nm : names)
932 throw err(
"activity \"" + nm +
"\" of task \"" + tname +
933 "\" is used in the precedence graph but not defined", ln);
936 static std::size_t act(Acts& A,
const std::string& name) {
937 std::map<std::string, std::size_t>::const_iterator it = A.by.find(name);
938 if (it != A.by.end())
return it->second;
941 A.by[
name] = A.defs.size();
943 return A.defs.size() - 1;
946 std::string actref(Acts& A, Model& m) {
947 const std::string
name = ident(
"activity");
951 const std::string e = ident(
"entry");
953 A.replies.emplace_back(e, name);
965 void connection(Acts& A, Model& m,
const std::string& tname) {
967 rec.pre.push_back(actref(A, m));
968 rec.pre_kind =
"pre";
970 rec.pre_kind =
"pre-AND";
973 rec.pre.push_back(actref(A, m));
977 rec.quorum = value(
"quorum");
980 }
else if (at(
"+")) {
981 rec.pre_kind =
"pre-OR";
984 rec.pre.push_back(actref(A, m));
987 A.prec.push_back(rec.pre);
994 const Val p = value(
"branch probability");
996 const std::string a = actref(A, m);
997 rec.items.emplace_back(a, p);
1004 rec.post_kind =
"post-OR";
1005 }
else if (is_value() && peek(1) && peek(1)->text ==
"*") {
1006 const Val count = value(
"loop count");
1008 const std::string body = actref(A, m);
1012 rec.end = actref(A, m);
1014 rec.post_kind =
"post-LOOP";
1015 rec.items.emplace_back(body, count);
1017 std::vector<std::string> post;
1018 post.push_back(actref(A, m));
1019 std::string kind =
"post";
1024 post.push_back(actref(A, m));
1026 }
else if (at(
"+")) {
1027 throw err(
"an OR fork in task \"" + tname +
"\" needs a probability on each branch, \"(p) act\"",
1030 rec.post_kind = kind;
1031 for (
const std::string& x : post) rec.items.emplace_back(x,
Val());
1033 std::vector<std::string> names;
1034 for (
const std::pair<std::string, Val>& it : rec.items) names.push_back(it.first);
1035 if (rec.has_end && !rec.end.empty()) names.push_back(rec.end);
1036 A.prec.push_back(names);
1038 A.conns.push_back(rec);
1042inline std::string
attr(
const char* name,
const Val& v) {
1043 return std::string(
" ") + name +
"=\"" +
esc(v.
text) +
"\"";
1048 std::string s = a.
s.
has ?
attr(
"host-demand-mean", a.
s) : std::string(
" host-demand-mean=\"0\"");
1049 if (a.
c.
has &&
nz(a.
s)) s +=
attr(
"host-demand-cvsq", a.
c);
1050 if (
nz(a.
Z)) s +=
attr(
"think-time", a.
Z);
1051 if (
nz(a.
M)) s +=
attr(
"max-service-time", a.
M);
1053 if (c.
v.
value != 0) calls.push_back(&c);
1054 if (
nz(a.
f) && !calls.empty()) s +=
" call-order=\"DETERMINISTIC\"";
1058inline void emit_activity(std::vector<std::string>& out,
const std::string& ind,
const std::string& head,
1060 std::vector<const Call*> calls;
1062 if (calls.empty()) {
1063 out.push_back(ind +
"<activity" + head + attrs +
"/>");
1066 out.push_back(ind +
"<activity" + head + attrs +
">");
1067 for (
const Call* c : calls)
1068 out.push_back(ind +
" <" + c->kind +
" dest=\"" +
esc(c->dst) +
"\" calls-mean=\"" +
esc(c->v.text) +
"\"/>");
1069 out.push_back(ind +
"</activity>");
1072inline void emit_task(std::vector<std::string>& out,
const std::string& ind,
const Task& t,
const Model& m) {
1073 std::string s = ind +
"<task name=\"" +
esc(t.
name) +
"\" scheduling=\"" + t.
sched +
"\"";
1079 out.push_back(s +
">");
1080 const std::string i2 = ind +
" ";
1081 for (
const std::pair<std::string, Val>& f : t.
fanin)
1082 out.push_back(i2 +
"<fan-in source=\"" +
esc(f.first) +
"\" value=\"" +
esc(f.second.text) +
"\"/>");
1083 for (
const std::pair<std::string, Val>& f : t.
fanout)
1084 out.push_back(i2 +
"<fan-out dest=\"" +
esc(f.first) +
"\" value=\"" +
esc(f.second.text) +
"\"/>");
1085 for (
const std::string& en : t.
entries) {
1088 throw err(
"entry \"" + en +
"\" declares both a start activity and phase parameters");
1089 s = i2 +
"<entry name=\"" +
esc(en) +
"\" type=\"" + (e.
has_start ?
"NONE" :
"PH1PH2") +
"\"";
1092 if (!e.
sem.empty()) s +=
" semaphore=\"" + e.
sem +
"\"";
1093 if (!e.
rw.empty()) s +=
" rwlock=\"" + e.
rw +
"\"";
1094 std::vector<std::string> body;
1095 const std::string i3 = i2 +
" ";
1096 for (
const std::pair<std::string, Val>& f : e.
fwd)
1097 body.push_back(i3 +
"<forwarding dest=\"" +
esc(f.first) +
"\" prob=\"" +
esc(f.second.text) +
"\"/>");
1099 std::vector<std::pair<int, const Work*>> phases;
1100 for (
const std::pair<const int, Work>& kv : e.
phases) {
1101 const Work& ph = kv.second;
1104 if (
nz(ph.
s) ||
nz(ph.
Z) || call) phases.emplace_back(kv.first, &ph);
1106 if (!phases.empty()) {
1107 body.push_back(i3 +
"<entry-phase-activities>");
1108 for (
const std::pair<int, const Work*>& ph : phases) {
1109 const std::string k = std::to_string(ph.first);
1110 const std::string head =
" name=\"" +
esc(en +
"_" + k) +
"\" phase=\"" + k +
"\"";
1113 body.push_back(i3 +
"</entry-phase-activities>");
1116 if (!body.empty()) {
1117 out.push_back(s +
">");
1118 out.insert(out.end(), body.begin(), body.end());
1119 out.push_back(i2 +
"</entry>");
1121 out.push_back(s +
"/>");
1124 std::map<std::string, Acts>::const_iterator ait = m.
acts.find(t.
name);
1125 if (ait != m.
acts.end()) {
1126 const Acts& A = ait->second;
1127 std::map<std::string, std::string> bound;
1128 for (
const std::string& en : t.
entries) {
1132 out.push_back(i2 +
"<task-activities>");
1133 const std::string i3 = i2 +
" ";
1135 std::string head =
" name=\"" +
esc(a.
name) +
"\"";
1136 std::map<std::string, std::string>::const_iterator b = bound.find(a.
name);
1137 if (b != bound.end()) head +=
" bound-to-entry=\"" +
esc(b->second) +
"\"";
1140 const std::string i4 = i3 +
" ", i5 = i3 +
" ";
1142 out.push_back(i3 +
"<precedence>");
1145 out.push_back(i4 +
"<" + rec.
pre_kind + q +
">");
1146 for (
const std::string& nm : rec.
pre) out.push_back(i5 +
"<activity name=\"" +
esc(nm) +
"\"/>");
1147 out.push_back(i4 +
"</" + rec.
pre_kind +
">");
1149 const std::string e = rec.
has_end ?
" end=\"" +
esc(rec.
end) +
"\"" : std::string();
1150 out.push_back(i4 +
"<" + rec.
post_kind + e +
">");
1151 for (
const std::pair<std::string, Val>& it : rec.
items) {
1153 out.push_back(i5 +
"<activity name=\"" +
esc(it.first) +
"\"" +
attr(
"prob", it.second) +
"/>");
1155 out.push_back(i5 +
"<activity name=\"" +
esc(it.first) +
"\"" +
attr(
"count", it.second) +
"/>");
1157 out.push_back(i5 +
"<activity name=\"" +
esc(it.first) +
"\"/>");
1159 out.push_back(i4 +
"</" + rec.
post_kind +
">");
1161 out.push_back(i3 +
"</precedence>");
1163 std::vector<std::string> order;
1164 for (
const std::pair<std::string, std::string>& r : A.
replies) {
1166 for (
const std::string& o : order) seen = seen || o == r.first;
1167 if (!seen) order.push_back(r.first);
1169 for (
const std::string& en : order) {
1172 throw err(
"activity of task \"" + t.
name +
"\" replies to entry \"" + en +
"\" of task \"" + e.
task +
1174 out.push_back(i3 +
"<reply-entry name=\"" +
esc(en) +
"\">");
1175 std::vector<std::string> seen;
1176 for (
const std::pair<std::string, std::string>& r : A.
replies) {
1177 if (r.first != en)
continue;
1179 for (
const std::string& x : seen) dup = dup || x == r.second;
1181 seen.push_back(r.second);
1182 out.push_back(i4 +
"<reply-activity name=\"" +
esc(r.second) +
"\"/>");
1184 out.push_back(i3 +
"</reply-entry>");
1186 out.push_back(i2 +
"</task-activities>");
1188 for (
const std::string& en : t.
entries) {
1191 throw err(
"entry \"" + en +
"\" starts at activity \"" + e.
start +
"\", but task \"" + t.
name +
1192 "\" has no activity section");
1195 out.push_back(ind +
"</task>");
1199inline std::string
stem(
const std::string& path) {
1200 const std::size_t slash = path.find_last_of(
'/');
1201 const std::string base = slash == std::string::npos ? path : path.substr(slash + 1);
1202 const std::size_t dot = base.rfind(
'.');
1203 const std::size_t first = base.find_first_not_of(
'.');
1204 if (dot == std::string::npos || first == std::string::npos || dot < first)
return base;
1205 return base.substr(0, dot);
1210 std::ifstream f(path.c_str(), std::ios::binary);
1212 std::ostringstream ss;
1214 const std::string raw = ss.str();
1216 s.reserve(raw.size());
1217 for (std::size_t i = 0; i < raw.size(); ++i) {
1218 if (raw[i] ==
'\r') {
1220 if (i + 1 < raw.size() && raw[i + 1] ==
'\n') ++i;
1239inline std::string
srvn2lqnx(
const std::string& src,
const std::string& name = std::string(
"model")) {
1242 const Model m = p.parse();
1243 std::vector<std::string> out;
1244 out.push_back(
"<?xml version=\"1.0\"?>");
1245 out.push_back(
"<lqn-model name=\"" + esc(name) +
"\" description=\"srvn2lqnx\">");
1246 std::string s =
" <solver-params comment=\"" + esc(m.comment) +
"\"";
1247 for (
const std::pair<std::string, Val>& kv : m.solver) s += attr(kv.first.c_str(), kv.second);
1248 if (!p.pragmas.empty()) {
1249 out.push_back(s +
">");
1250 for (
const std::pair<std::string, std::string>& kv : p.pragmas)
1251 out.push_back(
" <pragma param=\"" + esc(kv.first) +
"\" value=\"" + esc(kv.second) +
"\"/>");
1252 out.push_back(
" </solver-params>");
1254 out.push_back(s +
"/>");
1256 for (
const Proc& proc : m.procs) {
1257 s =
" <processor name=\"" + esc(proc.name) +
"\" scheduling=\"" + proc.sched +
"\"";
1258 if (proc.quantum.has) s += attr(
"quantum", proc.quantum);
1259 if (proc.mult.has && proc.sched !=
"inf" && proc.mult.value != 1) s += attr(
"multiplicity", proc.mult);
1260 if (proc.repl.has && proc.repl.value != 1) s += attr(
"replication", proc.repl);
1261 if (proc.speed.has && proc.speed.value != 1) s += attr(
"speed-factor", proc.speed);
1262 out.push_back(s +
">");
1263 for (std::size_t gi : proc.groups) {
1264 const Group& g = m.groups[gi];
1265 out.push_back(
" <group name=\"" + esc(g.name) +
"\"" + attr(
"share", g.share) +
1266 (g.cap ?
" cap=\"true\"" :
"") +
">");
1267 for (std::size_t ti : g.tasks) emit_task(out,
" ", m.tasks[ti], m);
1268 out.push_back(
" </group>");
1270 for (std::size_t ti : proc.tasks) emit_task(out,
" ", m.tasks[ti], m);
1271 out.push_back(
" </processor>");
1273 out.push_back(
"</lqn-model>");
1275 for (
const std::string& l : out) {
1289 std::ifstream f(path.c_str(), std::ios::binary);
1292 f.read(buf,
sizeof(buf));
1293 const std::streamsize got = f.gcount();
1294 for (std::streamsize i = 0; i < got; ++i)
1304 const std::size_t dot = path.find_last_of(
'.');
1305 const std::size_t slash = path.find_last_of(
'/');
1306 if (dot == std::string::npos || (slash != std::string::npos && dot < slash))
return false;
1307 std::string ext = path.substr(dot + 1);
1308 for (
char& c : ext) c =
static_cast<char>(std::tolower(
static_cast<unsigned char>(c)));
1309 if (ext ==
"in")
return true;
1310 if (ext !=
"lqn")
return false;
1311 std::ifstream f(path.c_str(), std::ios::binary);
1325 std::string msg = e.what();
1326 if (msg.compare(0, 4,
"SRVN") == 0) msg =
"SRVN " + path + msg.substr(4);
1329 std::string msg = e.what();
1330 if (msg.compare(0, 4,
"SRVN") == 0) msg =
"SRVN " + path + msg.substr(4);
std::string kind() const
The <kind> part, or empty for LINE:Error.
A refusal: the feature, method or analysis is not covered: LINE:unsupported:<kind>.
UnsupportedError(const std::string &what)
std::vector< std::pair< std::string, std::string > > pragmas
Parser(const std::string &src)
The exception types the port throws.
.lqnx -> LqnStruct, a port of matlab/src/lang/layered/@LayeredNetwork/parseXML.m followed by ....
const char * task_sched(const std::string &k)
std::size_t match_number(const std::string &src, std::size_t i)
End of [-+]?(?
void tokenize(const std::string &src, std::vector< Tok > &toks, std::vector< std::pair< std::string, std::string > > &pragmas)
std::string lstrip_plus(const std::string &s)
void emit_activity(std::vector< std::string > &out, const std::string &ind, const std::string &head, const Work &a)
bool is_space(char c)
Python's str.isspace over ASCII, which also counts the four separators 0x1c-0x1f.
const char * proc_sched(const std::string &k)
const char * rwlock_kind(const std::string &k)
std::string stem(const std::string &path)
Python's os.path.splitext(os.path.basename(path))[0].
std::string esc(const std::string &s)
double to_double(const std::string &s)
std::string attr(const char *name, const Val &v)
bool pragma_body(const std::string &line, std::string &body)
#\s*pragma\s+(.
InputError err(const std::string &msg, int line=-1)
std::string py_repr(const std::string &s)
Python's repr of an ASCII string, so messages read as the reference's do.
std::string read_text(const std::string &path)
The file as Python's text mode reads it: universal newlines, so \r\n and \r become \n.
void emit_task(std::vector< std::string > &out, const std::string &ind, const Task &t, const Model &m)
std::string strip(const std::string &s)
bool is_ident(char c)
[A-Za-z0-9_.
std::string activity_attrs(const Work &a, std::vector< const Call * > &calls)
std::string name(const Element *el, const std::string &key="name")
std::string srvn_file_to_lqnx(const std::string &path)
Translate the SRVN file path to .lqnx text, model name = its basename without extension.
LqnStruct< T > lqn_finalize(const LqnModel< T > &m)
Port of @LayeredNetwork/getStruct.m: flatten the model into its struct.
LqnModel< T > read_lqnx_model_text(const std::string &text)
read_lqnx_model on a .lqnx document held in a string (the SRVN reader's translation,...
LqnModel< T > read_srvn_model(const std::string &path)
Read an SRVN file (.in, or a .lqn that is not XML) into the intermediate LqnModel,...
std::string format_number(double x)
Shortest C g spelling that round-trips x; the four codebases share this rule.
LqnStruct< T > read_srvn(const std::string &path)
read_srvn_model followed by lqn_finalize: the struct SolverLN consumes, as read_lqnx is.
std::string srvn2lqnx(const std::string &src, const std::string &name=std::string("model"))
Translate SRVN text into an LQN XML document.
bool is_srvn_file(const std::string &path)
True when path holds SRVN text rather than XML: its first non-blank character is not '<'.
bool is_srvn_path(const std::string &path)
Whether path should be read as SRVN: a .in file, or a .lqn file that is not XML.
Conservation laws of a layered queueing network, enumerated from its structure.
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::vector< Conn > conns
std::vector< std::pair< std::string, std::string > > replies
(entry, activity)
std::map< std::string, std::size_t > by
std::vector< std::vector< std::string > > prec
std::string kind
synch-call | asynch-call
std::vector< std::string > pre
std::vector< std::pair< std::string, Val > > items
std::vector< std::pair< std::string, Val > > fwd
std::map< int, Work > phases
std::string rw
empty when not declared
std::vector< std::size_t > tasks
std::vector< std::string > entry_order
std::map< std::string, std::size_t > task_by
std::map< std::string, Entry > entries
std::vector< Task > tasks
std::map< std::string, std::size_t > proc_by
std::map< std::string, Acts > acts
std::vector< Group > groups
std::vector< Proc > procs
std::vector< std::pair< std::string, Val > > solver
std::vector< std::size_t > groups
into Model::groups
std::vector< std::size_t > tasks
into Model::tasks
std::vector< std::string > entries
std::vector< std::pair< std::string, Val > > fanin
std::vector< std::pair< std::string, Val > > fanout
A number as written: its source spelling and its value.
Val(std::string t, double v)
An entry phase or an activity: the s, c, f, Z, M values and the calls.
std::vector< Call > calls