1#!/usr/bin/ruby
2# encoding: utf-8
3
4require 'antlr3/test/functional'
5
6class TestTemplateOutput < ANTLR3::Test::Functional
7  
8  def parse( grammar, input, options = nil )
9    @grammar = inline_grammar( grammar )
10    compile_and_load( @grammar )
11    grammar_module = self.class.const_get( @grammar.name )
12    
13    parser_options = {}
14    if options
15      rule = options.fetch( :rule ) { grammar_module::Parser.default_rule }
16      group = options[ :templates ] and parser_options[ :templates ] = group
17    else
18      rule = grammar_module::Parser.default_rule
19    end
20    
21    @lexer  = grammar_module::Lexer.new( input )
22    @parser = grammar_module::Parser.new( @lexer, parser_options )
23    
24    out = @parser.send( rule ).template
25    return( out ? out.to_s : out )
26  end
27  
28  def parse_templates( source )
29    ANTLR3::Template::Group.parse( source.fixed_indent( 0 ) )
30  end
31  
32  
33  example 'inline templates' do
34    text = parse( <<-'END', "abc 34" )
35      grammar InlineTemplates;
36      options {
37        language = Ruby;
38        output = template;
39      }
40      
41      a : ID INT
42        -> template(id={$ID.text}, int={$INT.text})
43           "id=<%= @id %>, int=<%= @int %>"
44      ;
45      
46      ID : 'a'..'z'+;
47      INT : '0'..'9'+;
48      WS : (' '|'\n') {$channel=HIDDEN;} ;
49    END
50    
51    text.should == "id=abc, int=34"
52  end
53  
54  example 'external template' do
55    templates = ANTLR3::Template::Group.new do
56      define_template( :expr, <<-'END'.strip )
57        [<%= @args.join( @op.to_s ) %>]
58      END
59    end
60    
61    text = parse( <<-'END', 'a + b', :templates => templates )
62      grammar ExternalTemplate;
63      options {
64        language = Ruby;
65        output = template;
66      }
67      
68      a : r+=arg OP r+=arg
69        -> expr( op={$OP.text}, args={$r} )
70      ;
71      arg: ID -> template(t={$ID.text}) "<%= @t %>";
72      
73      ID : 'a'..'z'+;
74      OP: '+';
75      WS : (' '|'\n') {$channel=HIDDEN;} ;
76    END
77    
78    text.should == '[a+b]'
79  end
80
81  example "empty template" do
82    text = parse( <<-'END', 'abc 34' )
83      grammar EmptyTemplate;
84      options {
85        language=Ruby;
86        output=template;
87      }
88      a : ID INT
89        -> 
90      ;
91      
92      ID : 'a'..'z'+;
93      INT : '0'..'9'+;
94      WS : (' '|'\n') {$channel=HIDDEN;} ;
95      
96    END
97    text.should be_nil
98  end
99  
100  example "list" do
101    text = parse( <<-'END', "abc def ghi" )
102      grammar List;
103      options {
104        language=Ruby;
105        output=template;
106      }
107      a: (r+=b)* EOF
108        -> template(r={$r}) "<%= @r.join(',') %>"
109      ;
110      
111      b: ID
112        -> template(t={$ID.text}) "<%= @t %>"
113      ;
114      
115      ID : 'a'..'z'+;
116      WS : (' '|'\n') {$channel=HIDDEN;} ;
117    END
118    text.should == 'abc,def,ghi'
119  end
120  
121  example 'action' do
122    text = parse( <<-'END', "abc" )
123      grammar Action;
124      options {
125        language=Ruby;
126        output=template;
127      }
128      a: ID
129        -> { create_template( "hello" ) }
130      ;
131      
132      ID : 'a'..'z'+;
133      WS : (' '|'\n') {$channel=HIDDEN;} ;
134    END
135    
136    text.should == 'hello'
137  end
138  
139  example "template expression in action" do
140    text = parse( <<-'END', 'abc' )
141      grammar TemplateExpressionInAction;
142      options {
143        language=Ruby;
144        output=template;
145      }
146      a: ID
147        { $st = %{"hello"} }
148      ;
149      
150      ID : 'a'..'z'+;
151      WS : (' '|'\n') {$channel=HIDDEN;} ;
152    END
153    text.should == 'hello'
154  end
155  
156  #example "template expression in action2" do
157  #  text = parse( <<-'END', 'abc' )
158  #    grammar TemplateExpressionInAction2;
159  #    options {
160  #      language=Ruby;
161  #      output=template;
162  #    }
163  #    a: ID
164  #      {
165  #        res = %{"hello <%= @foo %>"}
166  #        %res.foo = "world";
167  #      }
168  #      -> { res }
169  #    ;
170  #    
171  #    ID : 'a'..'z'+;
172  #    WS : (' '|'\n') {$channel=HIDDEN;} ;
173  #  END
174  #  
175  #  text.should == 'hello world'
176  #end
177  
178  example "indirect template constructor" do
179    templates = ANTLR3::Template::Group.new do
180      define_template( :expr, <<-'END'.strip )
181        [<%= @args.join( @op.to_s ) %>]
182      END
183    end
184    
185    text = parse( <<-'END', 'abc', :templates => templates )
186      grammar IndirectTemplateConstructor;
187      options {
188        language=Ruby;
189        output=template;
190      }
191      
192      a: ID
193        {
194          $st = %({"expr"})(args={[1, 2, 3]}, op={"+"})
195        }
196      ;
197      
198      ID : 'a'..'z'+;
199      WS : (' '|'\n') {$channel=HIDDEN;} ;
200    END
201    
202    text.should == '[1+2+3]'
203  end
204  
205  example "predicates" do
206    text = parse( <<-'END', 'b 34' )
207      grammar Predicates;
208      options {
209        language=Ruby;
210        output=template;
211      }
212      a : ID INT
213        -> {$ID.text=='a'}? template(int={$INT.text})
214                            "A: <%= @int %>"
215        -> {$ID.text=='b'}? template(int={$INT.text})
216                            "B: <%= @int %>"
217        ->                  template(int={$INT.text})
218                            "C: <%= @int %>"
219      ;
220      
221      ID : 'a'..'z'+;
222      INT : '0'..'9'+;
223      WS : (' '|'\n') {$channel=HIDDEN;} ;
224    END
225    
226    text.should == 'B: 34'
227  end
228  
229  example "backtracking mode" do
230    text = parse( <<-'END', 'abc 34' )
231      grammar BacktrackingMode;
232      options {
233        language=Ruby;
234        output=template;
235        backtrack=true;
236      }
237      a : (ID INT)=> ID INT
238        -> template(id={$ID.text}, int={$INT.text})
239           "id=<%= @id %>, int=<%= @int %>"
240      ;
241      
242      ID : 'a'..'z'+;
243      INT : '0'..'9'+;
244      WS : (' '|'\n') {$channel=HIDDEN;} ;
245    END
246    
247    text.should == "id=abc, int=34"
248  end
249  
250  example "rewrite" do
251    input = <<-'END'.here_indent!
252    | if ( foo ) {
253    |   b = /* bla */ 2;
254    |   return 1 /* foo */;
255    | }
256    | 
257    | /* gnurz */
258    | return 12;
259    END
260    expected = <<-'END'.here_indent!
261    | if ( foo ) {
262    |   b = /* bla */ 2;
263    |   return boom(1) /* foo */;
264    | }
265    | 
266    | /* gnurz */
267    | return boom(12);
268    END
269    
270    parse( <<-'END', input )
271      grammar Rewrite;
272      options {
273        language=Ruby;
274        output=template;
275        rewrite=true;
276      }
277      
278      prog: stat+;
279      
280      stat
281          : 'if' '(' expr ')' stat
282          | 'return' return_expr ';'
283          | '{' stat* '}'
284          | ID '=' expr ';'
285          ;
286      
287      return_expr
288          : expr
289            -> template(t={$text}) <<boom(<%= @t %>)>>
290          ;
291          
292      expr
293          : ID
294          | INT
295          ;
296          
297      ID:  'a'..'z'+;
298      INT: '0'..'9'+;
299      WS: (' '|'\n')+ {$channel=HIDDEN;} ;
300      COMMENT: '/*' (options {greedy=false;} : .)* '*/' {$channel = HIDDEN;} ;
301    END
302    
303    @parser.input.render.should == expected
304  end
305  
306  example "tree rewrite" do
307    input = <<-'END'.here_indent!
308    | if ( foo ) {
309    |   b = /* bla */ 2;
310    |   return 1 /* foo */;
311    | }
312    | 
313    | /* gnurz */
314    | return 12;
315    END
316    expected = <<-'END'.here_indent!
317    | if ( foo ) {
318    |   b = /* bla */ 2;
319    |   return boom(1) /* foo */;
320    | }
321    | 
322    | /* gnurz */
323    | return boom(12);
324    END
325    
326    compile_and_load( inline_grammar( <<-'END' ) )
327      grammar TreeRewrite;
328      options {
329        language=Ruby;
330        output=AST;
331      }
332      
333      tokens {
334        BLOCK;
335        ASSIGN;
336      }
337      
338      prog: stat+;
339      
340      stat
341          : IF '(' e=expr ')' s=stat
342            -> ^(IF $e $s)
343          | RETURN expr ';'
344            -> ^(RETURN expr)
345          | '{' stat* '}'
346            -> ^(BLOCK stat*)
347          | ID '=' expr ';'
348            -> ^(ASSIGN ID expr)
349          ;
350          
351      expr
352          : ID
353          | INT
354          ;
355      
356      IF: 'if';
357      RETURN: 'return';
358      ID:  'a'..'z'+;
359      INT: '0'..'9'+;
360      WS: (' '|'\n')+ {$channel=HIDDEN;} ;
361      COMMENT: '/*' (options {greedy=false;} : .)* '*/' {$channel = HIDDEN;} ;
362    END
363    
364    compile_and_load( inline_grammar( <<-'END' ) )
365      tree grammar TreeRewriteTG;
366      options {
367        language=Ruby;
368        tokenVocab=TreeRewrite;
369        ASTLabelType=CommonTree;
370        output=template;
371        rewrite=true;
372      }
373      
374      prog: stat+;
375      
376      stat
377          : ^(IF expr stat)
378          | ^(RETURN return_expr)                
379          | ^(BLOCK stat*)                
380          | ^(ASSIGN ID expr)
381          ;
382      
383      return_expr
384          : expr
385            -> template(t={$text}) <<boom(<%= @t %>)>>
386          ;
387      
388      expr
389          : ID
390          | INT
391          ;
392    END
393    
394    lexer = TreeRewrite::Lexer.new( input )
395    tokens = ANTLR3::TokenRewriteStream.new( lexer )
396    parser = TreeRewrite::Parser.new( tokens )
397    tree = parser.prog.tree
398    nodes = ANTLR3::AST::CommonTreeNodeStream.new( tree )
399    nodes.token_stream = tokens
400    tree_parser = TreeRewriteTG::TreeParser.new( nodes )
401    tree_parser.prog
402    tokens.render.should == expected
403  end
404end
405