noratext 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 KOJIMA Satoshi
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,74 @@
1
+ = noratext
2
+
3
+ * http://github.com/skoji/noratext
4
+
5
+ == DESCRIPTION:
6
+
7
+ noratext is a simple (and rather stupid) lexer and parser generator for loosly marked-up text.
8
+ current version supports xml-like, but not valid-xml (like old HTML) markup.
9
+
10
+ will support wiki-style markup soon.
11
+
12
+ == FEATURES
13
+
14
+ * Noratext::Lexer/Noratext::Parser provides functionalty to parse markuped text.
15
+
16
+ == PROBLEMS
17
+
18
+ * only provides xml-style tags support.
19
+
20
+ * need to write duplicated rules for Lexar and Parser. e.g., need to define same tag for Lexar and Parser.
21
+
22
+ == SYNOPSIS
23
+
24
+ === lexer : style A
25
+ # define lexer :my_ml。
26
+ Noratext::Lexer.define :my_ml, :xml_style do
27
+ symbols :chapter, :section, :strong, :center, :right, :font-size, :blockquote, :code, :change_paragraph
28
+
29
+ without_close :change_paragraph
30
+ match_pattern :change_paragraph, 'p' # tag is <p> , not <change-paragraph>
31
+ rawtext_till_close :code
32
+
33
+ # add attribute parser
34
+ add_parser :font-size do
35
+ |s|
36
+ /size="(.?)"/ =~ s
37
+ { :size => $1 }
38
+ end
39
+ end
40
+
41
+ # use the lexer
42
+ Noratext::Lexer[:my_ml].parse(io)
43
+
44
+ === lexer : style B
45
+ # define symbols
46
+ Noratext::Lexer.define :my_ml, :xml_style do
47
+ symbol :chapter
48
+ symbol :section
49
+ symbol :strong
50
+ symbol :center
51
+ symbol :right
52
+ symbol :blockquote
53
+ symbol :font-size
54
+ add_parser do
55
+ |s|
56
+ /size="(.?)"/ =~ s
57
+ { :size => $1 }
58
+ end
59
+ end
60
+ symbol :code do
61
+ rawtext_till_close
62
+ end
63
+ symbol :change_paragraph do
64
+ without_close
65
+ match_pattern 'p'
66
+ end
67
+ end
68
+
69
+ # use the lexer
70
+ Noratext::Lexer[:my_ml].parse(io)
71
+
72
+ == Copyright
73
+
74
+ Copyright (c) 2010 KOJIMA Satoshi. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "noratext"
8
+ gem.summary = %Q{ noratext: simple lexer/parser generator for markuped text}
9
+ gem.description = %Q{noratext is a simple (and rather stupid) lexer and parser generator for loosly markuped text. }
10
+ gem.email = "skoji@mac.com"
11
+ gem.homepage = "http://github.com/skoji/noratext"
12
+ gem.authors = ["KOJIMA Satoshi"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "noratext #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/lib/noratext.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'noratext/lexer'
2
+ require 'noratext/xmly_lexer'
3
+ require 'noratext/parser_element'
4
+ require 'noratext/parser'
@@ -0,0 +1,153 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Noratext
3
+ class Lexer
4
+ @instances_block = {}
5
+ def self.define(name, style = :xml_style, &block)
6
+ case style
7
+ when :xml_style
8
+ lexer = XmlyLexer.new
9
+ end
10
+ @instances_block[name] = { :lexer_class => lexer.class, :block => block }
11
+ lexer.instance_eval(&block) # for check only
12
+ end
13
+
14
+ def self.[](name)
15
+ lexer = @instances_block[name][:lexer_class].new
16
+ lexer.instance_eval(&@instances_block[name][:block])
17
+ lexer
18
+ end
19
+
20
+ def initialize
21
+ @tags = {}
22
+ end
23
+
24
+ def process(io)
25
+ result = []
26
+ while line = io.gets
27
+ result = result + read_line(line, io.lineno)
28
+ end
29
+ result
30
+ end
31
+
32
+ def symbols(*symbols)
33
+ symbols.each {
34
+ |symbol|
35
+ @tags[symbol] = tag_class.new(symbol)
36
+ }
37
+ end
38
+
39
+ def symbol(symbol, &block)
40
+ @tags[symbol] = tag_class.new(symbol)
41
+ @tags[symbol].instance_eval(&block)
42
+ end
43
+
44
+ def match_pattern(tag, pattern)
45
+ @tags[tag].match_pattern pattern
46
+ end
47
+
48
+ def without_close(*tags)
49
+ tags.each {
50
+ |tag|
51
+ @tags[tag].without_close
52
+ }
53
+ end
54
+
55
+ def add_parser(tag, &block)
56
+ @tags[tag].attribute_parsers << block
57
+ end
58
+
59
+ def rawtext_till_close(tag, closetag = nil)
60
+ closetag ||= closetag_for(tag)
61
+ @tags[tag].rawtext_till_close closetag
62
+ end
63
+
64
+ def read_line(s, line_no)
65
+ return [] if s == ""
66
+ result = []
67
+
68
+ if @rawtext_tag
69
+ matched = /#{@rawtext_close_tag}/.match(s)
70
+ if matched.nil?
71
+ return [{ :type => :text, :data => s, :line => line_no }]
72
+ else
73
+ result << { :type => :text, :data => matched.pre_match, :line => line_no }
74
+ result << {
75
+ :type => @rawtext_tag.name,
76
+ :data => matched[0],
77
+ :line => line_no,
78
+ :tag => { :name => @rawtext_tag.name }.merge(@rawtext_tag.parse_attribute(matched[0])) }
79
+
80
+ @rawtext_tag = nil
81
+ @rawtext_close_tag = nil
82
+ return result + read_line(matched.post_match, line_no)
83
+ end
84
+ end
85
+
86
+ t = factory(s)
87
+ if (t.nil?)
88
+ return [{ :type => :text, :data => s, :line => line_no }]
89
+ else
90
+ result << { :type => :text, :data => t[:pre], :line => line_no } if t[:pre] != ""
91
+ result << { :type => t[:tag][:name], :data => t[:data], :line => line_no, :tag => t[:tag] }
92
+ result + read_line(t[:rest], line_no)
93
+ end
94
+ end
95
+
96
+ def factory(s)
97
+ matched = @tags.map {
98
+ |name, tag|
99
+ { :tag => tag, :match => tag.matcher(s) }
100
+ }.select { |m| !m[:match].nil? }.sort_by { |m| m[:match].begin(0) }
101
+
102
+ return nil if matched.size == 0
103
+ m = matched[0][:match]
104
+ tag = matched[0][:tag]
105
+
106
+ @rawtext_tag = tag if @rawtext_close_tag = tag.rawtext_till_close_tag
107
+ { :pre => m.pre_match,
108
+ :rest => m.post_match,
109
+ :data => m[0],
110
+ :tag => { :name => tag.name }.merge(tag.parse_attribute(m[0])) }
111
+ end
112
+
113
+ class Tag
114
+ attr_accessor :name, :attribute_parsers, :rawtext_till_close_tag
115
+
116
+ def initialize(name)
117
+ @name = name
118
+ @match_pattern = name.to_s
119
+ @attribute_parsers = []
120
+ @with_close = true
121
+ @rawtext_till_close_tag = nil
122
+ end
123
+
124
+ def without_close
125
+ @with_close = false
126
+ end
127
+
128
+ def rawtext_till_close(tag = nil)
129
+ tag ||= closetag_for(@name)
130
+ @rawtext_till_close_tag = tag
131
+ end
132
+
133
+ def add_parser(&block)
134
+ @attribute_parsers << block
135
+ end
136
+
137
+ def match_pattern(pattern)
138
+ @match_pattern = pattern
139
+ end
140
+
141
+ def parse_attribute(s)
142
+ result = {}
143
+ @attribute_parsers.each {
144
+ |parser|
145
+ result.merge!(parser.call(s))
146
+ }
147
+ result
148
+ end
149
+
150
+ end
151
+ end
152
+ end
153
+
@@ -0,0 +1,43 @@
1
+ module Noratext
2
+ class Parser
3
+ @instances_block = {}
4
+ def self.define(name, style = :xml_style, &block)
5
+ parser = Parser.new
6
+ @instances_block[name] = block
7
+ parser.instance_eval(&block) # for check only
8
+ end
9
+
10
+ def self.[](name)
11
+ parser = Parser.new
12
+ parser.instance_eval(&@instances_block[name])
13
+ parser
14
+ end
15
+
16
+ def element(name, &block)
17
+ element = Element.new(name, method(:logger), @elements)
18
+ @start_element = element if @elements.size == 0
19
+ @elements[name] = element
20
+ element.instance_eval(&block)
21
+ end
22
+
23
+ def initialize
24
+ @elements = {}
25
+ @log = []
26
+ end
27
+
28
+ def parse(sequence)
29
+ result = @start_element.process(sequence)
30
+ logger sequence[0][:line], "unexpected #{sequence[0][:data]}" if (sequence.size > 0)
31
+ result
32
+ end
33
+
34
+ def logger(lineno, log)
35
+ @log << { :lineno => lineno, :log => log}
36
+ end
37
+
38
+ def log
39
+ @log.sort_by { |entry| entry[:lineno] }.map { |entry| "#{entry[:lineno]}: #{entry[:log]}" }
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,189 @@
1
+ module Noratext
2
+ class Parser
3
+
4
+ module Contains
5
+ def element_to_parse
6
+ @element_to_parse ||= @contains.map { |element_name| @elements[element_name] }
7
+ end
8
+
9
+ def process(sequence)
10
+ preprocess(sequence)
11
+ children = []
12
+ while (sequence.size > 0 && elem = process_one_element(sequence))
13
+ children << elem
14
+ end
15
+ postprocess(sequence)
16
+ ParsedData.new(@name, children)
17
+ end
18
+ end
19
+
20
+ module IsOneof
21
+ def element_to_parse
22
+ @element_to_parse ||= @is_oneof.map { |element_name| @elements[element_name] }
23
+ end
24
+
25
+ def process(sequence)
26
+ if sequence.size > 0 && elem = process_one_element(sequence)
27
+ elem
28
+ else
29
+ nil
30
+ end
31
+ end
32
+ end
33
+
34
+ module OpenClose
35
+
36
+ def accept?(token)
37
+ is_opentag(token)
38
+ end
39
+
40
+ def preprocess(sequence)
41
+ @opentag = sequence.shift[:tag]
42
+ end
43
+
44
+ def is_end_of_element(token)
45
+ is_closetag(token)
46
+ end
47
+
48
+ def postprocess(sequence)
49
+ return if sequence.size == 0
50
+
51
+ if is_closetag(sequence[0])
52
+ sequence.shift
53
+ return
54
+ end
55
+
56
+ log @opentag[:line], "#{@name} is not closed}"
57
+ end
58
+ end
59
+
60
+ module ParseToken
61
+ def process(sequence)
62
+ return ParsedData.new(@name).set_attributes(@parse_token_proc.call(sequence.shift))
63
+ end
64
+ end
65
+
66
+ module ParseSequence
67
+ def process(sequence)
68
+ return ParsedData.new(@name).set_attributes(@parse_sequence_proc.call(sequence))
69
+ end
70
+ end
71
+
72
+ class Element
73
+ def accept?(token)
74
+ if (@accept_type)
75
+ return token[:type] == @accept_type
76
+ else
77
+ element_to_parse.each {
78
+ |element|
79
+ return true if (element.accept?(token))
80
+ }
81
+ return false
82
+ end
83
+ end
84
+
85
+ def is_closetag(token)
86
+ token[:type] == @name &&
87
+ token[:tag][:kind] == :closetag
88
+ end
89
+
90
+ def is_opentag(token)
91
+ token[:type] == @name &&
92
+ token[:tag][:kind] == :opentag
93
+ end
94
+
95
+ def initialize(name, logger, elements)
96
+ @name = name
97
+ @logger = logger
98
+ @elements = elements
99
+ end
100
+
101
+ def preprocess(sequence)
102
+ end
103
+
104
+ def postprocess(sequence)
105
+ end
106
+
107
+ def is_end_of_element(token)
108
+ false
109
+ end
110
+
111
+ def process_one_element(sequence)
112
+
113
+ while (sequence.size > 0 && sequence[0][:kind] == :closetag)
114
+ return nil if (is_end_of_element(sequence[0]))
115
+ log sequence[0][:line],"no opentag for #{sequence[0][:type]}"
116
+ sequence.shift
117
+ end
118
+ return nil if (sequence.size == 0)
119
+
120
+ element_to_parse.each {
121
+ |element|
122
+ return element.process(sequence) if element.accept?(sequence[0])
123
+ }
124
+ nil
125
+ end
126
+
127
+ def contains(*array)
128
+ raise 'already is_oneof are defined' if !@is_oneof.nil?
129
+ @contains = array
130
+ extend Contains
131
+ end
132
+
133
+ def is_oneof(*array)
134
+ raise 'already contains are defined' if !@contains.nil?
135
+ @is_oneof = array
136
+ extend IsOneof
137
+ end
138
+
139
+ def open_close
140
+ extend OpenClose
141
+ end
142
+
143
+ def accepts(type)
144
+ @accept_type = type
145
+ end
146
+
147
+ def parse_token(&block)
148
+ raise 'already defined as parse sequence type' if !@parse_sequence_proc.nil?
149
+ @parse_token_proc = block
150
+ @accept_type ||= @name
151
+ extend ParseToken
152
+ end
153
+
154
+ def parse_sequence(&block)
155
+ raise 'already defined as parse token type' if !@parse_token_proc.nil?
156
+ @parse_sequence_proc = block
157
+ @accept_type ||= @name
158
+ extend ParseSequence
159
+ end
160
+
161
+ def log(lineno, log)
162
+ @logger.call(lineno, log) if !@logger.nil?
163
+ end
164
+ end
165
+
166
+ class ParsedData
167
+ attr_accessor :type, :children
168
+
169
+ def initialize(type, children = [])
170
+ @children = children
171
+ @type = type
172
+ @attributes = {}
173
+ end
174
+
175
+ def is_leaf?
176
+ @children.size == 0
177
+ end
178
+
179
+ def set_attributes(value)
180
+ @attributes.merge!(value)
181
+ value.each {
182
+ |k,v|
183
+ (class<<self;self;end).instance_eval{define_method(k){v}}
184
+ }
185
+ self
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Noratext
3
+ class XmlyLexer < Lexer
4
+
5
+ def initialize
6
+ super
7
+ end
8
+
9
+ def tag_class
10
+ return XmlyTag
11
+ end
12
+
13
+ def closetag_for(tag)
14
+ "</#{tag.to_s}>"
15
+ end
16
+
17
+ class XmlyTag < Lexer::Tag
18
+ def self.default_attribute_parser
19
+ lambda {
20
+ |s|
21
+ { :kind => /^<\// =~ s ? :closetag : :opentag }
22
+ }
23
+ end
24
+
25
+ def initialize(name)
26
+ super
27
+ @attribute_parsers << self.class.default_attribute_parser
28
+ end
29
+
30
+ def matcher(s)
31
+ tagpattern = "<#{@match_pattern}.*?>"
32
+ tagpattern = "</?#{@match_pattern}.*?>" if @with_close
33
+ /#{tagpattern}/.match s
34
+ end
35
+ end
36
+
37
+ end
38
+ end
data/noratext.gemspec ADDED
@@ -0,0 +1,65 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{noratext}
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["KOJIMA Satoshi"]
12
+ s.date = %q{2010-07-13}
13
+ s.description = %q{noratext is a simple (and rather stupid) lexer and parser generator for loosly markuped text. }
14
+ s.email = %q{skoji@mac.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/noratext.rb",
27
+ "lib/noratext/lexer.rb",
28
+ "lib/noratext/parser.rb",
29
+ "lib/noratext/parser_element.rb",
30
+ "lib/noratext/xmly_lexer.rb",
31
+ "noratext.gemspec",
32
+ "spec/noratext_lexer_spec.rb",
33
+ "spec/noratext_parser_spec.rb",
34
+ "spec/noratext_spec.rb",
35
+ "spec/spec.opts",
36
+ "spec/spec_helper.rb",
37
+ "spec/ydml_grammer_definition.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/skoji/noratext}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.7}
43
+ s.summary = %q{noratext: simple lexer/parser generator for markuped text}
44
+ s.test_files = [
45
+ "spec/noratext_lexer_spec.rb",
46
+ "spec/noratext_parser_spec.rb",
47
+ "spec/noratext_spec.rb",
48
+ "spec/spec_helper.rb",
49
+ "spec/ydml_grammer_definition.rb"
50
+ ]
51
+
52
+ if s.respond_to? :specification_version then
53
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
54
+ s.specification_version = 3
55
+
56
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
58
+ else
59
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
63
+ end
64
+ end
65
+
@@ -0,0 +1,82 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe "Noratext::Lexer" do
5
+ before do
6
+ Noratext::Lexer.define :test do
7
+ symbols :center, :left, :right, :quote
8
+ rawtext_till_close :quote
9
+
10
+ symbol :paragraph do
11
+ match_pattern 'p'
12
+ without_close
13
+ end
14
+
15
+ symbol :image do
16
+ match_pattern 'img'
17
+ without_close
18
+ add_parser do
19
+ |s|
20
+ /src="(.*?)"/ =~ s
21
+ path = $1
22
+ /scale="(.*?)"/ =~ s
23
+ scale = $1
24
+ { :path => path, :scale => scale }
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ it "should parse tag correctly" do
31
+ lexer = Noratext::Lexer[:test]
32
+ opentag = lexer.factory('<center>')
33
+ closetag = lexer.factory('</center>')
34
+
35
+ opentag[:tag][:name].should == :center
36
+ opentag[:tag][:kind].should == :opentag
37
+
38
+ closetag[:tag][:name].should == :center
39
+ closetag[:tag][:kind].should == :closetag
40
+ end
41
+
42
+ it "should create parse path" do
43
+ lexer = Noratext::Lexer[:test]
44
+ tag = lexer.factory('<img src="../img/path.jpg">')
45
+ tag[:tag][:name].should == :image
46
+ tag[:tag][:kind].should == :opentag
47
+ tag[:tag][:path].should == '../img/path.jpg'
48
+ tag[:tag][:scale].should be_nil
49
+ end
50
+
51
+ it "should create parse path" do
52
+ lexer = Noratext::Lexer[:test]
53
+ tag = lexer.factory('<img src="../img/path.jpg" scale="90%">')
54
+ tag[:tag][:name].should == :image
55
+ tag[:tag][:kind].should == :opentag
56
+ tag[:tag][:path].should == '../img/path.jpg'
57
+ tag[:tag][:scale].should == '90%'
58
+ end
59
+
60
+ it "should parse rawtext tag" do
61
+ lexer = Noratext::Lexer[:test]
62
+ text = "<quote>この部分は、<center>とかはいっていても、そのまま見えるはず。
63
+ <bold>改行</bold>しても、扱えるはず。</quote>このへんは、タグを<center>読む。"
64
+ io = StringIO.new(text)
65
+
66
+ processed = lexer.process(io)
67
+ processed.size.should == 7
68
+ processed[0][:type].should == :quote
69
+ processed[1][:type].should == :text
70
+ processed[1][:data].should == "この部分は、<center>とかはいっていても、そのまま見えるはず。\n"
71
+ processed[2][:type].should == :text
72
+ processed[2][:data].should == '<bold>改行</bold>しても、扱えるはず。'
73
+ processed[3][:type].should == :quote
74
+ processed[4][:type].should == :text
75
+ processed[4][:data].should == 'このへんは、タグを'
76
+ processed[5][:type].should == :center
77
+ processed[6][:type].should == :text
78
+ processed[6][:data].should == '読む。'
79
+ end
80
+
81
+
82
+ end
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+ require File.expand_path(File.dirname(__FILE__) + '/ydml_grammer_definition')
4
+
5
+ describe "Noratext::Parser" do
6
+
7
+ it "should accept open-close" do
8
+ element = Noratext::Parser::Element.new(:center, nil, nil)
9
+ element.open_close
10
+ element.accept?({ :type => :center, :tag => { :name => :center, :kind => :opentag }}).should be_true
11
+ end
12
+
13
+ it "should accept text" do
14
+ element = Noratext::Parser::Element.new(:text, nil, nil)
15
+ element.accepts :text
16
+ element.accept?({ :type => :text })
17
+ end
18
+
19
+ it "should accept open-close" do
20
+ elements = {}
21
+ elements[:text] = Noratext::Parser::Element.new(:text, nil, elements)
22
+ elements[:text].accepts :text
23
+ elements[:text].parse_sequence do
24
+ |s|
25
+ a = s.shift
26
+ { :data => a[:data] }
27
+ end
28
+ element = Noratext::Parser::Element.new(:center, nil, elements)
29
+ element.open_close
30
+ element.contains :text
31
+ element.accept?({ :type => :center, :tag => { :name => :center, :kind => :opentag }}).should be_true
32
+ a = element.process([{ :type => :center, :tag => { :name => :center, :kind => :opentag }},
33
+ { :type => :text, :data => "foobar" },
34
+ { :type => :center, :tag => { :name => :center, :kind => :closetag}}])
35
+ a.type.should == :center
36
+ a.is_leaf?.should_not be_true
37
+ a.children.size.should == 1
38
+ a.children[0].type.should == :text
39
+ a.children[0].is_leaf?.should be_true
40
+ a.children[0].data.should == "foobar"
41
+ end
42
+
43
+ end
44
+
45
+ describe Noratext::Parser do
46
+ it "should parse valid test" do
47
+ seq = [ { :type => :text, :data=>'これが中身', :line => 1} ]
48
+ result = Noratext::Parser[:ydml].parse(seq)
49
+ result.type.should == :document
50
+ result.is_leaf?.should_not be_true
51
+ result.children.size.should == 1
52
+ result.children[0].is_leaf?.should_not be_true
53
+ result.children[0].type.should == :paragraph
54
+ result.children[0].children.size.should == 1
55
+ result.children[0].children[0].type.should == :text
56
+ result.children[0].children[0].data.should == 'これが中身'
57
+ end
58
+
59
+ it "should parse nested data" do
60
+ seq = [ { :type => :center, :tag => { :name => :center, :kind => :opentag}},
61
+ { :type => :text, :data=>'センタリング', :line => 1},
62
+ { :type => :center, :tag => { :name => :center, :kind => :closetag}},
63
+ { :type => :ruby, :tag => { :name => :ruby, :kind => :opentag }},
64
+ { :type => :text, :data => '蜻蛉/とんぼ', :line => 2 },
65
+ { :type => :ruby,:tag => { :name => :ruby, :kind => :closetag }}
66
+ ]
67
+
68
+ parser = Noratext::Parser[:ydml]
69
+ result = parser.parse(seq)
70
+ result.type.should == :document
71
+ parser.log.size.should == 0
72
+ result.children.size.should == 2
73
+ result.children[0].is_leaf?.should_not be_true
74
+ result.children[0].type.should == :center
75
+ result.children[0].children.size.should == 1
76
+ result.children[0].children[0].type.should == :paragraph
77
+ result.children[0].children[0].children[0].data.should == 'センタリング'
78
+ result.children[1].type.should == :paragraph
79
+ result.children[1].children.size.should == 1
80
+ result.children[1].children[0].type.should == :ruby
81
+ result.children[1].children[0].ruby.should == 'とんぼ'
82
+ result.children[1].children[0].body.should == '蜻蛉'
83
+
84
+ end
85
+
86
+ end
87
+
@@ -0,0 +1,5 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ describe "Noratext::Parser" do
5
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'noratext'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
@@ -0,0 +1,95 @@
1
+ Noratext::Parser.define :ydml do
2
+ element :document do
3
+ contains :block
4
+ end
5
+
6
+ element :block do
7
+ is_oneof :paragraph, :center, :left, :right, :quote, :hasen
8
+ end
9
+
10
+ element :paragraph do
11
+ contains :text, :large, :small, :bold, :image, :sonomama, :ruby
12
+ end
13
+
14
+ element :center do
15
+ open_close
16
+ contains :paragraph
17
+ end
18
+
19
+ element :left do
20
+ open_close
21
+ contains :paragraph
22
+ end
23
+
24
+ element :right do
25
+ open_close
26
+ contains :paragraph
27
+ end
28
+
29
+ element :quote do
30
+ open_close
31
+ contains :paragraph
32
+ end
33
+
34
+ element :hasen do
35
+ parse_token do
36
+ |token|
37
+ {}
38
+ end
39
+ end
40
+
41
+ element :text do
42
+ accepts :text
43
+ parse_sequence do
44
+ |sequence|
45
+ data = ""
46
+ while (sequence.size > 0 &&
47
+ sequence[0][:type] == :text)
48
+ data << sequence.shift[:data]
49
+ end
50
+ { :data => data }
51
+ end
52
+ end
53
+
54
+ element :large do
55
+ open_close
56
+ contains :paragraph
57
+ end
58
+
59
+ element :small do
60
+ open_close
61
+ contains :paragraph
62
+ end
63
+
64
+ element :bold do
65
+ open_close
66
+ contains :paragraph
67
+ end
68
+
69
+ element :image do
70
+ parse_token do
71
+ |token|
72
+ { :imagepath => token[:imagepath], :size => token[:size] }
73
+ end
74
+ end
75
+
76
+ element :sonomama do
77
+ open_close
78
+ contains :text
79
+ end
80
+
81
+ element :ruby do
82
+ parse_sequence do
83
+ |sequence|
84
+ sequence.shift
85
+ /(.+)\/(.+)/ =~ sequence[0][:data]
86
+ raise "#{sequence[0][:data]} is invalid inside ruby tag." if $1.nil?
87
+ sequence.shift
88
+ raise "rubytag is not closed" if !is_closetag(sequence[0])
89
+ sequence.shift
90
+ { :body => $1, :ruby => $2 }
91
+ end
92
+
93
+ end
94
+
95
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: noratext
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 0
10
+ version: 0.0.0
11
+ platform: ruby
12
+ authors:
13
+ - KOJIMA Satoshi
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-13 00:00:00 +09:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: "noratext is a simple (and rather stupid) lexer and parser generator for loosly markuped text. "
38
+ email: skoji@mac.com
39
+ executables: []
40
+
41
+ extensions: []
42
+
43
+ extra_rdoc_files:
44
+ - LICENSE
45
+ - README.rdoc
46
+ files:
47
+ - .document
48
+ - .gitignore
49
+ - LICENSE
50
+ - README.rdoc
51
+ - Rakefile
52
+ - VERSION
53
+ - lib/noratext.rb
54
+ - lib/noratext/lexer.rb
55
+ - lib/noratext/parser.rb
56
+ - lib/noratext/parser_element.rb
57
+ - lib/noratext/xmly_lexer.rb
58
+ - noratext.gemspec
59
+ - spec/noratext_lexer_spec.rb
60
+ - spec/noratext_parser_spec.rb
61
+ - spec/noratext_spec.rb
62
+ - spec/spec.opts
63
+ - spec/spec_helper.rb
64
+ - spec/ydml_grammer_definition.rb
65
+ has_rdoc: true
66
+ homepage: http://github.com/skoji/noratext
67
+ licenses: []
68
+
69
+ post_install_message:
70
+ rdoc_options:
71
+ - --charset=UTF-8
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 3
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ none: false
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ hash: 3
89
+ segments:
90
+ - 0
91
+ version: "0"
92
+ requirements: []
93
+
94
+ rubyforge_project:
95
+ rubygems_version: 1.3.7
96
+ signing_key:
97
+ specification_version: 3
98
+ summary: "noratext: simple lexer/parser generator for markuped text"
99
+ test_files:
100
+ - spec/noratext_lexer_spec.rb
101
+ - spec/noratext_parser_spec.rb
102
+ - spec/noratext_spec.rb
103
+ - spec/spec_helper.rb
104
+ - spec/ydml_grammer_definition.rb