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.
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