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.

Attributes

keywords[R]

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. manual is an optional reference to the UserManual object that uses this SyntaxReference.

    # File lib/SyntaxReference.rb, line 35
35:     def initialize(manual = nil)
36:       @manual = manual
37:       @messageHandler = MessageHandler.new(true)
38:       @parser = ProjectFileParser.new(@messageHandler)
39:       @parser.updateParserTables
40: 
41:       # This hash stores all documented keywords using the keyword as
42:       # index.
43:       @keywords = {}
44:       @parser.rules.each_value do |rule|
45:         rule.patterns.each do |pattern|
46:           #  Only patterns that are documented are of interest.
47:           next if pattern.doc.nil?
48: 
49:           # Make sure each keyword is unique.
50:           if @keywords.include?(pattern.keyword)
51:             raise "Multiple patterns have the same keyword #{pattern.keyword}"
52:           end
53: 
54:           argDocs = []
55:           # Create a new KeywordDocumentation object and fill-in all extracted
56:           # values.
57:           kwd = KeywordDocumentation.new(rule, pattern,
58:                   pattern.to_syntax(argDocs, @parser.rules), argDocs,
59:                   optionalAttributes(pattern, {}), @manual)
60:           @keywords[pattern.keyword] = kwd
61:         end
62:       end
63: 
64:       # Make sure all references to other keywords are present.
65:       @keywords.each_value do |kwd|
66:         kwd.crossReference(@keywords, @parser.rules)
67:       end
68: 
69:       # Figure out whether the keyword describes an inheritable attribute or
70:       # not.
71:       @keywords.each_value do |kwd|
72:         kwd.computeInheritance(@keywords, @parser.rules)
73:       end
74:     end

Public Instance Methods

all() click to toggle source

Return a sorted Array with all keywords (as String objects).

    # File lib/SyntaxReference.rb, line 77
77:     def all
78:       sorted = @keywords.keys.sort
79:       # Register the neighbours with each keyword so we can use this info in
80:       # navigation bars.
81:       pred = nil
82:       sorted.each do |kwd|
83:         keyword = @keywords[kwd]
84:         pred.successor = keyword if pred
85:         keyword.predecessor = pred
86:         pred = keyword
87:       end
88:     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 141
141:     def generateHTMLnavbar(directory, keywords)
142:       html = HTMLDocument.new
143:       head = html.generateHead('TaskJuggler Syntax Reference Navigator')
144:       head << XMLElement.new('base', 'target' => 'display')
145:       html << (body = XMLElement.new('body'))
146: 
147:       body << XMLNamedText.new('Table Of Contents', 'a', 'href' => 'toc.html')
148:       body << XMLElement.new('br')
149: 
150:       normalizedKeywords = {}
151:       keywords.each do |keyword|
152:         normalizedKeywords[@keywords[keyword].title] = keyword
153:       end
154:       letter = nil
155:       letters = []
156:       normalizedKeywords.keys.sort!.each do |normalized|
157:         if normalized[0, 1] != letter
158:           letter = normalized[0, 1]
159:           letters << letter
160:           body << (h = XMLElement.new('h3'))
161:           h << XMLNamedText.new(letter.upcase, 'a', 'name' => letter)
162:         end
163:         keyword = normalizedKeywords[normalized]
164:         body << XMLNamedText.new("#{normalized}", 'a',
165:                                  'href' => "#{keyword}.html")
166:         body << XMLElement.new('br')
167:       end
168: 
169:       html.write(directory + 'navbar.html')
170: 
171:       html = HTMLDocument.new
172:       head = html.generateHead('TaskJuggler Syntax Reference Navigator')
173:       head << XMLElement.new('base', 'target' => 'navigator')
174:       html << (body = XMLElement.new('body'))
175: 
176:       body << (h3 = XMLElement.new('h3'))
177:       letters.each do |l|
178:         h3 << XMLNamedText.new(l.upcase, 'a',
179:                                'href' => "navbar.html##{l}")
180:       end
181:       html.write(directory + 'alphabet.html')
182:     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 131
131:     def generateHTMLreference(directory, keyword)
132:       if checkKeyword(keyword)
133:         @keywords[keyword].generateHTML(directory)
134:       else
135:         ''
136:       end
137:     end
internalReferences() click to toggle source
     # File lib/SyntaxReference.rb, line 108
