#!/usr/bin/ruby
# encoding: utf-8

require 'antlr3/test/functional'

class TestTemplateOutput < ANTLR3::Test::Functional
  
  def parse( grammar, input, options = nil )
    @grammar = inline_grammar( grammar )
    compile_and_load( @grammar )
    grammar_module = self.class.const_get( @grammar.name )
    
    parser_options = {}
    if options
      rule = options.fetch( :rule ) { grammar_module::Parser.default_rule }
      group = options[ :templates ] and parser_options[ :templates ] = group
    else
      rule = grammar_module::Parser.default_rule
    end
    
    @lexer  = grammar_module::Lexer.new( input )
    @parser = grammar_module::Parser.new( @lexer, parser_options )
    
    out = @parser.send( rule ).template
    return( out ? out.to_s : out )
  end
  
  def parse_templates( source )
    ANTLR3::Template::Group.parse( source.fixed_indent( 0 ) )
  end
  
  
  example 'inline templates' do
    text = parse( <<-'END', "abc 34" )
      grammar InlineTemplates;
      options {
        language = Ruby;
        output = template;
      }
      
      a : ID INT
        -> template(id={$ID.text}, int={$INT.text})
           "id=<%= @id %>, int=<%= @int %>"
      ;
      
      ID : 'a'..'z'+;
      INT : '0'..'9'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == "id=abc, int=34"
  end
  
  example 'external template' do
    templates = ANTLR3::Template::Group.new do
      define_template( :expr, <<-'END'.strip )
        [<%= @args.join( @op.to_s ) %>]
      END
    end
    
    text = parse( <<-'END', 'a + b', :templates => templates )
      grammar ExternalTemplate;
      options {
        language = Ruby;
        output = template;
      }
      
      a : r+=arg OP r+=arg
        -> expr( op={$OP.text}, args={$r} )
      ;
      arg: ID -> template(t={$ID.text}) "<%= @t %>";
      
      ID : 'a'..'z'+;
      OP: '+';
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == '[a+b]'
  end

  example "empty template" do
    text = parse( <<-'END', 'abc 34' )
      grammar EmptyTemplate;
      options {
        language=Ruby;
        output=template;
      }
      a : ID INT
        -> 
      ;
      
      ID : 'a'..'z'+;
      INT : '0'..'9'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
      
    END
    text.should be_nil
  end
  
  example "list" do
    text = parse( <<-'END', "abc def ghi" )
      grammar List;
      options {
        language=Ruby;
        output=template;
      }
      a: (r+=b)* EOF
        -> template(r={$r}) "<%= @r.join(',') %>"
      ;
      
      b: ID
        -> template(t={$ID.text}) "<%= @t %>"
      ;
      
      ID : 'a'..'z'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    text.should == 'abc,def,ghi'
  end
  
  example 'action' do
    text = parse( <<-'END', "abc" )
      grammar Action;
      options {
        language=Ruby;
        output=template;
      }
      a: ID
        -> { create_template( "hello" ) }
      ;
      
      ID : 'a'..'z'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == 'hello'
  end
  
  example "template expression in action" do
    text = parse( <<-'END', 'abc' )
      grammar TemplateExpressionInAction;
      options {
        language=Ruby;
        output=template;
      }
      a: ID
        { $st = %{"hello"} }
      ;
      
      ID : 'a'..'z'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    text.should == 'hello'
  end
  
  #example "template expression in action2" do
  #  text = parse( <<-'END', 'abc' )
  #    grammar TemplateExpressionInAction2;
  #    options {
  #      language=Ruby;
  #      output=template;
  #    }
  #    a: ID
  #      {
  #        res = %{"hello <%= @foo %>"}
  #        %res.foo = "world";
  #      }
  #      -> { res }
  #    ;
  #    
  #    ID : 'a'..'z'+;
  #    WS : (' '|'\n') {$channel=HIDDEN;} ;
  #  END
  #  
  #  text.should == 'hello world'
  #end
  
  example "indirect template constructor" do
    templates = ANTLR3::Template::Group.new do
      define_template( :expr, <<-'END'.strip )
        [<%= @args.join( @op.to_s ) %>]
      END
    end
    
    text = parse( <<-'END', 'abc', :templates => templates )
      grammar IndirectTemplateConstructor;
      options {
        language=Ruby;
        output=template;
      }
      
      a: ID
        {
          $st = %({"expr"})(args={[1, 2, 3]}, op={"+"})
        }
      ;
      
      ID : 'a'..'z'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == '[1+2+3]'
  end
  
  example "predicates" do
    text = parse( <<-'END', 'b 34' )
      grammar Predicates;
      options {
        language=Ruby;
        output=template;
      }
      a : ID INT
        -> {$ID.text=='a'}? template(int={$INT.text})
                            "A: <%= @int %>"
        -> {$ID.text=='b'}? template(int={$INT.text})
                            "B: <%= @int %>"
        ->                  template(int={$INT.text})
                            "C: <%= @int %>"
      ;
      
      ID : 'a'..'z'+;
      INT : '0'..'9'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == 'B: 34'
  end
  
  example "backtracking mode" do
    text = parse( <<-'END', 'abc 34' )
      grammar BacktrackingMode;
      options {
        language=Ruby;
        output=template;
        backtrack=true;
      }
      a : (ID INT)=> ID INT
        -> template(id={$ID.text}, int={$INT.text})
           "id=<%= @id %>, int=<%= @int %>"
      ;
      
      ID : 'a'..'z'+;
      INT : '0'..'9'+;
      WS : (' '|'\n') {$channel=HIDDEN;} ;
    END
    
    text.should == "id=abc, int=34"
  end
  
  example "rewrite" do
    input = <<-'END'.here_indent!
    | if ( foo ) {
    |   b = /* bla */ 2;
    |   return 1 /* foo */;
    | }
    | 
    | /* gnurz */
    | return 12;
    END
    expected = <<-'END'.here_indent!
    | if ( foo ) {
    |   b = /* bla */ 2;
    |   return boom(1) /* foo */;
    | }
    | 
    | /* gnurz */
    | return boom(12);
    END
    
    parse( <<-'END', input )
      grammar Rewrite;
      options {
        language=Ruby;
        output=template;
        rewrite=true;
      }
      
      prog: stat+;
      
      stat
          : 'if' '(' expr ')' stat
          | 'return' return_expr ';'
          | '{' stat* '}'
          | ID '=' expr ';'
          ;
      
      return_expr
          : expr
            -> template(t={$text}) <<boom(<%= @t %>)>>
          ;
          
      expr
          : ID
          | INT
          ;
          
      ID:  'a'..'z'+;
      INT: '0'..'9'+;
      WS: (' '|'\n')+ {$channel=HIDDEN;} ;
      COMMENT: '/*' (options {greedy=false;} : .)* '*/' {$channel = HIDDEN;} ;
    END
    
    @parser.input.render.should == expected
  end
  
  example "tree rewrite" do
    input = <<-'END'.here_indent!
    | if ( foo ) {
    |   b = /* bla */ 2;
    |   return 1 /* foo */;
    | }
    | 
    | /* gnurz */
    | return 12;
    END
    expected = <<-'END'.here_indent!
    | if ( foo ) {
    |   b = /* bla */ 2;
    |   return boom(1) /* foo */;
    | }
    | 
    | /* gnurz */
    | return boom(12);
    END
    
    compile_and_load( inline_grammar( <<-'END' ) )
      grammar TreeRewrite;
      options {
        language=Ruby;
        output=AST;
      }
      
      tokens {
        BLOCK;
        ASSIGN;
      }
      
      prog: stat+;
      
      stat
          : IF '(' e=expr ')' s=stat
            -> ^(IF $e $s)
          | RETURN expr ';'
            -> ^(RETURN expr)
          | '{' stat* '}'
            -> ^(BLOCK stat*)
          | ID '=' expr ';'
            -> ^(ASSIGN ID expr)
          ;
          
      expr
          : ID
          | INT
          ;
      
      IF: 'if';
      RETURN: 'return';
      ID:  'a'..'z'+;
      INT: '0'..'9'+;
      WS: (' '|'\n')+ {$channel=HIDDEN;} ;
      COMMENT: '/*' (options {greedy=false;} : .)* '*/' {$channel = HIDDEN;} ;
    END
    
    compile_and_load( inline_grammar( <<-'END' ) )
      tree grammar TreeRewriteTG;
      options {
        language=Ruby;
        tokenVocab=TreeRewrite;
        ASTLabelType=CommonTree;
        output=template;
        rewrite=true;
      }
      
      prog: stat+;
      
      stat
          : ^(IF expr stat)
          | ^(RETURN return_expr)                
          | ^(BLOCK stat*)                
          | ^(ASSIGN ID expr)
          ;
      
      return_expr
          : expr
            -> template(t={$text}) <<boom(<%= @t %>)>>
          ;
      
      expr
          : ID
          | INT
          ;
    END
    
    lexer = TreeRewrite::Lexer.new( input )
    tokens = ANTLR3::TokenRewriteStream.new( lexer )
    parser = TreeRewrite::Parser.new( tokens )
    tree = parser.prog.tree
    nodes = ANTLR3::AST::CommonTreeNodeStream.new( tree )
    nodes.token_stream = tokens
    tree_parser = TreeRewriteTG::TreeParser.new( nodes )
    tree_parser.prog
    tokens.render.should == expected
  end
end