nora_mark 0.2beta3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +188 -0
- data/Rakefile +12 -0
- data/lib/nora_mark/html/abstract_item_writer.rb +14 -0
- data/lib/nora_mark/html/context.rb +119 -0
- data/lib/nora_mark/html/generator.rb +177 -0
- data/lib/nora_mark/html/header_writer.rb +35 -0
- data/lib/nora_mark/html/pages.rb +56 -0
- data/lib/nora_mark/html/paragraph_writer.rb +52 -0
- data/lib/nora_mark/html/tag_writer.rb +110 -0
- data/lib/nora_mark/html/util.rb +12 -0
- data/lib/nora_mark/html/writer_selector.rb +24 -0
- data/lib/nora_mark/parser.kpeg +123 -0
- data/lib/nora_mark/parser.kpeg.rb +3422 -0
- data/lib/nora_mark/parser.rb +31 -0
- data/lib/nora_mark/version.rb +3 -0
- data/lib/nora_mark.rb +46 -0
- data/nora_mark.gemspec +22 -0
- data/spec/created_files/.gitignore +1 -0
- data/spec/fixture/test_src_ja.nora +50 -0
- data/spec/nokogiri_test_helper.rb +41 -0
- data/spec/nora_mark_spec.rb +840 -0
- data/spec/spec_helper.rb +29 -0
- metadata +116 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module NoraMark
|
3
|
+
module Html
|
4
|
+
class ParagraphWriter
|
5
|
+
def initialize(generator)
|
6
|
+
@generator = generator
|
7
|
+
@context = generator.context
|
8
|
+
@writer_set = { use_paragraph_group: {
|
9
|
+
:paragraph =>
|
10
|
+
TagWriter.create('p', @generator, chop_last_space: true,
|
11
|
+
item_preprocessor: proc do |item|
|
12
|
+
add_class(item, 'noindent') if item[:children][0] =~/^(「|『|()/ # TODO: should be plaggable}
|
13
|
+
item
|
14
|
+
end
|
15
|
+
),
|
16
|
+
:paragraph_group =>
|
17
|
+
TagWriter.create("div", @generator,
|
18
|
+
item_preprocessor: proc do |item|
|
19
|
+
add_class item, 'pgroup'
|
20
|
+
item[:no_tag] = true unless @context.enable_pgroup
|
21
|
+
item
|
22
|
+
end
|
23
|
+
)
|
24
|
+
},
|
25
|
+
default: {
|
26
|
+
:paragraph =>
|
27
|
+
TagWriter.create(nil, @generator, chop_last_space: true,
|
28
|
+
item_preprocessor: proc do |item|
|
29
|
+
item[:no_tag] = true
|
30
|
+
item
|
31
|
+
end),
|
32
|
+
:paragraph_group =>
|
33
|
+
TagWriter.create("p", @generator,
|
34
|
+
item_preprocessor: proc do |item|
|
35
|
+
item[:children] = item[:children].inject([]) do |memo, item|
|
36
|
+
memo << { :type => :br, :args => [] } if !memo.last.nil? && memo.last[:type] == :paragraph && item[:type] == :paragraph
|
37
|
+
memo << item
|
38
|
+
end
|
39
|
+
item
|
40
|
+
end
|
41
|
+
)
|
42
|
+
}
|
43
|
+
}
|
44
|
+
end
|
45
|
+
def write(item)
|
46
|
+
writer_set = @writer_set[@context.paragraph_style]
|
47
|
+
writer_set = @writer_set['default'] if writer_set.nil?
|
48
|
+
writer_set[item[:type]].write(item)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module NoraMark
|
2
|
+
module Html
|
3
|
+
class TagWriter
|
4
|
+
include Util
|
5
|
+
attr_accessor :trailer, :item_preprocessors, :write_body_preprocessors
|
6
|
+
|
7
|
+
def self.create(tag_name, generator, item_preprocessor: nil, write_body_preprocessor: nil, trailer: "\n", chop_last_space: false)
|
8
|
+
instance = TagWriter.new(tag_name, generator, chop_last_space: chop_last_space)
|
9
|
+
instance.item_preprocessors << item_preprocessor unless item_preprocessor.nil?
|
10
|
+
instance.write_body_preprocessors << write_body_preprocessor unless write_body_preprocessor.nil?
|
11
|
+
instance.trailer = trailer
|
12
|
+
yield instance if block_given?
|
13
|
+
instance
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(tag_name, generator, **param)
|
17
|
+
@tag_name = tag_name
|
18
|
+
@generator = generator
|
19
|
+
@context = generator.context
|
20
|
+
@trailer = trailer
|
21
|
+
@item_preprocessors = []
|
22
|
+
@write_body_preprocessors = []
|
23
|
+
@param = param
|
24
|
+
end
|
25
|
+
|
26
|
+
def attr_string(attrs)
|
27
|
+
attrs.map do
|
28
|
+
|name, vals|
|
29
|
+
if vals.size == 0
|
30
|
+
''
|
31
|
+
else
|
32
|
+
" #{name}='#{vals.join(' ')}'"
|
33
|
+
end
|
34
|
+
end.join('')
|
35
|
+
end
|
36
|
+
|
37
|
+
def class_string(cls_array)
|
38
|
+
attr_string({'class' => cls_array})
|
39
|
+
end
|
40
|
+
|
41
|
+
def ids_string(ids_array)
|
42
|
+
attr_string({'id' => ids_array})
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_class(item, cls)
|
46
|
+
(item[:classes] ||= []) << cls
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_class_if_empty(item, cls)
|
50
|
+
add_class(item, cls) if item[:classes].nil? || item[:classes].size == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def tag_start(item)
|
54
|
+
return if item[:no_tag]
|
55
|
+
ids = item[:ids] || []
|
56
|
+
classes = item[:classes] || []
|
57
|
+
attr = item[:attrs] || {}
|
58
|
+
tag_name = @tag_name || item[:name]
|
59
|
+
@context << "<#{tag_name}#{ids_string(ids)}#{class_string(classes)}#{attr_string(attr)}"
|
60
|
+
if item[:no_body]
|
61
|
+
@context << " />"
|
62
|
+
else
|
63
|
+
@context << ">"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def output(string)
|
68
|
+
@context << string
|
69
|
+
end
|
70
|
+
|
71
|
+
def tag_end(item)
|
72
|
+
return if item[:no_tag]
|
73
|
+
tag_name = @tag_name || item[:name]
|
74
|
+
@context << "</#{tag_name}>#{@trailer}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def write(item)
|
78
|
+
@item_preprocessors.each { |x| item = instance_exec item.dup, &x }
|
79
|
+
@context.enable_pgroup, saved_ep = !(item[:args].include?('wo-pgroup') || !@context.enable_pgroup), @context.enable_pgroup
|
80
|
+
tag_start item
|
81
|
+
write_body item if !item[:no_body]
|
82
|
+
tag_end item if !item[:no_body]
|
83
|
+
@context.enable_pgroup = saved_ep
|
84
|
+
end
|
85
|
+
|
86
|
+
def write_body(item)
|
87
|
+
@write_body_preprocessors.each {
|
88
|
+
|x|
|
89
|
+
return if instance_exec(item, &x) == :done
|
90
|
+
}
|
91
|
+
write_children item
|
92
|
+
end
|
93
|
+
|
94
|
+
def write_children(item)
|
95
|
+
write_array(item[:children])
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_array(array)
|
99
|
+
return if array.nil? || array.size == 0
|
100
|
+
array.each { |x| @generator.to_html x }
|
101
|
+
@generator.context.chop_last_space if (@param[:chop_last_space])
|
102
|
+
end
|
103
|
+
|
104
|
+
def children_not_empty(item)
|
105
|
+
!item[:children].nil? && item[:children].size > 0 && item[:children].select { |x| (x.is_a? String) ? x.size >0 : !x.nil? }.size > 0
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module NoraMark
|
2
|
+
module Html
|
3
|
+
class WriterSelector
|
4
|
+
def initialize(generator, tag_writers = {}, trailer_default: "\n" )
|
5
|
+
@generator = generator
|
6
|
+
@common_tag_writer = TagWriter.create(nil, @generator, trailer: trailer_default)
|
7
|
+
@tag_writers = tag_writers
|
8
|
+
if !trailer_default.nil?
|
9
|
+
@tag_writers.each { |k, t|
|
10
|
+
if t.is_a? TagWriter
|
11
|
+
t.trailer = trailer_default
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(item)
|
19
|
+
writer = @tag_writers[item[:name]] || @common_tag_writer
|
20
|
+
writer.write(item)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
%% name = NoraMark::Parser
|
2
|
+
|
3
|
+
# literals
|
4
|
+
eof = !.
|
5
|
+
space = ' ' | '\t'
|
6
|
+
eof_comment = lh space* "#" (!eof .)*
|
7
|
+
comment = lh space* "#" (!nl .)* nl empty_line*
|
8
|
+
- = ( space | comment )*
|
9
|
+
empty_line = lh - nl
|
10
|
+
nl = /\r?\n/
|
11
|
+
lh = /^/
|
12
|
+
le = nl | eof
|
13
|
+
word = < /[\w0-9]/ ( '-' | /[\w0-9]/ )* > { text }
|
14
|
+
num = < [0-9]+ > { text.to_i }
|
15
|
+
|
16
|
+
|
17
|
+
#common syntax
|
18
|
+
classname = '.' word:classname { classname }
|
19
|
+
classnames = (classname)*:classnames { classnames }
|
20
|
+
idname = '#' word:idname { idname }
|
21
|
+
idnames = (idname)*:idnames { idnames }
|
22
|
+
|
23
|
+
commandname = word:name idnames?:idnames classnames?:classes { {:name => name, :ids => idnames, :classes => classes} }
|
24
|
+
parameter = < ( /[^,)]/* | '"' /[^"]/* '"' | "'" /[^']/* "'" ) > { text }
|
25
|
+
parameters = < parameter (',' parameter)* > { text }
|
26
|
+
command = commandname:cn ('(' - parameters:arg - ')')? { arg ||= ''; cn.merge({ :args => arg.split(',') }) }
|
27
|
+
|
28
|
+
# paragraph
|
29
|
+
implicit_paragraph = < (!paragraph_delimiter - documentline:p -) > { create_item(:paragraph, nil, p, raw: text) }
|
30
|
+
paragraph = explicit_paragraph | implicit_paragraph
|
31
|
+
|
32
|
+
# paragraph_group
|
33
|
+
paragraph_group = < paragraph+:p empty_line* > { create_item(:paragraph_group, nil, p, raw: text) }
|
34
|
+
|
35
|
+
# explicit block
|
36
|
+
blockhead = lh - command:command - '{' - nl empty_line* { command }
|
37
|
+
blockend = lh - '}' - le empty_line*
|
38
|
+
blockbody = (!blockend block)+:body { body }
|
39
|
+
explicit_block = < blockhead:head - blockbody:body - blockend > { create_item(:block, head, body, raw: text) }
|
40
|
+
|
41
|
+
# preformatted block
|
42
|
+
preformatted_command = command:command &{ ['pre', 'precode'].include? command[:name] }
|
43
|
+
preformatted_command_head = lh - preformatted_command:command - '<<' &/[\w0-9]/ { command }
|
44
|
+
preformat_end(start) = lh word:delimiter &{ delimiter == start }
|
45
|
+
preformatted_block = < lh - preformatted_command_head:command !nl word:delimiter nl (!preformat_end(delimiter) (lh charstring nl))+:content preformat_end(delimiter) > { create_item(:preformatted, command, content, raw: text) }
|
46
|
+
|
47
|
+
# inline command
|
48
|
+
inline = img_inline | common_inline
|
49
|
+
common_inline = < '[' command:c '{' documentcontent_except('}'):content '}' ']' > { create_item(:inline, c, content, raw: text) }
|
50
|
+
img_command = command:c &{ c[:name] == 'img' && c[:args].size == 2}
|
51
|
+
img_inline = < '[' img_command:c ']' > { create_item(:inline, c, nil, raw: text) }
|
52
|
+
|
53
|
+
# special line commands
|
54
|
+
commandname_for_special_line_command = newpage_command | explicit_paragraph_command
|
55
|
+
|
56
|
+
# newpage
|
57
|
+
newpage_command = command:command &{ command[:name] == 'newpage' }
|
58
|
+
newpage = < lh - newpage_command:c ':' documentcontent?:content - nl > { create_item(:newpage, c, content, raw:text) }
|
59
|
+
|
60
|
+
# explicit paragraph
|
61
|
+
explicit_paragraph_command = command:c &{ c[:name] == 'p' }
|
62
|
+
explicit_paragraph = < lh - explicit_paragraph_command:c ':' documentcontent?:content le empty_line*> { create_item(:paragraph, c, content, raw:text) }
|
63
|
+
|
64
|
+
|
65
|
+
# unordered list
|
66
|
+
unordered_list = < unordered_item+:items > { create_item(:ul, nil, items, raw: text) }
|
67
|
+
unordered_item = < lh '*:' documentcontent:content le > { create_item(:li, nil, content, raw: text) }
|
68
|
+
|
69
|
+
# ordered list
|
70
|
+
ordered_list = < ordered_item+:items > { create_item(:ol, nil, items, raw: text) }
|
71
|
+
ordered_item = < lh num ':' documentcontent:content le > { create_item(:li, nil, content, raw: text) }
|
72
|
+
|
73
|
+
# definition list
|
74
|
+
definition_list = < definition_item+:items > { create_item(:dl, nil, items, raw: text) }
|
75
|
+
definition_item = < lh ';:' - documentcontent_except(':'):term ':' - documentcontent:definition le > { create_item(:dtdd, {:args => [term, definition]}, nil, raw: text) }
|
76
|
+
|
77
|
+
items_list = unordered_list | ordered_list | definition_list
|
78
|
+
|
79
|
+
|
80
|
+
# generic line command
|
81
|
+
line_command = < lh - !commandname_for_special_line_command command:c ':' documentcontent?:content - le empty_line* > { create_item(:line_command, c, content, raw: text) }
|
82
|
+
|
83
|
+
# blocks
|
84
|
+
line_block = items_list | line_command
|
85
|
+
block = (preformatted_block | headed_section | line_block | explicit_block | paragraph_group ):block empty_line* {block}
|
86
|
+
block_delimiter = blockhead | blockend
|
87
|
+
paragraph_delimiter = block_delimiter | preformatted_command_head | line_block | newpage | headed_start
|
88
|
+
|
89
|
+
# markdown-style headings
|
90
|
+
h_start_mark(n) = < '='+ ':' > &{ text.length - 1 == n }
|
91
|
+
h_markup_terminator(n) = lh - < '='+ ':' > &{ text.length - 1 <= n }
|
92
|
+
|
93
|
+
h_start(n) = lh - h_start_mark(n) charstring:s le { { level: n, heading: s } }
|
94
|
+
h_section(n) = < h_start(n):h (!h_markup_terminator(n) !eof block)+:content > { create_item(:h_section, h, content, raw: text) }
|
95
|
+
|
96
|
+
headed_start = h_start(1) | h_start(2) | h_start(3) | h_start(4) | h_start(5) | h_start(6)
|
97
|
+
headed_section = h_section(1) | h_section(2)| h_section(3)| h_section(4)| h_section(5) | h_section(6)
|
98
|
+
|
99
|
+
#header
|
100
|
+
stylesheets = < lh - 'stylesheets:' !le charstring:s nl > { create_item(:stylesheets, {:stylesheets => s.split(',').map(&:strip)}, nil, raw:text) }
|
101
|
+
title = < lh - 'title:' !le charstring:t nl > { create_item(:title, {:title => t }, nil, raw:text) }
|
102
|
+
lang = < lh - 'lang:' !le charstring:l nl > { create_item(:lang, {:lang => l }, nil, raw:text) }
|
103
|
+
paragraph_style = < lh - 'paragraph-style:' !le charstring:l nl > { create_item(:paragraph_style, {:paragraph_style => l }, nil, raw:text) }
|
104
|
+
header = (stylesheets | title | lang | paragraph_style ) empty_line*
|
105
|
+
|
106
|
+
# texts
|
107
|
+
char = < /[[:print:]]/ > { text }
|
108
|
+
charstring = < char* > { text }
|
109
|
+
char_except(e) = char:c &{ c != e }
|
110
|
+
charstring_except(e) = < char_except(e)* > { text }
|
111
|
+
documentcontent_except(e) = (inline | !inline char_except(e))+:content ~parse_text(content)
|
112
|
+
documentcontent = (inline | !inline char)+:content ~parse_text(content)
|
113
|
+
documentline = lh documentcontent:content le { content }
|
114
|
+
|
115
|
+
#page
|
116
|
+
headers = header*:headers { create_item(:headers, nil, headers) }
|
117
|
+
page = headers:headers - (!newpage block)*:blocks { create_item(:page, nil, [headers] + blocks.select{ |x| !x.nil?}) }
|
118
|
+
newpaged_page = newpage:newpage page:page { page[:children] = page[:children].unshift newpage; page }
|
119
|
+
#root
|
120
|
+
root = page:page newpaged_page*:pages - eof_comment? eof { [ page ] + pages }
|
121
|
+
|
122
|
+
|
123
|
+
|