3709192002-03-20Martin Nilsson #pike __REAL_VERSION__
35eee72001-01-08David Norlin //======================================================================== // PARSING OF DOCUMENTATION COMMENTS //======================================================================== inherit "module.pmod"; inherit .PikeObjects;
d47c322001-03-06David Norlin #include "./debug.h"
35eee72001-01-08David Norlin class MetaData { string type;
2e0d132001-02-05David Norlin  string name; // if (type == "class", "module", "endmodule", or "endclass")
35eee72001-01-08David Norlin  array(PikeObject) decls = ({}); string belongs = 0; string appears = 0; } constant EOF = .PikeParser.EOF; constant METAKEYWORD = 1; // @decl constant BRACEKEYWORD = 2; // @i{ @} constant DELIMITERKEYWORD = 3; // @param constant CONTAINERKEYWORD = 4; // @mapping
2e0d132001-02-05David Norlin constant SINGLEKEYWORD = 5; // @deprecated constant ENDKEYWORD = 6; constant ERRORKEYWORD = 7;
35eee72001-01-08David Norlin 
2e0d132001-02-05David Norlin constant TEXTTOKEN = 8; constant ENDTOKEN = 9;
35eee72001-01-08David Norlin  // The following is the "DTD" of mapping(string : int) keywordtype = ([ "appears" : METAKEYWORD, "belongs" : METAKEYWORD, "class" : METAKEYWORD, "endclass" : METAKEYWORD, "module" : METAKEYWORD, "endmodule" : METAKEYWORD, "decl" : METAKEYWORD,
ecba372001-05-15David Norlin  "b" : BRACEKEYWORD,
35eee72001-01-08David Norlin  "i" : BRACEKEYWORD,
90cb472001-05-15David Norlin  "u" : BRACEKEYWORD,
35eee72001-01-08David Norlin  "tt" : BRACEKEYWORD,
a380f82001-05-29David Norlin  "url" : BRACEKEYWORD,
cc33b12001-07-17Martin Nilsson  "pre" : BRACEKEYWORD,
35eee72001-01-08David Norlin  "ref" : BRACEKEYWORD,
90cb472001-05-15David Norlin  "xml" : BRACEKEYWORD, // well, not really, but....
793b472001-05-15David Norlin  "code" : BRACEKEYWORD,
3821e52002-05-30Martin Nilsson  "expr" : BRACEKEYWORD,
7c14112001-05-09David Norlin  "image" : BRACEKEYWORD,
35eee72001-01-08David Norlin 
2e0d132001-02-05David Norlin  "deprecated" : SINGLEKEYWORD,
35eee72001-01-08David Norlin  "example" : DELIMITERKEYWORD, "note" : DELIMITERKEYWORD,
0d10ab2001-04-26Henrik Grubbström (Grubba)  "bugs" : DELIMITERKEYWORD,
7d3e642001-02-06David Norlin  "returns" : DELIMITERKEYWORD,
0d10ab2001-04-26Henrik Grubbström (Grubba)  "throws" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin  "param" : DELIMITERKEYWORD,
46b75b2001-01-08Henrik Grubbström (Grubba)  "seealso" : DELIMITERKEYWORD,
bbab332001-08-20Martin Nilsson  "fixme" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin 
fd033d2001-02-06David Norlin  "section" : CONTAINERKEYWORD,
3f97b32001-04-11David Norlin  "constant" : DELIMITERKEYWORD, // used inside @decl enum Foo
35eee72001-01-08David Norlin  "mapping" : CONTAINERKEYWORD, "member" : DELIMITERKEYWORD, "multiset" : CONTAINERKEYWORD, "index" : DELIMITERKEYWORD, "array" : CONTAINERKEYWORD, "elem" : DELIMITERKEYWORD,
141bba2001-02-06Henrik Grubbström (Grubba)  "int" : CONTAINERKEYWORD, "value" : DELIMITERKEYWORD,
57add72001-02-14Henrik Grubbström (Grubba)  "string" : CONTAINERKEYWORD,
141bba2001-02-06Henrik Grubbström (Grubba)  "mixed" : CONTAINERKEYWORD, "type" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin  "dl" : CONTAINERKEYWORD, "item" : DELIMITERKEYWORD,
bbab332001-08-20Martin Nilsson  "ol" : CONTAINERKEYWORD, "ul" : CONTAINERKEYWORD,
35eee72001-01-08David Norlin ]); mapping(string : array(string)) attributenames = ([ "item" : ({ "name" }), "param" : ({ "name" }), "mapping" : ({ "name" }), "array" : ({ "name" }), "multiset" : ({ "name" }),
141bba2001-02-06Henrik Grubbström (Grubba)  "int" : ({ "name" }),
57add72001-02-14Henrik Grubbström (Grubba)  "string" : ({ "name" }),
141bba2001-02-06Henrik Grubbström (Grubba)  "mixed" : ({ "name" }),
3f97b32001-04-11David Norlin  "constant" : ({ "name" }),
9b77a92002-11-25Henrik Grubbström (Grubba)  "typedef" : ({ "name" }), "enum" : ({ "name" }),
35eee72001-01-08David Norlin ]);
0d10ab2001-04-26Henrik Grubbström (Grubba) static constant standard = (<
bbab332001-08-20Martin Nilsson  "note", "bugs", "example", "seealso", "deprecated", "fixme"
0d10ab2001-04-26Henrik Grubbström (Grubba) >);
35eee72001-01-08David Norlin  mapping(string : multiset(string)) allowedChildren = (["_general" : standard,
0d10ab2001-04-26Henrik Grubbström (Grubba)  "_method" : (< "param", "returns", "throws" >) + standard,
8575a02001-02-06David Norlin  "_variable": standard, "_inherit" : standard,
9b1ad32001-02-06David Norlin  "_class" : standard, "_module" : standard, "_constant" : standard,
3f97b32001-04-11David Norlin  "_enum" : (< "constant" >) + standard,
9b77a92002-11-25Henrik Grubbström (Grubba)  "_typedef" : standard,
35eee72001-01-08David Norlin  "mapping" : (< "member" >), "multiset": (< "index" >), "array" : (< "elem" >),
141bba2001-02-06Henrik Grubbström (Grubba)  "int" : (< "value" >),
57add72001-02-14Henrik Grubbström (Grubba)  "string" : (< "value" >),
141bba2001-02-06Henrik Grubbström (Grubba)  "mixed" : (< "type" >),
35eee72001-01-08David Norlin  "dl" : (< "item" >),
bbab332001-08-20Martin Nilsson  "ol" : (< "item" >), "ul" : (< "item" >),
35eee72001-01-08David Norlin ]);
2e0d132001-02-05David Norlin mapping(string : multiset(string)) allowGrouping = (["param" : (< "param" >),
d47c322001-03-06David Norlin  "index" : (< "index" >),
8a6cdd2001-02-06Henrik Grubbström (Grubba)  "member" : (< "member" >),
161f1d2001-02-09Henrik Grubbström (Grubba)  "type" : (< "type" >),
57add72001-02-14Henrik Grubbström (Grubba)  "value" : (< "value" >),
3f97b32001-04-11David Norlin  "constant" : (< "constant" >),
ecba372001-05-15David Norlin  "item" : (< "item" >),
2e0d132001-02-05David Norlin ]);
35eee72001-01-08David Norlin 
3f97b32001-04-11David Norlin multiset(string) allowOnlyOne = (< "seealso", "returns",
0d10ab2001-04-26Henrik Grubbström (Grubba)  "throws",
3f97b32001-04-11David Norlin  "deprecated", >);
22923a2001-01-11David Norlin static int getKeywordType(string keyword) {
35eee72001-01-08David Norlin  if (keywordtype[keyword]) return keywordtype[keyword]; if (strlen(keyword) > 3 && keyword[0..2] == "end") return ENDKEYWORD; return ERRORKEYWORD; }
22923a2001-01-11David Norlin static int getTokenType(array(string) | string token) {
35eee72001-01-08David Norlin  if (arrayp(token)) return getKeywordType(token[0]); if (!token) return ENDTOKEN; return TEXTTOKEN; }
22923a2001-01-11David Norlin static int isSpaceChar(int char) {
35eee72001-01-08David Norlin  return (< '\t', '\n', ' ' >) [char]; }
22923a2001-01-11David Norlin static int isKeywordChar(int char) {
35eee72001-01-08David Norlin  return char >= 'a' && char <= 'z'; }
22923a2001-01-11David Norlin static array(string) extractKeyword(string line) {
35eee72001-01-08David Norlin  line += "\0"; int i = 0; while (i < strlen(line) && isSpaceChar(line[i])) ++i; if (line[i++] != '@') return 0; int keywordstart = i; while (isKeywordChar(line[i])) ++i; if (i == keywordstart || line[i] && !isSpaceChar(line[i])) return 0; string keyword = line[keywordstart .. i - 1]; // if (getKeywordType(keyword) == METAKEYWORD) return ({ keyword, line[i .. strlen(line) - 2] }); // skippa "\0" ... }
22923a2001-01-11David Norlin static int allSpaces(string s) {
35eee72001-01-08David Norlin  for (int i = strlen(s) - 1; i >= 0; --i) if (s[i] != ' ' && s[i] != '\t') return 0; return 1; }
d47c322001-03-06David Norlin static private class Token ( int type, string keyword, string arg, // the rest of the line, after the keyword string text, SourcePosition position, ) { string _sprintf() { return sprintf("Token(%d, %O, %O, %O, %O)", type, keyword, arg, text, position); } } static array(Token) split(string s, SourcePosition pos) { s = s - "\r"; s = replace(s, "@\n", "@\r"); array(string) lines = s / "\n"; // array holding the number of src lines per line array(int) counts = allocate(sizeof(lines)); for(int i = 0; i < sizeof(lines); ++i) { array(string) a = lines[i] / "@\r"; counts[i] = sizeof(a); lines[i] = a * ""; } array(Token) res = ({ }); string filename = pos->filename; string text = 0; int textFirstLine; int textLastLine; int curLine = pos->firstline; for(int i = 0; i < sizeof(lines); ++i) { string line = lines[i]; array(string) extr = extractKeyword(line); if (extr) { if (text) res += ({ Token(TEXTTOKEN, 0, 0, text, SourcePosition(filename, textFirstLine, textLastLine)) }); text = 0; res += ({ Token(getKeywordType(extr[0]), extr[0], extr[1], 0, SourcePosition(filename, curLine, curLine + counts[i] - 1)) });
35eee72001-01-08David Norlin  } else { if (allSpaces(line))
d47c322001-03-06David Norlin  line = ""; if (!text) { text = ""; textFirstLine = curLine; } text += line + "\n"; textLastLine = curLine;
35eee72001-01-08David Norlin  }
d47c322001-03-06David Norlin  curLine += counts[i];
35eee72001-01-08David Norlin  }
d47c322001-03-06David Norlin  if (text) res += ({ Token(TEXTTOKEN, 0, 0, text, SourcePosition(filename, textFirstLine, textLastLine)) });
35eee72001-01-08David Norlin  return res; }
d47c322001-03-06David Norlin static class DocParserClass {
35eee72001-01-08David Norlin 
ca8e252002-10-01Martin Nilsson  SourcePosition currentPosition = 0;
d47c322001-03-06David Norlin  static void parseError(string s, mixed ... args) { s = sprintf(s, @args); werror("DocParser error: %O\n", s); throw (AutoDocError(currentPosition, "DocParser", s)); } static array(Token) tokenArr = 0; static Token peekToken() { Token t = tokenArr[0]; currentPosition = t->position || currentPosition; return t; } static Token readToken() { Token t = peekToken(); tokenArr = tokenArr[1..]; return t; } // argHandlers: // // Contains functions that handle keywords with non-standard arg list // syntax. The function for each keyword can return a mapping or a string: // // If a mapping(string:string) is returned, it is interpreted as the // attributes to put in the tag. // If a string is returned, it is an XML fragment that gets inserted // inside the tag. mapping(string : function(string, string : string) | function(string, string : mapping(string : string))) argHandlers = ([ "member" : memberArgHandler, "elem" : elemArgHandler, "index" : indexArgHandler, "deprecated" : deprArgHandler, "section" : sectionArgHandler, "type" : typeArgHandler, "value" : valueArgHandler, ]); static string memberArgHandler(string keyword, string arg) { // werror("This is the @member arg handler "); .PikeParser parser = .PikeParser(arg, currentPosition); // werror("&&& %O\n", arg); Type t = parser->parseOrType(); if (!t) parseError("@member: expected type, got %O", arg); // werror("%%%%%% got type == %O\n", t->xml()); string s = parser->parseLiteral() || parser->parseIdents(); if (!s) parseError("@member: expected indentifier or literal constant, got %O", arg); parser->eat(EOF); return xmltag("type", t->xml()) + xmltag("index", xmlquote(s)); } static string elemArgHandler(string keyword, string arg) { // werror("This is the @elem arg handler\n"); .PikeParser parser = .PikeParser(arg, currentPosition); Type t = parser->parseOrType(); if (!t) parseError("@elem: expected type, got %O", arg); if (parser->peekToken() == "...") { t = VarargsType(t); parser->eat("..."); } string s = parser->parseLiteral() || parser->parseIdents(); string s2 = 0; int dots = 0; if (parser->peekToken() == "..") { dots = 1; parser->readToken(); s2 = parser->parseLiteral() || parser->parseIdents(); } parser->eat(EOF);
a32a5f2001-06-14David Norlin  string type = xmltag("type", t->xml());
d47c322001-03-06David Norlin  if (s) if (s2)
a32a5f2001-06-14David Norlin  return type + xmltag("minindex", xmlquote(s))
d47c322001-03-06David Norlin  + xmltag("maxindex", xmlquote(s2)); else
a32a5f2001-06-14David Norlin  return type + xmltag(dots ? "minindex" : "index", xmlquote(s));
d47c322001-03-06David Norlin  else if (s2)
a32a5f2001-06-14David Norlin  return type + xmltag("maxindex", xmlquote(s2));
d47c322001-03-06David Norlin  else parseError("@elem: expected identifier or literal"); } static string indexArgHandler(string keyword, string arg) { // werror("indexArgHandler\n"); .PikeParser parser = .PikeParser(arg, currentPosition); string s = parser->parseLiteral(); if (!s) parseError("@index: expected identifier, got %O", arg); parser->eat(EOF); return xmltag("value", xmlquote(s)); } static string deprArgHandler(string keyword, string arg) { .PikeParser parser = .PikeParser(arg, currentPosition); if (parser->peekToken() == EOF) return ""; string res = ""; for (;;) { string s = parser->parseIdents(); if (!s) parseError("@deprecated: expected list identifier, got %O", arg); res += xmltag("name", s); if (parser->peekToken() == EOF) return res; parser->eat(","); } } static mapping(string : string) sectionArgHandler(string keyword, string arg) { return ([ "name" : arg ]); } static string typeArgHandler(string keyword, string arg) { // werror("This is the @type arg handler "); .PikeParser parser = .PikeParser(arg, currentPosition); // werror("&&& %O\n", arg); Type t = parser->parseOrType(); if (!t) parseError("@member: expected type, got %O", arg); // werror("%%%%%% got type == %O\n", t->xml()); parser->eat(EOF);
a20f852001-07-26David Norlin  return t->xml();
d47c322001-03-06David Norlin  } static string valueArgHandler(string keyword, string arg) {
a20f852001-07-26David Norlin  // werror("This is the @value arg handler ");
d47c322001-03-06David Norlin  .PikeParser parser = .PikeParser(arg, currentPosition); // werror("&&& %O\n", arg); string s = parser->parseLiteral() || parser->parseIdents();
3d7c7c2001-04-26David Norlin  string s2 = 0; int dots = 0; if (parser->peekToken() == "..") { dots = 1; parser->readToken(); s2 = parser->parseLiteral() || parser->parseIdents(); }
d47c322001-03-06David Norlin  parser->eat(EOF);
3d7c7c2001-04-26David Norlin  if (s) if (s2) return xmltag("minvalue", xmlquote(s)) + xmltag("maxvalue", xmlquote(s2)); else
a20f852001-07-26David Norlin  return dots ? xmltag("minvalue", xmlquote(s)) : xmlquote(s);
3d7c7c2001-04-26David Norlin  else if (s2) return xmltag("maxvalue", xmlquote(s2)); else parseError("@value: expected indentifier or literal constant, got %O", arg);
d47c322001-03-06David Norlin  } static mapping(string : string) standardArgHandler(string keyword, string arg) { array(string) args = ({}); arg += "\0"; int i = 0; for (;;) { while (isSpaceChar(arg[i]))
35eee72001-01-08David Norlin  ++i;
d47c322001-03-06David Norlin  if (!arg[i]) break; if (arg[i] == '"' || arg[i] == '\'') { int quotechar = arg[i];
35eee72001-01-08David Norlin  ++i; int start = i;
d47c322001-03-06David Norlin  while(arg[i]) { if (arg[i] == quotechar) if (arg[i + 1] == quotechar) ++i; else break; // Or should we have \n style quoting?? //else if (arg[i] == '\\' && arg[i + 1]) // ++i; ++i; } if (!arg[i]) parseError("keyword parameter is unterminated string constant");
35eee72001-01-08David Norlin  else
d47c322001-03-06David Norlin  ++i; string s = arg[start .. i - 2]; array(string) replacefrom = ({ quotechar == '"' ? "\"\"" : "''" }); array(string) replaceto = ({ quotechar == '"' ? "\"" : "'" }); // Or should we have \n style quoting?? //array(string) replacefrom = ({ "\\n", "\\t", "\\r", "\\\"", "\\\\", // quotechar == '"' ? "\"\"" : "''" }); //array(string) replaceto = ({ "\n", "\t", "\r", "\"", "\\", // quotechar == '"' ? "\"" : "'" }); s = replace(s,replacefrom, replaceto); args += ({ s });
35eee72001-01-08David Norlin  }
d47c322001-03-06David Norlin  else { int start = i; while (arg[i] && !isSpaceChar(arg[i]))
35eee72001-01-08David Norlin  ++i;
d47c322001-03-06David Norlin  args += ({ arg[start .. i - 1] });
35eee72001-01-08David Norlin  } }
d47c322001-03-06David Norlin  mapping(string:string) res = ([]); array(string) attrnames = attributenames[keyword]; int attrcount = sizeof(attrnames || ({}) ); if (attrcount < sizeof(args)) parseError(sprintf("@%s with too many parameters", keyword)); for (int i = 0; i < sizeof(args); ++i) res[attrnames[i]] = attributequote(args[i]); return res; } static string|mapping(string:string) getArgs(string keyword, string arg) { return (argHandlers[keyword] || standardArgHandler)(keyword, arg); } static string xmlNode(string s) { /* now, @xml works like @i & @tt */ s += "\0"; string res = ""; int i = 0; array(string) tagstack = ({ }); int inXML = 0; while (s[i] == '\n') // strip leading empty lines.
35eee72001-01-08David Norlin  ++i;
d47c322001-03-06David Norlin  while (s[i]) { if (s[i] == '@') { ++i; if (s[i] == '@') { res += "@"; ++i; } else if (s[i] == '[') { // @ref shortcut int j = ++i; multiset(int) forbidden = (<'@','\n'>); int level = 1; while (s[j] && level && !forbidden[s[j]] ) { if (s[j] == ']') { level--; } else if (s[j] == '[') { level++; } ++j; } if (level) { if (forbidden[s[j]]) { parseError("forbidden character inside @[...]: %O", s[i-2..j]); } parseError("@[ without matching ]"); } res += xmltag("ref", xmlquote(s[i .. j - 2])); i = j; } else if (s[i] == '}') { if (!sizeof(tagstack)) { werror("///\n%O\n///\n", s); parseError("closing @} without corresponding @keyword{"); } if (tagstack[0] == "xml") --inXML; else res += closetag(tagstack[0]); tagstack = tagstack[1..]; ++i; } else if (isKeywordChar(s[i])) { int start = i; while (isKeywordChar(s[++i])) ; string keyword = s[start .. i - 1]; if (s[i] != '{' || keyword == "") parseError("expected @keyword{, got %O", s[start .. i]);
61c0812001-05-09David Norlin  if (getKeywordType(keyword) != BRACEKEYWORD) parseError("@%s cannot be used like this: @%s{ ... @}", keyword, keyword);
d47c322001-03-06David Norlin  ++i; tagstack = ({ keyword }) + tagstack; if (keyword == "xml") ++inXML; else res += opentag(keyword); } else parseError("expected @keyword{ or @}, got \"" + s[i-1 .. i+4] + "\""); } else if (s[i] == '\n' && !sizeof(tagstack)) { if (s[++i] == '\n') { // at least two conseq. '\n' while (s[i] == '\n') ++i; if (s[i]) // no </p><p> if it was trailing stuff res += "</p>\n<p>"; } else res += "\n"; } else { string add = s[i..i]; if (inXML == 0) add = replace(add, ({ "<", ">", "&" }), // if inside @xml{...@}, escape it ({ "&lt;", "&gt;", "&amp;" }) ); res += add; ++i; }
35eee72001-01-08David Norlin  }
d47c322001-03-06David Norlin  if (sizeof(tagstack)) parseError("@" + tagstack[0] + "{ without matching @}");
b297052001-10-04Martin Nilsson  res = String.trim_all_whites(res-"<p></p>");
43037f2001-10-03Martin Nilsson  if(!sizeof(res)) return "\n"; return "<p>" + res + "</p>\n";
35eee72001-01-08David Norlin  } // Read until the next delimiter token on the same level, or to // the end.
22923a2001-01-11David Norlin  static string xmlText() {
35eee72001-01-08David Norlin  string res = ""; for (;;) {
d47c322001-03-06David Norlin  Token token = peekToken(); switch (token->type) {
35eee72001-01-08David Norlin  case TEXTTOKEN:
d47c322001-03-06David Norlin  res += xmlNode(token->text); readToken();
35eee72001-01-08David Norlin  break; case CONTAINERKEYWORD:
d47c322001-03-06David Norlin  string keyword = token->keyword; string arg = token->arg;
35eee72001-01-08David Norlin  res += "<" + keyword; string|mapping(string:string) args = getArgs(keyword, arg); if (mappingp(args)) foreach(indices(args), string s) res += " " + s + "=\"" + args[s] + "\""; res += ">"; if (stringp(args)) res += args;
d47c322001-03-06David Norlin  readToken();
35eee72001-01-08David Norlin  res += xmlContainerContents(keyword);
d47c322001-03-06David Norlin  if (!(peekToken()->type == ENDKEYWORD && peekToken()->keyword == "end" + keyword))
1cd4712001-02-06Henrik Grubbström (Grubba)  parseError(sprintf("@%s without matching @end%s", keyword, keyword));
35eee72001-01-08David Norlin  res += closetag(keyword);
d47c322001-03-06David Norlin  readToken();
35eee72001-01-08David Norlin  break; default: return res; } } }
22923a2001-01-11David Norlin  static string xmlContainerContents(string container) {
35eee72001-01-08David Norlin  string res = "";
d47c322001-03-06David Norlin  Token token = peekToken(); switch(token->type) {
35eee72001-01-08David Norlin  case ENDTOKEN: return ""; case TEXTTOKEN: // SHOULD WE KILL EMPTY TEXT LIKE THIS ?? {
d47c322001-03-06David Norlin  string text = token->text;
35eee72001-01-08David Norlin  if (text - "\n" - "\t" - " " == "") {
d47c322001-03-06David Norlin  readToken();
35eee72001-01-08David Norlin  break; } else ; // fall through } case CONTAINERKEYWORD: res += opentag("text") + xmlText() + closetag("text"); break; case ERRORKEYWORD:
2e0d132001-02-05David Norlin  //werror("bosse larsson: %O\n", tokens);
d47c322001-03-06David Norlin  parseError("unknown keyword: @" + token->keyword);
35eee72001-01-08David Norlin  } for (;;) {
d47c322001-03-06David Norlin  token = peekToken(); if (! (<SINGLEKEYWORD, DELIMITERKEYWORD>) [token->type] )
35eee72001-01-08David Norlin  return res;
2e0d132001-02-05David Norlin  string single = 0; array(string) keywords = ({});
35eee72001-01-08David Norlin  res += opentag("group");
d47c322001-03-06David Norlin  while ( (<SINGLEKEYWORD, DELIMITERKEYWORD>) [token->type] ) { string keyword = token->keyword; single = single || (token->type == SINGLEKEYWORD && keyword);
35eee72001-01-08David Norlin  multiset(string) allow = allowedChildren[container];
cdec892001-07-16David Norlin  if (!allow || !allow[keyword]) { string e = sprintf("@%s is not allowed inside @%s", keyword, container); if (allow) e += sprintf(" (allowed children are:%{ @%s%})", indices(allow)); else e += " (no children are allowed)"; parseError(e); }
35eee72001-01-08David Norlin 
2e0d132001-02-05David Norlin  multiset(string) allowGroup = allowGrouping[keyword] || ([]); foreach (keywords, string k) if (!allowGroup[k]) parseError("@" + keyword + " may not be grouped together with @" + k); keywords += ({ keyword });
d47c322001-03-06David Norlin  string arg = token->arg;
35eee72001-01-08David Norlin  res += "<" + keyword; string|mapping(string:string) args = getArgs(keyword, arg); if (mappingp(args)) { foreach(indices(args), string s) res += " " + s + "=\"" + args[s] + "\""; res += "/>"; } else if (stringp(args)) res += ">" + args + "</" + keyword + ">"; else res += "/>";
d47c322001-03-06David Norlin  readToken(); token = peekToken();
35eee72001-01-08David Norlin  }
d47c322001-03-06David Norlin  switch(token->type) {
35eee72001-01-08David Norlin  case TEXTTOKEN:
d47c322001-03-06David Norlin  // SHOULD WE KILL EMPTY TEXT LIKE THIS ??
35eee72001-01-08David Norlin  {
d47c322001-03-06David Norlin  string text = token->text;
35eee72001-01-08David Norlin  if (text - "\n" - "\t" - " " == "") {
d47c322001-03-06David Norlin  readToken();
35eee72001-01-08David Norlin  break; } else ; // fall through } case CONTAINERKEYWORD:
2e0d132001-02-05David Norlin  if (single) parseError("cannot have text after @" + single);
35eee72001-01-08David Norlin  res += opentag("text") + xmlText() + closetag("text"); } res += closetag("group"); } }
d47c322001-03-06David Norlin  static void create(string | array(Token) s, SourcePosition|void position) { if (arrayp(s)) { tokenArr = s; } else { if (!position) throw(({ __FILE__,__LINE__,"position == 0"})); tokenArr = split(s, position); } tokenArr += ({ Token(ENDTOKEN, 0, 0, 0, 0) });
35eee72001-01-08David Norlin  } MetaData getMetaData() { MetaData meta = MetaData();
f4c6ad2001-02-09David Norlin  string scopeModule = 0;
d47c322001-03-06David Norlin  while(peekToken()->type == METAKEYWORD) { Token token = readToken(); string keyword = token->keyword; string arg = token->arg; string endkeyword = 0;
35eee72001-01-08David Norlin  switch (keyword) { case "class": case "module": {
d47c322001-03-06David Norlin  if (endkeyword) parseError("@%s must stand alone", endkeyword);
35eee72001-01-08David Norlin  if (meta->appears) parseError("@appears before @%s", keyword); if (meta->belongs) parseError("@belongs before @%s", keyword); if (meta->type) parseError("@%s can not be combined with @%s", keyword, meta->type); meta->type = keyword;
d47c322001-03-06David Norlin  .PikeParser nameparser = .PikeParser(arg, currentPosition);
2e0d132001-02-05David Norlin  string s = nameparser->readToken();
2a139d2001-02-15David Norlin  if (!isIdent(s) && s != "::") parseError("@%s: expected %s name, got %O", keyword, keyword, s);
ad66622001-02-08David Norlin  if (nameparser->peekToken() == "::")
2a139d2001-02-15David Norlin  if (keyword == "module") s += nameparser->readToken(); else parseError("@class: 'scope::' only allowed as @module name");
ad66622001-02-08David Norlin  if (nameparser->peekToken() != EOF)
2a139d2001-02-15David Norlin  parseError("@%s: expected %s name, got %O", keyword, keyword, arg);
35eee72001-01-08David Norlin  meta->name = s; } break; case "decl": {
d47c322001-03-06David Norlin  if (endkeyword) parseError("@%s must stand alone", endkeyword);
f4c6ad2001-02-09David Norlin  int first = !meta->type;
35eee72001-01-08David Norlin  if (!meta->type) meta->type = "decl"; else if (meta->type != "decl") parseError("@decl can not be combined with @%s", meta->type); if (meta->appears) parseError("@appears before @decl"); if (meta->belongs) parseError("@belongs before @decl"); meta->type = "decl";
d47c322001-03-06David Norlin  .PikeParser declparser = .PikeParser(arg, currentPosition);
c748382001-01-11David Norlin  PikeObject p = declparser->parseDecl(
f4c6ad2001-02-09David Norlin  ([ "allowArgListLiterals" : 1, "allowScopePrefix" : 1 ]) ); // constants/literals + scope::
35eee72001-01-08David Norlin  string s = declparser->peekToken(); if (s != ";" && s != EOF)
2a139d2001-02-15David Norlin  parseError("@decl: expected end of line, got %O", s);
f4c6ad2001-02-09David Norlin  int i = search(p->name, "::"); if (i >= 0) { string scope = p->name[0 .. i + 1]; p->name = p->name[i + 2 ..]; if (!first && scopeModule != scope)
2a139d2001-02-15David Norlin  parseError("@decl's must have identical 'scope::' prefix");
f4c6ad2001-02-09David Norlin  scopeModule = scope; } else if (!first && scopeModule)
2a139d2001-02-15David Norlin  parseError("@decl's must have identical 'scope::' prefix");
35eee72001-01-08David Norlin  meta->decls += ({ p }); } break; case "appears":
d47c322001-03-06David Norlin  if (endkeyword) parseError("@%s must stand alone", endkeyword);
35eee72001-01-08David Norlin  if (meta->type == "class" || meta->type == "decl" || meta->type == "module" || !meta->type) { if (meta->appears) parseError("duplicate @appears"); if (meta->belongs) parseError("both @appears and @belongs");
f4c6ad2001-02-09David Norlin  if (scopeModule)
2a139d2001-02-15David Norlin  parseError("both 'scope::' and @appears");
d47c322001-03-06David Norlin  .PikeParser idparser = .PikeParser(arg, currentPosition);
35eee72001-01-08David Norlin  string s = idparser->parseIdents(); if (!s)
2a139d2001-02-15David Norlin  parseError("@appears: expected identifier, got %O", arg);
35eee72001-01-08David Norlin  meta->appears = s; } else parseError("@appears not allowed here"); break; case "belongs":
d47c322001-03-06David Norlin  if (endkeyword) parseError("@%s must stand alone", endkeyword);
35eee72001-01-08David Norlin  if (meta->type == "class" || meta->type == "decl"
d47c322001-03-06David Norlin  || meta->type == "module" || !meta->type)
35eee72001-01-08David Norlin  { if (meta->belongs) parseError("duplicate @belongs"); if (meta->appears) parseError("both @appears and @belongs");
f4c6ad2001-02-09David Norlin  if (scopeModule)
2a139d2001-02-15David Norlin  parseError("both 'scope::' and @belongs");
d47c322001-03-06David Norlin  .PikeParser idparser = .PikeParser(arg, currentPosition);
35eee72001-01-08David Norlin  string s = idparser->parseIdents(); if (!s && idparser->peekToken() != EOF)
2a139d2001-02-15David Norlin  parseError("@belongs: expected identifier or blank, got %O", arg);
35eee72001-01-08David Norlin  meta->belongs = s || ""; // blank is allowed too, you know .. } break; case "endclass": case "endmodule":
2a139d2001-02-15David Norlin  {
4738e42001-05-08David Norlin  if (meta->type || meta->belongs || meta->appears || endkeyword) parseError("@%s must stand alone", keyword);
d47c322001-03-06David Norlin  meta->type = endkeyword = keyword; .PikeParser nameparser = .PikeParser(arg, currentPosition);
2a139d2001-02-15David Norlin  while (nameparser->peekToken() != EOF) meta->name = (meta->name || "") + nameparser->readToken(); }
35eee72001-01-08David Norlin  break; default: parseError("illegal keyword: @%s", keyword); }
d47c322001-03-06David Norlin  }
f4c6ad2001-02-09David Norlin  if (scopeModule) meta->belongs = scopeModule;
35eee72001-01-08David Norlin  return meta; } string getDoc(string context) { string xml = xmlContainerContents(context);
d47c322001-03-06David Norlin  switch (peekToken()->type) {
35eee72001-01-08David Norlin  case ENDTOKEN: break; case ERRORKEYWORD:
d47c322001-03-06David Norlin  parseError("illegal keyword: @"+ peekToken()->keyword);
35eee72001-01-08David Norlin  default: parseError("expected end of doc comment"); } return xml; } }
9556392001-01-18David Norlin // Each of the arrays in the returned array can be fed to // Parse::create()
d47c322001-03-06David Norlin array(array(Token)) splitDocBlock(string block, SourcePosition position) { array(Token) tokens = split(block, position); array(Token) current = ({ }); array(array(Token)) result = ({ });
9556392001-01-18David Norlin  int prevMeta = 0;
d47c322001-03-06David Norlin  foreach (tokens, Token token) { int meta = token->type == METAKEYWORD;
9556392001-01-18David Norlin  if (meta && !prevMeta && sizeof(current)) { result += ({ current }); current = ({ }); } prevMeta = meta; current += ({ token }); } result += ({ current }); return result; }
35eee72001-01-08David Norlin // This is a class, because you need to examine the meta lines // _before_ you can determine which context to parse the // actual doc lines in. class Parse { inherit DocParserClass; static int state;
d47c322001-03-06David Norlin  static MetaData mMetaData = 0;
35eee72001-01-08David Norlin  static string mDoc = 0; static string mContext = 0;
d47c322001-03-06David Norlin  void create(string | array(Token) s, SourcePosition|void sp) { ::create(s, sp);
dd2d3b2001-01-09David Norlin  state = 0; }
35eee72001-01-08David Norlin  MetaData metadata() {
d47c322001-03-06David Norlin  if (state == 0) { ++state; mMetaData = ::getMetaData(); } return mMetaData;
35eee72001-01-08David Norlin  } string doc(string context) {
d47c322001-03-06David Norlin  if (state == 1) { ++state; mContext == context; mDoc = ::getDoc(context); } else if (state == 0 || state > 1 && mContext != context) return 0; return mDoc;
35eee72001-01-08David Norlin  } }