class TaskJuggler::SyntaxReference
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.
Attributes
Public Class Methods
Source
# File lib/taskjuggler/SyntaxReference.rb, line 36 def initialize(manual = nil, ignoreOld = false) @manual = manual @parser = ProjectFileParser.new @parser.updateParserTables # This hash stores all documented keywords using the keyword as # index. @keywords = {} @parser.rules.each_value do |rule| rule.patterns.each do |pattern| # Only patterns that are documented are of interest. next if pattern.doc.nil? # Ignore deprecated and removed keywords if requested next if ignoreOld && [ :deprecated, :removed ].include?(pattern.supportLevel) # Make sure each keyword is unique. if @keywords.include?(pattern.keyword) raise "Multiple patterns have the same keyword #{pattern.keyword}" end argDocs = [] # Create a new KeywordDocumentation object and fill-in all extracted # values. kwd = KeywordDocumentation.new(rule, pattern, pattern.to_syntax(argDocs, @parser.rules), argDocs, optionalAttributes(pattern, {}), @manual) @keywords[pattern.keyword] = kwd end end # Make sure all references to other keywords are present. @keywords.each_value do |kwd| kwd.crossReference(@keywords, @parser.rules) end # Figure out whether the keyword describes an inheritable attribute or # not. @keywords.each_value do |kwd| kwd.computeInheritance end end
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
.
Public Instance Methods
Source
# File lib/taskjuggler/SyntaxReference.rb, line 80 def all sorted = @keywords.keys.sort # Register the neighbours with each keyword so we can use this info in # navigation bars. pred = nil sorted.each do |kwd| keyword = @keywords[kwd] pred.successor = keyword if pred keyword.predecessor = pred pred = keyword end end
Return a sorted Array with all keywords (as String
objects).
Source
# File lib/taskjuggler/SyntaxReference.rb, line 134 def generateHTMLreference(directory, keyword) if checkKeyword(keyword) @keywords[keyword].generateHTML(directory) else '' end 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.
Source
# File lib/taskjuggler/SyntaxReference.rb, line 111 def internalReferences references = {} @keywords.each_value do |keyword| (refs = keyword.references.uniq).empty? || references[keyword.keyword] = refs end references end
Source
# File lib/taskjuggler/SyntaxReference.rb, line 98 def tableOfContents(toc, sectionPrefix) keywords = all # Set the chapter name to 'Syntax Reference' with a link to the first # keyword. toc.addEntry(TOCEntry.new(sectionPrefix, 'Syntax Reference', keywords[0])) i = 1 keywords.each do |keyword| title = @keywords[keyword].title toc.addEntry(TOCEntry.new("#{sectionPrefix}.#{i}", title, keyword)) i += 1 end 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’.
Source
# File lib/taskjuggler/SyntaxReference.rb, line 123 def to_s(keyword) if checkKeyword(keyword) @keywords[keyword].to_s else '' end 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.
Private Instance Methods
Source
# File lib/taskjuggler/SyntaxReference.rb, line 240 def attributes(token, scenarioSpecific) raise "Token #{token} must reference a rule" if token[0] != :reference token = token[1] # Find the matching rule. rule = @parser.rules[token] attrs = {} # Now we look at the first token of each pattern. rule.patterns.each do |pattern| if pattern[0][0] == :literal # If it's a terminal symbol, we found what we are looking for. We add # it to the attrs hash and mark it as non scenario specific. attrs[pattern] = scenarioSpecific elsif pattern[0][0] == :reference && pattern[0][1] == :scenarioIdCol # A reference to the !scenarioId rule marks the next token of the # pattern as a reference to a rule with all scenario specific # attributes. attrs.merge!(attributes(pattern[1], true)) elsif pattern[0][0] == :reference # In case we have a reference to another rule, we just follow the # reference. If the pattern is documented we don't have to follow the # reference. We can use the pattern instead. if pattern.doc.nil? attrs.merge!(attributes(pattern[0], scenarioSpecific)) else attrs[pattern] = scenarioSpecific end else raise "Hit unknown token #{token}" end end attrs 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.
Source
# File lib/taskjuggler/SyntaxReference.rb, line 273 def checkKeyword(keyword) if keyword.nil? || @keywords[keyword].nil? unless keyword.nil? $stderr.puts "ERROR: #{keyword} is not a known keyword.\n\n" end # Create list of top-level keywords. kwdStr = '' @keywords.each_value do |kwd| if kwd.contexts.empty? || (kwd.contexts.length == 1 && kwd.contexts[0] == kwd) kwdStr += ', ' unless kwdStr.empty? kwdStr += kwd.keyword end end $stderr.puts "Try one of the following keywords as argument to this " + "program:\n" $stderr.puts "#{kwdStr}" return false end true end
Source
# File lib/taskjuggler/SyntaxReference.rb, line 204 def optionalAttributes(pattern, stack) # If we hit an endless recursion we won't find any attributes. So we push # each pattern we process on the 'stack'. If we hit it again, we just # return an empty hash. return {} if stack[pattern] # If we hit a pattern that is documented, we ignore it. return {} if !stack.empty? && pattern.doc # Push pattern onto 'stack'. stack[pattern] = true if pattern[0][1] == '{' && pattern[2][1] == '}' # We have found an optional attribute pattern! return attributes(pattern[1], false) end # If a token of the pattern is a reference, we recursively # follow the reference to the next pattern. pattern.each do |type, name| if type == :reference rule = @parser.rules[name] # Rules with multiple patterns won't lead to attributes. next if rule.patterns.length > 1 attrs = optionalAttributes(rule.patterns[0], stack) return attrs unless attrs.empty? end end {} end
Find optional attributes and return them hashed by the defining pattern.