This class can traverse the syntax rules of the ProjectFileParser and extract all documented keywords including their arguments and relations. All this work in done in the contructor. The documentation can then be generated for all found keyword or just a single one. Currently plain text output as well as HTML files are supported.
The constructor is the most important function of this class. It creates a parser object and then traverses all rules and extracts the documented patterns. In a second pass the extracted KeywordDocumentation objects are then cross referenced to capture their relationships. manual is an optional reference to the UserManual object that uses this SyntaxReference.
# File lib/SyntaxReference.rb, line 36 36: def initialize(manual = nil) 37: @manual = manual 38: @messageHandler = MessageHandler.new(true) 39: @parser = ProjectFileParser.new(@messageHandler) 40: @parser.updateParserTables 41: 42: # This hash stores all documented keywords using the keyword as 43: # index. 44: @keywords = {} 45: @parser.rules.each_value do |rule| 46: rule.patterns.each do |pattern| 47: # Only patterns that are documented are of interest. 48: next if pattern.doc.nil? 49: 50: # Make sure each keyword is unique. 51: if @keywords.include?(pattern.keyword) 52: raise "Multiple patterns have the same keyword #{pattern.keyword}" 53: end 54: 55: argDocs = [] 56: # Create a new KeywordDocumentation object and fill-in all extracted 57: # values. 58: kwd = KeywordDocumentation.new(rule, pattern, 59: pattern.to_syntax(argDocs, @parser.rules), argDocs, 60: optionalAttributes(pattern, {}), @manual) 61: @keywords[pattern.keyword] = kwd 62: end 63: end 64: 65: # Make sure all references to other keywords are present. 66: @keywords.each_value do |kwd| 67: kwd.crossReference(@keywords, @parser.rules) 68: end 69: 70: # Figure out whether the keyword describes an inheritable attribute or 71: # not. 72: @keywords.each_value do |kwd| 73: kwd.computeInheritance(@keywords, @parser.rules) 74: end 75: end
Return a sorted Array with all keywords (as String objects).
# File lib/SyntaxReference.rb, line 78 78: def all 79: sorted = @keywords.keys.sort 80: # Register the neighbours with each keyword so we can use this info in 81: # navigation bars. 82: pred = nil 83: sorted.each do |kwd| 84: keyword = @keywords[kwd] 85: pred.successor = keyword if pred 86: keyword.predecessor = pred 87: pred = keyword 88: end 89: end
Generate a documentation for the keyword or an error message. The result is a XML String for known keywords. In case of an error the result is empty but an error message will be send to $stderr.
# File lib/SyntaxReference.rb, line 132 132: def generateHTMLreference(directory, keyword) 133: if checkKeyword(keyword) 134: @keywords[keyword].generateHTML(directory) 135: else 136: '' 137: end 138: end
# File lib/SyntaxReference.rb, line 109 109: def internalReferences 110: references = {} 111: @keywords.each_value do |keyword| 112: (refs = keyword.references.uniq).empty? || 113: references[keyword.keyword] = refs 114: end 115: references 116: end
Generate entries for a TableOfContents for each of the keywords. The entries are appended to the TableOfContents toc. sectionPrefix is the prefix that is used for the chapter numbers. In case we have 20 keywords and sectionPrefix is ‘A’, the keywords will be enumerated ‘A.1’ to ‘A.20’.
# File lib/SyntaxReference.rb, line 96 96: def tableOfContents(toc, sectionPrefix) 97: keywords = all 98: # Set the chapter name to 'Syntax Reference' with a link to the first 99: # keyword. 100: toc.addEntry(TOCEntry.new(sectionPrefix, 'Syntax Reference', keywords[0])) 101: i = 1 102: keywords.each do |keyword| 103: title = @keywords[keyword].title 104: toc.addEntry(TOCEntry.new("#{sectionPrefix}.#{i}", title, keyword)) 105: i += 1 106: end 107: end
Generate a documentation for the keyword or an error message. The result is a multi-line plain text String for known keywords. In case of an error the result is empty but an error message will be send to $stderr.
# File lib/SyntaxReference.rb, line 121 121: def to_s(keyword) 122: if checkKeyword(keyword) 123: @keywords[keyword].to_s 124: else 125: '' 126: end 127: end
For the rule referenced by token all patterns are collected that define the terminal token of each first token of each pattern of the specified rule. The patterns are returned as a hash. For each pattern the hashed boolean value specifies whether the attribute is scenario specific or not.
# File lib/SyntaxReference.rb, line 224 224: def attributes(token, scenarioSpecific) 225: raise "Token #{token} must reference a rule" if token[0] != :reference 226: token = token[1] 227: # Find the matching rule. 228: rule = @parser.rules[token] 229: attrs = {} 230: # Now we look at the first token of each pattern. 231: rule.patterns.each do |pattern| 232: if pattern[0][0] == :literal 233: # If it's a terminal symbol, we found what we are looking for. We add 234: # it to the attrs hash and mark it as non scenario specific. 235: attrs[pattern] = scenarioSpecific 236: elsif pattern[0][0] == :reference && pattern[0][1] == :scenarioIdCol 237: # A reference to the !scenarioId rule marks the next token of the 238: # pattern as a reference to a rule with all scenario specific 239: # attributes. 240: attrs.merge!(attributes(pattern[1], true)) 241: elsif pattern[0][0] == :reference 242: # In case we have a reference to another rule, we just follow the 243: # reference. If the pattern is documented we don't have to follow the 244: # reference. We can use the pattern instead. 245: if pattern.doc.nil? 246: attrs.merge!(attributes(pattern[0], scenarioSpecific)) 247: else 248: attrs[pattern] = scenarioSpecific 249: end 250: else 251: raise "Hit unknown token #{token}" 252: end 253: end 254: attrs 255: end
# File lib/SyntaxReference.rb, line 257 257: def checkKeyword(keyword) 258: if keyword.nil? || @keywords[keyword].nil? 259: unless keyword.nil? 260: $stderr.puts "ERROR: #{keyword} is not a known keyword.\n\n" 261: end 262: # Create list of top-level keywords. 263: kwdStr = '' 264: @keywords.each_value do |kwd| 265: if kwd.contexts.empty? || 266: (kwd.contexts.length == 1 && kwd.contexts[0] == kwd) 267: kwdStr += ', ' unless kwdStr.empty? 268: kwdStr += kwd.keyword 269: end 270: end 271: $stderr.puts "Try one of the following keywords as argument to this " + 272: "program:\n" 273: $stderr.puts "#{kwdStr}" 274: return false 275: end 276: 277: true 278: end
Find optional attributes and return them hashed by the defining pattern.
# File lib/SyntaxReference.rb, line 188 188: def optionalAttributes(pattern, stack) 189: # If we hit an endless recursion we won't find any attributes. So we push 190: # each pattern we process on the 'stack'. If we hit it again, we just 191: # return an empty hash. 192: return {} if stack[pattern] 193: 194: # If we hit a pattern that is documented, we ignore it. 195: return {} if !stack.empty? && pattern.doc 196: 197: # Push pattern onto 'stack'. 198: stack[pattern] = true 199: 200: if pattern[0][1] == '{' && pattern[2][1] == '}' 201: # We have found an optional attribute pattern! 202: return attributes(pattern[1], false) 203: end 204: 205: # If a token of the pattern is a reference, we recursively 206: # follow the reference to the next pattern. 207: pattern.each do |type, name| 208: if type == :reference 209: rule = @parser.rules[name] 210: # Rules with multiple patterns won't lead to attributes. 211: next if rule.patterns.length > 1 212: 213: attrs = optionalAttributes(rule.patterns[0], stack) 214: return attrs unless attrs.empty? 215: end 216: end 217: {} 218: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.