Parent

Class Index [+]

Quicksearch

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.

Public Class Methods

new(manual = nil) click to toggle source

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

Public Instance Methods

all() click to toggle source

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
generateHTMLnavbar(directory, keywords) click to toggle source

Generate 2 files named navbar.html and alphabet.html. They are used to support navigating through the syntax reference.

     # File lib/SyntaxReference.rb, line 137
137:     def generateHTMLnavbar(directory, keywords)
138:       html = HTMLDocument.new
139:       head = html.generateHead('TaskJuggler Syntax Reference Navigator')
140:       head << XMLElement.new('base', 'target' => 'display')
141:       html << (body = XMLElement.new('body'))
142: 
143:       body << XMLNamedText.new('Table Of Contents', 'a', 'href' => 'toc.html')
144:       body << XMLElement.new('br')
145: 
146:       normalizedKeywords = {}
147:       keywords.each do |keyword|
148:         normalizedKeywords[@keywords[keyword].title] = keyword
149:       end
150:       letter = nil
151:       letters = []
152:       normalizedKeywords.keys.sort!.each do |normalized|
153:         if normalized[0, 1] != letter
154:           letter = normalized[0, 1]
155:           letters << letter
156:           body << (h = XMLElement.new('h3'))
157:           h << XMLNamedText.new(letter.upcase, 'a', 'name' => letter)
158:         end
159:         keyword = normalizedKeywords[normalized]
160:         body << XMLNamedText.new("#{normalized}", 'a',
161:                                  'href' => "#{keyword}.html")
162:         body << XMLElement.new('br')
163:       end
164: 
165:       html.write(directory + 'navbar.html')
166: 
167:       html = HTMLDocument.new
168:       head = html.generateHead('TaskJuggler Syntax Reference Navigator')
169:       head << XMLElement.new('base', 'target' => 'navigator')
170:       html << (body = XMLElement.new('body'))
171: 
172:       body << (h3 = XMLElement.new('h3'))
173:       letters.each do |l|
174:         h3 << XMLNamedText.new(l.upcase, 'a',
175:                                'href' => "navbar.html##{l}")
176:       end
177:       html.write(directory + 'alphabet.html')
178:     end
generateHTMLreference(directory, keyword) click to toggle source

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
internalReferences() click to toggle source
     # 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
tableOfContents(toc, sectionPrefix) click to toggle source

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
to_s(keyword) click to toggle source

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

Private Instance Methods

attributes(token, scenarioSpecific) click to toggle source

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
checkKeyword(keyword) click to toggle source
     # 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
optionalAttributes(pattern, stack) click to toggle source

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.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.