kwartz 3.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 +103 -0
- data/README.txt +37 -0
- data/bin/kwartz +12 -0
- data/doc-api/classes/Kwartz.html +218 -0
- data/doc-api/classes/Kwartz/Assertion.html +140 -0
- data/doc-api/classes/Kwartz/AssertionError.html +148 -0
- data/doc-api/classes/Kwartz/AttrInfo.html +320 -0
- data/doc-api/classes/Kwartz/BaseError.html +206 -0
- data/doc-api/classes/Kwartz/BaseTranslator.html +331 -0
- data/doc-api/classes/Kwartz/CharacterType.html +212 -0
- data/doc-api/classes/Kwartz/CommandOptionError.html +154 -0
- data/doc-api/classes/Kwartz/CommandOptions.html +374 -0
- data/doc-api/classes/Kwartz/Config.html +150 -0
- data/doc-api/classes/Kwartz/ConvertError.html +191 -0
- data/doc-api/classes/Kwartz/Converter.html +252 -0
- data/doc-api/classes/Kwartz/CssStyleParser.html +483 -0
- data/doc-api/classes/Kwartz/DocumentRuleset.html +369 -0
- data/doc-api/classes/Kwartz/ElementExpander.html +325 -0
- data/doc-api/classes/Kwartz/ElementInfo.html +312 -0
- data/doc-api/classes/Kwartz/ElementRuleset.html +582 -0
- data/doc-api/classes/Kwartz/EperlHandler.html +338 -0
- data/doc-api/classes/Kwartz/EperlTranslator.html +167 -0
- data/doc-api/classes/Kwartz/ErubisHandler.html +113 -0
- data/doc-api/classes/Kwartz/ErubisTranslator.html +168 -0
- data/doc-api/classes/Kwartz/ErubyHandler.html +337 -0
- data/doc-api/classes/Kwartz/ErubyTranslator.html +167 -0
- data/doc-api/classes/Kwartz/ExpandStatement.html +227 -0
- data/doc-api/classes/Kwartz/Expression.html +119 -0
- data/doc-api/classes/Kwartz/Handler.html +558 -0
- data/doc-api/classes/Kwartz/JstlHandler.html +657 -0
- data/doc-api/classes/Kwartz/JstlTranslator.html +226 -0
- data/doc-api/classes/Kwartz/KwartzError.html +146 -0
- data/doc-api/classes/Kwartz/Main.html +384 -0
- data/doc-api/classes/Kwartz/NativeExpression.html +236 -0
- data/doc-api/classes/Kwartz/NativeStatement.html +254 -0
- data/doc-api/classes/Kwartz/Node.html +156 -0
- data/doc-api/classes/Kwartz/ParseError.html +148 -0
- data/doc-api/classes/Kwartz/PhpHandler.html +333 -0
- data/doc-api/classes/Kwartz/PhpTranslator.html +194 -0
- data/doc-api/classes/Kwartz/PresentationLogicParser.html +830 -0
- data/doc-api/classes/Kwartz/PrintStatement.html +221 -0
- data/doc-api/classes/Kwartz/RailsHandler.html +587 -0
- data/doc-api/classes/Kwartz/RailsTranslator.html +167 -0
- data/doc-api/classes/Kwartz/RubyStyleParser.html +558 -0
- data/doc-api/classes/Kwartz/Ruleset.html +117 -0
- data/doc-api/classes/Kwartz/Statement.html +119 -0
- data/doc-api/classes/Kwartz/StrutsTranslator.html +190 -0
- data/doc-api/classes/Kwartz/TagInfo.html +314 -0
- data/doc-api/classes/Kwartz/TextConverter.html +270 -0
- data/doc-api/classes/Kwartz/Translator.html +318 -0
- data/doc-api/classes/Test.html +107 -0
- data/doc-api/classes/Test/Unit.html +101 -0
- data/doc-api/created.rid +1 -0
- data/doc-api/files/__/README_txt.html +150 -0
- data/doc-api/files/kwartz/assert_rb.html +114 -0
- data/doc-api/files/kwartz/binding/eperl_rb.html +116 -0
- data/doc-api/files/kwartz/binding/erubis_rb.html +116 -0
- data/doc-api/files/kwartz/binding/eruby_rb.html +115 -0
- data/doc-api/files/kwartz/binding/jstl_rb.html +116 -0
- data/doc-api/files/kwartz/binding/php_rb.html +116 -0
- data/doc-api/files/kwartz/binding/rails_rb.html +115 -0
- data/doc-api/files/kwartz/binding/struts_rb.html +117 -0
- data/doc-api/files/kwartz/config_rb.html +107 -0
- data/doc-api/files/kwartz/converter_rb.html +119 -0
- data/doc-api/files/kwartz/error_rb.html +107 -0
- data/doc-api/files/kwartz/main_rb.html +124 -0
- data/doc-api/files/kwartz/node_rb.html +114 -0
- data/doc-api/files/kwartz/parser_rb.html +117 -0
- data/doc-api/files/kwartz/translator_rb.html +115 -0
- data/doc-api/files/kwartz/util/assert-text-equal_rb.html +115 -0
- data/doc-api/files/kwartz/util/testcase-helper_rb.html +115 -0
- data/doc-api/files/kwartz_rb.html +120 -0
- data/doc-api/fr_class_index.html +75 -0
- data/doc-api/fr_file_index.html +45 -0
- data/doc-api/fr_method_index.html +216 -0
- data/doc-api/index.html +24 -0
- data/doc-api/rdoc-style.css +208 -0
- data/doc/docstyle.css +188 -0
- data/doc/p-pattern.html +1207 -0
- data/doc/reference.html +3396 -0
- data/doc/users-guide.html +1670 -0
- data/examples/breadcrumbs1/Makefile +15 -0
- data/examples/breadcrumbs1/breadcrumbs.eruby.plogic +27 -0
- data/examples/breadcrumbs1/breadcrumbs.html +12 -0
- data/examples/breadcrumbs1/breadcrumbs.jstl.plogic +28 -0
- data/examples/breadcrumbs1/breadcrumbs.php.plogic +26 -0
- data/examples/breadcrumbs1/main.php +12 -0
- data/examples/breadcrumbs1/main.rb +12 -0
- data/examples/breadcrumbs2/Makefile +15 -0
- data/examples/breadcrumbs2/breadcrumbs.eruby.plogic +22 -0
- data/examples/breadcrumbs2/breadcrumbs.html +14 -0
- data/examples/breadcrumbs2/breadcrumbs.jstl.plogic +24 -0
- data/examples/breadcrumbs2/breadcrumbs.php.plogic +23 -0
- data/examples/breadcrumbs2/main.php +12 -0
- data/examples/breadcrumbs2/main.rb +12 -0
- data/examples/pagelayout/Makefile +47 -0
- data/examples/pagelayout/content.eruby.plogic +44 -0
- data/examples/pagelayout/content.jstl.plogic +36 -0
- data/examples/pagelayout/content.php.plogic +37 -0
- data/examples/pagelayout/content1.html +36 -0
- data/examples/pagelayout/content2.html +29 -0
- data/examples/pagelayout/design.css +40 -0
- data/examples/pagelayout/layout.html +50 -0
- data/examples/pagelayout/main.php +55 -0
- data/examples/pagelayout/main.rb +77 -0
- data/examples/pagelayout/menu.eruby.plogic +14 -0
- data/examples/pagelayout/menu.html +13 -0
- data/examples/pagelayout/menu.jstl.plogic +14 -0
- data/examples/pagelayout/menu.php.plogic +14 -0
- data/examples/rails1/Makefile +36 -0
- data/examples/rails1/README +19 -0
- data/examples/rails1/application_helper.rb +31 -0
- data/examples/rails1/edit.html +28 -0
- data/examples/rails1/edit.plogic +10 -0
- data/examples/rails1/form.html +52 -0
- data/examples/rails1/form.plogic +33 -0
- data/examples/rails1/layout.plogic +15 -0
- data/examples/rails1/link_to.plogic +19 -0
- data/examples/rails1/list.html +48 -0
- data/examples/rails1/list.plogic +28 -0
- data/examples/rails1/new.html +27 -0
- data/examples/rails1/new.plogic +10 -0
- data/examples/rails1/reader.plogic +29 -0
- data/examples/rails1/show.html +40 -0
- data/examples/rails1/show.plogic +4 -0
- data/examples/rails1/writer.plogic +8 -0
- data/examples/table1/Makefile +15 -0
- data/examples/table1/main.php +11 -0
- data/examples/table1/main.rb +11 -0
- data/examples/table1/table1.eruby.plogic +21 -0
- data/examples/table1/table1.html +16 -0
- data/examples/table1/table1.jstl.plogic +21 -0
- data/examples/table1/table1.php.plogic +22 -0
- data/kwartz.gemspec +55 -0
- data/lib/kwartz.rb +13 -0
- data/lib/kwartz/assert.rb +31 -0
- data/lib/kwartz/binding/eperl.rb +166 -0
- data/lib/kwartz/binding/erubis.rb +61 -0
- data/lib/kwartz/binding/eruby.rb +164 -0
- data/lib/kwartz/binding/jstl.rb +334 -0
- data/lib/kwartz/binding/php.rb +167 -0
- data/lib/kwartz/binding/rails.rb +295 -0
- data/lib/kwartz/binding/struts.rb +109 -0
- data/lib/kwartz/config.rb +28 -0
- data/lib/kwartz/converter.rb +920 -0
- data/lib/kwartz/error.rb +41 -0
- data/lib/kwartz/main.rb +464 -0
- data/lib/kwartz/node.rb +454 -0
- data/lib/kwartz/parser.rb +903 -0
- data/lib/kwartz/translator.rb +153 -0
- data/lib/kwartz/util/assert-text-equal.rb +44 -0
- data/lib/kwartz/util/testcase-helper.rb +112 -0
- data/setup.rb +1331 -0
- data/test/test-compile.rb +36 -0
- data/test/test-compile.yaml +178 -0
- data/test/test-converter.rb +34 -0
- data/test/test-converter.yaml +127 -0
- data/test/test-directives.rb +32 -0
- data/test/test-directives.yaml +1411 -0
- data/test/test-main.rb +464 -0
- data/test/test-parser.rb +54 -0
- data/test/test-parser.yaml +394 -0
- data/test/test-rails.rb +28 -0
- data/test/test-rails.yaml +301 -0
- data/test/test-ruleset.rb +36 -0
- data/test/test-ruleset.yaml +804 -0
- data/test/test.rb +44 -0
- metadata +236 -0
@@ -0,0 +1,903 @@
|
|
1
|
+
###
|
2
|
+
### $Rev: 117 $
|
3
|
+
### $Release: 3.0.0 $
|
4
|
+
### copyright(c) 2004-2006 kuwata-lab.com all rights reserved
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'kwartz/assert'
|
8
|
+
require 'kwartz/error'
|
9
|
+
require 'kwartz/node'
|
10
|
+
require 'abstract'
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
module Kwartz
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
module CharacterType
|
19
|
+
|
20
|
+
|
21
|
+
def is_whitespace(ch)
|
22
|
+
return ch == ?\ || ch == ?\t || ch == ?\n || ch == ?\r
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def is_alpha(ch)
|
27
|
+
return (?a <= ch && ch <= ?z) || (?A <= ch && ch <= ?Z)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def is_digit(ch)
|
32
|
+
return ?0 <= ch && ch <= ?9
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def is_identchar(ch)
|
37
|
+
return is_alpha(ch) || is_digit(ch) || ch == ?_
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
class ParseError < BaseError
|
46
|
+
|
47
|
+
|
48
|
+
def initialize(message, filename, linenum, column)
|
49
|
+
super
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
##
|
58
|
+
## .[abstract] parser class for presentation logic
|
59
|
+
##
|
60
|
+
class PresentationLogicParser
|
61
|
+
include CharacterType
|
62
|
+
include Assertion
|
63
|
+
|
64
|
+
|
65
|
+
def initialize(properties={})
|
66
|
+
@properties = properties
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
## called from parse() and initialize parser object
|
71
|
+
def reset(input, filename='')
|
72
|
+
@input = input
|
73
|
+
@filename = filename
|
74
|
+
@linenum = 1 # 1 start
|
75
|
+
@column = 0 # 1 start
|
76
|
+
@pos = -1 # 0 start
|
77
|
+
@max_pos = @input.length - 1
|
78
|
+
@token = nil
|
79
|
+
@value = nil
|
80
|
+
@error = nil
|
81
|
+
@ch = nil
|
82
|
+
getch()
|
83
|
+
end
|
84
|
+
protected :reset
|
85
|
+
|
86
|
+
|
87
|
+
attr_reader :linenum, :column, :pos, :token, :value, :error
|
88
|
+
|
89
|
+
|
90
|
+
table = {}
|
91
|
+
%w[stag cont etag elem value attrs append].each do |word|
|
92
|
+
sym = word.intern
|
93
|
+
table[word] = sym
|
94
|
+
table[word.capitalize] = sym
|
95
|
+
table[word.upcase] = sym
|
96
|
+
end
|
97
|
+
%w[element remove tagname logic document global local fixture before after].each do |word|
|
98
|
+
table[word] = word.intern
|
99
|
+
end
|
100
|
+
PLOGIC_KEYWORDS = table
|
101
|
+
|
102
|
+
|
103
|
+
table = {}
|
104
|
+
%w[stag cont etag elem value attrs append].each do |word|
|
105
|
+
table[word] = nil # ex. value
|
106
|
+
table[word.capitalize] = true # ex. Value
|
107
|
+
table[word.upcase] = false # ex. VALUE
|
108
|
+
end
|
109
|
+
ESCAPE_FLAG_TABLE = table
|
110
|
+
|
111
|
+
|
112
|
+
def escape?(value)
|
113
|
+
return ESCAPE_FLAG_TABLE[value]
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
## scanner
|
118
|
+
|
119
|
+
def getch
|
120
|
+
return @ch = nil if @pos >= @max_pos
|
121
|
+
if @ch == ?\n
|
122
|
+
@linenum += 1
|
123
|
+
@column = 0
|
124
|
+
end
|
125
|
+
@pos += 1
|
126
|
+
@column += 1
|
127
|
+
@ch = @input[@pos]
|
128
|
+
return @ch
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def scan_ident
|
133
|
+
## identifer
|
134
|
+
if is_identchar(@ch)
|
135
|
+
sb = @ch.chr
|
136
|
+
while (c = getch()) && is_identchar(c)
|
137
|
+
sb << c.chr
|
138
|
+
end
|
139
|
+
@value = sb
|
140
|
+
return @token = :ident
|
141
|
+
end
|
142
|
+
return nil
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def scan_string_dquoted
|
147
|
+
return nil unless @ch == ?"
|
148
|
+
s = ''
|
149
|
+
while (c = getch()) && c != ?"
|
150
|
+
if c == ?\\
|
151
|
+
c = getch()
|
152
|
+
break unless c
|
153
|
+
case c
|
154
|
+
when ?n ; s << "\n"
|
155
|
+
when ?t ; s << "\t"
|
156
|
+
when ?r ; s << "\r"
|
157
|
+
when ?b ; s << "\b"
|
158
|
+
when ?\\ ; s << "\\"
|
159
|
+
when ?" ; s << '"'
|
160
|
+
else ; s << c.chr
|
161
|
+
end
|
162
|
+
else
|
163
|
+
s << c.chr
|
164
|
+
end
|
165
|
+
end
|
166
|
+
unless c
|
167
|
+
@error = :string_unclosed
|
168
|
+
return @token = :error
|
169
|
+
end
|
170
|
+
assert unless c == ?"
|
171
|
+
getch()
|
172
|
+
@value = s
|
173
|
+
return @token = :string
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
def scan_string
|
178
|
+
if @ch == ?'
|
179
|
+
return scan_string_quoted()
|
180
|
+
elsif @ch == ?"
|
181
|
+
return scan_string_dquoted()
|
182
|
+
else
|
183
|
+
return nil
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def scan_string_quoted
|
189
|
+
return nil unless @ch == ?'
|
190
|
+
s = ''
|
191
|
+
while (c = getch()) && c != ?'
|
192
|
+
if c == ?\\
|
193
|
+
c = getch()
|
194
|
+
break unless c
|
195
|
+
case c
|
196
|
+
when ?\\ ; s << "\\"
|
197
|
+
when ?' ; s << "'"
|
198
|
+
else ; s << "\\" << c.chr
|
199
|
+
end
|
200
|
+
else
|
201
|
+
s << c.chr
|
202
|
+
end
|
203
|
+
end
|
204
|
+
unless c
|
205
|
+
@error = :string_unclosed
|
206
|
+
return @token = :error
|
207
|
+
end
|
208
|
+
assert unless c == ?'
|
209
|
+
getch()
|
210
|
+
@value = s
|
211
|
+
return @token = :string
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
## called from scan()
|
216
|
+
def scan_hook
|
217
|
+
#not_implemented
|
218
|
+
end
|
219
|
+
|
220
|
+
## .[abstract] detect parser-specific keywords
|
221
|
+
##
|
222
|
+
## return symbol if keyword is token, else return nil
|
223
|
+
def keywords(keyword)
|
224
|
+
not_implemented
|
225
|
+
end
|
226
|
+
|
227
|
+
## scan token
|
228
|
+
def scan
|
229
|
+
## skip whitespaces
|
230
|
+
c = @ch
|
231
|
+
while is_whitespace(c)
|
232
|
+
c = getch()
|
233
|
+
end
|
234
|
+
|
235
|
+
## return nil when EOF
|
236
|
+
if c == nil
|
237
|
+
@value = nil
|
238
|
+
return @token = nil
|
239
|
+
end
|
240
|
+
|
241
|
+
## scan hook
|
242
|
+
ret = scan_hook() # scan_hook() is overrided in subclass
|
243
|
+
return ret if ret
|
244
|
+
|
245
|
+
## keyword or identifer
|
246
|
+
if is_identchar(c)
|
247
|
+
scan_ident()
|
248
|
+
@token = keywords(@value) || PLOGIC_KEYWORDS[@value] || :ident
|
249
|
+
return @token
|
250
|
+
end
|
251
|
+
|
252
|
+
## "string"
|
253
|
+
if c == ?"
|
254
|
+
return scan_string_dquoted()
|
255
|
+
end
|
256
|
+
|
257
|
+
## 'string'
|
258
|
+
if c == ?'
|
259
|
+
return scan_string_quoted()
|
260
|
+
end
|
261
|
+
|
262
|
+
## '{'
|
263
|
+
if c == ?{
|
264
|
+
@value = "{"
|
265
|
+
getch()
|
266
|
+
return @token = :'{'
|
267
|
+
end
|
268
|
+
|
269
|
+
## '}'
|
270
|
+
if c == ?}
|
271
|
+
@value = "}"
|
272
|
+
getch()
|
273
|
+
return @token = :'}'
|
274
|
+
end
|
275
|
+
|
276
|
+
##
|
277
|
+
@value = c.chr
|
278
|
+
@error = :invalid_char
|
279
|
+
return @token = :error
|
280
|
+
end
|
281
|
+
|
282
|
+
|
283
|
+
def scan_block(skip_open_curly=false)
|
284
|
+
unless skip_open_curly
|
285
|
+
token = scan()
|
286
|
+
unless token == ?{
|
287
|
+
@error = :block_notfound
|
288
|
+
return @token = :error
|
289
|
+
end
|
290
|
+
end
|
291
|
+
start_pos = @pos
|
292
|
+
count = 1
|
293
|
+
while (c = getch()) != nil
|
294
|
+
if c == ?{
|
295
|
+
count += 1
|
296
|
+
elsif c == ?}
|
297
|
+
count -= 1
|
298
|
+
break if count == 0
|
299
|
+
end
|
300
|
+
end
|
301
|
+
unless c
|
302
|
+
@error = :block_unclosed
|
303
|
+
return @token = :error
|
304
|
+
end
|
305
|
+
assert unless c == ?}
|
306
|
+
@value = @input[start_pos, @pos - start_pos]
|
307
|
+
@token = :block
|
308
|
+
getch()
|
309
|
+
return @value
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
def scan_line
|
314
|
+
sb = @ch.chr
|
315
|
+
while (c = getch()) != nil && c != ?\n
|
316
|
+
sb << c.chr
|
317
|
+
end
|
318
|
+
sb.chop if sb[-1] == ?\r
|
319
|
+
getch()
|
320
|
+
return sb
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
## parser
|
325
|
+
|
326
|
+
|
327
|
+
def parse_error(message, linenum=@linenum, column=@column)
|
328
|
+
return ParseError.new(message, @filename, linenum, column)
|
329
|
+
end
|
330
|
+
|
331
|
+
|
332
|
+
## .[abstract] parse input string and return list of ElementRuleset
|
333
|
+
def parse(input, filename='')
|
334
|
+
not_implemented
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
def _parse_block
|
339
|
+
scan()
|
340
|
+
unless @token == :'{'
|
341
|
+
raise parse_error("'#{@value}': '{' expected.")
|
342
|
+
end
|
343
|
+
start_linenum, start_column = @linenum, @column
|
344
|
+
t = scan_block(true)
|
345
|
+
if t == :error
|
346
|
+
if @error == :block_unclosed
|
347
|
+
raise parse_error("'{': not closed by '}'.", start_linenum, start_column)
|
348
|
+
else
|
349
|
+
assert("@error=#{@error}")
|
350
|
+
end
|
351
|
+
end
|
352
|
+
@value.sub!(/\A\s*\n/, '')
|
353
|
+
@value.sub!(/^\s+\z/, '')
|
354
|
+
return @value
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
@@class_table = {}
|
359
|
+
|
360
|
+
|
361
|
+
def self.register_class(css, klass)
|
362
|
+
@@class_table[css] = klass
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
def self.get_class(css)
|
367
|
+
return @@class_table[css]
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
end #class
|
372
|
+
|
373
|
+
|
374
|
+
|
375
|
+
##
|
376
|
+
## ruby style presentation logic parser
|
377
|
+
##
|
378
|
+
## presentation logic example (ruby style):
|
379
|
+
##
|
380
|
+
## # comment
|
381
|
+
## element "list" {
|
382
|
+
## value @var
|
383
|
+
## attrs "class"=>@clssname, "bgcolor"=>color
|
384
|
+
## append @value==item['list'] ? ' checked' : ''
|
385
|
+
## logic {
|
386
|
+
## @list.each { |item|
|
387
|
+
## _stag
|
388
|
+
## _cont
|
389
|
+
## _etag
|
390
|
+
## }
|
391
|
+
## }
|
392
|
+
## }
|
393
|
+
##
|
394
|
+
class RubyStyleParser < PresentationLogicParser
|
395
|
+
|
396
|
+
|
397
|
+
def parse(input, filename='')
|
398
|
+
reset(input, filename)
|
399
|
+
scan()
|
400
|
+
nodes = []
|
401
|
+
while @token != nil
|
402
|
+
if @token == :element
|
403
|
+
node = parse_element_ruleset()
|
404
|
+
elsif @token == :document
|
405
|
+
node = parse_document_ruleset()
|
406
|
+
else
|
407
|
+
raise parse_error("'#{@value}': element or document required.")
|
408
|
+
end
|
409
|
+
nodes << node
|
410
|
+
end
|
411
|
+
return nodes
|
412
|
+
end
|
413
|
+
|
414
|
+
|
415
|
+
protected
|
416
|
+
|
417
|
+
|
418
|
+
def scan_hook
|
419
|
+
## line comment
|
420
|
+
c = @ch
|
421
|
+
if c == ?#
|
422
|
+
scan_line
|
423
|
+
return scan()
|
424
|
+
end
|
425
|
+
return nil
|
426
|
+
end
|
427
|
+
|
428
|
+
|
429
|
+
def keywords(keyword)
|
430
|
+
return RUBYSTYLE_KEYWORDS[keyword]
|
431
|
+
end
|
432
|
+
RUBYSTYLE_KEYWORDS = { 'BEGIN' => :BEGIN, 'END' => :END }
|
433
|
+
|
434
|
+
|
435
|
+
def parse_document_ruleset
|
436
|
+
assert unless @token == :document
|
437
|
+
scan()
|
438
|
+
unless @token == :'{'
|
439
|
+
raise parse_error("'#{@value}': document requires '{'.")
|
440
|
+
end
|
441
|
+
ruleset = DocumentRuleset.new
|
442
|
+
while @token
|
443
|
+
scan()
|
444
|
+
case @token
|
445
|
+
when :global ; has_space? ; ruleset.set_global _parse_list()
|
446
|
+
when :local ; has_space? ; ruleset.set_local _parse_list()
|
447
|
+
when :fixture ; has_space? ; ruleset.set_fixture _parse_block()
|
448
|
+
when :before ; has_space? ; ruleset.set_before _parse_block()
|
449
|
+
when :after ; has_space? ; ruleset.set_after _parse_block()
|
450
|
+
when :'}' ; break
|
451
|
+
else ; raise parse_error("'#{@value}': invalid token.")
|
452
|
+
end
|
453
|
+
end
|
454
|
+
unless @token == :'}'
|
455
|
+
raise parse_error("'#{@value}': document is not closed by '}'.")
|
456
|
+
end
|
457
|
+
scan()
|
458
|
+
return DocumentRuleset.new(hash)
|
459
|
+
end
|
460
|
+
|
461
|
+
|
462
|
+
def has_space?
|
463
|
+
unless @ch == ?\ || @ch == ?\t
|
464
|
+
raise parse_error("'#{@value}': following spaces are required but got '#{@ch.chr}'.")
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
|
469
|
+
def _parse_list
|
470
|
+
line = scan_line()
|
471
|
+
list = line.split(/,\s+/).collect {|item| item.strip}
|
472
|
+
return list
|
473
|
+
end
|
474
|
+
|
475
|
+
|
476
|
+
def _parse_strs
|
477
|
+
list = _parse_list()
|
478
|
+
list2 = []
|
479
|
+
list.each do |item|
|
480
|
+
case item
|
481
|
+
when /\A"(.*)"\z/ ; list2 << $1
|
482
|
+
when /\A'(.*)'\z/ ; list2 << $1
|
483
|
+
else
|
484
|
+
raise parse_error("'#{item}': argument of 'remove' should be string.")
|
485
|
+
end
|
486
|
+
end
|
487
|
+
return list2
|
488
|
+
end
|
489
|
+
|
490
|
+
|
491
|
+
def _parse_str
|
492
|
+
return _parse_strs()[0]
|
493
|
+
end
|
494
|
+
|
495
|
+
|
496
|
+
def _parse_item
|
497
|
+
item = scan_line()
|
498
|
+
item.strip!
|
499
|
+
return item
|
500
|
+
end
|
501
|
+
|
502
|
+
|
503
|
+
def _parse_block
|
504
|
+
super
|
505
|
+
end
|
506
|
+
|
507
|
+
|
508
|
+
def _parse_tuples
|
509
|
+
line = scan_line()
|
510
|
+
items = line.split(/,\s+/)
|
511
|
+
tuples = []
|
512
|
+
items.each do |item|
|
513
|
+
unless item =~ /(.*?)=>(.*)/
|
514
|
+
raise parse_error("'#{item}': invalid pattern.")
|
515
|
+
end
|
516
|
+
key = $1; key.strip!
|
517
|
+
val = $2; val.strip!
|
518
|
+
if key =~ /\A"(.*)"\z/
|
519
|
+
key = $1
|
520
|
+
elsif key =~ /\A'(.*)'\z/
|
521
|
+
key = $1
|
522
|
+
else
|
523
|
+
raise parse_error("'#{key}': key must be \"...\" or '...'.") #
|
524
|
+
end
|
525
|
+
tuples << [key, val]
|
526
|
+
end
|
527
|
+
return tuples
|
528
|
+
end
|
529
|
+
|
530
|
+
|
531
|
+
def parse_element_ruleset
|
532
|
+
assert unless @token == :element
|
533
|
+
scan()
|
534
|
+
@token == :string or raise parse_error("'#{@value}': element name required in string.")
|
535
|
+
name = @value
|
536
|
+
name =~ /\A\w+\z/ or raise parse_error("'#{@value}': invalid element name.")
|
537
|
+
scan()
|
538
|
+
@token == :'{' or raise parse_error("'#{@value}': '{' required.")
|
539
|
+
ruleset = ElementRuleset.new(name)
|
540
|
+
while true
|
541
|
+
scan()
|
542
|
+
flag_escape = escape?(@value)
|
543
|
+
break unless @token
|
544
|
+
#if @token.is_a?(Symbol) && (@ch != ?\ && @ch != ?\t)
|
545
|
+
# raise parse_error("'#{@value}': following spaces are required but got '#{@ch.chr}'.")
|
546
|
+
#end
|
547
|
+
case @token
|
548
|
+
when :stag ; has_space? ; ruleset.set_stag _parse_item() , flag_escape
|
549
|
+
when :cont ; has_space? ; ruleset.set_cont _parse_item() , flag_escape
|
550
|
+
when :etag ; has_space? ; ruleset.set_etag _parse_item() , flag_escape
|
551
|
+
when :elem ; has_space? ; ruleset.set_elem _parse_item() , flag_escape
|
552
|
+
when :value ; has_space? ; ruleset.set_value _parse_item() , flag_escape
|
553
|
+
when :attrs ; has_space? ; ruleset.set_attrs _parse_tuples() , flag_escape
|
554
|
+
when :append ; has_space? ; ruleset.set_append _parse_list() , flag_escape
|
555
|
+
when :remove ; has_space? ; ruleset.set_remove _parse_strs()
|
556
|
+
when :tagname ; has_space? ; ruleset.set_tagname _parse_str()
|
557
|
+
when :logic ; has_space? ; ruleset.set_logic _parse_block()
|
558
|
+
when :'}' ; break
|
559
|
+
else ; raise parse_error("'#{@value}': invalid token.")
|
560
|
+
end
|
561
|
+
end
|
562
|
+
assert unless token == :'}'
|
563
|
+
scan()
|
564
|
+
return ruleset
|
565
|
+
end
|
566
|
+
|
567
|
+
|
568
|
+
end #class
|
569
|
+
PresentationLogicParser.register_class('ruby', RubyStyleParser)
|
570
|
+
|
571
|
+
|
572
|
+
|
573
|
+
##
|
574
|
+
## css style presentation logic parser
|
575
|
+
##
|
576
|
+
## example of presentation logic in css style:
|
577
|
+
##
|
578
|
+
## // comment
|
579
|
+
## #list {
|
580
|
+
## value: @var;
|
581
|
+
## attrs: "class" @classname, "bgcolro" color;
|
582
|
+
## append: @value==item['list'] ? ' checked' : '';
|
583
|
+
## logic: {
|
584
|
+
## @list.each { |item|
|
585
|
+
## _stag
|
586
|
+
## _cont
|
587
|
+
## _etag
|
588
|
+
## }
|
589
|
+
## }
|
590
|
+
## }
|
591
|
+
##
|
592
|
+
class CssStyleParser < PresentationLogicParser
|
593
|
+
|
594
|
+
|
595
|
+
def parse(input, filename='')
|
596
|
+
reset(input, filename)
|
597
|
+
scan()
|
598
|
+
rulesets = []
|
599
|
+
while @token == ?@
|
600
|
+
c = getch();
|
601
|
+
scan_ident()
|
602
|
+
name = @value
|
603
|
+
if name == 'import'
|
604
|
+
imported_rulesets = parse_import_command()
|
605
|
+
rulesets += imported_rulesets
|
606
|
+
else
|
607
|
+
raise parse_error("@#{name}: unsupported command.")
|
608
|
+
end
|
609
|
+
end
|
610
|
+
while @token == ?#
|
611
|
+
scan_ident()
|
612
|
+
name = @value
|
613
|
+
if name == 'DOCUMENT'
|
614
|
+
ruleset = parse_document_ruleset()
|
615
|
+
else
|
616
|
+
ruleset = parse_element_ruleset()
|
617
|
+
end
|
618
|
+
rulesets << ruleset
|
619
|
+
end
|
620
|
+
unless @token == nil
|
621
|
+
raise parse_error("'#{@value}': '#name' is expected.")
|
622
|
+
end
|
623
|
+
return rulesets
|
624
|
+
end
|
625
|
+
|
626
|
+
|
627
|
+
protected
|
628
|
+
|
629
|
+
|
630
|
+
def scan_hook
|
631
|
+
## comment
|
632
|
+
c = @ch
|
633
|
+
if c == ?/
|
634
|
+
c = getch()
|
635
|
+
if c == ?/ # line comment
|
636
|
+
scan_line()
|
637
|
+
getch()
|
638
|
+
return scan()
|
639
|
+
elsif c == ?* # region comment
|
640
|
+
start_linenum = @linenum
|
641
|
+
while true
|
642
|
+
c = getch() while c != ?*
|
643
|
+
break if c == nil
|
644
|
+
c = getch()
|
645
|
+
break if c == ?/
|
646
|
+
end
|
647
|
+
if c == nil
|
648
|
+
@error = :comment_unclosed
|
649
|
+
@value = start_linenum
|
650
|
+
return @token == :error
|
651
|
+
end
|
652
|
+
getch()
|
653
|
+
return scan()
|
654
|
+
else
|
655
|
+
@value = '/'
|
656
|
+
return @token = ?/
|
657
|
+
end #if
|
658
|
+
end #if
|
659
|
+
|
660
|
+
## '#mark'
|
661
|
+
if c == ?#
|
662
|
+
c = getch()
|
663
|
+
unless is_alpha(c)
|
664
|
+
@error = :invalid_char
|
665
|
+
@value = '#'
|
666
|
+
return @token = :error
|
667
|
+
end
|
668
|
+
@value = '#'
|
669
|
+
return @token = ?#
|
670
|
+
end #if
|
671
|
+
|
672
|
+
## '@import "foo.plogic"'
|
673
|
+
if c == ?@
|
674
|
+
@value = '@'
|
675
|
+
return @token = ?@
|
676
|
+
end
|
677
|
+
|
678
|
+
return nil
|
679
|
+
|
680
|
+
end #def
|
681
|
+
|
682
|
+
|
683
|
+
def keywords(keyword)
|
684
|
+
return CSSSTYLE_KEYWORDS[keyword]
|
685
|
+
end
|
686
|
+
CSSSTYLE_KEYWORDS = { 'begin'=>:begin, 'end'=>:end }
|
687
|
+
|
688
|
+
|
689
|
+
def parse_document_ruleset
|
690
|
+
assert unless @value == 'DOCUMENT'
|
691
|
+
start_linenum = @linenum
|
692
|
+
scan()
|
693
|
+
unless @token == :'{'
|
694
|
+
raise parse_error("'#{@value}': '{' is expected.")
|
695
|
+
end
|
696
|
+
ruleset = DocumentRuleset.new
|
697
|
+
while @token
|
698
|
+
scan()
|
699
|
+
case @token
|
700
|
+
when :'}' ; break
|
701
|
+
when :global ; has_colon?(); ruleset.set_global _parse_words()
|
702
|
+
when :local ; has_colon?(); ruleset.set_local _parse_words()
|
703
|
+
when :fixture ; has_colon?(); ruleset.set_fixture _parse_block()
|
704
|
+
when :begin ; has_colon?(); ruleset.set_begin _parse_block()
|
705
|
+
when :end ; has_colon?(); ruleset.set_end _parse_block()
|
706
|
+
#when :before ; has_colon?(); ruleset.set_before _parse_block()
|
707
|
+
#when :after ; has_colon?(); ruleset.set_after _parse_block()
|
708
|
+
else
|
709
|
+
unless @token
|
710
|
+
raise parse_error("'#DOCUMENT': is not closed by '}'.", start_linenum)
|
711
|
+
else
|
712
|
+
raise parse_error("'#{@value}': unexpected token.")
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
assert unless @token == :'}'
|
717
|
+
scan()
|
718
|
+
return ruleset
|
719
|
+
end
|
720
|
+
|
721
|
+
|
722
|
+
def has_colon?
|
723
|
+
unless @ch == ?:
|
724
|
+
raise parse_error("'#{@value}': ':' is required.")
|
725
|
+
end
|
726
|
+
getch()
|
727
|
+
end
|
728
|
+
|
729
|
+
|
730
|
+
def parse_element_ruleset
|
731
|
+
assert unless @token == :ident
|
732
|
+
start_linenum = @linenum
|
733
|
+
name = @value
|
734
|
+
scan()
|
735
|
+
unless @token == :'{'
|
736
|
+
raise parse_error("'#{@value}': '{' is expected.")
|
737
|
+
end
|
738
|
+
ruleset = ElementRuleset.new(name)
|
739
|
+
while true
|
740
|
+
scan()
|
741
|
+
flag_escape = escape?(@value)
|
742
|
+
case @token
|
743
|
+
when nil ; break
|
744
|
+
when :'}' ; break
|
745
|
+
when :stag ; has_colon?(); ruleset.set_stag _parse_expr() , flag_escape
|
746
|
+
when :cont ; has_colon?(); ruleset.set_cont _parse_expr() , flag_escape
|
747
|
+
when :etag ; has_colon?(); ruleset.set_etag _parse_expr() , flag_escape
|
748
|
+
when :elem ; has_colon?(); ruleset.set_elem _parse_expr() , flag_escape
|
749
|
+
when :value ; has_colon?(); ruleset.set_value _parse_expr() , flag_escape
|
750
|
+
when :attrs ; has_colon?(); ruleset.set_attrs _parse_pairs(), flag_escape
|
751
|
+
when :append ; has_colon?(); ruleset.set_append _parse_exprs(), flag_escape
|
752
|
+
when :remove ; has_colon?(); ruleset.set_remove _parse_strs()
|
753
|
+
when :tagname; has_colon?(); ruleset.set_tagname _parse_str()
|
754
|
+
when :logic ; has_colon?(); ruleset.set_logic _parse_block()
|
755
|
+
else
|
756
|
+
raise parse_error("'#{@value}': unexpected token.")
|
757
|
+
end
|
758
|
+
end
|
759
|
+
unless @token
|
760
|
+
raise parse_error("'##{name}': is not closed by '}'.", start_linenum)
|
761
|
+
end
|
762
|
+
assert "@token=#{@token.inspect}" unless @token == :'}'
|
763
|
+
scan()
|
764
|
+
return ruleset
|
765
|
+
end
|
766
|
+
|
767
|
+
|
768
|
+
def parse_import_command
|
769
|
+
c = @ch
|
770
|
+
c = getch() while is_whitespace(c)
|
771
|
+
t = scan_string()
|
772
|
+
t == :string or raise parse_error("@import: requires filename.")
|
773
|
+
filename = @value
|
774
|
+
test(?f, filename) or raise parse_error("'#{filename}': import file not found.")
|
775
|
+
c = @ch
|
776
|
+
c = getch() while is_whitespace(c)
|
777
|
+
c == ?; or raise parse_error("';' required.")
|
778
|
+
c = getch()
|
779
|
+
scan()
|
780
|
+
parser = self.class.new(@properties)
|
781
|
+
ruleset_list = parser.parse(File.read(filename), filename)
|
782
|
+
return ruleset_list
|
783
|
+
end
|
784
|
+
|
785
|
+
|
786
|
+
private
|
787
|
+
|
788
|
+
|
789
|
+
def _parse_expr
|
790
|
+
expr = ''
|
791
|
+
while true
|
792
|
+
line = scan_line()
|
793
|
+
unless line && line =~ /([,;])[ \t]*(?:\/\/.*)?$/
|
794
|
+
raise parse_error("'#{@token}:': ';' is required.")
|
795
|
+
end
|
796
|
+
indicator = $1
|
797
|
+
expr << $`
|
798
|
+
break if indicator == ';'
|
799
|
+
expr << ",\n"
|
800
|
+
end
|
801
|
+
expr.strip!
|
802
|
+
return expr
|
803
|
+
end
|
804
|
+
|
805
|
+
|
806
|
+
def _parse_pairs
|
807
|
+
hash = {}
|
808
|
+
while true
|
809
|
+
line = scan_line()
|
810
|
+
unless line && line =~ /([,;])[ \t]*(\/\/.*)?$/
|
811
|
+
raise parse_error("'#{@token}:': ';' is required.")
|
812
|
+
end
|
813
|
+
indicator = $1
|
814
|
+
str = $`
|
815
|
+
if str =~ /\A\s*"([-:\w]+)"\s+(.*)\z/
|
816
|
+
aname = $1 ; avalue = $2
|
817
|
+
elsif str =~ /\A\s*'([-:\w]+)'\s+(.*)\z/
|
818
|
+
aname = $1 ; avalue = $2
|
819
|
+
else
|
820
|
+
raise parse_error("'#{@token}:': invalid mapping pattern")
|
821
|
+
end
|
822
|
+
hash[aname] = avalue
|
823
|
+
break if indicator == ';'
|
824
|
+
end
|
825
|
+
return hash
|
826
|
+
end
|
827
|
+
|
828
|
+
|
829
|
+
def _parse_words
|
830
|
+
list = []
|
831
|
+
while true
|
832
|
+
line = scan_line()
|
833
|
+
unless line && line =~ /([,;])[ \t]*(\/\/.*)?$/
|
834
|
+
raise parse_error("'#{@token}:': ';' is required.")
|
835
|
+
end
|
836
|
+
indicator = $1
|
837
|
+
s = $`
|
838
|
+
s.split(/,/).each do |word|
|
839
|
+
word.strip!
|
840
|
+
list << word
|
841
|
+
end
|
842
|
+
break if indicator == ';'
|
843
|
+
end
|
844
|
+
return list
|
845
|
+
end
|
846
|
+
|
847
|
+
|
848
|
+
def _parse_exprs
|
849
|
+
list = []
|
850
|
+
while true
|
851
|
+
line = scan_line()
|
852
|
+
unless line && line =~ /([,;])[ \t]*(\/\/.*)?$/
|
853
|
+
raise parse_error("'#{@token}:': ';' is required.")
|
854
|
+
end
|
855
|
+
indicator = $1
|
856
|
+
expr = $`
|
857
|
+
expr.strip!
|
858
|
+
list << expr
|
859
|
+
break if indicator == ';'
|
860
|
+
end
|
861
|
+
return list
|
862
|
+
end
|
863
|
+
|
864
|
+
|
865
|
+
def _parse_strs
|
866
|
+
list = []
|
867
|
+
while true
|
868
|
+
line = scan_line()
|
869
|
+
unless line && line =~ /([,;])[ \t]*(\/\/.*)?$/
|
870
|
+
raise parse_error("'#{@token}:': ';' is required.")
|
871
|
+
end
|
872
|
+
indicator = $1
|
873
|
+
s = $`
|
874
|
+
strs = s.split(/,/)
|
875
|
+
strs.each do |str|
|
876
|
+
str.strip!
|
877
|
+
unless str =~ /\A'(.*)'\z/ || str =~ /\A"(.*)"\z/
|
878
|
+
raise parse_error("'#{@token}': string list is expected.")
|
879
|
+
end
|
880
|
+
list << $1
|
881
|
+
end
|
882
|
+
break if indicator == ';'
|
883
|
+
end
|
884
|
+
return list
|
885
|
+
end
|
886
|
+
|
887
|
+
|
888
|
+
def _parse_str
|
889
|
+
return _parse_strs()[0]
|
890
|
+
end
|
891
|
+
|
892
|
+
|
893
|
+
def _parse_block
|
894
|
+
super
|
895
|
+
end
|
896
|
+
|
897
|
+
|
898
|
+
end #class
|
899
|
+
PresentationLogicParser.register_class('css', CssStyleParser)
|
900
|
+
|
901
|
+
|
902
|
+
|
903
|
+
end #module
|