kwartz 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (169) hide show
  1. data/COPYING +340 -0
  2. data/ChangeLog +103 -0
  3. data/README.txt +37 -0
  4. data/bin/kwartz +12 -0
  5. data/doc-api/classes/Kwartz.html +218 -0
  6. data/doc-api/classes/Kwartz/Assertion.html +140 -0
  7. data/doc-api/classes/Kwartz/AssertionError.html +148 -0
  8. data/doc-api/classes/Kwartz/AttrInfo.html +320 -0
  9. data/doc-api/classes/Kwartz/BaseError.html +206 -0
  10. data/doc-api/classes/Kwartz/BaseTranslator.html +331 -0
  11. data/doc-api/classes/Kwartz/CharacterType.html +212 -0
  12. data/doc-api/classes/Kwartz/CommandOptionError.html +154 -0
  13. data/doc-api/classes/Kwartz/CommandOptions.html +374 -0
  14. data/doc-api/classes/Kwartz/Config.html +150 -0
  15. data/doc-api/classes/Kwartz/ConvertError.html +191 -0
  16. data/doc-api/classes/Kwartz/Converter.html +252 -0
  17. data/doc-api/classes/Kwartz/CssStyleParser.html +483 -0
  18. data/doc-api/classes/Kwartz/DocumentRuleset.html +369 -0
  19. data/doc-api/classes/Kwartz/ElementExpander.html +325 -0
  20. data/doc-api/classes/Kwartz/ElementInfo.html +312 -0
  21. data/doc-api/classes/Kwartz/ElementRuleset.html +582 -0
  22. data/doc-api/classes/Kwartz/EperlHandler.html +338 -0
  23. data/doc-api/classes/Kwartz/EperlTranslator.html +167 -0
  24. data/doc-api/classes/Kwartz/ErubisHandler.html +113 -0
  25. data/doc-api/classes/Kwartz/ErubisTranslator.html +168 -0
  26. data/doc-api/classes/Kwartz/ErubyHandler.html +337 -0
  27. data/doc-api/classes/Kwartz/ErubyTranslator.html +167 -0
  28. data/doc-api/classes/Kwartz/ExpandStatement.html +227 -0
  29. data/doc-api/classes/Kwartz/Expression.html +119 -0
  30. data/doc-api/classes/Kwartz/Handler.html +558 -0
  31. data/doc-api/classes/Kwartz/JstlHandler.html +657 -0
  32. data/doc-api/classes/Kwartz/JstlTranslator.html +226 -0
  33. data/doc-api/classes/Kwartz/KwartzError.html +146 -0
  34. data/doc-api/classes/Kwartz/Main.html +384 -0
  35. data/doc-api/classes/Kwartz/NativeExpression.html +236 -0
  36. data/doc-api/classes/Kwartz/NativeStatement.html +254 -0
  37. data/doc-api/classes/Kwartz/Node.html +156 -0
  38. data/doc-api/classes/Kwartz/ParseError.html +148 -0
  39. data/doc-api/classes/Kwartz/PhpHandler.html +333 -0
  40. data/doc-api/classes/Kwartz/PhpTranslator.html +194 -0
  41. data/doc-api/classes/Kwartz/PresentationLogicParser.html +830 -0
  42. data/doc-api/classes/Kwartz/PrintStatement.html +221 -0
  43. data/doc-api/classes/Kwartz/RailsHandler.html +587 -0
  44. data/doc-api/classes/Kwartz/RailsTranslator.html +167 -0
  45. data/doc-api/classes/Kwartz/RubyStyleParser.html +558 -0
  46. data/doc-api/classes/Kwartz/Ruleset.html +117 -0
  47. data/doc-api/classes/Kwartz/Statement.html +119 -0
  48. data/doc-api/classes/Kwartz/StrutsTranslator.html +190 -0
  49. data/doc-api/classes/Kwartz/TagInfo.html +314 -0
  50. data/doc-api/classes/Kwartz/TextConverter.html +270 -0
  51. data/doc-api/classes/Kwartz/Translator.html +318 -0
  52. data/doc-api/classes/Test.html +107 -0
  53. data/doc-api/classes/Test/Unit.html +101 -0
  54. data/doc-api/created.rid +1 -0
  55. data/doc-api/files/__/README_txt.html +150 -0
  56. data/doc-api/files/kwartz/assert_rb.html +114 -0
  57. data/doc-api/files/kwartz/binding/eperl_rb.html +116 -0
  58. data/doc-api/files/kwartz/binding/erubis_rb.html +116 -0
  59. data/doc-api/files/kwartz/binding/eruby_rb.html +115 -0
  60. data/doc-api/files/kwartz/binding/jstl_rb.html +116 -0
  61. data/doc-api/files/kwartz/binding/php_rb.html +116 -0
  62. data/doc-api/files/kwartz/binding/rails_rb.html +115 -0
  63. data/doc-api/files/kwartz/binding/struts_rb.html +117 -0
  64. data/doc-api/files/kwartz/config_rb.html +107 -0
  65. data/doc-api/files/kwartz/converter_rb.html +119 -0
  66. data/doc-api/files/kwartz/error_rb.html +107 -0
  67. data/doc-api/files/kwartz/main_rb.html +124 -0
  68. data/doc-api/files/kwartz/node_rb.html +114 -0
  69. data/doc-api/files/kwartz/parser_rb.html +117 -0
  70. data/doc-api/files/kwartz/translator_rb.html +115 -0
  71. data/doc-api/files/kwartz/util/assert-text-equal_rb.html +115 -0
  72. data/doc-api/files/kwartz/util/testcase-helper_rb.html +115 -0
  73. data/doc-api/files/kwartz_rb.html +120 -0
  74. data/doc-api/fr_class_index.html +75 -0
  75. data/doc-api/fr_file_index.html +45 -0
  76. data/doc-api/fr_method_index.html +216 -0
  77. data/doc-api/index.html +24 -0
  78. data/doc-api/rdoc-style.css +208 -0
  79. data/doc/docstyle.css +188 -0
  80. data/doc/p-pattern.html +1207 -0
  81. data/doc/reference.html +3396 -0
  82. data/doc/users-guide.html +1670 -0
  83. data/examples/breadcrumbs1/Makefile +15 -0
  84. data/examples/breadcrumbs1/breadcrumbs.eruby.plogic +27 -0
  85. data/examples/breadcrumbs1/breadcrumbs.html +12 -0
  86. data/examples/breadcrumbs1/breadcrumbs.jstl.plogic +28 -0
  87. data/examples/breadcrumbs1/breadcrumbs.php.plogic +26 -0
  88. data/examples/breadcrumbs1/main.php +12 -0
  89. data/examples/breadcrumbs1/main.rb +12 -0
  90. data/examples/breadcrumbs2/Makefile +15 -0
  91. data/examples/breadcrumbs2/breadcrumbs.eruby.plogic +22 -0
  92. data/examples/breadcrumbs2/breadcrumbs.html +14 -0
  93. data/examples/breadcrumbs2/breadcrumbs.jstl.plogic +24 -0
  94. data/examples/breadcrumbs2/breadcrumbs.php.plogic +23 -0
  95. data/examples/breadcrumbs2/main.php +12 -0
  96. data/examples/breadcrumbs2/main.rb +12 -0
  97. data/examples/pagelayout/Makefile +47 -0
  98. data/examples/pagelayout/content.eruby.plogic +44 -0
  99. data/examples/pagelayout/content.jstl.plogic +36 -0
  100. data/examples/pagelayout/content.php.plogic +37 -0
  101. data/examples/pagelayout/content1.html +36 -0
  102. data/examples/pagelayout/content2.html +29 -0
  103. data/examples/pagelayout/design.css +40 -0
  104. data/examples/pagelayout/layout.html +50 -0
  105. data/examples/pagelayout/main.php +55 -0
  106. data/examples/pagelayout/main.rb +77 -0
  107. data/examples/pagelayout/menu.eruby.plogic +14 -0
  108. data/examples/pagelayout/menu.html +13 -0
  109. data/examples/pagelayout/menu.jstl.plogic +14 -0
  110. data/examples/pagelayout/menu.php.plogic +14 -0
  111. data/examples/rails1/Makefile +36 -0
  112. data/examples/rails1/README +19 -0
  113. data/examples/rails1/application_helper.rb +31 -0
  114. data/examples/rails1/edit.html +28 -0
  115. data/examples/rails1/edit.plogic +10 -0
  116. data/examples/rails1/form.html +52 -0
  117. data/examples/rails1/form.plogic +33 -0
  118. data/examples/rails1/layout.plogic +15 -0
  119. data/examples/rails1/link_to.plogic +19 -0
  120. data/examples/rails1/list.html +48 -0
  121. data/examples/rails1/list.plogic +28 -0
  122. data/examples/rails1/new.html +27 -0
  123. data/examples/rails1/new.plogic +10 -0
  124. data/examples/rails1/reader.plogic +29 -0
  125. data/examples/rails1/show.html +40 -0
  126. data/examples/rails1/show.plogic +4 -0
  127. data/examples/rails1/writer.plogic +8 -0
  128. data/examples/table1/Makefile +15 -0
  129. data/examples/table1/main.php +11 -0
  130. data/examples/table1/main.rb +11 -0
  131. data/examples/table1/table1.eruby.plogic +21 -0
  132. data/examples/table1/table1.html +16 -0
  133. data/examples/table1/table1.jstl.plogic +21 -0
  134. data/examples/table1/table1.php.plogic +22 -0
  135. data/kwartz.gemspec +55 -0
  136. data/lib/kwartz.rb +13 -0
  137. data/lib/kwartz/assert.rb +31 -0
  138. data/lib/kwartz/binding/eperl.rb +166 -0
  139. data/lib/kwartz/binding/erubis.rb +61 -0
  140. data/lib/kwartz/binding/eruby.rb +164 -0
  141. data/lib/kwartz/binding/jstl.rb +334 -0
  142. data/lib/kwartz/binding/php.rb +167 -0
  143. data/lib/kwartz/binding/rails.rb +295 -0
  144. data/lib/kwartz/binding/struts.rb +109 -0
  145. data/lib/kwartz/config.rb +28 -0
  146. data/lib/kwartz/converter.rb +920 -0
  147. data/lib/kwartz/error.rb +41 -0
  148. data/lib/kwartz/main.rb +464 -0
  149. data/lib/kwartz/node.rb +454 -0
  150. data/lib/kwartz/parser.rb +903 -0
  151. data/lib/kwartz/translator.rb +153 -0
  152. data/lib/kwartz/util/assert-text-equal.rb +44 -0
  153. data/lib/kwartz/util/testcase-helper.rb +112 -0
  154. data/setup.rb +1331 -0
  155. data/test/test-compile.rb +36 -0
  156. data/test/test-compile.yaml +178 -0
  157. data/test/test-converter.rb +34 -0
  158. data/test/test-converter.yaml +127 -0
  159. data/test/test-directives.rb +32 -0
  160. data/test/test-directives.yaml +1411 -0
  161. data/test/test-main.rb +464 -0
  162. data/test/test-parser.rb +54 -0
  163. data/test/test-parser.yaml +394 -0
  164. data/test/test-rails.rb +28 -0
  165. data/test/test-rails.yaml +301 -0
  166. data/test/test-ruleset.rb +36 -0
  167. data/test/test-ruleset.yaml +804 -0
  168. data/test/test.rb +44 -0
  169. 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