ntxt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
File without changes
data/bin/ntxt ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'optparse'
5
+ require 'ntxt'
6
+
7
+ $configs = {
8
+ :cmd => 'tag',
9
+ :tag_string => ''
10
+ }
11
+
12
+ OptionParser.new do |opt|
13
+
14
+ opt.on('--trace', 'Trace through the file printing summaries.' ) do |v|
15
+ $configs[:cmd] = 'trace'
16
+ end
17
+
18
+ opt.on('-p','--print', 'Print the whole file one block at a time.' ) do |v|
19
+ $configs[:cmd] = 'print'
20
+ end
21
+
22
+ opt.on('-T', '--print_tags', 'Print all tags in the document.') do |v|
23
+ $configs[:cmd] = 'print_tags'
24
+ end
25
+
26
+ opt.on('-t', '--tag=String', 'Search and print the give so-tagged blocks.') do |v|
27
+ $configs[:cmd] = 'tag'
28
+ $configs[:tag_string] = v
29
+ end
30
+
31
+ opt.on('-s','--search=String', 'Search the text for.' ) do |v|
32
+ $configs[:cmd] = 'search'
33
+ $configs[:search_string] = v
34
+ end
35
+ end.parse! ARGV
36
+
37
+ if ARGV.length > 0
38
+ $configs[:filename] = ARGV.shift
39
+ else
40
+ puts "No file specified. Exiting"
41
+ exit 1
42
+ end
43
+
44
+ def printNonEmpty(txt)
45
+ if txt
46
+ txt = txt.strip
47
+ print txt, "\n" if txt.length > 0
48
+ end
49
+ end
50
+
51
+ $configs[:tag_string] = ARGV.shift if ARGV.length > 0
52
+
53
+ ntxt = File.open($configs[:filename]) { |io| Ntxt::Ntxt.new(io.read) }
54
+
55
+ case $configs[:cmd]
56
+ when 'print_tags'
57
+ # Notice that we are re-wrapping the tags back into square brackets.
58
+ puts "[#{ntxt.rootBlock.tags.sort.join('] [')}]"
59
+ when 'tag'
60
+ ntxt.walkText(
61
+ lambda { |txt, depth, block|
62
+ printNonEmpty txt if block.tags.join(', ').index( $configs[:tag_string])},
63
+ lambda { |depth, block| },
64
+ lambda { |depth, block| } )
65
+ when 'search'
66
+ ntxt.walkText(
67
+ lambda { |txt, depth, block|
68
+ printNonEmpty txt if txt && txt.index( $configs[:search_string])},
69
+ lambda { |depth, block| },
70
+ lambda { |depth, block| } )
71
+ when 'print'
72
+ ntxt.walkText(
73
+ lambda { |txt, depth, block| print txt },
74
+ lambda { |depth, block| },
75
+ lambda { |depth, block| } )
76
+ when 'trace'
77
+ ntxt.walkText(
78
+ lambda { |txt, depth, block| print txt },
79
+ lambda { |depth, block| puts "-----> #{depth} #{block.tags.join(',')}" },
80
+ lambda { |depth, block| puts "<----- #{depth} " } )
81
+ when ''
82
+ # nop
83
+ when nil
84
+ # nop
85
+ else
86
+ puts "Command #{$configs[:cmd]} not found."
87
+ end
data/lib/ntxt.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'ntxt/block'
2
+ require 'ntxt/parser'
3
+ require 'ntxt/ntxt'
data/lib/ntxt/block.rb ADDED
@@ -0,0 +1,99 @@
1
+
2
+ module Ntxt
3
+
4
+ # Block constructor
5
+ # ntxtObj the root Ntxt object.
6
+ # parentBlock the parent Block object.
7
+ # startTxt the starting offset in ntxtObj.text where this starts
8
+ # stopTxt the offset in ntxtObje.text after the startTxt position.
9
+ # The block of text submitted must be a valid block, meaning, it may
10
+ # ONLY contain subblocks.
11
+ class Block
12
+
13
+ attr_accessor :children, :tags, :start, :offset, :ntxt, :parent
14
+
15
+ if RUBY_VERSION =~ /^1.8/
16
+ def self.blockReMatch(re, txt, offset)
17
+ re.match(txt[offset..-1])
18
+ end
19
+ else
20
+ def self.blockReMatch(re, txt, offset)
21
+ re.match(text, offset)
22
+ end
23
+ end
24
+
25
+ def initialize(ntxtObj, parentBlock=nil, startTxt=0, stopTxt=0)
26
+ @children = []
27
+ @tags = []
28
+ @start = startTxt
29
+ @offset = stopTxt || ntxtObj.text.length
30
+ @ntxt = ntxtObj
31
+ @parent = parentBlock
32
+
33
+ if @parent
34
+ re = /\s*\S/m
35
+ #m = re.match( ntxtObj.text, @start )
36
+ m = Block::blockReMatch(re, ntxtObj.text, @start)
37
+ if m
38
+ @indent = m[0].length
39
+ else
40
+ @indent = 0
41
+ end
42
+ @parent.children.push(self)
43
+ else
44
+ @indent = 0
45
+ end
46
+ end
47
+
48
+ def addTag(tag)
49
+ @tags.push(tag)
50
+ @parent.addTag(tag) if @parent
51
+ end
52
+
53
+ def text
54
+ @ntxt.text[@start, @offset]
55
+ end
56
+
57
+ def is_root?
58
+ @parent
59
+ end
60
+
61
+ def walk(&y)
62
+ yield self
63
+ @children.each { |c| c.walk(&y) }
64
+ end
65
+
66
+ # printFunc is a lambda that takes the text, depth, and the node.
67
+ # enterChild is a lambda that takes depth and the node.
68
+ # exitChild is a lambda that takes depth and the node.
69
+ def walkText(printFunc, enterChild, exitChild)
70
+ walkTextHelper(printFunc, enterChild, exitChild, 0)
71
+ end
72
+
73
+ def walkTextHelper(printFunc, enterChild, exitChild, depth)
74
+ enterChild.call(depth, self)
75
+
76
+ if @children.length == 0
77
+ printFunc.call(text(), depth, self)
78
+ else
79
+ prevEnd = @start
80
+
81
+ @children.each do |child|
82
+ start = prevEnd
83
+ offset = child.start - start
84
+ printFunc.call(@ntxt.text[start, offset], depth, self)
85
+ child.walkTextHelper(printFunc, enterChild, exitChild, depth+1)
86
+ prevEnd = child.start + child.offset
87
+ end # @children.each
88
+
89
+ lastChild = @children[-1]
90
+ start = lastChild.start + lastChild.offset
91
+ offset = @start + @offset - start
92
+
93
+ printFunc.call(@ntxt.text[start, offset], depth, self)
94
+ end # else of if @children.length == 0
95
+
96
+ exitChild.call(depth, self)
97
+ end
98
+ end
99
+ end
data/lib/ntxt/ntxt.rb ADDED
@@ -0,0 +1,26 @@
1
+
2
+ module Ntxt
3
+
4
+ # Root class that contains the text array that Blocks reference
5
+ # and the root block.
6
+ class Ntxt
7
+ attr_accessor :text, :rootBlock
8
+
9
+ def initialize(text)
10
+ @text = text
11
+ @rootBlock = (Parser.new).parse(self)
12
+ end
13
+
14
+ # walkText(print, enter, exit)
15
+ # Walk the ntxt tree with 3 callbacks, print, enter, and exit.
16
+ # Print is a lambda that takes the text, the depth, and a copy of
17
+ # the Ntxt::Block it is in.
18
+ # Enter is the same, but is called with no text argument and is called
19
+ # when a block is entered (that is, the depth has increased by 1).
20
+ # Exit is the same, but is called with no text argument and is called
21
+ # when a block is exited (that is, the depth has decreated by 1).
22
+ def walkText(print, enter, exit)
23
+ @rootBlock.walkText(print, enter, exit)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,237 @@
1
+ require 'ntxt/block'
2
+
3
+ module Ntxt
4
+ class Parser
5
+
6
+ ###########################################################################
7
+ class State
8
+
9
+ attr_accessor :lines, :block, :lineStart, :lineEnd, :line, :start, :offset
10
+
11
+ def initialize(lines, block, lineStart, start, lineEnd)
12
+ @lines = lines
13
+ @block = block
14
+ @lineStart = lineStart
15
+ @lineEnd = lineEnd
16
+ @line = lineStart
17
+ @start = start
18
+ @offset = 0
19
+ end
20
+
21
+ # Return the current line.
22
+ def currLine
23
+ @lines[@line]
24
+ end
25
+
26
+ # Shift the state to the next line and return that line.
27
+ # If this goes out of bounds of the text nil is returned.
28
+ def nextLine
29
+ nextLine = @line+1
30
+ if nextLine < @lineEnd
31
+ nextOffset = @offset + @lines[@line].length + 1
32
+ @offset = nextOffset
33
+ @line = nextLine
34
+ @lines[nextLine]
35
+ else
36
+ nil
37
+ end
38
+ end # nextLine
39
+
40
+ # Shift the state to the previous line and return that line.
41
+ # If this goes out of bounds of the text nil is returned.
42
+ def prevLine
43
+ nextLine = @line - 1
44
+ if nextLine >= @lineStart
45
+ nextOffset = @offset - @lines[nextLine].length - 1
46
+ @offset = nextOffset
47
+ @line = nextLine
48
+ @lines[nextLine]
49
+ else
50
+ nil
51
+ end
52
+ end # prevLine
53
+
54
+ # Shift the state starting points to the current position of
55
+ # what has been read in this state, effecitvely consuming that input.
56
+ def consume
57
+ @start = @start + @offset
58
+ @offset = 0
59
+ @lineStart = @line
60
+ end
61
+
62
+ # Create a new state that is framed from the lineStart+1 of this state
63
+ # and ends at the current line of the given state.
64
+ def lowerSubState
65
+ endOfFrame = @line+1
66
+
67
+ endOfFrame = @lineEnd if endOfFrame > @lineEnd
68
+
69
+ State.new(@lines,
70
+ @block,
71
+ @lineStart+1,
72
+ @start + @lines[@lineStart].length + 1,
73
+ endOfFrame)
74
+ end # lowerSubState
75
+
76
+ # Create a new state that is framed with the remaining contents of
77
+ # this state
78
+ def upperSubState
79
+ State.new(@lines, @block, @line, @start + @offset, @lineEnd)
80
+ end
81
+
82
+ def to_s
83
+ "lineStart: %s, lineEnd: %s, line: %s, start: %s, offset: %s"%[
84
+ @lineStart, @lineEnd, @line, @start, @offset
85
+ ]
86
+ end
87
+
88
+ # Seek this state's position to the tiven state's position.
89
+ def seek(state)
90
+ @line = state.line
91
+ @offset = (state.start + state.offset - @start).abs()
92
+ end # seek
93
+
94
+ end # Parser::State
95
+ ###########################################################################
96
+
97
+ # Return an array in which the first element is the indent length and
98
+ # the second element is the contained text. Nil otherwise.
99
+ def self.hlevel(line)
100
+ case line
101
+ when /^\s*=([^=].*)=\s*$/
102
+ [ 1, $~[1] ]
103
+ when /^\s*==([^=].*)==\s*$/
104
+ [ 2, $~[1] ]
105
+ when /^\s*===([^=].*)===\s*$/
106
+ [ 3, $~[1] ]
107
+ when /^\s*====([^=].*)====\s*$/
108
+ [ 4, $~[1] ]
109
+ when /^\s*=====([^=].*)=====\s*$/
110
+ [ 5, $~[1] ]
111
+ when /^\s*======([^=].*)======\s*$/
112
+ [ 6, $~[1] ]
113
+ else
114
+ nil
115
+ end
116
+ end # self.hlevel
117
+
118
+ def self.extractTags(block, line)
119
+ while line =~ /^\s*\[([^\[]+)\]/m
120
+ block.addTag($~[1])
121
+ matchLength = $~[0].length
122
+ line = line[matchLength,line.length - matchLength]
123
+ end
124
+ end # self.extractTags
125
+
126
+ def parse(ntxtObj)
127
+
128
+ # If ntxtObj isn't an Ntxt, create it as one.
129
+ ( ntxtObj = Ntxt.new(ntxtObj) ) unless ntxtObj.is_a?( Ntxt )
130
+
131
+ lines = ntxtObj.text.split("\n")
132
+
133
+ rootBlock = Block.new(ntxtObj)
134
+
135
+ @stack = [ State.new( lines, rootBlock, 0, 0, lines.length ) ]
136
+
137
+ parseLines()
138
+
139
+ if @stack.length == 1
140
+ # parse success!
141
+ rootBlock
142
+ else
143
+ # parse failure.
144
+ nil
145
+ end
146
+ end # parse(ntxtObj)
147
+
148
+ def parseHlevel(level, title)
149
+ state = @stack[-1]
150
+
151
+ begin
152
+ line = state.nextLine
153
+
154
+ if line
155
+ hl = Parser::hlevel(line)
156
+
157
+ if hl && hl[0].to_i <= level
158
+ state.prevLine # Rewind. We steped onto another h block.
159
+ break
160
+ end
161
+ end
162
+ end while line
163
+
164
+ block = Block.new(state.block.ntxt,
165
+ state.block,
166
+ state.start,
167
+ state.offset)
168
+ subState = state.lowerSubState
169
+ subState.block = block
170
+ @stack.push subState
171
+ parseLines
172
+ @stack.pop
173
+ state.consume
174
+ end # parseHlevel(leve, title)
175
+
176
+ def parseIndent(indentLevel, text)
177
+ state = @stack[-1]
178
+ line = state.currLine
179
+
180
+ # BUild the block. Update the offset.
181
+ block = Block.new(state.block.ntxt,
182
+ state.block,
183
+ state.start,
184
+ state.offset)
185
+
186
+ # Position state at the ed of the block.
187
+ # Blocks are ended by empty lines or lines with the = starting them.
188
+ while line
189
+
190
+ break unless line =~ /^(\s*)([^=\s].*)$/
191
+
192
+ nextIndentLevel = $~[1].length
193
+ nextLine = $~[2]
194
+
195
+ break if nextIndentLevel < indentLevel
196
+
197
+ if nextIndentLevel > indentLevel
198
+ # Advance to the next line after parsing a subblock.
199
+ subState = state.upperSubState()
200
+ subState.block = block
201
+ @stack.push subState
202
+ parseIndent(nextIndentLevel, nextLine)
203
+ @stack.pop
204
+ state.seek subState
205
+ line = state.currLine
206
+ else
207
+ Parser::extractTags(block, line)
208
+ line = state.nextLine
209
+ end # if nextIndentLevel > indentLevel
210
+
211
+ end # while line
212
+
213
+ block.offset = state.offset
214
+ state.consume
215
+ end # parseIndent(indentLevel, text)
216
+
217
+ def parseLines
218
+ state = @stack[-1]
219
+ state.block.children = []
220
+ line = state.currLine
221
+
222
+ while line
223
+ tmp = Parser::hlevel(line)
224
+
225
+ if tmp
226
+ state.consume
227
+ parseHlevel(tmp[0].to_i, tmp[1])
228
+ elsif line =~ /^(\s*)(\S.*)$/
229
+ state.consume
230
+ parseIndent($~[1].length, $~[2])
231
+ end # if tmp
232
+
233
+ line = state.nextLine
234
+ end # while line
235
+ end # parseLines
236
+ end
237
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ntxt
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Sam Baskinger
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-10-30 00:00:00 -05:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: |
22
+ A collection of tools to assist Sam in coding.
23
+
24
+ email: basking2@yahoo.com
25
+ executables:
26
+ - ntxt
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - README.rdoc
33
+ - lib/ntxt/block.rb
34
+ - lib/ntxt/parser.rb
35
+ - lib/ntxt/ntxt.rb
36
+ - lib/ntxt.rb
37
+ - bin/ntxt
38
+ has_rdoc: true
39
+ homepage: http://coffeesgone.wordpress.com
40
+ licenses: []
41
+
42
+ post_install_message:
43
+ rdoc_options: []
44
+
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A parser and some tools for the ntxt text block format.
70
+ test_files: []
71
+