108:     def internalReferences
109:       references = {}
110:       @keywords.each_value do |keyword|
111:         (refs = keyword.references.uniq).empty? ||
112:           references[keyword.keyword] = refs
113:       end
114:       references
115:     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 95
 95:     def tableOfContents(toc, sectionPrefix)
 96:       keywords = all
 97:       # Set the chapter name to 'Syntax Reference' with a link to the first
 98:       # keyword.
 99:       toc.addEntry(TOCEntry.new(sectionPrefix, 'Syntax Reference', keywords[0]))
100:       i = 1
101:       keywords.each do |keyword|
102:         title = @keywords[keyword].title
103:         toc.addEntry(TOCEntry.new("#{sectionPrefix}.#{i}", title, keyword))
104:         i += 1
105:       end
106:     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 120
120:     def to_s(keyword)
121:       if checkKeyword(keyword)
122:         @keywords[keyword].to_s
123:       else
124:         ''
125:       end
126:     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 223
223:     def attributes(token, scenarioSpecific)
224:       raise "Token #{token} must reference a rule" if token[0] != :reference
225:       token = token[1]
226:       # Find the matching rule.
227:       rule = @parser.rules[token]
228:       attrs = {}
229:       # Now we look at the first token of each pattern.
230:       rule.patterns.each do |pattern|
231:         if pattern[0][0] == :literal
232:           # If it's a terminal symbol, we found what we are looking for. We add
233:           # it to the attrs hash and mark it as non scenario specific.
234:           attrs[pattern] = scenarioSpecific
235:         elsif pattern[0][0] == :reference && pattern[0][1] == :scenarioIdCol
236:           # A reference to the !scenarioId rule marks the next token of the
237:           # pattern as a reference to a rule with all scenario specific
238:           # attributes.
239:           attrs.merge!(attributes(pattern[1], true))
240:         elsif pattern[0][0] == :reference
241:           # In case we have a reference to another rule, we just follow the
242:           # reference. If the pattern is documented we don't have to follow the
243:           # reference. We can use the pattern instead.
244:           if pattern.doc.nil?
245:             attrs.merge!(attributes(pattern[0], scenarioSpecific))
246:           else
247:             attrs[pattern] = scenarioSpecific
248:           end
249:         else
250:           raise "Hit unknown token #{token}"
251:         end
252:       end
253:       attrs
254:     end
checkKeyword(keyword) click to toggle source
     # File lib/SyntaxReference.rb, line 256
256:     def checkKeyword(keyword)
257:       if keyword.nil? || @keywords[keyword].nil?
258:         unless keyword.nil?
259:           $stderr.puts "ERROR: #{keyword} is not a known keyword.\n\n"
260:         end
261:         # Create list of top-level keywords.
262:         kwdStr = ''
263:         @keywords.each_value do |kwd|
264:           if kwd.contexts.empty? ||
265:              (kwd.contexts.length == 1 && kwd.contexts[0] == kwd)
266:             kwdStr += ', ' unless kwdStr.empty?
267:             kwdStr += kwd.keyword
268:           end
269:         end
270:         $stderr.puts "Try one of the following keywords as argument to this " +
271:                      "program:\n"
272:         $stderr.puts "#{kwdStr}"
273:         return false
274:       end
275: 
276:       true
277:     end
optionalAttributes(pattern, stack) click to toggle source

Find optional attributes and return them hashed by the defining pattern.

     # File lib/SyntaxReference.rb, line 187
187:     def optionalAttributes(pattern, stack)
188:       # If we hit an endless recursion we won't find any attributes. So we push
189:       # each pattern we process on the 'stack'. If we hit it again, we just
190:       # return an empty hash.
191:       return {} if stack[pattern]
192: 
193:       # If we hit a pattern that is documented, we ignore it.
194:       return {} if !stack.empty? && pattern.doc
195: 
196:       # Push pattern onto 'stack'.
197:       stack[pattern] = true
198: 
199:       if pattern[0][1] == '{' && pattern[2][1] == '}'
200:         # We have found an optional attribute pattern!
201:         return attributes(pattern[1], false)
202:       end
203: 
204:       # If a token of the pattern is a reference, we recursively
205:       # follow the reference to the next pattern.
206:       pattern.each do |type, name|
207:         if type == :reference
208:           rule = @parser.rules[name]
209:           # Rules with multiple patterns won't lead to attributes.
210:           next if rule.patterns.length > 1
211: 
212:           attrs = optionalAttributes(rule.patterns[0], stack)
213:           return attrs unless attrs.empty?
214:         end
215:       end
216:       {}
217:     end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.