class TaskJuggler::TextParser::Rule
The TextParserRule holds the basic elment of the syntax description. Each rule has a name and a set of patterns. The parser uses these rules to parse the input files. The first token of a pattern must resolve to a terminal token. The resolution can run transitively over a set of rules. The first tokens of each pattern of a rule must resolve to a terminal symbol and all terminals must be unique in the scope that they appear in. The parser uses this first token to select the next pattern it uses for the syntactical analysis. A rule can be marked as repeatable and/or optional. In this case the syntax element described by the rule may occur 0 or multiple times in the parsed file.
Attributes
Public Class Methods
Create a new syntax rule called name
.
# File lib/taskjuggler/TextParser/Rule.rb, line 33 def initialize(name) @name = name @patterns = [] @repeatable = false @optional = false @keyword = nil flushCache end
Public Instance Methods
Add a new pattern
to the Rule
. It should be of type TextParser::Pattern
.
# File lib/taskjuggler/TextParser/Rule.rb, line 52 def addPattern(pattern) @patterns << pattern end
Return a Hash of all state transitions caused by the 1st token of each pattern of this rule.
# File lib/taskjuggler/TextParser/Rule.rb, line 100 def addTransitionsToState(states, rules, stateStack, sourceState, loopBack) @patterns.each do |pattern| pattern.addTransitionsToState(states, rules, stateStack.dup, sourceState, self, 0, loopBack) end end
# File lib/taskjuggler/TextParser/Rule.rb, line 182 def dump puts "Rule: #{name} #{@optional ? "[optional]" : ""} " + "#{@repeatable ? "[repeatable]" : ""}" @patterns.length.times do |i| puts " Pattern: \"#{@patterns[i]}\"" unless @transitions[i] puts "No transitions for this pattern!" next end @transitions[i].each do |key, rule| if key[0] == ?_ token = "\"" + key.slice(1, key.length - 1) + "\"" else token = key.slice(1, key.length - 1) end puts " #{token} -> #{rule.name}" end end puts end
# File lib/taskjuggler/TextParser/Rule.rb, line 43 def flushCache # A rule is considered to describe optional tokens in case the @optional # flag is set or all of the patterns reference optional rules again. # This variable caches the transitively determined optional value. @transitiveOptional = nil end
# File lib/taskjuggler/TextParser/Rule.rb, line 86 def generateStates(rules) # First, add an entry State for this rule. Entry states are never # reached by normal state transitions. They are only used as (re-)start # states. states = [ State.new(self) ] @patterns.each do |pattern| states += pattern.generateStates(self, rules) end states end
# File lib/taskjuggler/TextParser/Rule.rb, line 56 def include?(token) @patterns.each { |p| return true if p[0][1] == token } false end
Return true if the rule describes optional elements. The evaluation recursively descends into the pattern if necessary and stores the result to be reused for later calls.
# File lib/taskjuggler/TextParser/Rule.rb, line 69 def optional?(rules) # If we have a cached result, use this. return @transitiveOptional if @transitiveOptional # If the rule is marked optional, then it is optional. if @optional return @transitiveOptional = true end # If all patterns describe optional content, then this rule is optional # as well. @transitiveOptional = true @patterns.each do |pat| return @transitiveOptional = false unless pat.optional?(rules) end end
Return a reference the pattern of this Rule
.
# File lib/taskjuggler/TextParser/Rule.rb, line 156 def pattern(idx) @patterns[idx] end
Add a description for a pattern element of the last added pattern.
# File lib/taskjuggler/TextParser/Rule.rb, line 124 def setArg(idx, doc) raise 'No pattern defined yet' if @patterns.empty? @patterns[-1].setArg(idx, doc) end
Add a description for the syntax elements of this Rule
. doc
is a RichText and keyword
is a unique name of this Rule
. To avoid ambiguouties, an optional scope can be appended, separated by a dot (E.g. name.scope).
# File lib/taskjuggler/TextParser/Rule.rb, line 118 def setDoc(keyword, doc) raise 'No pattern defined yet' if @patterns.empty? @patterns[-1].setDoc(keyword, doc) end
Add a reference to a code example. file
is the name of the file. tag
is a tag within the file that specifies a part of this file.
# File lib/taskjuggler/TextParser/Rule.rb, line 151 def setExample(file, tag) @patterns[-1].setExample(file, tag) end
Specify the index idx
of the last token to be used for the syntax documentation. All subsequent tokens will be ignored.
# File lib/taskjuggler/TextParser/Rule.rb, line 131 def setLastSyntaxToken(idx) raise 'No pattern defined yet' if @patterns.empty? raise 'Token index too large' if idx >= @patterns[-1].tokens.length @patterns[-1].setLastSyntaxToken(idx) end
Mark the rule as an optional element of the syntax.
# File lib/taskjuggler/TextParser/Rule.rb, line 62 def setOptional @optional = true end
Mark the syntax element described by this Rule
as a repeatable element that can occur once or more times in sequence.
# File lib/taskjuggler/TextParser/Rule.rb, line 110 def setRepeatable @repeatable = true end
Add a reference to another rule for documentation purposes.
# File lib/taskjuggler/TextParser/Rule.rb, line 144 def setSeeAlso(also) raise 'No pattern defined yet' if @patterns.empty? @patterns[-1].setSeeAlso(also) end
Specify the support level of the current pattern.
# File lib/taskjuggler/TextParser/Rule.rb, line 138 def setSupportLevel(level) raise 'No pattern defined yet' if @patterns.empty? @patterns[-1].setSupportLevel(level) end
# File lib/taskjuggler/TextParser/Rule.rb, line 160 def to_syntax(stack, docs, rules, skip) str = '' str << '[' if @optional || @repeatable str << '(' if @patterns.length > 1 first = true pStr = '' @patterns.each do |pat| if first first = false else pStr << ' | ' end pStr << pat.to_syntax_r(stack, docs, rules, skip) end return '' if pStr == '' str << pStr str << '...' if @repeatable str << ')' if @patterns.length > 1 str << ']' if @optional || @repeatable str end