kwaff 1.0.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.
- data/COPYING +340 -0
- data/ChangeLog.txt +118 -0
- data/README.txt +96 -0
- data/bin/kwaff +131 -0
- data/doc/docstyle.css +307 -0
- data/doc/examples.html +1283 -0
- data/doc/users-guide.html +395 -0
- data/examples/Makefile +17 -0
- data/examples/ant/Makefile +7 -0
- data/examples/ant/build.kwaff +57 -0
- data/examples/hibernate/AuctionItem.hbm.kwaff +45 -0
- data/examples/hibernate/Bid.hbm.kwaff +42 -0
- data/examples/hibernate/Makefile +9 -0
- data/examples/hibernate/User.hbm.kwaff +65 -0
- data/examples/hibernate/hibernate.cfg.kwaff +17 -0
- data/examples/seasar2/Makefile +9 -0
- data/examples/seasar2/seasar2.kwaff +62 -0
- data/examples/servlet/Makefile +9 -0
- data/examples/servlet/web.kwaff +37 -0
- data/examples/spring/Makefile +9 -0
- data/examples/spring/applicationContext.kwaff +147 -0
- data/examples/xhtml/Makefile +9 -0
- data/examples/xhtml/README.kwaff +84 -0
- data/examples/xi/Makefile +9 -0
- data/examples/xi/bbs.kwaff +58 -0
- data/lib/kwaff.rb +85 -0
- data/lib/kwaff/doctype.rb +124 -0
- data/lib/kwaff/doctypes.yaml +138 -0
- data/lib/kwaff/errors.rb +26 -0
- data/lib/kwaff/node.rb +101 -0
- data/lib/kwaff/optparse.rb +101 -0
- data/lib/kwaff/orderedhash.rb +56 -0
- data/lib/kwaff/parser.rb +342 -0
- data/lib/kwaff/rexml.rb +394 -0
- data/lib/kwaff/translator.rb +275 -0
- data/setup.rb +1331 -0
- data/test/ex101.exp +10 -0
- data/test/ex101.in +8 -0
- data/test/ex102.exp +14 -0
- data/test/ex102.in +16 -0
- data/test/ex103.exp +13 -0
- data/test/ex103.in +13 -0
- data/test/ex104.exp +24 -0
- data/test/ex104.in +24 -0
- data/test/ex105.exp +12 -0
- data/test/ex105.in +18 -0
- data/test/ex106.exp +22 -0
- data/test/ex106.in +21 -0
- data/test/ex107.exp +14 -0
- data/test/ex107.in +14 -0
- data/test/ex111.exp +7 -0
- data/test/ex111.in +13 -0
- data/test/ex112.exp +15 -0
- data/test/ex112.in +16 -0
- data/test/ex113.exp +23 -0
- data/test/ex113.in +22 -0
- data/test/ex114.exp +12 -0
- data/test/ex114.in +17 -0
- data/test/ex115.exp +9 -0
- data/test/ex115.in +12 -0
- data/test/ex121.exp +10 -0
- data/test/ex121.in +8 -0
- data/test/ex122.exp +10 -0
- data/test/ex122.in +8 -0
- data/test/ex301.exp +13 -0
- data/test/ex301.in +14 -0
- data/test/ex302.exp +15 -0
- data/test/ex302.in +18 -0
- data/test/ex303.exp +15 -0
- data/test/ex303.in +16 -0
- data/test/ex304.exp +17 -0
- data/test/ex304.in +15 -0
- data/test/ex311.exp +18 -0
- data/test/ex311.in +32 -0
- data/test/ex312.exp +21 -0
- data/test/ex312.in +18 -0
- data/test/ex313.exp +20 -0
- data/test/ex313.in +33 -0
- data/test/ex314.exp +34 -0
- data/test/ex314.in +24 -0
- data/test/ex801.exp +16 -0
- data/test/ex801.in +15 -0
- data/test/test.rb +273 -0
- data/todo.txt +1 -0
- metadata +132 -0
data/lib/kwaff/errors.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
##
|
2
|
+
## errors and assertions
|
3
|
+
##
|
4
|
+
## $Id: errors.rb 7 2005-05-18 14:33:37Z kwatch $
|
5
|
+
## $Release: 1.0.0 $
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved
|
7
|
+
##
|
8
|
+
|
9
|
+
module Kwaff
|
10
|
+
class KwaffError < StandardError
|
11
|
+
end
|
12
|
+
|
13
|
+
class NotImplementedError < KwaffError
|
14
|
+
end
|
15
|
+
|
16
|
+
class AssertionError < KwaffError
|
17
|
+
def initialize(linenum, msg=nil)
|
18
|
+
super("assertion error: #{linenum}" + (msg ? ": msg=#{msg};" : ""))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.assert(message=nil)
|
23
|
+
linenum = caller()[0]
|
24
|
+
AssertionError.new(linenum, message)
|
25
|
+
end
|
26
|
+
end
|
data/lib/kwaff/node.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
##
|
2
|
+
## definitions of node, element and document
|
3
|
+
##
|
4
|
+
## $Id: node.rb 7 2005-05-18 14:33:37Z kwatch $
|
5
|
+
## $Release: 1.0.0 $
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved
|
7
|
+
##
|
8
|
+
|
9
|
+
require 'kwaff/errors'
|
10
|
+
require 'kwaff/orderedhash'
|
11
|
+
|
12
|
+
module Kwaff
|
13
|
+
|
14
|
+
## abstract class
|
15
|
+
class Node
|
16
|
+
## abstract method
|
17
|
+
def accept(translator, level=0)
|
18
|
+
raise NotImplementedError.new("#{self.class.name}#accept() is not implemented.")
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_xml(toppings={})
|
22
|
+
translator = XmlTranslator.new(toppings)
|
23
|
+
return translator ? translator.translate(self) : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize()
|
27
|
+
@empty_lines = 0
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_accessor :empty_lines
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
class Element < Node
|
35
|
+
def accept(translator, level=0)
|
36
|
+
return translator.translate_element(self, level)
|
37
|
+
end
|
38
|
+
def initialize(tag, children=[], attrs=OrderedHash.new)
|
39
|
+
@tag = tag
|
40
|
+
@children = children
|
41
|
+
@attrs = attrs ## Hash or OrderedHash
|
42
|
+
end
|
43
|
+
attr_reader :children, :attrs
|
44
|
+
attr_accessor :tag
|
45
|
+
|
46
|
+
def text
|
47
|
+
if @children.length > 0 && @children[0].is_a?(Text)
|
48
|
+
text = children[0]
|
49
|
+
return text.str
|
50
|
+
end
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def text=(str, flag_escape=true)
|
55
|
+
children[0] = Text.new(str, flag_escape)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
class Document < Node
|
61
|
+
def accept(translator, level=0)
|
62
|
+
return translator.translate_document(self, level)
|
63
|
+
end
|
64
|
+
def initialize(headers=OrderedHash.new, root=nil)
|
65
|
+
@headers = headers
|
66
|
+
@root = root
|
67
|
+
end
|
68
|
+
attr_reader :headers
|
69
|
+
attr_accessor :root
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
class Comment < Node
|
74
|
+
def accept(translator, level=0)
|
75
|
+
return translator.translate_comment(self, level)
|
76
|
+
end
|
77
|
+
def initialize(str)
|
78
|
+
@str = str
|
79
|
+
end
|
80
|
+
attr_reader :str
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
class Text < Node
|
85
|
+
def accept(translator, level=0)
|
86
|
+
return translator.translate_text(self, level)
|
87
|
+
end
|
88
|
+
def initialize(str, flag_escape=true)
|
89
|
+
@str = str
|
90
|
+
@escape = flag_escape
|
91
|
+
end
|
92
|
+
attr_accessor :str, :escape
|
93
|
+
#def str
|
94
|
+
# return @escape ? Kwaff::escape(@str) : @str
|
95
|
+
#end
|
96
|
+
def escape?
|
97
|
+
return @escape
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
##
|
2
|
+
## required only from 'bin/kwaff' script
|
3
|
+
##
|
4
|
+
## $Id: optparse.rb 11 2005-06-09 12:32:33Z kwatch $
|
5
|
+
## $Release: 1.0.0 $
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved
|
7
|
+
##
|
8
|
+
|
9
|
+
require 'kwaff/errors'
|
10
|
+
|
11
|
+
module Kwaff
|
12
|
+
|
13
|
+
class CommandOptionError < KwaffError
|
14
|
+
end
|
15
|
+
|
16
|
+
class CommandOptionParser
|
17
|
+
|
18
|
+
def initialize(noarg_opts='', argneed_opts='', argoptional_opts='', flag_str2value=true)
|
19
|
+
@noarg_opts = noarg_opts
|
20
|
+
@argneed_opts = argneed_opts
|
21
|
+
@argoptional_opts = argoptional_opts
|
22
|
+
@flag_str2value = flag_str2value
|
23
|
+
@options = nil
|
24
|
+
@properties = nil
|
25
|
+
end
|
26
|
+
attr_reader :options, :properites
|
27
|
+
|
28
|
+
|
29
|
+
def parse(argv=ARGV)
|
30
|
+
options = {}
|
31
|
+
properties = {}
|
32
|
+
|
33
|
+
while !argv.empty? && argv[0][0] == ?-
|
34
|
+
opt = argv.shift
|
35
|
+
if opt[1] == ?- ## properteis
|
36
|
+
propstr = opt.sub(/^--/, '')
|
37
|
+
unless propstr =~ /^([-\w]+)(?:=(.*))?$/
|
38
|
+
raise CommandOptionError.new("--#{optstr}: invalid property.")
|
39
|
+
end
|
40
|
+
name = $1
|
41
|
+
value = $2
|
42
|
+
name.gsub!(/-/, '_')
|
43
|
+
if !value
|
44
|
+
value = true
|
45
|
+
elsif flag_str2value
|
46
|
+
value = self.to_value(value)
|
47
|
+
end
|
48
|
+
properties[name.intern] = value
|
49
|
+
|
50
|
+
else ## options
|
51
|
+
optstr = opt.sub(/^-/, '')
|
52
|
+
while optstr && !optstr.empty?
|
53
|
+
optchar = optstr[0]
|
54
|
+
optstr = optstr[1, optstr.length-1]
|
55
|
+
if @noarg_opts.include?(optchar)
|
56
|
+
options[optchar] = true
|
57
|
+
elsif @argneed_opts.include?(optchar)
|
58
|
+
arg = optstr
|
59
|
+
optstr = nil
|
60
|
+
arg = argv.shift if !arg || arg.empty?
|
61
|
+
unless arg
|
62
|
+
raise CommandOptionError.new("-#{optchar.chr}: argument required.")
|
63
|
+
end
|
64
|
+
options[optchar] = arg
|
65
|
+
elsif @argoptional_opts.include?(optchar)
|
66
|
+
arg = optstr && !optstr.empty? ? optstr : true
|
67
|
+
optstr = nil
|
68
|
+
options[optchar] = arg
|
69
|
+
else
|
70
|
+
raise CommandOptionError.new("-#{optchar.chr}: invalid option.")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end # if
|
75
|
+
|
76
|
+
end # while
|
77
|
+
|
78
|
+
@options = options
|
79
|
+
@properties = properties
|
80
|
+
return options, properties
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def to_value(str)
|
85
|
+
case str
|
86
|
+
when 'true', 'yes' ; return true
|
87
|
+
when 'false', 'no' ; return false
|
88
|
+
when 'null', 'nil' ; return nil
|
89
|
+
when /\A\d+\z/ ; return str.to_i
|
90
|
+
when /\A\d+\.\d+\z/ ; return str.to_f
|
91
|
+
when /\A\/(.*)\/\z/ ; return Regexp.new($1)
|
92
|
+
when /\A'.*'\z/ ; return eval(str)
|
93
|
+
when /\A".*"\z/ ; return eval(str)
|
94
|
+
else
|
95
|
+
return str
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
##
|
2
|
+
## hash which keeps order of keys added into itself
|
3
|
+
##
|
4
|
+
## $Id: orderedhash.rb 7 2005-05-18 14:33:37Z kwatch $
|
5
|
+
## $Release: 1.0.0 $
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved
|
7
|
+
##
|
8
|
+
|
9
|
+
module Kwaff
|
10
|
+
|
11
|
+
class OrderedHash
|
12
|
+
include Enumerable
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@keys = []
|
16
|
+
@hash = {}
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :keys, :hash
|
20
|
+
|
21
|
+
def each(&block)
|
22
|
+
keys.each do |key|
|
23
|
+
value = @hash[key]
|
24
|
+
yield(key, value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def [](key)
|
29
|
+
return @hash[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
def []=(key, value)
|
33
|
+
@keys << key unless @hash.key?(key)
|
34
|
+
@hash[key] = value
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(key)
|
38
|
+
@keys.delete(key)
|
39
|
+
@hash.delete(key)
|
40
|
+
end
|
41
|
+
|
42
|
+
def default
|
43
|
+
return @hash.default
|
44
|
+
end
|
45
|
+
|
46
|
+
def default=(value)
|
47
|
+
@hash.default = value
|
48
|
+
end
|
49
|
+
|
50
|
+
def key?(key)
|
51
|
+
return @hash.key?(key)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
data/lib/kwaff/parser.rb
ADDED
@@ -0,0 +1,342 @@
|
|
1
|
+
##
|
2
|
+
## parse Kwaff document
|
3
|
+
##
|
4
|
+
## $Id: parser.rb 14 2005-06-10 09:01:18Z kwatch $
|
5
|
+
## $Release: 1.0.0 $
|
6
|
+
## copyright(c) 2005 kuwata-lab.com all rights reserved
|
7
|
+
##
|
8
|
+
|
9
|
+
require 'kwaff/errors'
|
10
|
+
require 'kwaff/orderedhash'
|
11
|
+
require 'kwaff/node'
|
12
|
+
require 'kwaff/doctype'
|
13
|
+
|
14
|
+
module Kwaff
|
15
|
+
|
16
|
+
class ParseError < KwaffError
|
17
|
+
def initialize(msg, parser)
|
18
|
+
super(msg)
|
19
|
+
@parser = parser
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
index = @parser.index
|
24
|
+
line = @parser.lines[index]
|
25
|
+
super() + " - (line #{index + 1}: #{line.inspect})\n"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
## parse Kwaff string and create Kwaff::Document.
|
30
|
+
##
|
31
|
+
## ex.
|
32
|
+
## kwaff_str = File.open('file.kwaff') { |f| f.read }
|
33
|
+
## parser = Kwaff::Parser.new(kwaff_str)
|
34
|
+
## kwaff_doc = parser.parse_document()
|
35
|
+
## translator = Kwaff::XmlTranslator.new()
|
36
|
+
## xml_str = translator.translate(kwaff_doc)
|
37
|
+
##
|
38
|
+
## if you want to get REXML::Document instead of Kwaff::Document,
|
39
|
+
## use Kwaff::Rexml::Parser instead of Kwaff::Parser.
|
40
|
+
##
|
41
|
+
class Parser
|
42
|
+
def initialize(str, toppings={})
|
43
|
+
reset(str)
|
44
|
+
@toppings = toppings
|
45
|
+
@_s_cache = {} # for spclen()
|
46
|
+
@indent_width = toppings[:indent_width] || 2
|
47
|
+
end
|
48
|
+
attr_reader :lines, :index, :newline
|
49
|
+
|
50
|
+
|
51
|
+
def reset(str)
|
52
|
+
@str = str
|
53
|
+
idx = str.index(?\n)
|
54
|
+
@newline = (idx && str[idx-1] == ?\r) ? "\r\n" : "\n"
|
55
|
+
@lines = str.split(@newline, -1)
|
56
|
+
@lines.pop() # remove the last line
|
57
|
+
@index = 0
|
58
|
+
@limit = @lines.length
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def _read_line
|
63
|
+
return nil if @index >= @limit
|
64
|
+
@index += 1
|
65
|
+
return @lines[@index]
|
66
|
+
end
|
67
|
+
private :_read_line
|
68
|
+
|
69
|
+
def _current_line
|
70
|
+
return @lines[@index]
|
71
|
+
end
|
72
|
+
private :_current_line
|
73
|
+
|
74
|
+
|
75
|
+
## space length, with tab expanding
|
76
|
+
def spclen(str)
|
77
|
+
return 0 unless str
|
78
|
+
len = (@_s_cache[str] ||= (str.gsub(/([^\t]{8})|([^\t]*)\t/n){[$+].pack("A8")}).length)
|
79
|
+
return len
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
## factory method
|
84
|
+
def create_document(headers, root)
|
85
|
+
return Document.new(headers, root)
|
86
|
+
end
|
87
|
+
|
88
|
+
## factory method
|
89
|
+
def create_element(tag, children, attrs)
|
90
|
+
return Element.new(tag, children, attrs)
|
91
|
+
end
|
92
|
+
|
93
|
+
## factory method
|
94
|
+
def create_comment(str)
|
95
|
+
return Comment.new(str)
|
96
|
+
end
|
97
|
+
|
98
|
+
## factory method
|
99
|
+
def create_text(str, flag_escape=true)
|
100
|
+
return Text.new(str, flag_escape)
|
101
|
+
end
|
102
|
+
|
103
|
+
##
|
104
|
+
def set_node_option(node, name, value)
|
105
|
+
node.empty_lines = value if name == :empty_lines
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
def parse_document(str=nil)
|
110
|
+
reset(str) if str
|
111
|
+
headers = OrderedHash.new
|
112
|
+
line = _current_line()
|
113
|
+
while line
|
114
|
+
if line =~ /^\?[ \t]*([-:\w]+)[ \t]*[:=][ \t]*(.*)/ ## *pattern*
|
115
|
+
headers[$1.intern] = $2.strip
|
116
|
+
elsif line =~ /^\s*$/
|
117
|
+
# ignore empty line
|
118
|
+
elsif line =~ /^\s*\/\// ## *pattern*
|
119
|
+
# ignore comment line
|
120
|
+
else
|
121
|
+
break
|
122
|
+
end
|
123
|
+
line = _read_line()
|
124
|
+
end
|
125
|
+
if headers[:doctype] =~ /\A@[-:\w]+\z/
|
126
|
+
nickname = headers[:doctype]
|
127
|
+
doctype = DocType.get(nickname)
|
128
|
+
unless doctype
|
129
|
+
raise ParseError.new("doctype `#{headers[:doctype]}' not registered.", self)
|
130
|
+
end
|
131
|
+
headers[:doctype] = doctype
|
132
|
+
end
|
133
|
+
root_elem = parse_element()
|
134
|
+
doc = create_document(headers, root_elem)
|
135
|
+
set_node_option(doc, :empty_lines, @empty_lines)
|
136
|
+
return doc
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def parse_node_list(level=0)
|
141
|
+
list = []
|
142
|
+
str = nil
|
143
|
+
line = _current_line()
|
144
|
+
while line
|
145
|
+
if line =~ /^(\s*)([*\#!.\~])/ # node ## *pattern*
|
146
|
+
curr_level = spclen($1)
|
147
|
+
ch = $2
|
148
|
+
if str
|
149
|
+
list << create_text(str)
|
150
|
+
str = nil
|
151
|
+
end
|
152
|
+
if curr_level == level # sibling node
|
153
|
+
empty_lines = @empty_lines || 0
|
154
|
+
@empty_lines = 0
|
155
|
+
case ch
|
156
|
+
when '*' ## *pattern*
|
157
|
+
node = parse_element(level)
|
158
|
+
when '#', '!' ## *pattern*
|
159
|
+
node = parse_comment(level)
|
160
|
+
when '.' ## *pattern*
|
161
|
+
node = parse_text(level)
|
162
|
+
when '~' ## *pattern*
|
163
|
+
node = parse_cdata(level)
|
164
|
+
else
|
165
|
+
raise ParseError.new("internal error", self)
|
166
|
+
end
|
167
|
+
set_node_option(node, :empty_lines, empty_lines)
|
168
|
+
list << node
|
169
|
+
line = _current_line()
|
170
|
+
next
|
171
|
+
elsif curr_level < level # parent node
|
172
|
+
break
|
173
|
+
else # child node
|
174
|
+
raise ParseError.new("illegal indentation.", self) # or internal error
|
175
|
+
end
|
176
|
+
elsif line =~ /^\s*$/ # empty lines
|
177
|
+
@empty_lines ||= 0
|
178
|
+
@empty_lines += 1
|
179
|
+
line = _read_line()
|
180
|
+
elsif line =~ /^\s*\/\// # line-comment # *pattern*
|
181
|
+
line = _read_line()
|
182
|
+
else
|
183
|
+
#raise ParseError.new("invalid line.", self)
|
184
|
+
str ||= ''
|
185
|
+
str << line
|
186
|
+
line = _read_line()
|
187
|
+
end
|
188
|
+
end
|
189
|
+
list << create_text(str) if str
|
190
|
+
return list
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def _parse_heredoc(terminator, flag_join=false)
|
195
|
+
s = ""
|
196
|
+
while line = _read_line()
|
197
|
+
break if terminator === line.strip
|
198
|
+
s << @newline unless flag_join || s.empty?
|
199
|
+
s << line
|
200
|
+
end
|
201
|
+
raise ParseError.new("'<<#{terminator.to_s}' is not ended.", self) unless line
|
202
|
+
if line =~ /\A\s*/
|
203
|
+
space = $&
|
204
|
+
s.gsub!(/^#{space}/, '')
|
205
|
+
end
|
206
|
+
return s
|
207
|
+
end
|
208
|
+
private :_parse_heredoc
|
209
|
+
|
210
|
+
|
211
|
+
def parse_element(level=0)
|
212
|
+
## tag and text
|
213
|
+
line = _current_line()
|
214
|
+
line =~ /^([ \t]*)\*[ \t]*([-:\w]+)([ \t]*=[ \t]*(.*))?/ ## *pattern*
|
215
|
+
Kwaff::assert() unless level == spclen($1)
|
216
|
+
tag = $2 || ''
|
217
|
+
value = $4
|
218
|
+
tag.strip!
|
219
|
+
if tag.empty?
|
220
|
+
raise ParseError.new("tag is missing.", self)
|
221
|
+
end
|
222
|
+
flag_escape = true
|
223
|
+
if value =~ /^<<<?('?\w+'?)$/
|
224
|
+
terminator = $1
|
225
|
+
if terminator =~ /\A'(.*)'\z/
|
226
|
+
terminator = $1
|
227
|
+
flag_escape = false
|
228
|
+
end
|
229
|
+
value = _parse_heredoc(terminator)
|
230
|
+
end
|
231
|
+
|
232
|
+
## text
|
233
|
+
text = value ? create_text(value, flag_escape) : nil
|
234
|
+
|
235
|
+
## attrs
|
236
|
+
attrs = OrderedHash.new
|
237
|
+
line = _read_line()
|
238
|
+
while line
|
239
|
+
if line =~ /^\s*\/\/$/ # line comment ## *pattern*
|
240
|
+
# nothing
|
241
|
+
#elsif line =~ /^\s*$/ # empty line
|
242
|
+
# @empty_lines += 1
|
243
|
+
elsif line =~ /^\s*\-/ # attribute ## *pattern*
|
244
|
+
unless line =~ /\A([ \t]*)\-[ \t]+([-:\w]+)[ \t]*=[ \t]*(.*)/ ## *pattern*
|
245
|
+
raise ParseError.new("invalid attribute format.", self)
|
246
|
+
end
|
247
|
+
attr_level = spclen($1)
|
248
|
+
attr_name = $2
|
249
|
+
attr_value = $3
|
250
|
+
unless attr_level == level + @indent_width ## *indent*
|
251
|
+
raise ParseError.new("attr indent width is not #{@indent_width}.", self)
|
252
|
+
end
|
253
|
+
if attr_value =~ /^<<([_A-Z]+)$/
|
254
|
+
terminator = $1
|
255
|
+
attr_value = _parse_heredoc(terminator)
|
256
|
+
end
|
257
|
+
attrs[attr_name] = attr_value
|
258
|
+
else
|
259
|
+
break
|
260
|
+
end
|
261
|
+
line = _read_line()
|
262
|
+
end
|
263
|
+
|
264
|
+
## empty lines
|
265
|
+
@empty_lines = 0
|
266
|
+
while line && line =~ /\A\s*\z/
|
267
|
+
@empty_lines += 1
|
268
|
+
line = _read_line()
|
269
|
+
end
|
270
|
+
|
271
|
+
## children
|
272
|
+
if line && line =~ /^(\s*)[*\#!.\~]/ && spclen($1) > level ## *pattern*
|
273
|
+
#children = parse_node_list(spclen($1))
|
274
|
+
unless spclen($1) == level + @indent_width ## *indent*
|
275
|
+
raise ParseError.new("indent width is not #{@indent_width}.", self)
|
276
|
+
end
|
277
|
+
children = parse_node_list(level+@indent_width)
|
278
|
+
else
|
279
|
+
children = []
|
280
|
+
end
|
281
|
+
children.unshift(text) if text
|
282
|
+
|
283
|
+
## element
|
284
|
+
return create_element(tag, children, attrs)
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
def parse_comment(level=0)
|
289
|
+
line = _current_line()
|
290
|
+
line =~ /^([ \t]*)[\#\!][ \t]*(.*)$/ # *pattern*
|
291
|
+
Kwaff::assert() unless level == spclen($1)
|
292
|
+
str = $2
|
293
|
+
comment = create_comment(str)
|
294
|
+
line = _read_line()
|
295
|
+
return comment
|
296
|
+
end
|
297
|
+
|
298
|
+
|
299
|
+
def parse_text(level=0)
|
300
|
+
return _parse_textline(/^([ \t]*)\. ?(.*)$/, true, level) # *pattern*
|
301
|
+
end
|
302
|
+
|
303
|
+
def parse_cdata(level=0)
|
304
|
+
return _parse_textline(/^([ \t]*)\~ ?(.*)$/, false, level) # *pattern*
|
305
|
+
end
|
306
|
+
|
307
|
+
def _parse_textline(pattern, flag_escape, level=0)
|
308
|
+
line = _current_line()
|
309
|
+
str = ''
|
310
|
+
while line
|
311
|
+
break unless line =~ pattern
|
312
|
+
curr_level = spclen($1)
|
313
|
+
value = $2
|
314
|
+
if curr_level == level
|
315
|
+
str << @newline unless str.empty?
|
316
|
+
str << value
|
317
|
+
line = _read_line()
|
318
|
+
elsif curr_level < level
|
319
|
+
break
|
320
|
+
else
|
321
|
+
raise Parser.new("invalid indentaton.", self)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
text = create_text(str, flag_escape)
|
325
|
+
return text
|
326
|
+
end
|
327
|
+
private :_parse_textline
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
##
|
333
|
+
## ex.
|
334
|
+
## kwaff_str = File.open('file.kwaff') { |f| f.read }
|
335
|
+
## kwaff_document = Kwaff::parse_document(kwaff_str)
|
336
|
+
## xml_str = kwaff_document.to_xml
|
337
|
+
##
|
338
|
+
def self.parse_document(input, toppings={})
|
339
|
+
return Parser.new(input, toppings).parse_document()
|
340
|
+
end
|
341
|
+
|
342
|
+
end
|