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.
# File lib/SyntaxReference.rb, line 31 31: def initialize(manual = nil) 32: @manual = manual 33: @messageHandler = MessageHandler.new(true) 34: @parser = ProjectFileParser.new(@messageHandler) 35: @parser.updateParserTables 36: 37: # This hash stores all documented keywords using the keyword as 38: # index. 39: @keywords = {} 40: @parser.rules.each_value do |rule| 41: rule.patterns.each do |pattern| 42: # Only patterns that are documented are of interest. 43: next if pattern.doc.nil? 44: 45: # Make sure each keyword is unique. 46: if @keywords.include?(pattern.keyword) 47: raise "Multiple patterns have the same keyword #{pattern.keyword}" 48: end 49: 50: argDocs = [] 51: # Create a new KeywordDocumentation object and fill-in all extracted 52: # values. 53: kwd = KeywordDocumentation.new(rule, pattern, 54: pattern.to_syntax(argDocs, @parser.rules), argDocs, 55: optionalAttributes(pattern, {}), @manual) 56: @keywords[pattern.keyword] = kwd 57: end 58: end 59: 60: # Make sure all references to other keywords are present. 61: @keywords.each_value do |kwd| 62: kwd.crossReference(@keywords, @parser.rules) 63: end 64: 65: # Figure out whether the keyword describes an inheritable attribute or 66: # not. 67: @keywords.each_value do |kwd| 68: kwd.computeInheritance(@keywords, @parser.rules) 69: end 70: end
Return a sorted Array with all keywords.
# File lib/SyntaxReference.rb, line 73 73: def all 74: sorted = @keywords.keys.sort 75: # Register the neighbours with each keyword so we can use this info in 76: # navigation bars. 77: pred = nil 78: sorted.each do |kwd| 79: keyword = @keywords[kwd] 80: pred.successor = keyword if pred 81: keyword.predecessor = pred 82: pred = keyword 83: end 84: 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 127 127: def generateHTMLreference(directory, keyword) 128: if checkKeyword(keyword) 129: @keywords[keyword].generateHTML(directory) 130: else 131: '' 132: end 133: end
# File lib/SyntaxReference.rb, line 104 104: def internalReferences 105: references = {} 106: @keywords.each_value do |keyword| 107: (refs = keyword.references.uniq).empty? || 108: references[keyword.keyword] = refs 109: end 110: references 111: 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 91 91: def tableOfContents(toc, sectionPrefix) 92: keywords = all 93: # Set the chapter name to 'Syntax Reference' with a link to the first 94: # keyword. 95: toc.addEntry(TOCEntry.new(sectionPrefix, 'Syntax Reference', keywords[0])) 96: i = 1 97: keywords.each do |keyword| 98: title = @keywords[keyword].title 99: toc.addEntry(TOCEntry.new("#{sectionPrefix}.#{i}", title, keyword)) 100: i += 1 101: end 102: 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 116 116: def to_s(keyword) 117: if checkKeyword(keyword) 118: @keywords[keyword].to_s 119: else 120: '' 121: end 122: 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 220 220: def attributes(token, scenarioSpecific) 221: raise "Token #{token} must reference a rule" if token[0] != !! 222: token = token.slice(1, token.length - 1) 223: # Find the matching rule. 224: rule = @parser.rules[token] 225: attrs = {} 226: # Now we look at the first token of each pattern. 227: rule.patterns.each do |pattern| 228: if pattern[0][0] == __ 229: # If it's a terminal symbol, we found what we are looking for. We add 230: # it to the attrs hash and mark it as non scenario specific. 231: attrs[pattern] = scenarioSpecific 232: elsif pattern[0] == '!scenarioIdCol' 233: # A reference to the !scenarioId rule marks the next token of the 234: # pattern as a reference to a rule with all scenario specific 235: # attributes. 236: attrs.merge!(attributes(pattern[1], true)) 237: elsif pattern[0][0] == !! 238: # In case we have a reference to another rule, we just follow the 239: # reference. If the pattern is documented we don't have to follow the 240: # reference. We can use the pattern instead. 241: if pattern.doc.nil? 242: attrs.merge!(attributes(pattern[0], scenarioSpecific)) 243: else 244: attrs[pattern] = scenarioSpecific 245: end 246: else 247: raise "Hit unknown token #{token}" 248: end 249: end 250: attrs 251: end
# File lib/SyntaxReference.rb, line 253 253: def checkKeyword(keyword) 254: if keyword.nil? || @keywords[keyword].nil? 255: unless keyword.nil? 256: $stderr.puts "ERROR: #{keyword} is not a known keyword.\n\n" 257: end 258: # Create list of top-level keywords. 259: kwdStr = '' 260: @keywords.each_value do |kwd| 261: if kwd.contexts.empty? || 262: (kwd.contexts.length == 1 && kwd.contexts[0] == kwd) 263: kwdStr += ', ' unless kwdStr.empty? 264: kwdStr += kwd.keyword 265: end 266: end 267: $stderr.puts "Try one of the following keywords as argument to this " + 268: "program:\n" 269: $stderr.puts "#{kwdStr}" 270: return false 271: end 272: 273: true 274: end
Find optional attributes and return them hashed by the defining pattern.
# File lib/SyntaxReference.rb, line 183 183: def optionalAttributes(pattern, stack) 184: # If we hit an endless recursion we won't find any attributes. So we push 185: # each pattern we process on the 'stack'. If we hit it again, we just 186: # return an empty hash. 187: return {} if stack[pattern] 188: 189: # If we hit a pattern that is documented, we ignore it. 190: return {} if !stack.empty? && pattern.doc 191: 192: # Push pattern onto 'stack'. 193: stack[pattern] = true 194: 195: if pattern[0] == '_{' && pattern[2] == '_}' 196: # We have found an optional attribute pattern! 197: return attributes(pattern[1], false) 198: end 199: 200: # If a token of the pattern is a reference, we recursively 201: # follow the reference to the next pattern. 202: pattern.each do |token| 203: if token[0] == !! 204: token = token.slice(1, token.length - 1) 205: rule = @parser.rules[token] 206: # Rules with multiple patterns won't lead to attributes. 207: next if rule.patterns.length > 1 208: 209: attrs = optionalAttributes(rule.patterns[0], stack) 210: return attrs unless attrs.empty? 211: end 212: end 213: {} 214: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.