Class: TaskJuggler::TextParser

Inherits:
Object
  • Object
show all
Includes:
MessageHandler
Defined in:
lib/taskjuggler/TextParser.rb,
lib/taskjuggler/TextParser/Rule.rb,
lib/taskjuggler/TextParser/State.rb,
lib/taskjuggler/TextParser/Pattern.rb,
lib/taskjuggler/TextParser/Scanner.rb,
lib/taskjuggler/TextParser/TokenDoc.rb,
lib/taskjuggler/TextParser/MacroTable.rb,
lib/taskjuggler/TextParser/StackElement.rb,
lib/taskjuggler/TextParser/SourceFileInfo.rb

Overview

StackElement.rb – The TaskJuggler III Project Management Software

Copyright © 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014

by Chris Schlaeger <[email protected]>

This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation.

Direct Known Subclasses

ProjectFileParser, RichTextParser

Defined Under Namespace

Classes: Macro, MacroTable, Pattern, Rule, Scanner, SourceFileInfo, StackElement, State, StateTransition, TextParserResultArray, TokenDoc

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from MessageHandler

#critical, #debug, #fatal, #info

Constructor Details

#initializeTextParser

Create a new TextParser object.



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/taskjuggler/TextParser.rb', line 80

def initialize
  # This Hash will store the ruleset that the parser is operating on.
  @rules = { }
  # Array to hold the token types that the scanner can return.
  @variables = []
  # An list of token types that are not allowed in the current context.
  # For performance reasons we use a hash with the token as key. The value
  # is irrelevant.
  @blockedVariables = {}
  # The currently processed rule.
  @cr = nil

  @states = {}
  # The stack used by the FSM.
  @stack = nil
end

Instance Attribute Details

#rulesObject (readonly)

Returns the value of attribute rules.



77
78
79
# File 'lib/taskjuggler/TextParser.rb', line 77

def rules
  @rules
end

Instance Method Details

#error(id, text, sfi = nil, data = nil) ⇒ Object



220
221
222
223
224
225
226
227
228
229
# File 'lib/taskjuggler/TextParser.rb', line 220

def error(id, text, sfi = nil, data = nil)
  sfi ||= sourceFileInfo
  if @scanner
    # The scanner has some more context information, so we pass the error
    # on to the TextScanner.
    @scanner.error(id, text, sfi, data)
  else
    error(id, text, sfi, data)
  end
end

#initRulesObject

Call all methods that start with ‘rule_’ to initialize the rules.



112
113
114
115
116
117
118
119
120
121
# File 'lib/taskjuggler/TextParser.rb', line 112

def initRules
  methods.each do |m|
    if m[0, 5] == 'rule_'
      # Create a new rule with the suffix of the function name as name.
      newRule(m[5..-1])
      # Call the function.
      send(m)
    end
  end
end

#limitTokenSet(tokenSet) ⇒ Object

Limit the allowed tokens of the scanner to the subset passed by the tokenSet Array.



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/taskjuggler/TextParser.rb', line 99

def limitTokenSet(tokenSet)
  return unless tokenSet

  # Create a copy of all supported variables.
  blockedVariables = @variables.dup
  # Then delete all that are in the limited set.
  blockedVariables.delete_if { |v| tokenSet.include?(v) }
  # And convert the list into a Hash for faster lookups.
  @blockedVariables = {}
  blockedVariables.each { |v| @blockedVariables[v] = true }
end

#newRule(name) ⇒ Object

Add a new rule to the rule set. name must be a unique identifier. The function also sets the class variable @cr to the new rule. Subsequent calls to TextParser#pattern, TextParser#optional or TextParser#repeatable will then implicitly operate on the most recently added rule.



128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/taskjuggler/TextParser.rb', line 128

def newRule(name)
  # Use a symbol instead of a String.
  name = name.intern
  raise "Fatal Error: Rule #{name} already exists" if @rules.has_key?(name)

  if block_given?
    saveCr = @cr
    @rules[name] = @cr = TextParser::Rule.new(name)
    yield
    @cr = saveCr
  else
    @rules[name] = @cr = TextParser::Rule.new(name)
  end
end

#optionalObject

Identify the patterns of the most recently added rule as optional syntax elements.



160
161
162
# File 'lib/taskjuggler/TextParser.rb', line 160

def optional
  @cr.setOptional
end

#parse(ruleName) ⇒ Object

To parse the input this function needs to be called with the name of the rule to start with. It returns the result of the processing function of the top-level parser rule that was specified by ruleName. In case of an error, the result is false.



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/taskjuggler/TextParser.rb', line 197

def parse(ruleName)
  @stack = []
  @@expectedTokens = []
  begin
    result = parseFSM(@rules[ruleName])
  rescue TjException => msg
    if msg.message && !msg.message.empty?
      critical('parse', msg.message)
    end
    return false
  end

  result
end

#pattern(tokens, func = nil) ⇒ Object

Add a new pattern to the most recently added rule. tokens is an array of strings that specify the syntax elements of the pattern. Each token must start with an character that identifies the type of the token. The following types are supported.

  • ! a reference to another rule

  • $ a variable token as delivered by the scanner

  • _ a literal token.

func is a Proc object that is called whenever the parser has completed the processing of this rule.



154
155
156
# File 'lib/taskjuggler/TextParser.rb', line 154

def pattern(tokens, func = nil)
  @cr.addPattern(TextParser::Pattern.new(tokens, func))
end

#repeatableObject

Identify the patterns of the most recently added rule as repeatable syntax elements.



166
167
168
# File 'lib/taskjuggler/TextParser.rb', line 166

def repeatable
  @cr.setRepeatable
end

#sourceFileInfoObject

Return the SourceFileInfo of the TextScanner at the beginning of the currently processed TextParser::Rule. Or return nil if we don’t have a current position.



215
216
217
218
# File 'lib/taskjuggler/TextParser.rb', line 215

def sourceFileInfo
  return @scanner.sourceFileInfo if @stack.nil? || @stack.length <= 1
  @stack.last.firstSourceFileInfo
end

#updateParserTablesObject

This function needs to be called whenever new rules or patterns have been added and before the next call to TextParser#parse. It’s perfectly ok to call this function from within a parse() call as long as the states that are currently on the stack have not been modified.



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/taskjuggler/TextParser.rb', line 174

def updateParserTables
  saveFsmStack
  # Invalidate some cached data.
  @rules.each_value { |rule| rule.flushCache }
  @states = {}
  # Generate the parser states for all patterns of all rules.
  @rules.each_value do |rule|
    rule.generateStates(@rules).each do |s|
      @states[[ s.rule, s.pattern, s.index ]] = s
    end
    checkRule(rule)
  end
  # Compute the transitions between the generated states.
  @states.each_value do |state|
    state.addTransitions(@states, @rules)
  end
  restoreFsmStack
end

#warning(id, text, sfi = nil, data = nil) ⇒ Object



231
232
233
234
235
236
237
238
239
240
# File 'lib/taskjuggler/TextParser.rb', line 231

def warning(id, text, sfi = nil, data = nil)
  sfi ||= sourceFileInfo
  if @scanner
    # The scanner has some more context information, so we pass the
    # warning on to the TextScanner.
    @scanner.warning(id, text, sfi, data)
  else
    warning(id, text, sfi, data)
  end
end