35eee72001-01-08David Norlin //======================================================================== // PARSING OF DOCUMENTATION COMMENTS //======================================================================== inherit "module.pmod"; #define DEB werror("###DocParser.Pike: %d\n", __LINE__);
22923a2001-01-11David Norlin static void parseError(string s, mixed ... args) {
35eee72001-01-08David Norlin  s = sprintf(s, @args);
729d1f2001-01-08Henrik Grubbström (Grubba)  werror(s+"\n");
dd2d3b2001-01-09David Norlin  s = "DocParser error: " + s;
729d1f2001-01-08Henrik Grubbström (Grubba)  throw(({ s, 0 }));
35eee72001-01-08David Norlin } inherit .PikeObjects; 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; int global = 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, "global" : METAKEYWORD, "class" : METAKEYWORD, "endclass" : METAKEYWORD, "module" : METAKEYWORD, "endmodule" : METAKEYWORD, "decl" : METAKEYWORD, "i" : BRACEKEYWORD, "tt" : BRACEKEYWORD, "ref" : BRACEKEYWORD,
2e0d132001-02-05David Norlin  "deprecated" : SINGLEKEYWORD,
35eee72001-01-08David Norlin  "example" : DELIMITERKEYWORD, "note" : DELIMITERKEYWORD,
7d3e642001-02-06David Norlin  "returns" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin  "param" : DELIMITERKEYWORD,
46b75b2001-01-08Henrik Grubbström (Grubba)  "seealso" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin 
fd033d2001-02-06David Norlin  "section" : CONTAINERKEYWORD,
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, "mixed" : CONTAINERKEYWORD, "type" : DELIMITERKEYWORD,
35eee72001-01-08David Norlin  "dl" : CONTAINERKEYWORD, "item" : DELIMITERKEYWORD, ]); mapping(string : array(string)) attributenames = ([ "item" : ({ "name" }), "param" : ({ "name" }), "mapping" : ({ "name" }), "array" : ({ "name" }), "multiset" : ({ "name" }),
141bba2001-02-06Henrik Grubbström (Grubba)  "int" : ({ "name" }), "mixed" : ({ "name" }),
35eee72001-01-08David Norlin ]);
2e0d132001-02-05David Norlin static constant standard = (< "note", "example", "seealso", "deprecated" >);
35eee72001-01-08David Norlin  mapping(string : multiset(string)) allowedChildren = (["_general" : standard, "_method" : (< "param", "returns" >) + standard,
8575a02001-02-06David Norlin  "_variable": standard, "_inherit" : standard,
9b1ad32001-02-06David Norlin  "_class" : standard, "_module" : standard, "_constant" : standard,
35eee72001-01-08David Norlin  "mapping" : (< "member" >), "multiset": (< "index" >), "array" : (< "elem" >),
141bba2001-02-06Henrik Grubbström (Grubba)  "int" : (< "value" >), "mixed" : (< "type" >),
35eee72001-01-08David Norlin  "dl" : (< "item" >), ]);
2e0d132001-02-05David Norlin mapping(string : multiset(string)) allowGrouping = (["param" : (< "param" >),
8a6cdd2001-02-06Henrik Grubbström (Grubba)  "member" : (< "member" >),
2e0d132001-02-05David Norlin ]);
35eee72001-01-08David Norlin  // 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.
2e0d132001-02-05David Norlin mapping(string : function(string, string : string) | function(string, string : mapping(string : string))) argHandlers =
35eee72001-01-08David Norlin ([ "member" : memberArgHandler, "elem" : elemArgHandler, "index" : indexArgHandler,
2e0d132001-02-05David Norlin  "deprecated" : deprArgHandler,
fd033d2001-02-06David Norlin  "section" : sectionArgHandler,
141bba2001-02-06Henrik Grubbström (Grubba)  "type" : typeArgHandler,
35eee72001-01-08David Norlin ]);
22923a2001-01-11David Norlin static string memberArgHandler(string keyword, string arg) { // werror("This is the @member arg handler ");
35eee72001-01-08David Norlin  .PikeParser parser = .PikeParser(arg);
22923a2001-01-11David Norlin  // werror("&&& %O\n", arg);
35eee72001-01-08David Norlin  Type t = parser->parseOrType(); if (!t)
2e0d132001-02-05David Norlin  parseError("@member: expected type, got %O", arg);
22923a2001-01-11David Norlin  // werror("%%%%%% got type == %O\n", t->xml());
35eee72001-01-08David Norlin  string s = parser->parseLiteral() || parser->parseIdents(); if (!s)
2e0d132001-02-05David Norlin  parseError("@member: expected indentifier or literal constant, got %O", arg);
35eee72001-01-08David Norlin  parser->eat(EOF); return xmltag("type", t->xml()) + xmltag("index", xmlquote(s)); }
22923a2001-01-11David Norlin static string elemArgHandler(string keyword, string arg) { // werror("This is the @elem arg handler\n");
35eee72001-01-08David Norlin  .PikeParser parser = .PikeParser(arg); Type t = parser->parseOrType(); if (!t)
2e0d132001-02-05David Norlin  parseError("@elem: expected type, got %O", arg);
729d1f2001-01-08Henrik Grubbström (Grubba)  if (parser->peekToken() == "...") { t = VarargsType(t); parser->eat("..."); }
22923a2001-01-11David Norlin  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); if (s)
35eee72001-01-08David Norlin  if (s2) return xmltag("minindex", xmlquote(s)) + xmltag("maxindex", xmlquote(s2)); else
22923a2001-01-11David Norlin  return xmltag(dots ? "minindex" : "index", xmlquote(s)); else if (s2) return xmltag("maxindex", xmlquote(s2)); else
2e0d132001-02-05David Norlin  parseError("@elem: expected identifier or literal");
35eee72001-01-08David Norlin }
22923a2001-01-11David Norlin static string indexArgHandler(string keyword, string arg) {
2e0d132001-02-05David Norlin  // werror("indexArgHandler\n");
35eee72001-01-08David Norlin  .PikeParser parser = .PikeParser(arg); string s = parser->parseLiteral(); if (!s)
2e0d132001-02-05David Norlin  parseError("@index: expected identifier, got %O", arg);
35eee72001-01-08David Norlin  parser->eat(EOF); return xmltag("value", xmlquote(s)); }
2e0d132001-02-05David Norlin static string deprArgHandler(string keyword, string arg) { .PikeParser parser = .PikeParser(arg); 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(","); } }
fd033d2001-02-06David Norlin static mapping(string : string) sectionArgHandler(string keyword, string arg) { return ([ "name" : arg ]); }
141bba2001-02-06Henrik Grubbström (Grubba) static string typeArgHandler(string keyword, string arg) { // werror("This is the @type arg handler "); .PikeParser parser = .PikeParser(arg); // 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); return xmltag("type", t->xml()); }
22923a2001-01-11David Norlin static mapping(string : string) standardArgHandler(string keyword, string arg)
35eee72001-01-08David Norlin { array(string) args = ({}); arg += "\0"; int i = 0; for (;;) { while (isSpaceChar(arg[i])) ++i; if (!arg[i]) break; if (arg[i] == '"' || arg[i] == '\'') { int quotechar = arg[i]; ++i; int start = i; 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"); else ++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 }); } else { int start = i; while (arg[i] && !isSpaceChar(arg[i])) ++i; args += ({ arg[start .. i - 1] }); } } mapping(string:string) res = ([]); array(string) attrnames = attributenames[keyword]; int attrcount = sizeof(attrnames || ({}) ); if (attrcount < sizeof(args))
1cd4712001-02-06Henrik Grubbström (Grubba)  parseError(sprintf("@%s with too many parameters", keyword));
35eee72001-01-08David Norlin  for (int i = 0; i < sizeof(args); ++i) res[attrnames[i]] = attributequote(args[i]); return res; }
22923a2001-01-11David Norlin static string|mapping(string:string) getArgs(string keyword, string arg) {
35eee72001-01-08David Norlin  return (argHandlers[keyword] || standardArgHandler)(keyword, arg); }
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; } static array(string|array(string)) split(string s) { array(string) lines = (s - "\r" - "@\n") / "\n"; array(string|array(string)) res = ({ }); string currentText = ""; foreach(lines, string line) { array(string) keyword = extractKeyword(line); if (keyword) { if (strlen(currentText)) res += ({ currentText }); currentText = ""; res += ({ keyword }); } else { if (allSpaces(line)) currentText += "\n"; else currentText += line + "\n"; } } if (strlen(currentText)) res += ({ currentText }); return res; }
22923a2001-01-11David Norlin static string xmlNode(string s) { /* now, @xml works like @i & @tt */
35eee72001-01-08David Norlin  s += "\0"; string res = ""; int i = 0; array(string) tagstack = ({ }); int inXML = 0; while (s[i] == '\n') // strip leading empty lines. ++i; while (s[i]) { if (s[i] == '@') { ++i; if (s[i] == '@') { res += "@"; ++i; } else if (s[i] == '[') { // @ref shortcut int j = ++i;
729d1f2001-01-08Henrik Grubbström (Grubba)  multiset(int) forbidden = (<'@','\n'>); int level = 1; while (s[j] && level && !forbidden[s[j]] ) { if (s[j] == ']') { level--; } else if (s[j] == '[') { level++; }
35eee72001-01-08David Norlin  ++j;
729d1f2001-01-08Henrik Grubbström (Grubba)  } if (level) { if (forbidden[s[j]]) { parseError("forbidden character inside @[...]: %O", s[i-2..j]); }
35eee72001-01-08David Norlin  parseError("@[ without matching ]");
729d1f2001-01-08Henrik Grubbström (Grubba)  } res += xmltag("ref", xmlquote(s[i .. j - 2])); i = j;
35eee72001-01-08David Norlin  } 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 == "")
fd033d2001-02-06David Norlin  parseError("expected @keyword{, got %O", s[start .. i]);
35eee72001-01-08David 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
46c5622001-01-11Henrik Grubbström (Grubba)  res += "</p>\n<p>";
35eee72001-01-08David Norlin  } 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; } } if (sizeof(tagstack)) parseError("@" + tagstack[0] + "{ without matching @}");
46c5622001-01-11Henrik Grubbström (Grubba)  return "<p>" + res + "</p>\n";
35eee72001-01-08David Norlin }
22923a2001-01-11David Norlin static class DocParserClass { static array(array(string)|string) tokens;
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 (;;) { switch (getTokenType(tokens[0])) { case TEXTTOKEN: res += xmlNode(tokens[0]); tokens = tokens[1..]; break; case CONTAINERKEYWORD: string keyword = tokens[0][0]; string arg = tokens[0][1]; 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; tokens = tokens[1..]; res += xmlContainerContents(keyword); if (!(arrayp(tokens[0]) && tokens[0][0] == "end" + keyword))
1cd4712001-02-06Henrik Grubbström (Grubba)  parseError(sprintf("@%s without matching @end%s", keyword, keyword));
35eee72001-01-08David Norlin  res += closetag(keyword); tokens = tokens[1..]; break; default: return res; } } }
22923a2001-01-11David Norlin  static string xmlContainerContents(string container) {
35eee72001-01-08David Norlin  string res = ""; switch( getTokenType(tokens[0]) ) { case ENDTOKEN: return ""; case TEXTTOKEN: // SHOULD WE KILL EMPTY TEXT LIKE THIS ?? { string text = tokens[0]; if (text - "\n" - "\t" - " " == "") { tokens = tokens[1..]; 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);
35eee72001-01-08David Norlin  parseError("unknown keyword: @" + tokens[0][0]); } for (;;) {
2e0d132001-02-05David Norlin  if (! (<SINGLEKEYWORD, DELIMITERKEYWORD>) [getTokenType(tokens[0])] )
35eee72001-01-08David Norlin  return res;
2e0d132001-02-05David Norlin  string single = 0; array(string) keywords = ({});
35eee72001-01-08David Norlin  res += opentag("group");
2e0d132001-02-05David Norlin  while ( (<SINGLEKEYWORD, DELIMITERKEYWORD>) [getTokenType(tokens[0])] ) {
35eee72001-01-08David Norlin  string keyword = tokens[0][0];
2e0d132001-02-05David Norlin  single = single || getTokenType(tokens[0]) == SINGLEKEYWORD && keyword;
35eee72001-01-08David Norlin  multiset(string) allow = allowedChildren[container]; if (!allow || !allow[keyword]) parseError("@" + keyword + " is not allowed inside @" + container);
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 });
35eee72001-01-08David Norlin  string arg = tokens[0][1]; 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 += "/>"; tokens = tokens[1..]; } switch(getTokenType(tokens[0])) { case TEXTTOKEN: // SHOULD WE KILL EMPTY TEXT LIKE THIS ?? { string text = tokens[0]; if (text - "\n" - "\t" - " " == "") { tokens = tokens[1..]; 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"); } }
9556392001-01-18David Norlin  static void create(string | array(string|array(string)) s) { tokens = (arrayp(s) ? s : split(s)) + ({ 0 }); // end sentinel
35eee72001-01-08David Norlin  } MetaData getMetaData() { int i = 0; while (arrayp(tokens[i]) && getKeywordType(tokens[i][0]) == METAKEYWORD) ++i; tokens[0 .. i - 1]; MetaData meta = MetaData(); foreach (tokens[0 .. i - 1], [string keyword, string arg]) switch (keyword) { case "class": case "module": { 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; .PikeParser nameparser = .PikeParser(arg);
2e0d132001-02-05David Norlin  string s = nameparser->readToken(); if (!isIdent(s) || nameparser->readToken() != EOF)
35eee72001-01-08David Norlin  parseError("expected %s name, got %O", keyword, s); meta->name = s; } break; case "decl": { 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"; .PikeParser declparser = .PikeParser(arg);
c748382001-01-11David Norlin  PikeObject p = declparser->parseDecl( ([ "allowArgListLiterals" : 1 ]) ); // with constants/literals
35eee72001-01-08David Norlin  string s = declparser->peekToken(); if (s != ";" && s != EOF) parseError("expected end of line, got %O", s); meta->decls += ({ p }); } break; case "global": if (meta->type == "class" || meta->type == "decl" || meta->type == "module" || !meta->type) { if (meta->global) parseError("duplicate @global"); .PikeParser ucko = .PikeParser(arg); if (ucko->peekToken() != EOF) parseError("expected end of line, got %O", arg); meta->global = 1; } else parseError("@global not allowed here"); break; case "appears": 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"); .PikeParser idparser = .PikeParser(arg); string s = idparser->parseIdents(); if (!s) parseError("expected identifier, got %O", arg); meta->appears = s; } else parseError("@appears not allowed here"); break; case "belongs": if (meta->type == "class" || meta->type == "decl" || meta->type == "module" || !meta->type) { if (meta->belongs) parseError("duplicate @belongs"); if (meta->appears) parseError("both @appears and @belongs"); .PikeParser idparser = .PikeParser(arg); string s = idparser->parseIdents(); if (!s && idparser->peekToken() != EOF) parseError("expected identifier or blank, got %O", arg); meta->belongs = s || ""; // blank is allowed too, you know .. } break; case "endclass": case "endmodule": if (i > 1) parseError("@%s must stand alone", keyword); meta->type = keyword;
2e0d132001-02-05David Norlin  .PikeParser nameparser = .PikeParser(arg); string s = nameparser->peekToken(); if (s != EOF) if (isIdent(s)) meta->name = s; else parseError("expected %s name, got %O", keyword, s);
35eee72001-01-08David Norlin  break; default: parseError("illegal keyword: @%s", keyword); } tokens = tokens[i ..]; return meta; } array(array(string)) getMetaDataOLD() { // OLD VERSION // collect all meta info at the start of the block int i = 0; // werror("\n%%%%%% %O\n", tokens); while (arrayp(tokens[i]) && getKeywordType(tokens[i][0]) == METAKEYWORD) ++i; array(array(string)) metadata = tokens[0..i-1]; tokens = tokens[i..]; return metadata; } string getDoc(string context) { string xml = xmlContainerContents(context); switch (getTokenType(tokens[0])) { case ENDTOKEN: break; case ERRORKEYWORD: parseError("illegal keyword: @"+ tokens[0][0]); 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() array(array(string|array(string))) splitDocBlock(string block) { array(string|array(string)) tokens = split(block); array(string|array(string)) current = ({ }); array(array(string|array(string))) result = ({ }); int prevMeta = 0; foreach (tokens, string|array(string) token) { int meta = arrayp(token) && getKeywordType(token[0]) == METAKEYWORD; 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; static MetaData mMetadata = 0; static string mDoc = 0; static string mContext = 0;
dd2d3b2001-01-09David Norlin  SourcePosition sourcePos = 0;
9556392001-01-18David Norlin  void create(string | array(string|array(string)) s, SourcePosition|void sp) {
dd2d3b2001-01-09David Norlin  ::create(s); state = 0; sourcePos = sp; }
35eee72001-01-08David Norlin  MetaData metadata() {
dd2d3b2001-01-09David Norlin  mixed err = catch { if (state == 0) { ++state; mMetadata = ::getMetaData(); } return mMetadata; }; if (sourcePos) throw(({ err[0], sourcePos }));
35eee72001-01-08David Norlin  } string doc(string context) {
dd2d3b2001-01-09David Norlin  mixed err = catch { if (state == 1) { ++state; mContext == context; mDoc = ::getDoc(context); } else if (state == 0 || state > 1 && mContext != context) return 0; return mDoc; }; if (sourcePos) throw(({ err[0], sourcePos }));
35eee72001-01-08David Norlin  } }