Class: Ntxt::Parser

Inherits:
Object
  • Object
show all
Defined in:
lib/ntxt/parser.rb

Overview

The parser for Ntxt. Most of this a typical user will not find useful with the exception of Parser.parse.

Defined Under Namespace

Classes: State

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extractTags(block, line) ⇒ Object

Extract all the tags from the given line.

block

The block to which all tags will be added with Block#addTag. All parent blocks recieve copies of the child block’s tag.

line

The line to extract all tags from. Tags are square-bracket-enclosed strings found in sequence at the beginning of a line. If the sequence is broken, extraction stops.

Some tag examples:
  [a tag] [another tag]
  [a tag] [another tag] Not a tag. [not a tag]
  No tag on this line.
  No tag on this line either. [not a tag]


157
158
159
160
161
162
163
# File 'lib/ntxt/parser.rb', line 157

def self.extractTags(block, line)
  while line =~ /^\s*\[([^\[]+)\]/m
    block.addTag($~[1])
    matchLength = $~[0].length
    line = line[matchLength,line.length - matchLength]
  end
end

.hlevel(line) ⇒ Object

Return an array in which the first element is the indent length and the second element is the contained text. Nil otherwise.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/ntxt/parser.rb', line 127

def self.hlevel(line)
  case line
  when /^\s*=([^=].*)=\s*$/
    [ 1, $~[1] ]
  when /^\s*==([^=].*)==\s*$/
    [ 2, $~[1] ]
  when /^\s*===([^=].*)===\s*$/
    [ 3, $~[1] ]
  when /^\s*====([^=].*)====\s*$/
    [ 4, $~[1] ]
  when /^\s*=====([^=].*)=====\s*$/
    [ 5, $~[1] ]
  when /^\s*======([^=].*)======\s*$/
    [ 6, $~[1] ]
  else
    nil
  end
end

Instance Method Details

#parse(ntxtObj) ⇒ Object

Parse the given Ntxt ‘s Ntxt#text.

ntxtObj

If this is an Ntxt object, Ntxt#text is parsed. If ntxtObj is not an Ntxt object, it is assumed to be a valid argument for Ntxt.new and a new Ntxt is constructed.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/ntxt/parser.rb', line 169

def parse(ntxtObj)

  # If ntxtObj isn't an Ntxt, create it as one.
  ( ntxtObj = Ntxt.new(ntxtObj) ) unless ntxtObj.is_a?( Ntxt )

  lines = ntxtObj.text.split("\n")
  
  rootBlock = Block.new(ntxtObj)
  
  @stack = [ State.new( lines, rootBlock, 0, 0, lines.length ) ]
  
  parseLines()
  
  if @stack.length == 1
    # parse success!
    rootBlock
  else
    # parse failure.
    nil
  end
end

#parseHlevel(level, title) ⇒ Object

Take the state off the top of the #stack and attempt to parse an Hlevel block. An HLevel block is a wiki-like header block of text. For example:

= Header 1 =
== Header 2 ==
level

an integer from 1 to 6.

title

a string of the text found between the equal signs.



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/ntxt/parser.rb', line 198

def parseHlevel(level, title)
  state = @stack[-1]
  
  # If in parseHlevel, don't get the current line. That is contained
  # in the title argument. Instead, get the next line and proceed.
  line = state.nextLine

  while line
    # Check if we have discovered another block in the form of an hlevel.
    hl = Parser::hlevel(line)
    
    if hl && hl[0].to_i <= level
      break
    end
    
    line = state.nextLine
  end
  
  block = Block.new(
    state.block.ntxt,
    state.block,
    state.start,
    state.offset)

  subState = State.new(
    state.lines,
    block,
    state.lineStart+1,
    state.start + state.lines[state.lineStart].length + 1,
    state.line)
  
  @stack.push subState
  parseLines
  @stack.pop
  state.consume
end

#parseIndent(indentLevel, text) ⇒ Object

Parse blocks of text that are indented at the given level or greater.

indentLevel

an integer denoteing the number of characters this line is indented at.

text

the content of the line that was indented.



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/ntxt/parser.rb', line 239

def parseIndent(indentLevel, text)
  state = @stack[-1]
  line = state.currLine

  # Build the block. Update the offset.
  block = Block.new(state.block.ntxt,
                    state.block,
                    state.start,
                    state.offset)

  # Position state at the ed of the block.
  # Blocks are ended by empty lines or lines with the = starting them.
  while line

    break if Parser::hlevel(line)
    break unless line =~ /^(\s*)(..*)$/

    nextIndentLevel = $~[1].length
    nextLine = $~[2]
    
    break if nextIndentLevel < indentLevel

    if nextIndentLevel > indentLevel
      # Advance to the next line after parsing a subblock.
      subState = State.new(
        state.lines,
        block,
        state.line, 
        state.start + state.offset,
        state.lineEnd)

      @stack.push subState
      parseIndent(nextIndentLevel, nextLine)
      @stack.pop
      state.seek subState
      line = state.currLine
    else
      Parser::extractTags(block, line)
      line = state.nextLine
    end # if nextIndentLevel > indentLevel
    
  end # while line
  block.offset = state.offset
  state.consume
end

#parseLinesObject

This is the root of the parser’s call tree after #parse sets up the parse. This plucks the State off the Parser.stack, obtains the State.currLine.

When an indented line is found, #parseIndent is called. When a header line is found, #parseHlevel is caled. Otherwise, we move to the next line.



292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/ntxt/parser.rb', line 292

def parseLines
  state = @stack[-1]
  line = state.currLine
  
  while line
    tmp = Parser::hlevel(line)

    if tmp
      state.consume
      parseHlevel(tmp[0].to_i, tmp[1])
      line = state.currLine
    elsif line =~ /^(\s*)(\S.*)$/
      state.consume
      parseIndent($~[1].length, $~[2])
      line = state.currLine
    else
      line = state.nextLine
    end # if tmp
  end # while line
end