depager 0.2.3 → 0.3.0.b20160729

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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/README.en +4 -19
  3. data/README.ja +42 -79
  4. data/bin/depager +42 -45
  5. data/examples/action_pl0d/pl0d.action.dr +421 -0
  6. data/examples/action_pl0d/test.pl0ds +49 -0
  7. data/examples/c89/c89.dr +493 -496
  8. data/examples/c89/test.c89 +10 -10
  9. data/examples/extension/astdf.rb +10 -0
  10. data/examples/extension/atree.dr +55 -0
  11. data/examples/{sample_calc → extension}/calc.atree.dr +42 -43
  12. data/examples/extension/calc.simple_action.dr +33 -0
  13. data/examples/extension/paction.dr +16 -15
  14. data/examples/extension/pactiontest.dr +14 -14
  15. data/examples/extension/simple_action.rb +44 -0
  16. data/examples/pl0d/pl0ds.dr +337 -334
  17. data/examples/pl0d/test.pl0ds +33 -33
  18. data/examples/rie_calc/calc.rie.dr +57 -0
  19. data/examples/rie_calc/test.calc +4 -0
  20. data/examples/rie_dcuse/dcuse.rie.dr +71 -0
  21. data/examples/rie_dcuse/test.dcuse +1 -0
  22. data/examples/rie_pl0/orig_ex/exerrdg.pl0 +44 -0
  23. data/examples/rie_pl0/orig_ex/exerrm.pl0 +19 -0
  24. data/examples/rie_pl0/orig_ex/exerrmre.pl0 +20 -0
  25. data/examples/rie_pl0/orig_ex/exerrtok.pl0 +18 -0
  26. data/examples/rie_pl0/orig_ex/exmdg.pl0 +40 -0
  27. data/examples/rie_pl0/orig_ex/exmdgwwl.pl0 +43 -0
  28. data/examples/rie_pl0/orig_ex/exmrw.pl0 +22 -0
  29. data/examples/rie_pl0/orig_ex/exmwwl.pl0 +18 -0
  30. data/examples/rie_pl0/orig_ex/exnorw.pl0 +17 -0
  31. data/examples/rie_pl0/pl0.rie.dr +450 -0
  32. data/examples/rie_pl0/test.pl0 +10 -0
  33. data/examples/sample_calc/calc.action.dr +33 -33
  34. data/examples/sample_calc/calc.ast.action.dr +65 -66
  35. data/examples/sample_calc/calc.ast.dr +55 -55
  36. data/examples/sample_calc/calc.cst.dr +45 -45
  37. data/examples/sample_calc/calc.dr +43 -43
  38. data/examples/sample_calc/calc.lex.dr +29 -29
  39. data/examples/sample_calc/{calc_prec.nvaction.dr → calc_prec.action.dr} +31 -31
  40. data/examples/slex_test/divreg.slex.dr +29 -29
  41. data/examples/slex_test/ljoin.slex.dr +36 -36
  42. data/examples/slex_test/test.divreg +1 -1
  43. data/examples/slex_test/test.ljoin +3 -3
  44. data/lib/depager.rb +582 -670
  45. data/lib/depager/grammar.rb +256 -291
  46. data/lib/depager/lr.rb +574 -579
  47. data/lib/depager/parser.rb +282 -277
  48. data/lib/depager/ruby/plugins/_rie_debug.rb +35 -0
  49. data/lib/depager/ruby/plugins/action.rb +53 -43
  50. data/lib/depager/ruby/plugins/ast.dr +364 -269
  51. data/lib/depager/ruby/plugins/ast.rb +1367 -1308
  52. data/lib/depager/ruby/plugins/cst.dr +172 -180
  53. data/lib/depager/ruby/plugins/cst.rb +587 -626
  54. data/lib/depager/ruby/plugins/lex.dr +85 -89
  55. data/lib/depager/ruby/plugins/lex.rb +310 -336
  56. data/lib/depager/ruby/plugins/rie.dr +723 -0
  57. data/lib/depager/ruby/plugins/rie.rb +1653 -0
  58. data/lib/depager/ruby/plugins/slex.dr +202 -200
  59. data/lib/depager/ruby/plugins/slex.rb +780 -817
  60. data/lib/depager/ruby/plugins/srp.rb +56 -51
  61. data/lib/depager/ruby/templates/extension_lalr_master.erb +46 -51
  62. data/lib/depager/ruby/templates/extension_lalr_slave.erb +99 -107
  63. data/lib/depager/ruby/templates/single_lalr_parser.erb +115 -117
  64. data/lib/depager/utils.rb +148 -318
  65. data/lib/depager/version.rb +4 -3
  66. metadata +52 -60
  67. data/ChangeLog +0 -16
  68. data/data/depager/pre-setup.rb +0 -3
  69. data/examples/c89/c89.tab.rb +0 -7127
  70. data/examples/pl0d/pl0ds.tab.rb +0 -2698
  71. data/examples/sample_calc/calc.action.tab.rb +0 -457
  72. data/examples/sample_calc/calc.ast.action.tab.rb +0 -749
  73. data/examples/sample_calc/calc.ast.tab.rb +0 -665
  74. data/examples/sample_calc/calc.astdf.dr +0 -54
  75. data/examples/sample_calc/calc.astdf.tab.rb +0 -672
  76. data/examples/sample_calc/calc.atree.tab.rb +0 -451
  77. data/examples/sample_calc/calc.cst.tab.rb +0 -644
  78. data/examples/sample_calc/calc.lex.tab.rb +0 -374
  79. data/examples/sample_calc/calc.nvaction.dr +0 -33
  80. data/examples/sample_calc/calc.nvaction.tab.rb +0 -465
  81. data/examples/sample_calc/calc.tab.rb +0 -365
  82. data/examples/sample_calc/calc_prec.nvaction.tab.rb +0 -431
  83. data/examples/slex_test/divreg.slex.tab.rb +0 -303
  84. data/examples/slex_test/ljoin.slex.tab.rb +0 -370
  85. data/lib/depager/ruby/plugins/_ast_tmpl.rb +0 -73
  86. data/lib/depager/ruby/plugins/astdf.rb +0 -6
  87. data/lib/depager/ruby/plugins/atree.dr +0 -55
  88. data/lib/depager/ruby/plugins/atree.rb +0 -347
  89. data/lib/depager/ruby/plugins/nvaction.rb +0 -19
  90. data/lib/depager/ruby/templates/simple.erb +0 -23
  91. data/setup.rb +0 -1585
