casetdown 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7db61cc282539ad38b94b953931b8801cc91dbd43c1bfe2941c7a56fce92abaa
4
+ data.tar.gz: 79034f081e7a5bcc75b0e00a579ed5c26c312a4acfc909457c0247b5cb806052
5
+ SHA512:
6
+ metadata.gz: f6a439b490240278afb3e86d3f6061b3b8115023d6daada7f5075ad3ceb6f304a671332b45089904ac571892078598db3e0e757cab62d08f979e4abfdd794981
7
+ data.tar.gz: d0c622a8dde0dbe7457ba7909bfa82bf8a8a25fda7275eceaed39589bb4070dcbe7d2637a770d2698c428de7e9322158a2e9873bd1cbea9c44e604e599ab5d10
data/CasetDown/bin/cm ADDED
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env ruby
2
+ #coding:utf-8
3
+ require 'CasetDown/casetdown'
4
+
5
+ pool,check,source = ARGV,false,false
6
+
7
+ if pool.include?('-h') or pool.include?('--help')
8
+ puts %Q{<<LIST>>
9
+ cm [options] <filename>[.md]
10
+
11
+ options:
12
+ --help 查看帮助
13
+ --check 检查文档结构
14
+ --source 查看文档中的源代码
15
+ --rtab 导入外部表格到文档中
16
+ --wtab 导出内部表格到外部文件
17
+ --input 导出源代码文件并渲染文档
18
+ --output 导出代码运行结果
19
+ --all 导出源代码和运行结果(input+output)
20
+ --preview 预览渲染文档(不生成渲染结果)
21
+ --debug 调试开关, 不删除中间代码
22
+ --rendfile 渲染文档并生成一个时间戳(默认)
23
+ };exit
24
+ end
25
+
26
+ if pool.include?('--check')
27
+ check = true
28
+ pool.delete '--check'
29
+ end
30
+ if pool.include?('--source')
31
+ source = true
32
+ pool.delete '--source'
33
+ end
34
+
35
+ table,file = [],[]
36
+ ['--rtab','--wtab'].each do|tag|
37
+ if pool.include?(tag)
38
+ table << tag
39
+ pool.delete tag
40
+ end
41
+ end
42
+ ['--input','--output','--all','--preview','--debug'].each do|tag|
43
+ if pool.include?(tag)
44
+ file << tag
45
+ pool.delete tag
46
+ end
47
+ end
48
+
49
+ path = pool.last
50
+ path = Dir["./*.md"].last unless path
51
+ exit unless path
52
+
53
+ if File.exist?(path)
54
+ cds = CasetDown.load path
55
+ elsif File.exist?("#{path}.md")
56
+ cds = CasetDown.load "#{path}.md"
57
+ else
58
+ puts "File #{path} not exist.";exit
59
+ end
60
+
61
+ if check
62
+ puts "checking>>>>"
63
+ pp CasetDown::Check.all(cds)
64
+ exit
65
+ end
66
+ if source
67
+ puts "source>>>>"
68
+ pp CasetDown.search_codes(cds)
69
+ exit
70
+ end
71
+
72
+ tmp = CasetDown.render_table(cds, opt: table.map{|i|i[2..-1].to_sym})
73
+ CasetDown.run_to_file cds, stream: tmp[:text], data: tmp[:data], fact: tmp[:fact], file: file.map{|i|i[2..-1].to_sym}+[:rendfile]
data/CasetDown/bin/cml ADDED
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ #coding:utf-8
3
+ require 'digest'
4
+ require 'fileutils'
5
+
6
+ if ARGV.empty?
7
+ cd = Dir.pwd
8
+ fs = '*'
9
+ else
10
+ cd = ARGV.first
11
+ fs = ARGV.size>1 ? ARGV.last : '*'
12
+ (warn "No file(#{cd}/#{fs}) exist.";exit) unless File.exist?("#{cd}/#{fs}")
13
+ end
14
+
15
+ warn "DONT USE POSTFIX .latest.md, FILENAME.latest.md WONT BE UPDATED!"
16
+
17
+ table = {}
18
+ loop do
19
+ Dir["#{cd}/#{fs}"].each do|path|
20
+ next unless path[-3..-1]=='.md'
21
+ next if path[-10..-1]=='.latest.md'
22
+ unless table[path]
23
+ table[path] = [File.stat(path).atime, File.stat(path).size, Digest::MD5.hexdigest(File.read(path))]
24
+ end
25
+ otime, osize, odigest = table[path]
26
+ atime = File.stat(path).atime
27
+ asize = File.stat(path).size
28
+ if atime>otime # && (asize != osize || asize == osize)
29
+ adigest = Digest::MD5.hexdigest File.read(path)
30
+ if adigest!= odigest
31
+ `cm #{path}`
32
+ table[path] = [atime, asize, adigest]
33
+ latpath = (Dir["#{path[0..-4]}.*.md"] - ["#{path[0..-4]}.latest.md"]).max
34
+ File.delete("#{path[0..-4]}.latest.md")
35
+ puts ">#{path} changed -> #{latpath}(#{adigest}).";$stdout.flush
36
+ FileUtils.copy_file(latpath, "#{path[0..-4]}.latest.md")
37
+ end
38
+ end
39
+ end
40
+ sleep 1
41
+ end
@@ -0,0 +1,80 @@
1
+ #coding:utf-8
2
+ require 'csv'
3
+
4
+ module CasetDown
5
+ module_function
6
+
7
+ # handle table
8
+ def loadcsv path,format=:raw
9
+ begin
10
+ csv = File.read(path).force_encoding("GBK").encode("UTF-8")
11
+ rescue
12
+ csv = File.read(path).encode("UTF-8")
13
+ end
14
+ content = CSV.parse(csv, headers:(format==:csv))
15
+ table = content.绑定表头 content[0]
16
+ return table
17
+ end
18
+
19
+ def maketab table
20
+ printab = table.map{|row|"|"+row.join("|")+"|"}
21
+ printab.insert 1, Array.new(table[0].size, '|---').join+"|"
22
+ return printab.join("\n")
23
+ end
24
+
25
+ def render_table cds, option={ stream: nil, opt: []} # empty, :wtab, :rtab, :rendfile
26
+ $_tmp_endata_ = {}
27
+ # write table to csv file
28
+ docs = CasetDown::Check.all(cds)
29
+ docs.each_with_index do|doc,index|
30
+ if doc[:name]=='table'
31
+ table_name = ( index-1 >= 0 && docs[index-1][:name]=='p' && docs[index-1][:text].strip[0..1]=='((' && docs[index-1][:text].strip[-2..-1]=='))' ) ? docs[index-1][:text][2..-3].strip : 'Anonymous'
32
+ table_body = [doc[:head]]+doc[:body]
33
+ $_tmp_endata_["[table@endata ~]$ruby #{table_name}"] = table_body.to_s
34
+ CSV.open(table_name+".csv","w")do|writer|
35
+ table_body.each do|record|
36
+ writer << record
37
+ end
38
+ end if option[:opt].include?(:wtab)
39
+ end
40
+ end
41
+
42
+ # load table from csv file
43
+ result = {}
44
+ codtab = docs.select{|doc|doc[:name]=='code'}
45
+ codtab.each do|code|
46
+ interpreter, namespace, config, target, flow, segment, input, output, rows, cols = magic_head code
47
+ next unless output.to_s[-4..-1]=='.csv'
48
+ tab = loadcsv(output)
49
+ seltab = rows.inject([tab[0]]) do|seltab,row|
50
+ seltab += [tab[row]] if row.is_a?(Integer)
51
+ seltab += tab[row] if row.is_a?(Range)
52
+ seltab
53
+ end
54
+ seltab.绑定表头 seltab[0]
55
+ newtab = seltab.选择(*cols)
56
+ result[output] = maketab(newtab)
57
+ $_tmp_endata_["[table@endata ~]$ruby #{output}"] = newtab.to_s
58
+ end
59
+
60
+ $_tmp_endata_.each do|head, table|
61
+ ($_scr_tail_ ||= "__END__") << "\n#{head}\n#{table}\n"
62
+ ($_erl_tail_ ||= "") << "\n\n"+ToErlang.fact(head.split('$ruby ')[1], ToErlang.table(eval(table)))
63
+ end
64
+ origin_text = option[:stream] || cds[:doc].text
65
+ result.each do|output, newtab|
66
+ wrapped = "((#{output}))\n\n#{newtab}"
67
+ origins = CasetDown.search_output(origin_text, output)
68
+ origins.each do|origin|
69
+ origin_text = origin_text.gsub(origin, wrapped)
70
+ end
71
+ end if option[:opt].include?(:rtab)
72
+
73
+ if option[:opt].include?(:rendfile)
74
+ pazz = cds[:path].split('.')
75
+ pazz.insert(-2,Time.new.strftime("%Y%m%d%H%M%S"))
76
+ File.write pazz.join('.'), origin_text
77
+ end
78
+ return { text: origin_text, data: $_scr_tail_, fact: ($_erl_tail_||nil)}
79
+ end
80
+ end
@@ -0,0 +1,228 @@
1
+ #coding:utf-8
2
+
3
+ module CasetDown
4
+ module_function
5
+
6
+ def textchange text
7
+ text.gsub('&gt;','>').gsub('&lt;','<')
8
+ end
9
+
10
+ def wrap_output text, id, tag='output'
11
+ output_start = "```shell\n##{tag}>#{id}"
12
+ output_finish= "```"
13
+ return [output_start, text, output_finish].join("\n")
14
+ end
15
+
16
+ def search_output text, id, tag='output'
17
+ # [TODO] REFACTORING for recognition between '#output>path' and '#output> path'
18
+ output_start = "```shell\n##{tag}>#{id}"
19
+ output_finish= "```"
20
+ match = TextAbstract.match_paragraph text, output_start, output_finish
21
+ return match.map{|m|[output_start, m, output_finish].join}
22
+ end
23
+
24
+ def magic_head code
25
+ states = textchange(code[:code]).split("\n")
26
+ interpreter = code[:class]
27
+ script = states.find{|s|s[0..4]=='#src>'||s[0..7]=='#script>'}.to_s.split('>')[1]
28
+ interpreter = script if script # :default
29
+
30
+ namespace = states.find{|s|s[0..3]=='#ns>'||s[0..10]=='#namespace>'}.to_s.split('>')[1] || :ns
31
+ config = states.find{|s|s[0..5]=='#conf>'||s[0..7]=='#config>'}.to_s.split('>')[1] || :conf
32
+ target = states.find{|s|s[0..4]=='#dst>'||s[0..7]=='#target>'}.to_s.split('>')[1]
33
+
34
+ flow = states.find{|s|s[0..5]=='#flow>'||s[0..5]=='#step>'}.to_s.split('>')[1] || :flow
35
+ segment = states.find{|s|s[0..4]=='#seg>'||s[0..8]=='#segment>'}.to_s.split('>')[1] || :seg
36
+
37
+ input = states.find{|s|s[0..3]=='#in>'||s[0..6]=='#input>'}.to_s.split('>')[1] || :in
38
+ output = states.find{|s|s[0..4]=='#out>'||s[0..7]=='#output>'}.to_s.split('>')[1] || :out
39
+
40
+ [interpreter, namespace, target, flow, segment, input, output].each{|tag|tag.strip! if tag.instance_of?(String)}
41
+
42
+ rows = if row = states.find{|s|s[0..4]=='#row>'}.to_s.split('>')[1]
43
+ row.gsub('~','..').split(',').map{|r|eval(r)}
44
+ else
45
+ :rows
46
+ end
47
+ cols = if col = states.find{|s|s[0..4]=='#col>'}.to_s.split('>')[1]
48
+ col.split(',').map{|c|
49
+ c.split('').inject(true){|f,chr|
50
+ f&&(('0'..'9').to_a+['-','~']).include?(chr)
51
+ } ? eval(c.gsub('~','..')) : c.strip
52
+ }
53
+ else
54
+ :cols
55
+ end
56
+
57
+ return interpreter, namespace, config, target, flow, segment, input, output, rows, cols
58
+ end
59
+
60
+ def search_codes cds
61
+ codes = CasetDown::Check.all(cds).select{|cd|cd[:name]=='code'}
62
+
63
+ table = codes.inject({}) do|table, code|
64
+ interpreter, namespace, config, target, flow, segment, input, output, rows, cols = magic_head code
65
+
66
+ content = textchange(code[:code]).split("\n")[1..-1].select{|s|
67
+ !['#ns>','#in>'].include?(s[0..3]) &&
68
+ !['#out>','#src>','#dst>','#seg>','#row>','#col>'].include?(s[0..4]) &&
69
+ !['#flow>','#step>','#conf>'].include?(s[0..5]) &&
70
+ !['#input>'].include?(s[0..6]) &&
71
+ !['#script>','#target>','#output>','#config>'].include?(s[0..7]) &&
72
+ !['#segment>'].include?(s[0..8]) &&
73
+ !['#namespace>'].include?(s[0..10])
74
+ }.join("\n")
75
+ table[[code.object_id, interpreter, namespace, config, target, [rows,cols], flow, segment, input, output]] = content
76
+ table
77
+ end
78
+ return table
79
+ end
80
+
81
+ def run cds
82
+ table = CasetDown.search_codes(cds)
83
+ $_tmp_global_ = {}
84
+ $_scr_head_ = ['#coding:utf-8','["EnData/endata","EnData/endata-app","TinText/tum","TinText/cache","TinText/tin_text","TinText/tintext","Tabot/newtab","Tabot/simtab"].each{|mod|require(mod)}'].join("\n")
85
+ result = {}
86
+ table.each do|key, text|
87
+ oid, interpreter, ns, conf, target, range, flow, seg, input, output = key
88
+ unless conf==:conf # 注意全局配置累积生效,所以有先后顺序
89
+ $_tmp_global_.merge!(
90
+ (File.exist?(conf) ? YAML.load(File.read conf) : {}) ||
91
+ (File.exist?("#{conf}.yml") ? YAML.load(File.read "#{conf}.yml") : {}) ||
92
+ (File.exist?("#{conf}.yaml") ? YAML.load(File.read "#{conf}.yaml") : {})
93
+ )
94
+ $_scr_tail_ = "\n__END__\n[global@endata ~]$ruby global\n#{$_tmp_global_.to_s}"
95
+ $_erl_tail_ = $_tmp_global_.empty? ? '' : ToErlang.fact('global', (ToErlang.hash($_tmp_global_)) )
96
+ end
97
+ if interpreter=='shell' || interpreter==:default
98
+ if RUBY_PLATFORM.include?('mingw')
99
+ else
100
+ File.write "./~tmp.sh",text
101
+ running = `bash ./~tmp.sh`
102
+ File.delete "./~tmp.sh"
103
+ end
104
+ elsif interpreter=='ruby'
105
+ File.write "./~tmp.rb","#{$_scr_head_}\n\n#{text}\n\n#{$_scr_tail_}"
106
+ running = `ruby ./~tmp.rb`
107
+ File.delete "./~tmp.rb"
108
+ elsif interpreter=='erlang' || interpreter=='erl'
109
+ File.write "./_tmp.erl","-module(_tmp).\n-export([main/1]).\n\n#{text}\n\n#{$_erl_tail_}\n\nfact(UnknownMessage)->\n io:format(\"~p\",[UnknownMessage])."
110
+ running = `escript ./_tmp.erl`
111
+ File.delete "./_tmp.erl"
112
+ elsif interpreter=='python3' || interpreter=='python'
113
+ File.write "./~tmp.py",text
114
+ running = `python ./~tmp.py`
115
+ File.delete "./~tmp.py"
116
+ elsif interpreter=='lms'
117
+ running = `lms "#{text}"`
118
+ elsif interpreter=='lmx'
119
+ running = `lmx "#{text}"`
120
+ else
121
+ end
122
+ result[input] = running
123
+ end
124
+ return result
125
+ end
126
+
127
+ def run_to_file cds,option={stream: nil, data: nil, fact: nil,file:[]} # empty, :input, :output, :all
128
+ $_tmp_global_ = {}
129
+ $_scr_head_ = ['#coding:utf-8','["EnData/endata","EnData/endata-app","TinText/tum","TinText/cache","TinText/tin_text","TinText/tintext","Tabot/newtab","Tabot/simtab"].each{|mod|require(mod)}'].join("\n")
130
+ $_scr_tail_ = option[:data] ? option[:data] : ''
131
+ $_erl_tail_ = option[:fact] ? option[:fact] : ''
132
+
133
+ table = CasetDown.search_codes(cds)
134
+ result = {}
135
+ table.each do|key, text|
136
+ oid, interpreter, ns, conf, target, range, flow, seg, input, output = key
137
+ unless conf==:conf # 注意全局配置累积生效,所以有先后顺序
138
+ $_tmp_global_.merge!(
139
+ (File.exist?(conf) ? YAML.load(File.read conf) : {}) ||
140
+ (File.exist?("#{conf}.yml") ? YAML.load(File.read "#{conf}.yml") : {}) ||
141
+ (File.exist?("#{conf}.yaml") ? YAML.load(File.read "#{conf}.yaml") : {})
142
+ )
143
+ $_scr_tail_ = $_scr_tail_.empty? ? "\n__END__\n[global@endata ~]$ruby global\n#{$_tmp_global_.to_s}" : "#{$_scr_tail_}\n[global@endata ~]$ruby global\n#{$_tmp_global_.to_s}"
144
+ $_erl_tail_ = $_tmp_global_.empty? ? $_erl_tail_ : $_erl_tail_+"\n\n"+ToErlang.fact('global', (ToErlang.hash($_tmp_global_)) )
145
+ end
146
+ if interpreter==:default
147
+ # NOTHING
148
+ elsif interpreter=='shell' || interpreter=='bash'
149
+ if RUBY_PLATFORM.include?('mingw')
150
+ else
151
+ File.write "./~tmp.sh",text
152
+ running = `bash ./~tmp.sh`
153
+ File.delete "./~tmp.sh" unless option[:file].include?(:debug)
154
+ end
155
+ elsif interpreter=='ruby'
156
+ File.write "./~tmp.rb","#{$_scr_head_}\n\n#{text}\n\n#{$_scr_tail_}"
157
+ running = `ruby ./~tmp.rb`
158
+ File.delete "./~tmp.rb" unless option[:file].include?(:debug)
159
+ elsif interpreter=='erlang' || interpreter=='erl'
160
+ File.write "./_tmp.erl","-module(_tmp).\n-export([main/1]).\n\n#{text}\n\n#{$_erl_tail_}\n\nfact(UnknownMessage)->\n io:format(\"~p\",[UnknownMessage])."
161
+ running = `escript ./_tmp.erl`
162
+ File.delete "./_tmp.erl" unless option[:file].include?(:debug)
163
+ elsif interpreter=='python3' || interpreter=='python'
164
+ File.write "./~tmp.py",text
165
+ running = `python ./~tmp.py`
166
+ File.delete "./~tmp.py" unless option[:file].include?(:debug)
167
+ elsif interpreter=='lms'
168
+ running = `lms "#{text}"`
169
+ elsif interpreter=='lmx'
170
+ running = `lmx "#{text}"`
171
+ else
172
+ end
173
+ result[input] = running
174
+ File.write input, text if !(option[:file] & [:input, :all]).empty? && input.instance_of?(String)
175
+ end
176
+
177
+ origin_text = option[:stream] || cds[:doc].text
178
+ result.each do|input, running| # input == output
179
+ File.write input, running if !(option[:file] & [:output, :all]).empty? && input.instance_of?(String)
180
+ origins = CasetDown.search_output(origin_text, input)
181
+ wrapped = CasetDown.wrap_output(running,input)
182
+ if origins.empty?
183
+ origins = CasetDown.search_output(origin_text, input, 'out')
184
+ wrapped = CasetDown.wrap_output(running,input, 'out')
185
+ end
186
+ origins.each do|origin|
187
+ # [TODO] 无法解决不同后缀的前置匹配替换问题,待修复
188
+ origin_text = origin_text.gsub(origin, wrapped)
189
+ end
190
+ end
191
+
192
+ if option[:file].include?(:rendfile)
193
+ if option[:file].include?(:preview)
194
+ prvw = cds[:path].split('.')
195
+ prvw.insert(-2,'preview')
196
+ File.write prvw.join('.'), origin_text
197
+ else
198
+ pazz = cds[:path].split('.')
199
+ pazz.insert(-2,Time.new.strftime("%Y%m%d%H%M%S"))
200
+ File.write pazz.join('.'), origin_text
201
+ end
202
+ end
203
+ return origin_text
204
+ end
205
+ end
206
+
207
+ module ToErlang
208
+ module_function
209
+
210
+ def table data
211
+ fields = data.first
212
+ erlang_data = data.drop(1).map do |row|
213
+ row_data = fields.zip(row).map do |field, value|
214
+ "{\"#{field}\", \"#{value}\"}"
215
+ end.join(",")
216
+ "{#{row_data}}"
217
+ end
218
+ "[#{erlang_data.join(", ")}]"
219
+ end
220
+
221
+ def hash data
222
+ "{"+data.map{|k,v|"{\"#{k}\", \"#{v}\"}"}.join(",")+"}"
223
+ end
224
+
225
+ def fact name, body
226
+ "fact(\"#{name.to_s}\")->\n #{body};"
227
+ end
228
+ end
@@ -0,0 +1,117 @@
1
+ #coding:utf-8
2
+
3
+ module CasetDown
4
+ module Check
5
+ module_function
6
+
7
+ def all cds
8
+ epool = cds[:src].stack.inject([]) do|epool, cd|
9
+ res = nil
10
+ [:head, :table, :para, :pre, :list, :quote].each do|tag|
11
+ res ||= CasetDown::Check.send tag, cd
12
+ end
13
+ epool += res.instance_of?(Array) ? res : [res]
14
+ end.compact
15
+ end
16
+
17
+ def head cd
18
+ if %w{h1 h2 h3 h4 h5 h6}.include? cd.name
19
+ unless cd.elements.empty?
20
+ ha = {name: cd.name, text: cd.attributes[:text], inline: [] }
21
+ cd.elements.each{|sub|ha[:inline]<<(sub.name=='a' ? a(sub) : {})}
22
+ ha
23
+ else
24
+ {name: cd.name, text: cd.attributes[:text] }
25
+ end
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ def table cd
32
+ if cd.name=='table'
33
+ hd = cd.to_doc['table'][1]['thead'][1]['tr'][1..-1].map{|th|th['th'][0][:text]}
34
+ bd = cd.to_doc['table'][2]['tbody'][1..-1].map{|tr|tr['tr'][1..-1].map{|td|td['td'][0][:text]}}
35
+ {name: 'table', head: hd, body: bd}
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def para cd
42
+ if cd.name=='p'
43
+ cur = []
44
+ cur << (cd.attributes[:text] ? {name: 'p', text: cd.attributes[:text]||cd.attributes["text"]} : nil)
45
+ cd.elements.each do|cs|
46
+ cur << code(cs)
47
+ cur << img(cs)
48
+ cur << a(cs)
49
+ end
50
+ return cur
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ def img cd
57
+ if cd.name=='img'
58
+ {name: 'img', src: cd.attributes['src'], alt: cd.attributes['alt']}
59
+ else
60
+ nil
61
+ end
62
+ end
63
+
64
+ def a cd
65
+ if cd.name=='a'
66
+ {name: 'a', href: cd.attributes['href'], text: cd.attributes[:text]}
67
+ else
68
+ nil
69
+ end
70
+ end
71
+
72
+ def pre cd
73
+ if cd.name=='pre'
74
+ code(cd.elements.first)
75
+ else
76
+ nil
77
+ end
78
+ end
79
+
80
+ def code cd
81
+ if cd.name=='code'
82
+ {name: 'code', class: (cd.attributes['class'] ? cd.attributes['class'] : :default), code: cd.attributes[:text]}
83
+ else
84
+ nil
85
+ end
86
+ end
87
+
88
+ def list cd
89
+ # TODO: ONLY TOP LEVEL, PENDING TO RECURSION
90
+ if cd.name=='ul'
91
+ subs = cd.elements.inject([]) do|subs, li|
92
+ sub = {name: 'li', text: li.attributes[:text]}
93
+ li.elements.each do|subsub|
94
+ sub.merge!(inline: quote(subsub)) if subsub.name=='blockquote'
95
+ sub.merge!(inline: code(subsub)) if subsub.name=='code'
96
+ sub.merge!(inline: img(subsub)) if subsub.name=='img'
97
+ sub.merge!(inline: a(subsub)) if subsub.name=='a'
98
+ end
99
+ subs << sub
100
+ end
101
+ {name: 'ul', list: subs}
102
+ else
103
+ nil
104
+ end
105
+ end
106
+
107
+ def quote cd
108
+ # TODO: ONLY TOP LEVEL, PENDING TO RECURSION
109
+ if cd.name=='blockquote'
110
+ {name: 'blockquote', quote: cd.to_doc['blockquote'][1]['p'][0][:text]}
111
+ else
112
+ nil
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,13 @@
1
+ #coding:utf-8
2
+ #USE: XMLUtils/XmlUtils
3
+ #USE: TextUtils/text_abstract
4
+ #USE: Tabot/simtab
5
+
6
+ [
7
+ 'EnData/endata', 'EnData/endata-app',
8
+ 'Tabot/newtab', 'Tabot/simtab',
9
+ 'TextUtils/text_abstract', 'TextUtils/text_absparser', 'TextUtils/text_mind',
10
+ 'TinText/tum', 'TinText/cache', 'TinText/tin_text', 'TinText/tintext',
11
+ 'XMLUtils/XmlUtils',
12
+ 'CasetDown/casetter', 'CasetDown/casetdoc', 'CasetDown/casetcode', 'CasetDown/casetable'
13
+ ].each{|lib|require lib}
@@ -0,0 +1,72 @@
1
+ #coding:utf-8
2
+ require 'rdiscount'
3
+
4
+ module CasetDown
5
+ module_function
6
+
7
+ #################################################################################################
8
+ # Loader #
9
+ #################################################################################################
10
+ def load path, option={}
11
+ args = {path: path}.merge(option)
12
+ text = (File.read path).gsub("\r","").split("\n").map{|line|line.rstrip}.join("\n")
13
+ return self.parse(text, args)
14
+ end
15
+
16
+ #################################################################################################
17
+ # InlineStructs #
18
+ #################################################################################################
19
+ def parse text, option={}
20
+ args = {
21
+ parser: RDiscount, # parser := {Kramdown::Document | RDiscount} ★ Kramdown::Document无法处理好多重空行问题
22
+ doc: true,
23
+ tree: true,
24
+ src: true,
25
+ path: 'tmp'
26
+ }.merge(option)
27
+ document = args[:parser].new(text)
28
+ html = "<html>"+document.to_html+"</html>"
29
+ script, tree = [],{}
30
+ XmlParser.parse(html).elements.each do|node|
31
+ doc = node.to_a
32
+ script << node
33
+ tree[doc[0]] ||= []
34
+ tree[doc[0]] << node
35
+ end
36
+ result = {path: args[:path]}
37
+ result[:src] = Script.new(native: script) if args[:src]
38
+ result[:tree] = Tree.new(native: tree) if args[:tree]
39
+ result[:doc] = document if args[:doc]
40
+ return result
41
+ end
42
+
43
+ class Tree
44
+ def initialize options={}
45
+ @content ||= options[:native]
46
+ @content ||= JSON.parse(options[:json]) if options[:json]
47
+ @content ||= CasetDown.load(options[:path])[-1] if options[:path]
48
+ @content ||= CasetDown.parse(options[:doc])[-1] if options[:doc]
49
+ @content ||= {}
50
+ end
51
+
52
+ def nodes &block
53
+ block = lambda{|content|content} unless block
54
+ block.call(@content)
55
+ end
56
+ end
57
+
58
+ class Script
59
+ def initialize options={}
60
+ @content ||= options[:native]
61
+ @content ||= JSON.parse(options[:json]) if options[:json]
62
+ @content ||= CasetDown.load(options[:path])[0] if options[:path]
63
+ @content ||= CasetDown.parse(options[:doc])[0] if options[:doc]
64
+ @content ||= []
65
+ end
66
+
67
+ def stack &block
68
+ block = lambda{|content|content} unless block
69
+ block.call(@content)
70
+ end
71
+ end
72
+ end