//===================================================================== /// <summary> /// Writes a single entry in the symbol index file. /// </summary> /// <param name="symbolName">the name of the symbol</param> /// <param name="htmlFileName">link to the documentation file for this symbol</param> /// <param name="anchor">link to the documentation file for this symbol</param> /// <param name="type">the type of symbol (class, method, macro, enum, etc.)</param> /// <param name="container">the containing class, or null</param> /// <param name="sourceFileName">the source file in which the symbol was defined</param> /// <param name="prefix">file name prefix: "../" or ""</param> private void WriteSymbolIndexEntry(StreamWriter w, String symbolName, String htmlFileName, String anchor, String type, Modification container, SourceLoc sourceFile, String prefix) { w.Write(Hyperlink(prefix + htmlFileName, anchor, null, "<code><b>" + symbolName + "</b></code>") + " - " + type); if (container != null) w.WriteLine(" of " + Hyperlink(prefix + this.ObjectFileName(container), container.Name)); w.WriteLine(" in " + FileLinks(sourceFile) + "<br>"); // Hyperlink(prefix + this.FileFileName(sourceFile), sourceFile.Name) + "<br>"); }
//===================================================================== /// <summary> /// Merges the given modification into the class. /// </summary> public void MergeModification(Modification mod) { // if (mod.Description != "") // this.Description += "\r\n" + mod.Description; foreach (MethodDef modMethod in mod.Methods) { MethodDef origMethod = this.FindMethod(modMethod.Name); if (origMethod == null) this.Methods.Add(modMethod); else { origMethod.Modifications.Add(modMethod.Source); origMethod.ModificationMods.Add(modMethod); // if (modMethod.Description != "") // origMethod.Description += "\r\n" + modMethod.Description; } } foreach (PropertyDef modProp in mod.Properties) { PropertyDef origProp = this.FindProperty(modProp.Name); if (origProp == null) this.Properties.Add(modProp); else { origProp.Modifications.Add(modProp.Source); origProp.ModificationMods.Add(modProp); } } }
//===================================================================== /// <summary> /// Returns the name of the object documentation file for the given object, /// relative to the OutputDir. /// </summary> private String ObjectFileName(Modification od) { return ObjectDir + "/" + od.FileName + ".html"; }
//===================================================================== /// <summary> /// Constructor. /// </summary> /// <param name="name">the name of the property</param> /// <param name="desc">the comment immediately preceding the property</param> /// <param name="cl">the class or object to which the property belongs</param> /// <param name="file">the source file where the property was found</param> /// <param name="line">the line where the property was found</param> public PropertyDef(String name, String desc, Modification cl, String file, int line) : base(file, line) { this.Name = name; this.Description = desc; this.ClassOrObject = cl; }
//===================================================================== /// <summary> /// Sets an object symbol's filename member such that the filename /// is unique among all object symbols so far, even in file systems /// that are insensitive to case. /// </summary> public void SetObjectFileName(Modification obj) { // start with the object name String fname = obj.Name; String lcname; // keep going until we find a unique name for (int i = 1 ; ; i++) { // get the lower-case version of the name - we'll use this // as the filename for indexing purposes, since we want to // ignore case in order to respect case-insensitive file // systems lcname = fname.ToLower(); // see if this is in the table already bool found = false; foreach (String s in ObjectFileNames) { if (s.CompareTo(lcname) == 0) { found = true; break; } } // if we didn't find it, we can use the current name if (!found) break; // this name collides with a name already in the table; // add a numeric suffix to the base name and try again fname = obj.Name + i; } // add the lower-case name to the file list, so that we'll find // any collisions with future objects added to the table ObjectFileNames.Add(lcname); // remember the mixed-case name as the object filename obj.FileName = fname; }
//===================================================================== /// <summary> /// Processes a line which appears to be a "modify" statement extending an intrinsic class. /// "modify" space identifier /// </summary> private void ProcessModify(String line) { // note: the keyword "modify" has already been parsed Modification mod = new Modification(Path.GetFileName(CurrentFileName), LineNumber); mod.Name = GetNextToken(ref line); mod.Description = LastComment; LastComment = ""; if (mod.Name == "") return; // must not be a modify statement after all // Note: we don't set the name for the object's html file here, // since we want to refer to the file generated for the original // base (pre-'modify') object. We might not have parsed the // base object yet, so we'll have to go back and fix these up // after we've finished parsing the entire library, since // we'll know the correct filenames at that point and can // correlate the modification entries with the base entries // based on the symbol name. SymbolTable.Modifications.Add(mod); CurrClassOrObj = mod; }
//===================================================================== /// <summary> /// Processes a line which appears to define an intrinsic class. /// "intrinsic class" space identifier "'" stuff "'" ":" [identifier List] /// </summary> private void ProcessIntrinsicClass(String line) { // note: the keyboards "intrinsic class" have already been parsed ClassDef cr = new ClassDef(Path.GetFileName(CurrentFileName), LineNumber); cr.Name = GetNextToken(ref line); cr.Description = LastComment; LastComment = ""; if (cr.Name == "") return; // must not be a class after all int n = line.IndexOf(':'); if (n >= 0) { line = line.Substring(n+1); while (line != "") // collect base class names { cr.BaseClasses.Add(GetNextToken(ref line)); GetNextToken(ref line); // throw away comma } } cr.IsIntrinsic = true; SymbolTable.SetObjectFileName(cr); SymbolTable.Classes.Add(cr); this.CurrentSourceFile.Classes.Add(cr); CurrClassOrObj = cr; }
//===================================================================== /// <summary> /// Processes a line which appears to define a grammar object. /// "grammar" identifier "(" identifier ")" ":" [token-list] /// ":" [class-identifier-list] /// </summary> private void ProcessGrammar(String line, String origLine) { // note: the keyword "grammar" has already been parsed ClassDef cr = new ClassDef(Path.GetFileName(CurrentFileName), LineNumber); cr.Name = GetNextToken(ref line); cr.Description = LastComment; cr.IsGrammar = true; String origLine2 = origLine; bool isVerbRule = (GetNextToken(ref origLine2) == "VerbRule"); LastComment = ""; if (cr.Name == "") return; // must not be a class after all // parse the tag, if present if (cr.Name == "(" || GetNextToken(ref line) == "(") { // store the tagged version of the name String tag = GetNextToken(ref line); if (GetNextToken(ref line) != ")") return; if (isVerbRule) cr.Name = "VerbRule"; cr.Name += "(" + tag + ")"; // if we created this with VerbRule, remember this if (isVerbRule) { origLine = origLine.Trim(); int idx = origLine.IndexOf(")"); cr.OrigDef = origLine.Substring(0, idx + 1); } } // check for the ":" if (GetNextToken(ref line) != ":" && !isVerbRule) return; // must not be a grammar statement after all // skip the production token list, which probably spans // multiple lines String rule = ""; for (char qu = '\0' ; ; ) { int idx; bool found = false; // scan to the ":", or to end of line for (idx = 0 ; idx < line.Length ; ++idx) { switch (line[idx]) { case '\\': // skip the next character ++idx; break; case ':': // stop unless in a string if (qu == '\0') found = true; break; case '\'': case '"': if (qu != '\0') { if (qu == line[idx]) qu = '\0'; } else qu = line[idx]; break; } // if we found the ':', stop here if (found) break; } // add up to the end of this scan to the rule if (found) { // found it - add up to the ':' to the rule rule += line.Substring(0, idx).TrimEnd(); // continue from just past the ':' line = line.Substring(idx + 1); // we're done scanning the rule break; } // didn't find it - add this line and a newline to the rule if (line.Trim() != "") rule += line + "\u0001"; // continue to the next line of the rule line = GetNextLine(); } // now parse the class list line = line.Trim(); while (line != "") // collect base class names { // get the token cr.BaseClasses.Add(GetNextToken(ref line)); // check for and skip the comma if (GetNextToken(ref line) != ",") break; } // add the grammar list to the class cr.GrammarRule = rule; SymbolTable.SetObjectFileName(cr); SymbolTable.Classes.Add(cr); this.CurrentSourceFile.Classes.Add(cr); CurrClassOrObj = cr; // get the GrammarProd name - this is just the class name // sans the (tag) part int n; String gpName; if ((n = cr.Name.IndexOf('(')) >= 0) gpName = cr.Name.Substring(0, n); else gpName = cr.Name; // find the existing GrammarProd GrammarProd g = SymbolTable.FindGrammarProd(gpName); // if we didn't find one, create it if (g == null) { g = new GrammarProd(Path.GetFileName(CurrentFileName), LineNumber); g.Name = gpName; SymbolTable.SetObjectFileName(g); SymbolTable.GrammarProds.Add(g); } // add me to the GrammarProd's list of match objects g.MatchObjects.Add(cr); cr.GrammarProdObj = g; //if (isVerbRule) //{ // while (!line.Contains("action")) // { // line = GetNextLine(); // if (line.Contains(";")) // break; // } // if (line.Contains("action")) // { // while (GetNextToken(ref line) != "action") // ; // if (GetNextToken(ref line) != "=") // return; // String action = GetNextToken(ref line); // } //} }
//===================================================================== /// <summary> /// Processes a line which appears to define a class. /// "class" space identifier ":" [identifier List] /// </summary> private void ProcessClass(String line, String origLine) { // note: the keyword "class" has already been parsed ClassDef cr = new ClassDef(Path.GetFileName(CurrentFileName), LineNumber); cr.Name = GetNextToken(ref line); cr.Description = LastComment; LastComment = ""; if (cr.Name == "") return; // must not be a class after all if (GetNextToken(ref line) != ":") return; // must not be a class after all line = line.Trim(); while (line != "") // collect base class names { cr.BaseClasses.Add(GetNextToken(ref line)); // stop when we reach anything other than a comma if (GetNextToken(ref line) != ",") break; } // add the defined-as string to the description if applicable String origLine2 = origLine; String otok = GetNextToken(ref origLine2); if (otok.StartsWith("Define") && (otok.EndsWith("Action") || otok.EndsWith("ActionSub"))) { origLine = origLine.Trim(); int idx = origLine.IndexOf(")"); cr.OrigDef = origLine.Substring(0, idx + 1); cr.IsAction = true; } SymbolTable.SetObjectFileName(cr); SymbolTable.Classes.Add(cr); this.CurrentSourceFile.Classes.Add(cr); CurrClassOrObj = cr; }
//===================================================================== /// <summary> /// Processes a line which appears to define a global object. /// identifier ":" [identifier List] /// </summary> private void ProcessObject(String name, String line, bool isTransient, bool isAction) { // note: the identifer and colon have already been parsed ObjectDef od = new ObjectDef(Path.GetFileName(CurrentFileName), LineNumber); od.Name = name; od.Description = LastComment; od.IsTransient = isTransient; od.isAction = isAction; LastComment = ""; if (od.Name == "") return; // must not be a class after all line = line.Trim(); while (line != "") // collect base class names { String t; t = GetNextToken(ref line); if (t.Contains("Action")) od.isAction = true; od.BaseClasses.Add(t); t = GetNextToken(ref line); if (t != ",") break; } SymbolTable.SetObjectFileName(od); SymbolTable.GlobalObjects.Add(od); this.CurrentSourceFile.GlobalObjects.Add(od); CurrClassOrObj = od; }