[svn] r7134: nemerle/trunk/ncc/hierarchy/XmlDump.n
malekith
svnadmin at nemerle.org
Fri Dec 22 01:11:19 CET 2006
Log:
More inteligent parsing of XML comments. Fully implement detection or auto <summary>/<remarks>, add <para> at empty lines. Can use </> as a shortcut for tag closing. No other extensions implemented yet.
Author: malekith
Date: Fri Dec 22 01:11:18 2006
New Revision: 7134
Modified:
nemerle/trunk/ncc/hierarchy/XmlDump.n
Modified: nemerle/trunk/ncc/hierarchy/XmlDump.n
==============================================================================
--- nemerle/trunk/ncc/hierarchy/XmlDump.n (original)
+++ nemerle/trunk/ncc/hierarchy/XmlDump.n Fri Dec 22 01:11:18 2006
@@ -31,6 +31,7 @@
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
+using Nemerle.Utility;
using System.Xml;
using System.Text;
@@ -95,65 +96,229 @@
}
}
- parse_comment (comment : string) : XmlNode
+ variant XToken
+ {
+ | EmptyLine
+ | EOF
+ | Text { t : string; }
+ | XmlStart { n : string; v : string; }
+ | XmlEnd { n : string; }
+ | WhiteSpace { w : string }
+ }
+
+ static tokenize (comment : string) : array [XToken]
{
def lines = comment.Split (array ['\n']);
mutable all_stars = true;
def white_space = array [' ', '\t', '\r', '\n'];
+ def res = System.Collections.Generic.List ();
for (mutable i = 0; i < lines.Length; ++i) {
- lines [i] = lines [i].Trim (white_space);
- when (i > 0 && lines[i] != "" && !lines [i].StartsWith ("*"))
+ lines[i] = lines[i].TrimEnd (white_space);
+ when (i > 0 &&
+ lines[i].Trim (white_space) != "" &&
+ !lines [i].TrimStart (white_space).StartsWith ("*"))
all_stars = false;
}
when (all_stars)
for (mutable i = 1; i < lines.Length; ++i)
- when (lines [i] != "")
- lines [i] = lines [i].Substring (1).Trim (white_space);
+ when (lines [i].Trim (white_space) != "")
+ lines [i] = lines [i].TrimStart (white_space).Substring (1);
- def sb = StringBuilder ("<summary>");
- mutable summary = true;
- mutable remarks = false;
- mutable para = false;
+ def buf = string.Join ("\n", lines) + "\n";
mutable i = 0;
- while (i < lines.Length && lines [i] == "")
- ++i;
+ while (i < buf.Length)
+ {
+ def start = i;
+
+ def space_tab (ch) { ch == ' ' || ch == '\t' }
+ def scan (pred) {
+ while (i < buf.Length && pred (buf [i])) i++;
+ buf.Substring (start, i - start)
+ }
+
+ if (space_tab (buf [i]))
+ res.Add (XToken.WhiteSpace (scan (space_tab)));
+ else if (buf [i] == '\n')
+ match (scan (_ == '\n')) {
+ | "\n" =>
+ res.Add (XToken.WhiteSpace ("\n"));
+ | s =>
+ res.Add (XToken.WhiteSpace (s));
+ res.Add (XToken.EmptyLine ());
+ }
+ else if (buf [i] == '<') {
+ def v = scan (_ != '>');
+ if (i == buf.Length)
+ // location?
+ Message.Warning ("unfinished XML tag");
+ else i++;
+ def v = v.Substring (1).Trim (white_space);
+ if (v.StartsWith ("/"))
+ res.Add (XToken.XmlEnd (v.Substring (1)));
+ else {
+ def idx = v.IndexOfAny (array [' ', '\t']);
+ if (idx == -1)
+ if (v.EndsWith ("/")) {
+ def v = v.Substring (0, v.Length - 1);
+ res.Add (XToken.XmlStart (v, ""));
+ res.Add (XToken.XmlEnd (v));
+ }
+ else
+ res.Add (XToken.XmlStart (v, ""));
+ else {
+ def name = v.Substring (0, idx);
+ def args = v.Substring (idx);
+ if (args.EndsWith ("/")) {
+ def args = args.Substring (0, args.Length - 1);
+ res.Add (XToken.XmlStart (name, args));
+ res.Add (XToken.XmlEnd (name));
+ } else
+ res.Add (XToken.XmlStart (name, args));
+ }
+ }
+ } else
+ res.Add (XToken.Text (scan (c => c != '<' && c != '\n')));
+ }
+
+ res.Add (XToken.EOF ());
+ res.ToArray ();
+ }
+
+ /*
+ Top-level tags:
+ <remarks>, <summary>, <example>, <exception>, <param>, <permission>,
+ <returns>, <seealso>, <include>, <value>
+ Other tags:
+ <c>, <code>, <list>, <listheader>, <item>, <term>, <description>,
+ <para>, <paramref>, <see>
+ */
+
+ parse_comment (comment : string) : XmlNode
+ {
+ def is_top_tag (_) {
+ | "summary" | "remarks" | "example" | "exception" | "param"
+ | "permission" | "returns" | "seealso" | "include" | "value" => true
+ | _ => false
+ }
+
+ def is_text_holding (_) {
+ | "para" | "code" | "list" => true
+ | _ => false
+ }
+
+ def tokens = tokenize (comment).ToList ().GetEnumerator ();
+ // stack of currently active tags, along with information if
+ // they were user-supplied
+ mutable tags = [];
+ def sb = StringBuilder ("");
+
+ def skip_ws () {
+ match (tokens.Current) {
+ | WhiteSpace | EmptyLine =>
+ _ = tokens.MoveNext (); skip_ws ()
+ | _ => {}
+ }
+ }
- for (; i < lines.Length; ++i) {
- def line = lines [i];
- if (line == "") {
- if (summary) {
- _ = sb.Append ("</summary>");
- summary = false;
- } else if (para) {
- _ = sb.Append ("</para>");
- para = false;
- } else {}
- } else {
- when (!summary && !remarks) {
- _ = sb.Append ("<remarks>");
- remarks = true;
- }
-/*
- when (!summary && !para) {
- _ = sb.Append ("<para>");
- para = true;
- }
-*/
- _ = sb.Append (line).Append ('\n');
+ def output (_ : XToken) {
+ | EmptyLine
+ | EOF => {}
+ | WhiteSpace (t)
+ | Text (t) => _ = sb.Append (t)
+ | XmlStart (n, v) => _ = sb.Append ("<" + n + v + ">");
+ | XmlEnd (n) => _ = sb.Append ("</" + n + ">");
}
+
+ def output_and_move ()
+ {
+ output (tokens.Current);
+ _ = tokens.MoveNext ();
+ }
+
+ def possibly_close (pred)
+ {
+ match (tags) {
+ | (name, false) :: xs when pred (name) =>
+ tags = xs;
+ output (XToken.XmlEnd (name));
+ | _ => {}
+ }
+ }
+
+ def open_fake (name)
+ {
+ tags ::= (name, false);
+ output (XToken.XmlStart (name, ""));
}
- when (para)
- _ = sb.Append ("</para>");
- when (summary)
- _ = sb.Append ("</summary>");
- when (remarks)
- _ = sb.Append ("</remarks>");
+ _ = tokens.MoveNext ();
+ skip_ws ();
+
+ match (tokens.Current) {
+ | XmlStart (n, _) when is_top_tag (n) =>
+ output_and_move ();
+ skip_ws ();
+ tags ::= (n, true);
+ | _ =>
+ open_fake ("summary")
+ }
+
+ while (! (tokens.Current is XToken.EOF))
+ {
+ def t = tokens.Current;
+ match (t) {
+ | EOF => {}
+
+ | EmptyLine =>
+ possibly_close (_ == "para");
+ possibly_close (_ == "summary");
+
+ | Text =>
+ // TODO: for <code> we should strip some common prefix
+ when (!tags.Exists ((n, _) => is_top_tag (n)))
+ open_fake ("remarks");
+ when (!tags.Exists ((n, _) => is_text_holding (n)))
+ open_fake ("para");
+ output (t);
+
+ | XmlStart (n, _) =>
+ when (is_text_holding (n) || is_top_tag (n))
+ possibly_close (_ == "para");
+ when (is_top_tag (n))
+ // any auto summary/remarks
+ possibly_close (is_top_tag);
+ tags ::= (n, true);
+ output (t);
+
+ | XmlEnd (n) =>
+ // close any auto tags at the top
+ while (tags is (_, false) :: _)
+ possibly_close (_ => true);
+ match (tags) {
+ | (n', true) :: xs when n' == n || n == "" =>
+ tags = xs;
+ output (XToken.XmlEnd (n'));
+ | _ =>
+ Message.Warning ($"trying to close XML tag <$n> that is not open")
+ }
+
+ | WhiteSpace =>
+ output (t)
+ }
+ _ = tokens.MoveNext ();
+ }
+
+ while (tags is (_, false) :: _)
+ possibly_close (_ => true);
+
+ foreach ((n, true) in tags)
+ Message.Warning ($"unclosed XML tag <$n>");
def frag = document.CreateDocumentFragment ();
+ //Message.Debug ($"comment: $sb");
frag.InnerXml = sb.ToString ();
frag
}
@@ -170,15 +335,18 @@
acc
}
+ Util.locate (curr, {
def comment = Comments.Fold (null, choose);
when (comment != null && comment != "") {
try {
_ = x.AppendChild (parse_comment (comment));
}
catch {
- | e is XmlException => Message.Warning (curr, "Cannot parse XML in comment: " + e.Message);
+ | e is XmlException =>
+ Message.Warning ("Cannot parse XML in comment: " + e.Message);
}
}
+ });
last_loc = curr;
}
More information about the svn
mailing list