@@ -1,29 +1,29 @@
1
- %class TinyCalc
2
- %extend Lexer ('plugins/lex.rb')
3
- %%
4
-
5
- %LEX{
6
- /\s+/, /\#.*/, /\n/ { }
7
- /[1-9][0-9]*/ { yield token(:NUM, $&.to_i) }
8
- /./ { yield token($&, $&) }
9
- %}
10
-
11
- #begin-rule
12
- expr :
13
- expr '+' term
14
- | expr '-' term
15
- | term
16
- ;
17
- term :
18
- term '*' fact
19
- | term '/' fact
20
- | fact
21
- ;
22
- fact :
23
- NUM
24
- | '(' expr ')'
25
- ;
26
- #end-rule
27
- %%
28
- parser = TinyCalc.new()
29
- parser.parse(STDIN)
1
+ %class TinyCalc::Parser
2
+ %extend Depager::Lexer ('plugins/lex.rb')
3
+ %%
4
+
5
+ %LEX{
6
+ /\s+/, /\#.*/, /\n/ { }
7
+ /[1-9][0-9]*/ { yield token(:NUM, $&.to_i) }
8
+ /./ { yield token($&, $&) }
9
+ %}
10
+
11
+ #begin-rule
12
+ expr :
13
+ expr '+' term
14
+ | expr '-' term
15
+ | term
16
+ ;
17
+ term :
18
+ term '*' fact
19
+ | term '/' fact
20
+ | fact
21
+ ;
22
+ fact :
23
+ NUM
24
+ | '(' expr ')'
25
+ ;
26
+ #end-rule
27
+ %%
28
+ parser = TinyCalc.create_decorated_parser()
29
+ parser.parse(STDIN)
@@ -1,31 +1,31 @@
1
- %class TinyCalc
2
- %extend Lexer ('plugins/lex.rb')
3
- %extend NVAction ('plugins/nvaction.rb')
4
- %decorate @NVAction
5
- #%decorate ShiftReducePrinter ('plugins/srp.rb')
6
- %prec{
7
- left '*' '/'
8
- left '+' '-'
9
- %}
10
- %%
11
-
12
- %LEX{
13
- /\s+/, /\#.*/ { }
14
- /[1-9][0-9]*/ { yield token(:NUM, $&.to_i) }
15
- /./ { yield token($&, $&) }
16
- %}
17
-
18
- #begin-rule
19
- expr :
20
- expr-left '+' expr-right { _left + _right }
21
- | expr-left '-' expr-right { _left - _right }
22
- | expr-left '*' expr-right { _left * _right }
23
- | expr-left '/' expr-right { _left / _right }
24
- | '(' expr ')' { _expr }
25
- | NUM { _NUM.value }
26
- ;
27
- #end-rule
28
- %%
29
- parser = createDecoratedTinyCalc
30
- r, = parser.parse(STDIN)
31
- puts r
1
+ %class TinyCalc::Parser
2
+ %extend Depager::Lexer ('plugins/lex.rb')
3
+ %extend Depager::Action ('plugins/action.rb')
4
+ %decorate @Action
5
+ #%decorate Depager::LALR::ShiftReducePrinter ('plugins/srp.rb')
6
+ %prec{
7
+ left '*' '/'
8
+ left '+' '-'
9
+ %}
10
+ %%
11
+
12
+ %LEX{
13
+ /\s+/, /\#.*/ { }
14
+ /[1-9][0-9]*/ { yield token(:NUM, $&.to_i) }
15
+ /./ { yield token($&, $&) }
16
+ %}
17
+
18
+ #begin-rule
19
+ expr :
20
+ expr-left '+' expr-right { _left + _right }
21
+ | expr-left '-' expr-right { _left - _right }
22
+ | expr-left '*' expr-right { _left * _right }
23
+ | expr-left '/' expr-right { _left / _right }
24
+ | '(' expr ')' { _expr }
25
+ | NUM { _NUM.value }
26
+ ;
27
+ #end-rule
28
+ %%
29
+ parser = TinyCalc.create_decorated_parser
30
+ r, = parser.parse(STDIN)
31
+ puts r
@@ -1,29 +1,29 @@
1
- %class StatefulLexerTest1
2
- %extend StatefulLexer ('plugins/slex.rb')
3
- %extend NVAction ('plugins/nvaction.rb')
4
- %decorate @NVAction
5
- %decorate @StatefulLexer
6
- #%decorate ShiftReducePrinter ('plugins/srp.rb')
7
- %%
8
- %LEX{
9
- <START>
10
- /\s+/ { }
11
- /\/(([^\/\\]+|\\.)*)\// { yield token(:REGEXP, $&) }
12
- /./ { yield token($&, $&) }
13
- <OPMODE : START>
14
- '/' { yield token($&, $&) }
15
- %}
16
-
17
- #begin-rule
18
- expr:
19
- expr '/' [>:START ?] fact
20
- | fact
21
- ;
22
- fact:
23
- REGEXP [>:OPMODE ?]
24
- { warn "REGEXP: #{_REGEXP.value}" }
25
- ;
26
- #end-rule
27
- %%
28
- p = createDecoratedStatefulLexerTest1
29
- p.parse(STDIN)
1
+ %class StatefulLexerTest1::Parser
2
+ %extend Depager::StatefulLexer ('plugins/slex.rb')
3
+ %extend Depager::Action ('plugins/action.rb')
4
+ %decorate @Action
5
+ %decorate @StatefulLexer
6
+ #%decorate Depager::LALR::ShiftReducePrinter ('plugins/srp.rb')
7
+ %%
8
+ %LEX{
9
+ <START>
10
+ /\s+/ { }
11
+ /\/(([^\/\\]+|\\.)*)\// { yield token(:REGEXP, $&) }
12
+ /./ { yield token($&, $&) }
13
+ <OPMODE : START>
14
+ '/' { yield token($&, $&) }
15
+ %}
16
+
17
+ #begin-rule
18
+ expr:
19
+ expr '/' [>:START ?] fact
20
+ | fact
21
+ ;
22
+ fact:
23
+ REGEXP [>:OPMODE ?]
24
+ { warn "REGEXP: #{_REGEXP.value}" }
25
+ ;
26
+ #end-rule
27
+ %%
28
+ p = StatefulLexerTest1.create_decorated_parser
29
+ p.parse(STDIN)
@@ -1,36 +1,36 @@
1
- %class StatefulLexerTest2
2
- %extend StatefulLexer ('plugins/slex.rb')
3
- %extend NVAction ('plugins/nvaction.rb')
4
- %decorate @NVAction
5
- %decorate @StatefulLexer
6
- #%decorate ShiftReducePrinter ('plugins/srp.rb')
7
- %%
8
- %LEX{
9
- <START>
10
- /[ \t]+/ { }
11
- /\n/ { yield token(:EOL, $&) }
12
- /\w+/ { yield token(:ID, $&) }
13
- /./ { yield token($&, $&) }
14
- <LCONT : START>
15
- /\r?\n/ { }
16
- %}
17
-
18
- #begin-rule
19
- stmts:
20
- | stmts expr EOL
21
- { warn "#{_expr} <EOL>:#{@lexstate}" }
22
- ;
23
- expr:
24
- expr '+' [>:LCONT] fact
25
- { "#{_expr} + #{_fact}" }
26
- | fact
27
- { _fact }
28
- ;
29
- fact:
30
- ID [>:START]
31
- { _ID.value }
32
- ;
33
- #end-rule
34
- %%
35
- p = createDecoratedStatefulLexerTest2
36
- p.parse(STDIN)
1
+ %class StatefulLexerTest2::Parser
2
+ %extend Depager::StatefulLexer ('plugins/slex.rb')
3
+ %extend Depager::Action ('plugins/action.rb')
4
+ %decorate @Action
5
+ %decorate @StatefulLexer
6
+ #%decorate Depager::LALR::ShiftReducePrinter ('plugins/srp.rb')
7
+ %%
8
+ %LEX{
9
+ <START>
10
+ /[ \t]+/ { }
11
+ /\r?\n/ { yield token(:EOL, $&) }
12
+ /\w+/ { yield token(:ID, $&) }
13
+ /./ { yield token($&, $&) }
14
+ <LCONT : START>
15
+ /\r?\n/ { }
16
+ %}
17
+
18
+ #begin-rule
19
+ stmts:
20
+ | stmts expr EOL
21
+ { warn "#{_expr} <EOL>:#{@lexstate}" }
22
+ ;
23
+ expr:
24
+ expr '+' [>:LCONT] fact
25
+ { "#{_expr} + #{_fact}" }
26
+ | fact
27
+ { _fact }
28
+ ;
29
+ fact:
30
+ ID [>:START]
31
+ { _ID.value }
32
+ ;
33
+ #end-rule
34
+ %%
35
+ p = StatefulLexerTest2.create_decorated_parser
36
+ p.parse(STDIN)
@@ -1 +1 @@
1
- /aaa/ / /bbb/
1
+ /aaa/ / /bbb/
@@ -1,3 +1,3 @@
1
- x +
2
- y +
3
- z
1
+ x +
2
+ y +
3
+ z
@@ -1,670 +1,582 @@
1
- require 'erb'
2
- require "depager/parser.rb"
3
- require "depager/grammar.rb"
4
- require "depager/utils.rb"
5
-
6
- module Depager
7
- LIBDIR = File.expand_path "#{File.dirname(__FILE__)}/depager/ruby"
8
- Tmpldir = "#{LIBDIR}/templates"
9
- $LOAD_PATH.unshift LIBDIR
10
-
11
- #
12
- # file manager
13
- #
14
- class FileManager
15
- def initialize f=nil
16
- @files = []
17
- @files << f if f
18
- end
19
- def __file= f
20
- # @file = f
21
- end
22
- def eof?
23
- ceof = current.eof?
24
- if !ceof
25
- ceof
26
- elsif @files.size == 1
27
- ceof
28
- else
29
- @files.pop
30
- self.eof?
31
- end
32
- end
33
- # get file name
34
- def fname
35
- File.basename current.path
36
- end
37
- # get file path
38
- def path
39
- current.path
40
- end
41
- # get current line no
42
- def lineno
43
- current.lineno
44
- end
45
- # add file to files
46
- def add_file f
47
- @files << f
48
- end
49
- #current file
50
- def current
51
- @files.last
52
- end
53
-
54
- # get next line
55
- def getline
56
- current.gets unless eof?
57
- end
58
- alias gets getline
59
- end
60
-
61
- #
62
- # = declaration part parser
63
- #
64
- class DeclParser
65
- attr_accessor :target_name, :g_parser, :generator
66
- def initialize
67
- end
68
- def parse(file, fname=nil)
69
- @files = FileManager.new(file)
70
- @target_name = nil
71
-
72
- parse_decl
73
- end
74
- def files
75
- @files
76
- end
77
- alias file files
78
- def parse_decl
79
- until files.eof?
80
- case line = files.getline
81
- when /^\s*(#.*)?$/
82
- #skip space and comment.
83
- when /^%class\s+(\S+)\s+based_on\s+(\S+)(\s+\(\s*'(.+)'\s*\))?\s*$/
84
- @target_name = $1
85
- require $4 if $4
86
- ns = $2.split(/::/)
87
- ge = ns.pop
88
- m = ns.inject(Object){|c,name| c.const_get(name) }
89
- @generator = m.const_get("#{ge}Generator").new(self)
90
- break
91
- when /^%class\s+(\S+)\s*$/
92
- require 'depager/lr.rb'
93
- @target_name = $1
94
- @generator = LALR::SingleParserGenerator.new(self)
95
- break
96
- when /^%defext\s+(\S+)\s*$/
97
- require 'depager/lr.rb'
98
- @target_name = $1
99
- @generator = LALR::ExtensionParserGenerator.new(self)
100
- break
101
- else
102
- error_exit "%class not found."
103
- end
104
- end
105
- @generator.parse_decl
106
- end
107
- def parsing_method
108
- @generator.parsing_method
109
- end
110
- def error_exit msg, lineno=nil
111
- lineno ||= files.lineno
112
- warn "#{files.path}:#{lineno}: #{msg}"
113
- exit 1
114
- end
115
- def warning msg, lineno=nil
116
- lineno ||= files.lineno
117
- warn "#{files.path}:#{lineno}: warning: #{msg}"
118
- end
119
- end
120
-
121
- #
122
- # = grammar part parser
123
- #
124
- class GrammarParser
125
- include FileUtils
126
-
127
- HOOK_KEYS = [
128
- :pre_rule_list, :post_rule_list,
129
- :pre_rule, :post_rule,
130
- :post_lhs,
131
- :post_rhs_list, :pre_rhs_list,
132
- :pre_rhs, :post_rhs, :post_symbol
133
- ] #:nodoc:
134
- def self.hook_name? s
135
- HOOK_KEYS.include? s.to_sym
136
- end
137
-
138
- def files
139
- @d_parser.files
140
- end
141
-
142
- private
143
- GToken = Struct.new(:tag, :value, :name, :lineno)
144
- def _GToken tag, value, name=nil
145
- @token = GToken[tag, value, name, lineno]
146
- return @token
147
- end
148
- def parse_grammar
149
- gettoken
150
- do_hook :pre_rule_list
151
- parse_rulelist
152
- do_hook :post_rule_list
153
- unless @token.tag == nil
154
- error_exit "syntax error(grammar). "<<
155
- "unexpected '#{@token.tag}' expect '%%'"
156
- end
157
- end
158
- def parse_rulelist
159
- do_hook :pre_rule
160
- parse_rule
161
- do_hook :post_rule
162
- if @token.tag == :NT
163
- parse_rulelist
164
- end
165
- end
166
- def parse_rule
167
- if @token[0] == :NT
168
- @lhs = @token.value
169
-
170
- gettoken
171
- do_hook :post_lhs
172
-
173
- unless @token.tag == ':'
174
- error_exit "syntax error(grammar). "<<
175
- "unexpected '#{@token.tag}' expecting ':'"
176
- end
177
- gettoken
178
-
179
- @nrhs = 0
180
- do_hook :pre_rhs_list
181
- parse_rhslist
182
- do_hook :post_rhs_list
183
-
184
- unless @token.tag == ';'
185
- error_exit "syntax error(grammar). " <<
186
- "unexpected '#{@token.tag}' expecting ';'"
187
- end
188
- gettoken
189
- end
190
- end
191
- def parse_rhslist
192
- @rhs = []; @rhsni = {}; @prec = nil
193
- @optval = Array.new(@nparam)
194
- do_hook :pre_rhs
195
- parse_syms
196
- add_rule(@lhs, @rhs, @prec)
197
- do_hook :post_rhs
198
- @nrhs += 1
199
-
200
- if @token.tag == '|'
201
- gettoken
202
- parse_rhslist
203
- end
204
- end
205
- def parse_syms
206
- if @token.tag == :NT || @token.tag == :T
207
- rhs_insert_sym(-1, @token.value, @token.name)
208
- gettoken
209
-
210
- do_hook :post_symbol
211
- parse_syms
212
- end
213
- end
214
-
215
- def gettoken
216
- while !@line.empty? || !eof?
217
- if @line.empty?
218
- getline
219
- if @line =~ /^%include\s+'(.+?)'\s*$/
220
- files.add_file File.open($1)
221
- @line = ''
222
- end
223
- end
224
- @oldline = @line
225
- tag = s = name = val = nil
226
- until @line.empty? do
227
- case @line
228
- when /^\s+/, /^\#.*/ ;
229
- # skip
230
- when /^=([a-zA-Z]+)/
231
- @prec = s = $1.intern
232
- val = @terms[s] ||= - (@terms.size + 1)
233
- when /^([a-z][a-z0-9_]*)(-([a-z][_a-z0-9]*))?/
234
- tag = :NT
235
- s = $1.intern
236
- name = $3 ? $3 : $1
237
- val = @nonterms[s] ||= @nonterms.size
238
- when /^([A-Z][A-Z0-9_]*)(-([a-z][_a-z0-9]*))?/
239
- tag = :T
240
- s = $1.intern
241
- name = $3 ? $3 : $1
242
- val = @terms[s] ||= - (@terms.size + 1)
243
- when /^'(.+?)'/
244
- tag = :T
245
- name = s = $1
246
- val = @terms[s] ||= - (@terms.size + 1)
247
- when /^%%/
248
- return _GToken(nil, nil)
249
- when /^([:;|])/
250
- tag = $&
251
- val = $&
252
- else
253
- return _GToken(:UNKNOWN, @line[0].chr)
254
- end
255
- @oldline = @line; @line = $'
256
- @i2s[val] ||= s if val && s
257
- return _GToken(tag, val, name) if tag
258
- end
259
- end
260
- return _GToken(nil, nil)
261
- end
262
-
263
- private
264
- def do_hook hook
265
- @hooks[hook].each{|o, m| o.send m}
266
- end
267
-
268
- public
269
- # update line and get token
270
- def update_context line
271
- @line = line
272
- gettoken
273
- end
274
-
275
- # get number of param
276
- def getnparam a = nil
277
- return nil unless a
278
- return @nparams[a] if @nparams[a]
279
- @nparam += 1
280
- @nparams[a] = @nparam
281
- end
282
-
283
- def int_to_sym n
284
- @i2s[n]
285
- end
286
- alias i2s int_to_sym
287
-
288
- # add nonterm
289
- # sym:: symbol
290
- def add_nonterm sym
291
- sym = sym.to_s.intern
292
- isym = @nonterms[sym] = @nonterms.size
293
- @i2s[isym] = sym
294
- return isym
295
- end
296
-
297
- #
298
- # add rule
299
- #
300
- def add_rule lhs, rhs, prec=nil
301
- @lhs_syms[lhs] = true
302
- rule = @parsing_method::Rule[lhs, rhs, prec]
303
- @rules.push rule
304
- return rule
305
- end
306
-
307
- # get rhs index by name
308
- # name :: name
309
- def name_to_rhs_index name
310
- @rhsni[name]
311
- end
312
- alias n2ri name_to_rhs_index
313
-
314
- # insert sym into rhs
315
- def rhs_insert_sym pos, token_value, token_name
316
- if pos < 0
317
- @rhs.push token_value
318
- @rhsni[token_name] = @rhs.size - 1
319
- @rhs_syms[token_value] = true
320
- else
321
- @rhsni.each{|k, v| @rhsni[k] += 1 if v >= pos }
322
- @rhs.insert(pos, token_value)
323
- @rhsni[token_name] = pos
324
- @rhs_syms[token_value] = true
325
- end
326
- end
327
-
328
- attr_accessor :rules, :terms, :nonterms, :precs, :table
329
- attr_accessor :line, :line0, :oldline, :token
330
- attr_accessor :optouter, :optinner, :optmain, :mixin
331
- attr_accessor :nparams, :lhs, :rhs, :nrhs, :rhsni, :nparam, :nrules
332
- attr_reader :d_parser, :target_name, :parsing_method, :hooks
333
-
334
- def initialize d_parser
335
- @yydebug = true
336
- @d_parser = d_parser
337
-
338
- @optinner = []
339
- @optouter = []
340
- @optmain = []
341
- @nparams = {}
342
- @nparam = 1
343
- init_hook
344
- end
345
-
346
- private
347
- # check grammar
348
- def check_grammar
349
- lhs_warns = []
350
- @lhs_syms.each do |s, _|
351
- lhs_warns << s if s != 1 and !@rhs_syms[s]
352
- end
353
- rhs_warns = []
354
- @rhs_syms.each do |s, _|
355
- rhs_warns << s if s > 0 and !@lhs_syms[s]
356
- end
357
- lhs_warns.uniq.each do |s|
358
- warning "the lhs '#{@i2s[s]}' is not used"
359
- end
360
- rhs_warns.uniq.each do |s|
361
- warning "the symbol '#{@i2s[s]}' is undefined"
362
- end
363
- end
364
-
365
- # make grammar object
366
- # precs:: prec infos
367
- def make_grammar precs
368
- ts = @nonterms.size - 1
369
- @terms.each{|k,v| @terms[k] = ts - v}
370
- # @grammar[0].act = Array.new(@nparam-1)
371
-
372
- @precs = {}
373
- precs.each_with_index do |p, x|
374
- p[1].each do |i|
375
- if @terms[i]
376
- @precs[@terms[i]] = [p[0], x]
377
- else
378
- error_exit "prec error '#{i}'."
379
- end
380
- end
381
- end
382
- @rules.each do |rule|
383
- rule.rhs.each_with_index{|i, x| rule.rhs[x] = ts - i if i < 0}
384
- rule.prec &&= @precs[@terms[rule.prec]]
385
- end
386
- end
387
- alias mkg make_grammar
388
-
389
- # initialize hook
390
- def init_hook
391
- @hooks = HOOK_KEYS.inject({}){|r,i| r[i]=[]; r }
392
- end
393
-
394
- def init_grammar
395
- @rules = [@parsing_method::Rule[0 , [1]]]
396
- @terms = { nil => -1, false => -2 }
397
- @nonterms = {'$start' => 0}
398
- @i2s = {}
399
- @rhs_syms = {}
400
- @lhs_syms = {}
401
- end
402
-
403
- public
404
- # extend paser
405
- # extentions:: array of [name, paht]
406
- # options:: hash of options
407
- def extend_paser extentions, options
408
- init_hook
409
- extentions.each do |f,m|
410
- require f if f
411
- e = Object.const_get("#{m}Extension").new
412
- options[m] and options[m].each do |k,v|
413
- begin
414
- e.send("#{k}=", v)
415
- rescue NoMethodError
416
- warning "no such a option '#{m}.#{k}'."
417
- end
418
- end
419
- e.send(:__regext__, self)
420
- end
421
- end
422
-
423
- # get next line
424
- def getline
425
- @line0 = @line = files.getline
426
- end
427
-
428
- # parse and make LALR(1) table
429
- # file:: input file object
430
- # target_name:: class name
431
- # mixin:: mixin modules
432
- # precs:: prec infos
433
- # return:: LALRTable
434
- def parse(target_name = nil, mixin = [], precs = [], pm=nil)
435
- @line = @line0 = ''
436
- @oldline = nil
437
- @target_name = target_name
438
- @mixin = mixin
439
- @parsing_method = pm || @d_parser.generator.parsing_method
440
-
441
- init_grammar
442
- parse_grammar
443
- check_grammar
444
- make_grammar precs
445
- @table = @parsing_method::Table.new(
446
- @parsing_method::Grammar.new(@rules, @terms, @nonterms, @precs))
447
- @table.check_table @d_parser
448
- @table
449
- end
450
- end
451
-
452
- #
453
- # generators
454
- #
455
-
456
- # Generator base
457
- class Generator
458
- include FileUtils
459
- def files
460
- @d_parser.files
461
- end
462
- Tmpldir = Depager::Tmpldir
463
- Tmplfile = "**NOTHING**"
464
- def tmpldir
465
- Generator::Tmpldir
466
- end
467
- def tmplf
468
- self.class::Tmplfile
469
- end
470
- attr_reader :d_parser, :parsing_method
471
- attr_accessor :optouter, :optinner, :optmain
472
- attr_accessor :basis_name, :deco, :req, :options
473
- def initialize d_parser
474
- @d_parser = d_parser
475
- @parsing_method = nil
476
- @basis_name = nil
477
-
478
- @deco = []
479
- @req = []
480
- @ext = []
481
- @options = {}
482
-
483
- @optinner = []
484
- @optouter = []
485
- @optmain = []
486
- @precs = []
487
- @mixin = []
488
-
489
- @dr_option = {}
490
-
491
- @tmpldir = Generator::Tmpldir+'/'
492
- end
493
-
494
- def parse_prec
495
- precs = []
496
- until files.eof?
497
- case line = files.getline
498
- when /^\s*$/, /^\s*#.*$/
499
- # skip
500
- when /^\s*(left|right|nonassoc)\s+(.*)$/
501
- dir = $1.upcase.intern
502
- syms = $2.split.map{|i| (i =~ /'(.+?)'/) ? $1 : i.intern}
503
- precs.push [dir, syms]
504
- when /^%\}\s*$/
505
- break
506
- else
507
- error_exit "syntax error(prec).\n> #{line}"
508
- end
509
- end
510
- return precs
511
- end
512
-
513
- def parse_block
514
- val = ''
515
- until eof?
516
- line = getline
517
- break if line =~ /^%\}/
518
- val.concat line
519
- end
520
- return val
521
- end
522
-
523
- def parse_common line
524
- case line
525
- when /^\s*(#.*)?$/
526
- #skip space and comment.
527
- when /^%decorate\s+(.*?)\s*\((.*)\)\s*$/
528
- @deco.push $1
529
- @req.push $2
530
- when /^%decorate\s+(.*?)\s*$/
531
- @deco.push $1
532
- when /^%extend\s+(.*?)\s*\(\s*'(.*)'\s*\)\s*$/
533
- @ext.push [$2.strip, $1] unless @ext.find{|i| i[1] == $1}
534
- when /^%option\s+(\w+).(\w+)\s+(.*)\s*$/
535
- if $1 == 'Generator'
536
- @dr_option[$2] = $3
537
- else
538
- @options[$1] ||= []
539
- @options[$1] << [$2, $3]
540
- end
541
- when /^%require\s+'(.+?)'\s*$/
542
- @req.push "'#{$1}'"
543
- when /^%mixin\s+(\S+)\s*(\((.+)\)\s*)?$/
544
- @mixin.push $1
545
- @req.push $3 if $3
546
- when /^%inner\s*\{\s*$/
547
- @optinner.push parse_block
548
- when /^%prec\s*\{\s*$/
549
- @precs = parse_prec
550
- else
551
- warning "syntax error(declaration).\n> #{line}"
552
- end
553
- end
554
- end
555
-
556
-
557
- module Simple
558
- Grammar = Depager::ParsingMethod::Grammar
559
- Rule = Depager::ParsingMethod::Rule
560
- class Table
561
- def initialize *args
562
- end
563
- def check_table *args
564
- true
565
- end
566
- end
567
- class SingleGenerator < Generator
568
- Tmplfile = 'simple.erb'
569
- def initialize d_parser
570
- super
571
- @parsing_method = Simple
572
- end
573
- def out_code g_parser
574
- ERB.new(File.read("#{tmpldir}/#{tmplf}")).result(binding)
575
- end
576
- def parse_decl
577
- until eof?
578
- line = getline
579
- case line
580
- when /^%%\s*$/
581
- g_parser = GrammarParser.new(@d_parser)
582
- g_parser.extend_paser @ext, @options
583
- g_parser.parse(@d_parser.target_name, @mixin, @precs)
584
- @optmain.push parse_block
585
- return out_code(g_parser)
586
- else
587
- parse_common(line)
588
- end
589
- end
590
- return nil
591
- end
592
- end
593
-
594
- class ExtensionGenerator < Generator
595
- TmplfileMaster = '**NOTHING**'
596
- TmplfileSlave = '**NOTHING**'
597
- attr_accessor :regs, :paramkey
598
- def initialize d_parser
599
- super
600
- @parsing_method = Simple
601
- @basis_name = 'self'
602
- @paramkey = nil
603
- @regs = { }
604
- end
605
-
606
- def parse_decl
607
- until eof?
608
- line = getline
609
- case line
610
- when /^%hook\s*(.*?)\s*$/
611
- hs = $1
612
- mtsk = if hs =~ /\/(([^\/\\]+|\\.)*)\/\s*(skip)?$/
613
- hs = $`
614
- [$1, $3 ? true : false]
615
- end
616
- hook = hs.split(' ')
617
- hookname = hook.join('__')
618
- hook.each do |i| i = i.to_sym
619
- raise unless GrammarParser.hook_name? i
620
- @regs[i] = "#{@d_parser.target_name}_#{hookname}"
621
- end
622
- @optouter.push(parse_hook(hookname, mtsk))
623
- when /^%param\s*(.*)\s*$/
624
- @paramkey = $1
625
- else
626
- parse_common(line)
627
- end
628
- end
629
- return out_master_code
630
- end
631
-
632
- def parse_hook hookname, mtsk
633
- inner = ''
634
- precs = []
635
- banner = nil
636
- mixin = [].concat(@mixin)
637
- target_name = "#{@d_parser.target_name}_#{hookname}"
638
- until eof?
639
- line = getline
640
- case line
641
- when /^%banner\s*'(([^'\\]+|\\.)*)'\s*$/
642
- banner = $1
643
- when /^%inner\s*\{\s*$/
644
- inner = parse_block
645
- when /^%mixin\s*(.+?)\s*(\((.+)\)\s*)?$/
646
- mixin.push $1
647
- @req.push $3 if $3
648
- when /^%prec\s*(.*?)\s*$/
649
- precs = parse_prec
650
- when /^%%\s*$/
651
- g_parser = GrammarParser.new(@d_parser)
652
- g_parser.extend_paser @ext, @options
653
- g_parser.parse(target_name, mixin, precs)
654
- g_parser.optinner.push inner
655
- return out_slave_code(g_parser, mixin, mtsk, banner)
656
- else
657
- warning "syntax error(declaration).\n> #{line}", lineno
658
- end
659
- end
660
- end
661
-
662
- def out_slave_code g_parser, mixin, mtsk, banner
663
- ERB.new(File.read(self.class::TmplfileSlave)).result(binding)
664
- end
665
- def out_master_code
666
- ERB.new(File.read(self.class::TmplfileMaster)).result(binding)
667
- end
668
- end
669
- end
670
- end
1
+ # -*- coding: utf-8 -*-
2
+ require 'erb'
3
+ require "depager/parser.rb"
4
+ require "depager/grammar.rb"
5
+ require "depager/utils.rb"
6
+
7
+ module Depager
8
+ def self.configuration
9
+ @flags ||= {}
10
+ end
11
+
12
+ def self.debug_mode? flag = ''
13
+ value = configuration[:debug] or return false
14
+ !! value.match(/#{flag.to_s}/)
15
+ end
16
+
17
+ def self.verbose_mode?
18
+ configuration[:verbose]
19
+ end
20
+
21
+ class DeclarationPartParser
22
+ include Depager::Utils::CommonMethods
23
+
24
+ attr_reader :file, :target_namespace, :target_name, :g_parser, :generator
25
+
26
+ def parse(file)
27
+ @file = file
28
+ @g_parser = GrammarPartParser.new(self)
29
+
30
+ until file.eof?
31
+ case line = file.gets
32
+ when /^\s*(#.*)?$/
33
+ #skip space and comment.
34
+ when /^%class\s+(\S+)(\s+based_on\s+(\S+)(\s+\(\s*'(.+)'\s*\))?)?\s*$/
35
+ @target_name = $1
36
+ if $2
37
+ require $5 if $5
38
+ nesting = $3.split(/::/).tap{|it| it[-1] = "#{it[-1]}Generator"}
39
+ generator_class = nesting.inject(Object){|c,name| c.const_get(name) }
40
+ else
41
+ require 'depager/lr.rb'
42
+ generator_class = LALR::ParserGenerator
43
+ end
44
+
45
+ if @target_name.match('(.+)::([^:]+)')
46
+ @target_namespace = $1
47
+ @target_name = $2
48
+ else
49
+ error_exit "Namespace required"
50
+ end
51
+
52
+ @generator = generator_class.new(self)
53
+ break
54
+ when /^%defext\s+(\S+)\s*$/
55
+ require 'depager/lr.rb'
56
+
57
+ @target_namespace = $1
58
+ @target_name = 'Master'
59
+
60
+ @generator = LALR::ExtensionGenerator.new(self)
61
+ break
62
+ else
63
+ error_exit "%class not found."
64
+ end
65
+ end
66
+ @generator.parse
67
+ end
68
+
69
+ def parsing_method
70
+ @generator.parsing_method
71
+ end
72
+ end
73
+
74
+ class GrammarPartParser
75
+ include Depager::Utils::CommonMethods
76
+
77
+ Token = Struct.new(:tag, :value, :name, :lineno)
78
+
79
+ HOOK_KEYS = [
80
+ :pre_rule_list, :post_rule_list,
81
+ :pre_rule, :post_rule,
82
+ :post_lhs,
83
+ :post_rhs_list, :pre_rhs_list,
84
+ :pre_rhs, :post_rhs,
85
+ :post_symbol
86
+ ] #:nodoc:
87
+
88
+ attr_accessor :rules, :terms, :nonterms, :precs, :table
89
+ attr_accessor :lhs, :rhs, :nrhs, :rhs_names, :nrules
90
+ attr_accessor :line, :original_line, :old_line, :token
91
+ attr_accessor :outer_code, :inner_code, :mixins
92
+ attr_reader :d_parser, :target_namespace, :target_name, :hooks
93
+
94
+ def initialize d_parser
95
+ @d_parser = d_parser
96
+ end
97
+
98
+ # parse and make LALR(1) table
99
+ def parse(full_target_name, precs = [], extensions = [], options = {})
100
+ @original_line = @old_line = @line = ''
101
+ @inner_code = ''
102
+ @outer_code = ''
103
+
104
+ full_target_name.match(/::([^:]+)$/) or raise
105
+ @target_namespace = $`
106
+ @target_name = $1
107
+
108
+ extend_paser(extensions, options)
109
+ init_grammar
110
+ parse_grammar
111
+ check_grammar
112
+ make_grammar precs
113
+
114
+ grammar = d_parser.parsing_method::Grammar.new(@rules, @terms, @nonterms, @precs)
115
+ @table = d_parser.parsing_method::Table.new(grammar)
116
+ @table.check_table d_parser
117
+ @extensions.each{|o| o.send :term_extension }
118
+ @table
119
+ end
120
+
121
+ def hook_name? s
122
+ HOOK_KEYS.include? s.to_sym
123
+ end
124
+
125
+ def update_context line
126
+ @line = line
127
+ get_token
128
+ end
129
+
130
+ def int_to_sym n
131
+ @int_to_sym[n]
132
+ end
133
+
134
+ def lhs_name
135
+ int_to_sym(lhs)
136
+ end
137
+
138
+ def add_nonterm sym
139
+ sym = sym.to_s.intern
140
+ isym = @nonterms[sym] = @nonterms.size
141
+ @int_to_sym[isym] = sym
142
+ return isym
143
+ end
144
+
145
+ def add_rule lhs, rhs, prec = nil, rhs_names = nil
146
+ @lhs_syms[lhs] = true
147
+ rule = @parsing_method::Rule[lhs, rhs, prec, rhs_names]
148
+ @rules.push rule
149
+ return rule
150
+ end
151
+
152
+ def name_to_rhs_index name
153
+ @rhs_names.index(name)
154
+ end
155
+
156
+ def insert_sym_to_rhs pos, token_value, token_name
157
+ if pos < 0
158
+ @rhs.push token_value
159
+ @rhs_names[@rhs.size - 1] = token_name
160
+ @rhs_syms[token_value] = true
161
+ else
162
+ @rhs.insert(pos, token_value)
163
+ @rhs_names.insert(pos, token_name)
164
+ @rhs_syms[token_value] = true
165
+ end
166
+ end
167
+
168
+ private
169
+ def parse_grammar
170
+ get_token
171
+ do_hook :pre_rule_list
172
+ parse_rulelist
173
+ do_hook :post_rule_list
174
+ unless @token.tag == nil
175
+ error_exit "syntax error(grammar). unexpected '#{@token.tag}' expect '%%'"
176
+ end
177
+ end
178
+
179
+ def parse_rulelist
180
+ do_hook :pre_rule
181
+ parse_rule
182
+ do_hook :post_rule
183
+ if @token.tag == :NT
184
+ parse_rulelist
185
+ end
186
+ end
187
+
188
+ def parse_rule
189
+ if @token[0] == :NT
190
+ @lhs = @token.value
191
+
192
+ get_token
193
+ do_hook :post_lhs
194
+
195
+ unless @token.tag == ':'
196
+ error_exit "syntax error(grammar). unexpected '#{@token.tag}' expecting ':'"
197
+ end
198
+ get_token
199
+
200
+ @nrhs = 0
201
+ do_hook :pre_rhs_list
202
+ parse_rhslist
203
+ do_hook :post_rhs_list
204
+
205
+ unless @token.tag == ';'
206
+ error_exit "syntax error(grammar). unexpected '#{@token.tag}' expecting ';'"
207
+ end
208
+ get_token
209
+ end
210
+ end
211
+
212
+ def parse_rhslist
213
+ @rhs = []; @rhs_names = []; @prec = nil
214
+ do_hook :pre_rhs
215
+ parse_syms
216
+ add_rule(@lhs, @rhs, @prec, @rhs_names)
217
+ do_hook :post_rhs
218
+ @nrhs += 1
219
+
220
+ if @token.tag == '|'
221
+ get_token
222
+ parse_rhslist
223
+ end
224
+ end
225
+
226
+ def parse_syms
227
+ if @token.tag == :NT || @token.tag == :T
228
+ insert_sym_to_rhs(-1, @token.value, @token.name)
229
+ get_token
230
+
231
+ do_hook :post_symbol
232
+ parse_syms
233
+ end
234
+ end
235
+
236
+ def make_token tag, value, name = nil
237
+ @token = Token.new(tag, value, name, file.lineno)
238
+ end
239
+
240
+ def get_token
241
+ while !@line.empty? || !file.eof?
242
+ @original_line = @old_line = @line = file.gets if @line.empty?
243
+
244
+ tag = s = name = val = nil
245
+ until @line.empty? do
246
+ case @line
247
+ when /^\s+/, /^\#.*/ ;
248
+ # skip
249
+ when /^=([a-zA-Z]+)/
250
+ @prec = s = $1.intern
251
+ val = @terms[s] ||= - (@terms.size + 1)
252
+ when /^([a-z][a-z0-9_]*)(-([a-z][_a-z0-9]*))?/
253
+ tag = :NT
254
+ s = $1.intern
255
+ name = $3 ? $3 : $1
256
+ val = @nonterms[s] ||= @nonterms.size
257
+ when /^([A-Z][A-Z0-9_]*)(-([a-z][_a-z0-9]*))?/
258
+ tag = :T
259
+ s = $1.intern
260
+ name = $3 ? $3 : $1
261
+ val = @terms[s] ||= - (@terms.size + 1)
262
+ when /^'(.+?)'/
263
+ tag = :T
264
+ name = s = $1
265
+ val = @terms[s] ||= - (@terms.size + 1)
266
+ when /^%%/
267
+ return make_token(nil, nil)
268
+ when /^([:;|])/
269
+ tag = $&
270
+ val = $&
271
+ else
272
+ return make_token(:UNKNOWN, @line[0].chr)
273
+ end
274
+ @old_line, @line = @line, $'
275
+ @int_to_sym[val] ||= s if val && s
276
+ return make_token(tag, val, name) if tag
277
+ end
278
+ end
279
+ return make_token(nil, nil)
280
+ end
281
+
282
+ def extend_paser extensions, options
283
+ init_hook
284
+ @extensions = []
285
+ extensions.each do |f, m|
286
+ if f && f.match(%r!^\./!)
287
+ require File.expand_path(f, File.dirname(file.path))
288
+ elsif f
289
+ require f
290
+ end
291
+
292
+ nesting = m.split(/::/).tap{|it| it[-1] = "#{it[-1]}Extension"}
293
+ extension = nesting.inject(Object){|c,name| c.const_get(name) }.new
294
+ options[m] and options[m].each do |k, v|
295
+ begin
296
+ e.send("#{k}=", v)
297
+ rescue NoMethodError
298
+ warning "no such a option '#{m}.#{k}'."
299
+ end
300
+ end
301
+ @extensions << extension
302
+ extension.extension_registered self
303
+ extension.init_extension
304
+ end
305
+ end
306
+
307
+ def init_grammar
308
+ @rules = [ d_parser.parsing_method::Rule[0 , [1]]]
309
+ @terms = { nil => -1, false => -2 }
310
+ @nonterms = { '$start' => 0 }
311
+ @int_to_sym = {}
312
+ @rhs_syms = {}
313
+ @lhs_syms = {}
314
+ end
315
+
316
+ def make_grammar precs
317
+ ts = @nonterms.size - 1
318
+ @terms.each{|k,v| @terms[k] = ts - v}
319
+
320
+ @precs = {}
321
+ precs.each_with_index do |p, x|
322
+ p[1].each do |i|
323
+ if @terms[i]
324
+ @precs[@terms[i]] = [p[0], x]
325
+ else
326
+ error_exit "prec error '#{i}'."
327
+ end
328
+ end
329
+ end
330
+ @rules.each do |rule|
331
+ rule.rhs.each_with_index{|i, x| rule.rhs[x] = ts - i if i < 0}
332
+ rule.prec &&= @precs[@terms[rule.prec]]
333
+ end
334
+ end
335
+
336
+ def check_grammar
337
+ lhs_warns = []
338
+ @lhs_syms.each do |s, _|
339
+ lhs_warns << s if s != 1 and !@rhs_syms[s]
340
+ end
341
+ rhs_warns = []
342
+ @rhs_syms.each do |s, _|
343
+ rhs_warns << s if s > 0 and !@lhs_syms[s]
344
+ end
345
+ lhs_warns.uniq.each do |s|
346
+ warning "the lhs '#{@int_to_sym[s]}' is not used"
347
+ end
348
+ rhs_warns.uniq.each do |s|
349
+ warning "the symbol '#{@int_to_sym[s]}' is undefined"
350
+ end
351
+ end
352
+
353
+ def init_hook
354
+ @hooks = HOOK_KEYS.inject({}){|r, i| r[i] = []; r }
355
+ end
356
+
357
+ def do_hook hook
358
+ @hooks[hook].each{|o, m| o.send m}
359
+ end
360
+ end
361
+
362
+ class Generator
363
+ include Depager::Utils::CommonMethods
364
+
365
+ TEMPLATES_DIR = File.expand_path('../depager/ruby/templates', __FILE__)
366
+
367
+ attr_reader :d_parser, :g_parser, :parsing_method
368
+ attr_accessor :outer_code, :inner_code
369
+ attr_accessor :decorators, :requirements, :options
370
+
371
+ def initialize d_parser
372
+ @d_parser = d_parser
373
+ @g_parser = d_parser.g_parser
374
+ @parsing_method = nil
375
+
376
+ @options = {}
377
+ @decorators = []
378
+ @requirements = []
379
+ @extensions = []
380
+ @inner_code = ''
381
+ @outer_code = ''
382
+ @precs = []
383
+ @mixins = []
384
+ end
385
+
386
+ def generate_code template
387
+ ERB.new(template || '', nil, '-').result(binding)
388
+ end
389
+
390
+ def parse_prec
391
+ precs = []
392
+ until file.eof?
393
+ case line = file.gets
394
+ when /^\s*$/, /^\s*#.*$/
395
+ # skip
396
+ when /^\s*(left|right|nonassoc)\s+(.*)$/
397
+ dir = $1.upcase.intern
398
+ syms = $2.split.map{|i| (i =~ /'(.+?)'/) ? $1 : i.intern}
399
+ precs.push [dir, syms]
400
+ when /^%\}\s*$/
401
+ break
402
+ else
403
+ error_exit "syntax error(prec).\n> #{line}"
404
+ end
405
+ end
406
+ return precs
407
+ end
408
+
409
+ def parse_block raw = false
410
+ lineno, val = self.file.lineno, ''
411
+ until file.eof?
412
+ line = file.gets
413
+ break if line =~ /^%\}/
414
+ val.concat line
415
+ end
416
+ return val if raw
417
+
418
+ delimiter = Depager.configuration[:expanded_code_delimiter] ||
419
+ ".,.,#{Time.now.to_i}#{rand(0xffff)}.,.,"
420
+ return "module_eval <<'#{delimiter}', '#{file.path}', #{lineno + 1}\n" <<
421
+ "#{val.gsub('\\', '\\\\')}\n" <<
422
+ "#{delimiter}\n"
423
+ end
424
+
425
+ def parse_common line
426
+ case line
427
+ when /^\s*(#.*)?$/
428
+ #skip space and comment.
429
+ when /^%decorate\s+(.*?)\s*\((.*)\)\s*$/
430
+ @decorators.push $1
431
+ @requirements.push $2
432
+ when /^%decorate\s+(.*?)\s*$/
433
+ @decorators.push $1
434
+ when /^%extend\s+(.*?)\s*\(\s*'(.*)'\s*\)\s*$/
435
+ @extensions.push [$2.strip, $1] unless @extensions.find{|i| i[1] == $1}
436
+ when /^%option\s+(\w+).(\w+)\s+(.*)\s*$/
437
+ if $1 == 'Generator'
438
+ Depager.configuration[$2.to_sym] = $3
439
+ else
440
+ @options[$1] ||= []
441
+ @options[$1] << [$2, $3]
442
+ end
443
+ when /^%require\s+'(.+?)'\s*$/
444
+ @requirements.push "'#{$1}'"
445
+ when /^%mixin\s+(\S+)\s*(\((.+)\)\s*)?$/
446
+ @mixins.push $1
447
+ @requirements.push $3 if $3
448
+ when /^%inner\s*\{\s*$/
449
+ @inner_code << parse_block
450
+ when /^%prec\s*\{\s*$/
451
+ @precs = parse_prec
452
+ when /^%expanded_code_delimiter\s+(\S+)\s*$/
453
+ Depager.configuration[:expanded_code_delimiter] = $1
454
+ else
455
+ warning "syntax error(declaration).\n> #{line}"
456
+ end
457
+ end
458
+ end
459
+
460
+ class ParserGenerator < Generator
461
+ def initialize d_parser
462
+ super
463
+ end
464
+
465
+ def parse
466
+ until file.eof?
467
+ line = file.gets
468
+ case line
469
+ when /^%%\s*$/
470
+ g_parser.parse(@d_parser.full_target_name, @precs, @extensions, @options)
471
+ @outer_code << parse_block(true)
472
+ return generate_code parser_code_template
473
+ else
474
+ parse_common(line)
475
+ end
476
+ end
477
+ return nil
478
+ end
479
+
480
+ def parser_code_template
481
+ end
482
+ end
483
+
484
+ class ExtensionGenerator < Generator
485
+ attr_accessor :slaves
486
+ def initialize d_parser
487
+ super
488
+ @slaves = { }
489
+ end
490
+
491
+ def parse
492
+ until file.eof?
493
+ line = file.gets
494
+ case line
495
+ when /^%hook\s+([\w \t]+?)(\/((?:[^\/\\]+|\\.)+)\/(\s+skip)?)?\s*$/
496
+ hooks = $1.split(' ')
497
+ @banner = hooks.join(',')
498
+ @do_parse_re = $3
499
+ @do_parse_skip = $4
500
+
501
+ target_name = "#{d_parser.target_namespace}::Slave_#{ hooks.join('__') }::Parser"
502
+ hooks.each do |i| i = i.to_sym
503
+ error_exit "Unknown hook: #{i}" unless g_parser.hook_name? i
504
+ @slaves[i] = target_name
505
+ end
506
+ @outer_code << parse_hook(target_name)
507
+ when /^%%\s*$/
508
+ @outer_code << parse_block(true)
509
+ else
510
+ parse_common(line)
511
+ end
512
+ end
513
+ return generate_code master_code_template
514
+ end
515
+
516
+ def parse_hook target_name
517
+ inner = ''
518
+ precs = []
519
+ @slave_mixins = @mixins.dup
520
+ until file.eof?
521
+ line = file.gets
522
+ case line
523
+ when /^%banner\s*'(([^'\\]+|\\.)*)'\s*$/
524
+ @banner = $1
525
+ when /^%inner\s*\{\s*$/
526
+ inner = parse_block
527
+ when /^%mixin\s*(.+?)\s*(\((.+)\)\s*)?$/
528
+ @slave_mixins << $1
529
+ @requirements.push $3 if $3
530
+ when /^%prec\s*(.*?)\s*$/
531
+ precs = parse_prec
532
+ when /^%%\s*$/
533
+ g_parser.parse(target_name, precs, @extensions, @options)
534
+ g_parser.inner_code << inner
535
+ return generate_code slave_code_template
536
+ else
537
+ warning "syntax error(declaration).\n> #{line}", file.lineno
538
+ end
539
+ end
540
+ end
541
+
542
+ def slave_code_template
543
+ end
544
+
545
+ def master_code_template
546
+ end
547
+ end
548
+
549
+ class Extension
550
+ include Depager::Utils::CommonMethods
551
+ include Depager::Utils::CodeGeneratorMethods
552
+
553
+ attr_reader :d_parser, :g_parser
554
+
555
+ def init_extension
556
+ end
557
+
558
+ def term_extension
559
+ end
560
+
561
+ def extension_registered g_parser
562
+ @g_parser = g_parser
563
+ @d_parser = g_parser.d_parser
564
+
565
+ self.methods.sort.each do |m|
566
+ m = m.to_sym
567
+ g_parser.hooks[m].push [self, m] if g_parser.hook_name? m
568
+ end
569
+ end
570
+
571
+ def decorator_name
572
+ self.class.name.split('::').last.sub(/Extension$/, '')
573
+ end
574
+ end
575
+ end
576
+
577
+ class String
578
+ def unindent
579
+ indent = match(/\n(\s+)/) ? $1 : ''
580
+ gsub(/^#{indent}/m, '')
581
+ end
582
+ end