kwaff 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|