1#!/usr/bin/ruby
2# encoding: utf-8
3
4require 'antlr3'
5require 'antlr3/test/functional'
6
7ENV.delete( 'RUBYOPT' )
8ENV[ 'RUBYLIB' ] = ANTLR3.library_path
9
10class TestMainUtility < ANTLR3::Test::Functional
11
12  example 'overriding the built-in script action using the @main named-action' do
13    grammar = inline_grammar( <<-'END' )
14      lexer grammar MainOverride;
15      options { language = Ruby; }
16
17      @main {
18        raise( "the main block ran" )
19      }
20
21      ID: ('a'..'z' | '\u00c0'..'\u00ff')+;
22      WS: ' '+ { $channel = HIDDEN; };
23    END
24
25    # when this grammar is compiled and the resulting ruby files
26    # are loaded as a library, the custom @main block
27    # should not be executed
28    proc { compile_and_load( grammar ) }.should_not raise_error
29
30    # this assertion verifies that the main region is executed
31    # when the parser script is run directly
32    lexer_script = grammar.target_files.first
33    out = `ruby #{ lexer_script } 2>&1`.chomp
34    out.should =~ /the main block ran/
35  end
36
37  example 'using Lexer.main() to run the built-in lexer utility script on a source file' do
38    input_path = local_path( 'input.txt' )
39    open( input_path, 'w' ) { |f| f.write( "yada yada" ) }
40
41    compile_and_load inline_grammar( <<-'END' )
42      lexer grammar LexerMainWithSourceFile;
43      options { language = Ruby; }
44
45      ID: 'a'..'z'+;
46      WS: ' '+ { $channel = HIDDEN; };
47    END
48
49    begin
50      output = StringIO.new
51      input = File.open( input_path )
52      LexerMainWithSourceFile::Lexer.main( [], :input => input, :output => output )
53
54      out_lines = output.string.split( /\n/ )
55      out_lines.should have( 3 ).things
56    ensure
57      File.delete( input_path )
58    end
59  end
60
61  example 'using Lexer.main to run the built-in lexer utility script on input from $stdin' do
62    input = StringIO.new( "yada yada" )    # <- used to simulate $stdin
63    output = StringIO.new
64
65    compile_and_load inline_grammar( <<-'END' )
66      lexer grammar LexerMainFromStdIO;
67      options { language = Ruby; }
68
69      ID: 'a'..'z'+;
70      WS: ' '+ { $channel = HIDDEN; };
71    END
72
73    LexerMainFromStdIO::Lexer.main( [], :input => input, :output => output )
74    lines = output.string.split( /\n/ )
75    lines.should have( 3 ).things
76  end
77
78  example 'using Parser.main to run the built-in parser script utility with a combo grammar' do
79    compile_and_load inline_grammar( <<-'END' )
80      grammar MainForCombined;
81      options { language = Ruby; }
82      r returns [res]: (ID)+ EOF { $res = $text; };
83
84      ID: 'a'..'z'+;
85      WS: ' '+ { $channel = HIDDEN; };
86    END
87
88    output = StringIO.new
89    input = StringIO.new( 'yada yada' )
90
91    MainForCombined::Parser.main(
92        %w(--rule r --lexer-name MainForCombined::Lexer),
93        :input => input, :output => output )
94    lines = output.string.split( "\n" )
95    lines.should have( 4 ).things
96  end
97
98  example 'using built-in main to inspect AST constructed by an AST-building parser' do
99    compile_and_load inline_grammar( <<-'END' )
100      grammar ASTParserMain;
101      options {
102        language = Ruby;
103        output = AST;
104      }
105      r: ID OP^ ID EOF!;
106
107      ID: 'a'..'z'+;
108      OP: '+';
109      WS: ' '+ { $channel = HIDDEN; };
110    END
111
112    output = StringIO.new
113    input  = StringIO.new 'yada + yada'
114    ASTParserMain::Parser.main(
115      %w(--rule r --lexer-name ASTParserMain::Lexer),
116      :input => input, :output => output )
117    output = output.string.strip
118    output.should == "(+ yada yada)"
119  end
120
121  example "using a tree parser's built-in main" do
122    compile_and_load inline_grammar( <<-'END' )
123      grammar TreeMain;
124      options {
125        language = Ruby;
126        output = AST;
127      }
128
129      r: ID OP^ ID EOF!;
130
131      ID: 'a'..'z'+;
132      OP: '+';
133      WS: ' '+ { $channel = HIDDEN; };
134    END
135    compile_and_load inline_grammar( <<-'END' )
136      tree grammar TreeMainWalker;
137      options {
138        language=Ruby;
139        ASTLabelType=CommonTree;
140        tokenVocab=TreeMain;
141      }
142      r returns [res]: ^(OP a=ID b=ID)
143        { $res = "\%s \%s \%s" \% [$a.text, $OP.text, $b.text] }
144        ;
145    END
146
147    output = StringIO.new
148    input  = StringIO.new 'a+b'
149
150    TreeMainWalker::TreeParser.main(
151      %w(--rule r --parser-name TreeMain::Parser
152         --parser-rule r --lexer-name TreeMain::Lexer),
153      :input => input, :output => output )
154    output = output.string.strip
155    output.should == '"a + b"'
156  end
157
158  example "using a tree parser's built-in main to inspect AST rewrite output" do
159    compile_and_load inline_grammar( <<-'END' )
160      grammar TreeRewriteMain;
161      options {
162        language = Ruby;
163        output = AST;
164      }
165
166      r: ID OP^ ID EOF!;
167
168      ID: 'a'..'z'+;
169      OP: '+';
170      WS: ' '+ { $channel = HIDDEN; };
171    END
172    compile_and_load inline_grammar( <<-'END' )
173      tree grammar TreeRewriteMainWalker;
174      options {
175        language=Ruby;
176        ASTLabelType=CommonTree;
177        tokenVocab=TreeRewriteMain;
178        output=AST;
179      }
180      tokens { ARG; }
181      r: ^(OP a=ID b=ID) -> ^(OP ^(ARG ID) ^(ARG ID));
182    END
183
184    output = StringIO.new
185    input  = StringIO.new 'a+b'
186    TreeRewriteMainWalker::TreeParser.main(
187      %w(--rule r --parser-name TreeRewriteMain::Parser
188         --parser-rule r --lexer-name TreeRewriteMain::Lexer),
189      :input => input, :output => output
190    )
191
192    output = output.string.strip
193    output.should == '(+ (ARG a) (ARG b))'
194  end
195
196  example 'using built-in main with a delegating grammar' do
197    inline_grammar( <<-'END' )
198      parser grammar MainSlave;
199      options { language=Ruby; }
200      a : B;
201    END
202    master = inline_grammar( <<-'END' )
203      grammar MainMaster;
204      options { language=Ruby; }
205      import MainSlave;
206      s returns [res]: a { $res = $a.text };
207      B : 'b' ; // defines B from inherited token space
208      WS : (' '|'\n') {skip} ;
209    END
210    master.compile
211    for file in master.target_files
212      require( file )
213    end
214
215    output = StringIO.new
216    input = StringIO.new 'b'
217
218    MainMaster::Parser.main(
219      %w(--rule s --lexer-name MainMaster::Lexer),
220      :input => input, :output => output )
221    output = output.string.strip
222    output.should == 'b'.inspect
223  end
224
225  #test :LexerEncoding do
226  #  broken!("Non-ASCII encodings have not been implemented yet")
227  #  grammar = inline_grammar(<<-'END')
228  #    lexer grammar T3;
229  #    options {
230  #      language = Ruby;
231  #      }
232  #
233  #    ID: ('a'..'z' | '\u00c0'..'\u00ff')+;
234  #    WS: ' '+ { $channel = HIDDEN; };
235  #  END
236  #  compile grammar
237  #  input = StringIO.new("föö bär")
238  #  output = StringIO.new('')
239  #  lexer_class.main(%w(--encoding utf-8), :input => input, :output => output)
240  #  puts output.string
241  #  lines = output.string.split(/\n/)
242  #  lines.should have(3).things
243  #end
244
245end
246