10#include <unordered_set>
13 inline bool isIdentStart(
char c) {
return std::isalpha(
static_cast<unsigned char>(c)) || c ==
'_'; }
14 inline bool isIdentChar(
char c) {
return std::isalnum(
static_cast<unsigned char>(c)) || c ==
'_'; }
15 std::string lower(std::string s) {
16 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) { return std::tolower(c); });
19 std::string html_escape(
const std::string &s) {
21 out.reserve(s.size() * 12 / 10 + 8);
57 std::unordered_set<std::string> s;
58 for (
const auto &name :
IncType) {
61 s.insert(lower(name));
67 std::unordered_set<std::string> s;
70 const char *extra[] = {
71 "function",
"export",
"return",
"start",
"done",
"true",
"false",
"null",
72 "string",
"int",
"byte",
"float",
"ptr",
"object",
"program",
"module",
"data",
"code",
"section"};
80 static const std::unordered_set<std::string>
kTypes = [] {
81 return std::unordered_set<std::string>{
"string",
"int",
"byte",
"float",
"ptr"};
87 out <<
"<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">";
88 out <<
"<title>" << filename <<
" - [ MXVM Source ]</title>";
90 out <<
"body{background:#FFFFFF;color:#000000;margin:0;font:13px/1.6 \"Courier New\",Courier,monospace}";
91 out <<
".wrap{max-width:1100px;margin:0 auto;padding:24px}";
92 out <<
"pre.code{white-space:pre-wrap;word-wrap:break-word;background:#FFFFFF;padding:16px;border:1px solid #CCCCCC;border-radius:2px}";
93 out <<
".kw{color:#000080;font-weight:bold}";
94 out <<
".op{color:#000080;font-weight:bold}";
95 out <<
".id{color:#000000}";
96 out <<
".num{color:#800000}";
97 out <<
".str{color:#008080}";
98 out <<
".com{color:#808080;font-style:italic}";
99 out <<
".lbl{color:#008000;font-weight:bold}";
100 out <<
".typ{color:#000080;font-weight:bold}";
101 out <<
"</style></head><body><div class=\"wrap\"><pre class=\"code\"><code>";
102 const std::string &s =
source;
103 size_t i = 0, n = s.size();
106 enum class ModState {
None, SawSection, SawModule, Inside };
107 ModState modState = ModState::None;
133 while (j < n && s[j] !=
'\n')
135 std::string t = s.substr(i, j - i);
136 out <<
"<span class=\"com\">" << html_escape(t) <<
"</span>";
145 if (!esc && s[j] ==
'"') {
149 esc = (!esc && s[j] ==
'\\');
152 std::string t = s.substr(i, j - i);
153 out <<
"<span class=\"str\">" << html_escape(t) <<
"</span>";
158 if (std::isdigit(
static_cast<unsigned char>(c)) || (c ==
'-' && i + 1 < n && std::isdigit(
static_cast<unsigned char>(s[i + 1])))) {
162 if (j + 1 < n && s[j] ==
'0' && (s[j + 1] ==
'x' || s[j + 1] ==
'X')) {
164 while (j < n && std::isxdigit(
static_cast<unsigned char>(s[j])))
167 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j])))
169 if (j < n && s[j] ==
'.') {
171 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j])))
175 std::string t = s.substr(i, j - i);
176 out <<
"<span class=\"num\">" << html_escape(t) <<
"</span>";
181 if (isIdentStart(c)) {
183 while (j < n && isIdentChar(s[j]))
185 bool is_label = (j < n && s[j] ==
':');
186 std::string ident = s.substr(i, j - i);
187 std::string lid = lower(ident);
189 out <<
"<span class=\"lbl\">" << html_escape(ident) <<
"</span>:";
191 modState = ModState::None;
196 if (lid ==
"section" && modState == ModState::None) {
197 modState = ModState::SawSection;
198 }
else if (lid ==
"module" && modState == ModState::SawSection) {
199 modState = ModState::SawModule;
200 }
else if (modState != ModState::Inside) {
201 modState = ModState::None;
206 bool suppress = (modState == ModState::Inside);
209 out <<
"<span class=\"op\">" << html_escape(ident) <<
"</span>";
212 out <<
"<span class=\"typ\">" << html_escape(ident) <<
"</span>";
214 out <<
"<span class=\"kw\">" << html_escape(ident) <<
"</span>";
216 out <<
"<span class=\"id\">" << html_escape(ident) <<
"</span>";
222 if (c ==
'{' || c ==
'}' || c ==
',' || c ==
'(' || c ==
')' || c ==
'[' || c ==
']') {
223 if (c ==
'{' && modState == ModState::SawModule) {
224 modState = ModState::Inside;
225 }
else if (c ==
'}' && modState == ModState::Inside) {
226 modState = ModState::None;
228 out << html_escape(std::string(1, c));
233 out << html_escape(std::string(1, c));
236 out <<
"</code></pre></div></body></html>";
248 std::string pas_html_escape(
const std::string &s) {
250 out.reserve(s.size() + 16);
253 case '&': out +=
"&";
break;
254 case '<': out +=
"<";
break;
255 case '>': out +=
">";
break;
256 case '"': out +=
""";
break;
257 case '\'': out +=
"'";
break;
258 default: out.push_back(c);
break;
267 const std::unordered_set<std::string> &kPasKeywords() {
268 static const std::unordered_set<std::string> s = {
269 "program",
"unit",
"interface",
"implementation",
"uses",
270 "var",
"const",
"type",
"procedure",
"function",
"forward",
272 "if",
"then",
"else",
"while",
"do",
273 "for",
"to",
"downto",
"repeat",
"until",
274 "case",
"of",
"with",
"goto",
"label",
275 "in",
"and",
"or",
"not",
"div",
"mod",
"xor",
"shl",
"shr",
276 "packed",
"set",
"file",
"array",
"record",
282 const std::unordered_set<std::string> &kPasTypes() {
283 static const std::unordered_set<std::string> s = {
284 "integer",
"real",
"boolean",
"char",
285 "byte",
"word",
"longint",
"shortint",
"smallint",
"cardinal",
286 "string",
"text",
"double",
"single",
"extended",
287 "comp",
"currency",
"ptr",
"pointer",
293 const std::unordered_set<std::string> &kPasBuiltins() {
294 static const std::unordered_set<std::string> s = {
296 "writeln",
"write",
"readln",
"read",
297 "assign",
"reset",
"rewrite",
"append",
"close",
"eof",
"eoln",
299 "new",
"dispose",
"setlength",
"high",
"low",
301 "exit",
"break",
"continue",
"halt",
303 "nil",
"true",
"false",
307 "ord",
"chr",
"succ",
"pred",
309 "abs",
"sqr",
"sqrt",
"trunc",
"round",
311 "random",
"randomize",
"seed_random",
"rand_number",
313 "length",
"copy",
"pos",
"concat",
"delete",
"insert",
"str",
"val",
315 "include",
"exclude",
328 "/* mxvm-highlight.css — syntax highlight stylesheet for mxvm-html\n"
329 " Inspired by the Borland Delphi 7 editor default colour scheme.\n"
330 " Edit this file to change colours; it takes precedence over the\n"
331 " inline fallback styles that are embedded in every HTML file. */\n"
334 " background: #FFFFFF;\n"
337 " font: 13px/1.6 \"Courier New\", Courier, monospace;\n"
340 " max-width: 1100px;\n"
345 " white-space: pre-wrap;\n"
346 " word-wrap: break-word;\n"
347 " background: #FFFFFF;\n"
349 " border: 1px solid #CCCCCC;\n"
350 " border-radius: 2px;\n"
353 "/* ---- token spans ---- */\n"
355 "/* reserved keywords — bold navy (classic Delphi blue) */\n"
356 ".kw { color: #000080; font-weight: bold; }\n"
357 "/* built-in types — same as keywords */\n"
358 ".typ { color: #000080; font-weight: bold; }\n"
359 "/* built-in routines and predefined constants */\n"
360 ".bi { color: #000080; }\n"
361 "/* string / char literals — teal */\n"
362 ".str { color: #008080; }\n"
363 "/* numeric literals — maroon */\n"
364 ".num { color: #800000; }\n"
365 "/* comments — gray italic */\n"
366 ".com { color: #808080; font-style: italic; }\n"
367 "/* plain identifiers — black */\n"
368 ".id { color: #000000; }\n"
370 "/* ---- mxvm-specific (also used by .mxvm output) ---- */\n"
372 "/* VM instructions */\n"
373 ".op { color: #000080; font-weight: bold; }\n"
375 ".lbl { color: #008000; font-weight: bold; }\n";
379 const std::string &cssHref) {
380 out <<
"<!doctype html><html><head>"
381 <<
"<meta charset=\"utf-8\">"
382 <<
"<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">"
383 <<
"<title>" << pas_html_escape(filename) <<
" - [ Pascal Source ]</title>"
387 <<
"<link rel=\"stylesheet\" href=\"" << pas_html_escape(cssHref) <<
"\">"
388 <<
"</head><body><div class=\"wrap\"><pre class=\"code\"><code>";
390 const std::string &s =
source;
391 size_t i = 0, n = s.size();
397 if (c ==
'\r') { ++i;
continue; }
398 if (c ==
'\n') { out <<
'\n'; ++i;
continue; }
399 if (c ==
'\t') { out <<
'\t'; ++i;
continue; }
400 if (c ==
' ') { out <<
' '; ++i;
continue; }
403 if (c ==
'/' && i + 1 < n && s[i + 1] ==
'/') {
405 while (j < n && s[j] !=
'\n') ++j;
406 out <<
"<span class=\"com\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
414 while (j < n && s[j] !=
'}') ++j;
416 out <<
"<span class=\"com\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
422 if (c ==
'(' && i + 1 < n && s[i + 1] ==
'*') {
424 while (j + 1 < n && !(s[j] ==
'*' && s[j + 1] ==
')')) ++j;
425 if (j + 1 < n) j += 2;
426 out <<
"<span class=\"com\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
434 if (j < n && s[j] ==
'$') {
436 while (j < n && std::isxdigit(
static_cast<unsigned char>(s[j]))) ++j;
438 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j]))) ++j;
440 out <<
"<span class=\"num\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
451 if (j < n && s[j] ==
'\'') { ++j;
continue; }
456 out <<
"<span class=\"str\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
464 while (j < n && std::isxdigit(
static_cast<unsigned char>(s[j]))) ++j;
465 out <<
"<span class=\"num\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
471 if (std::isdigit(
static_cast<unsigned char>(c))) {
473 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j]))) ++j;
474 if (j < n && s[j] ==
'.' && (j + 1 >= n || s[j + 1] !=
'.')) {
476 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j]))) ++j;
478 if (j < n && (s[j] ==
'e' || s[j] ==
'E')) {
480 if (j < n && (s[j] ==
'+' || s[j] ==
'-')) ++j;
481 while (j < n && std::isdigit(
static_cast<unsigned char>(s[j]))) ++j;
483 out <<
"<span class=\"num\">" << pas_html_escape(s.substr(i, j - i)) <<
"</span>";
489 if (isIdentStart(c)) {
491 while (j < n && isIdentChar(s[j])) ++j;
492 std::string ident = s.substr(i, j - i);
493 std::string lid = lower(ident);
496 if (kPasKeywords().count(lid))
497 out <<
"<span class=\"kw\">" << pas_html_escape(ident) <<
"</span>";
498 else if (kPasTypes().count(lid))
499 out <<
"<span class=\"typ\">" << pas_html_escape(ident) <<
"</span>";
500 else if (kPasBuiltins().count(lid))
501 out <<
"<span class=\"bi\">" << pas_html_escape(ident) <<
"</span>";
503 out <<
"<span class=\"id\">" << pas_html_escape(ident) <<
"</span>";
508 out << pas_html_escape(std::string(1, c));
512 out <<
"</code></pre></div></body></html>";
void output(std::ostream &out, std::string filename)
Write the generated HTML to an output stream.
HTMLGen(const std::string &src)
Construct an HTML generator from MXVM source text.
std::string source
the MXVM source text to convert
PasHTMLGen(const std::string &src)
Construct from Pascal source text.
void output(std::ostream &out, const std::string &filename, const std::string &cssHref="mxvm-highlight.css")
Write generated HTML to an output stream.
static std::string defaultCss()
Return the default stylesheet text.
HTML documentation generator from MXVM source code.
Instruction set enum, operand/instruction structs, variable types, and Variable/Variable_Value defini...
std::vector< std::string > IncType
String representations of Inc opcodes, indexed by enum value.
@ None
exact fixed operand count required
static const std::unordered_set< std::string > kInstructions
static std::unordered_set< std::string > buildKeywordSet()
static bool is_label(const std::string &line)
static std::unordered_set< std::string > buildInstructionSet()
static const std::unordered_set< std::string > kKeywords
static const std::unordered_set< std::string > kTypes
std::vector< std::string > keywords
String representations of Keywords enum values.