1#!/usr/bin/ruby
2# encoding: utf-8
3
4require 'antlr3/test/functional'
5
6class TestParser001 < ANTLR3::Test::Functional
7  inline_grammar( <<-'END' )
8    grammar Identifiers;
9    options { language = Ruby; }
10    
11    @parser::init {
12      @identifiers = []
13      @reported_errors = []
14    }
15    
16    @parser::members {
17      attr_reader :reported_errors, :identifiers
18      
19      def found_identifier(name)
20          @identifiers << name
21      end
22      
23      def emit_error_message(msg)
24        @reported_errors << msg
25      end
26    }
27    
28    document:
29            t=IDENTIFIER {found_identifier($t.text)}
30            ;
31    
32    IDENTIFIER: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
33  END
34  
35  example "parsing 'blah_de_blah'" do
36    # to build a parser, this is the standard chain of calls to prepare the input
37    input = ANTLR3::StringStream.new( 'blah_de_blah', :file => 'blah.txt' )
38    lexer  = Identifiers::Lexer.new( input )
39    tokens = ANTLR3::CommonTokenStream.new( lexer )
40    parser = Identifiers::Parser.new( tokens )
41    
42    parser.document
43    
44    parser.reported_errors.should be_empty
45    parser.identifiers.should == %w(blah_de_blah)
46  end
47  
48  example "error from empty input" do
49    # if you don't need to use a customized stream, lexers and parsers will
50    # automatically wrap input in the standard stream classes
51    lexer = Identifiers::Lexer.new( '' )
52    parser = Identifiers::Parser.new( lexer )
53    parser.document
54    
55    parser.reported_errors.should have( 1 ).thing
56  end
57  
58  example 'automatic input wrapping' do
59    # if the parser is able to figure out what lexer class
60    # to use (typically when it comes from a combined grammar),
61    # and you don't need to do any special token processing
62    # before making a parser, this is an extra shortcut for
63    # parser construction
64    parser = Identifiers::Parser.new( 'blah_de_blah', :file => 'blah.txt' )
65    
66    parser.document
67    
68    parser.reported_errors.should be_empty
69    parser.identifiers.should == %w(blah_de_blah)
70  end
71end
72
73class TestParser002 < ANTLR3::Test::Functional
74  inline_grammar( <<-'END' )
75    grammar SimpleLanguage;
76    options {
77      language = Ruby;
78    }
79    
80    @parser::init {
81      @events = []
82      @reported_errors = []
83    }
84    
85    @parser::members {
86      attr_reader :reported_errors, :events
87      
88      def emit_error_message(msg)
89        @reported_errors << msg
90      end
91    }
92    
93    document:
94            ( declaration
95            | call
96            )*
97            EOF
98        ;
99    
100    declaration:
101            'var' t=IDENTIFIER ';'
102            {@events << ['decl', $t.text]}
103        ;
104    
105    call:
106            t=IDENTIFIER '(' ')' ';'
107            {@events << ['call', $t.text]}
108        ;
109    
110    IDENTIFIER: ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*;
111    WS:  (' '|'\r'|'\t'|'\n') {$channel=HIDDEN;};
112  END
113  
114  
115  example "parsing decls and calls" do
116    lexer  = SimpleLanguage::Lexer.new( "var foobar; gnarz(); var blupp; flupp ( ) ;" )
117    parser = SimpleLanguage::Parser.new( lexer )
118    
119    parser.document
120    
121    parser.reported_errors.should be_empty
122    parser.events.should == [ 
123      %w(decl foobar),
124      %w(call gnarz),
125      %w(decl blupp),
126      %w(call flupp)
127    ]
128  end
129  
130  example "bad declaration" do
131    lexer  = SimpleLanguage::Lexer.new( 'var; foo()' )
132    parser = SimpleLanguage::Parser.new( lexer )
133    
134    parser.document
135    
136    parser.reported_errors.should have( 1 ).thing
137    parser.events.should be_empty
138  end
139  
140  example "error recovery via token insertion" do
141    lexer  = SimpleLanguage::Lexer.new( 'gnarz(; flupp();' )
142    parser = SimpleLanguage::Parser.new( lexer )
143    
144    parser.document
145    
146    parser.reported_errors.should have( 1 ).thing
147    parser.events.should == [ 
148      %w(call gnarz),
149      %w(call flupp)
150    ]
151  end
152  
153end
154
155class TestParser003 < ANTLR3::Test::Functional
156  inline_grammar( <<-'END' )
157    grammar MoreComplicated;
158    
159    options { language = Ruby; }
160    
161    @init {
162      @reported_errors = []
163    }
164    
165    @members {
166      attr_reader :reported_errors
167      
168      def emit_error_message(msg)
169        @reported_errors << msg
170      end
171    }
172    
173    program
174        :   declaration+
175        ;
176    
177    declaration
178        :   variable
179        |   functionHeader ';'
180        |   functionHeader block
181        ;
182    
183    variable
184        :   type declarator ';'
185        ;
186    
187    declarator
188        :   ID 
189        ;
190    
191    functionHeader
192        :   type ID '(' ( formalParameter ( ',' formalParameter )* )? ')'
193        ;
194    
195    formalParameter
196        :   type declarator        
197        ;
198    
199    type
200        :   'int'   
201        |   'char'  
202        |   'void'
203        |   ID        
204        ;
205    
206    block
207        :   '{'
208                variable*
209                stat*
210            '}'
211        ;
212    
213    stat: forStat
214        | expr ';'      
215        | block
216        | assignStat ';'
217        | ';'
218        ;
219    
220    forStat
221        :   'for' '(' assignStat ';' expr ';' assignStat ')' block        
222        ;
223    
224    assignStat
225        :   ID '=' expr        
226        ;
227    
228    expr:   condExpr
229        ;
230    
231    condExpr
232        :   aexpr ( ('==' | '<') aexpr )?
233        ;
234    
235    aexpr
236        :   atom ( '+' atom )*
237        ;
238    
239    atom
240        : ID      
241        | INT      
242        | '(' expr ')'
243        ; 
244    
245    ID  :   ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
246        ;
247    
248    INT :	('0'..'9')+
249        ;
250    
251    WS  :   (   ' '
252            |   '\t'
253            |   '\r'
254            |   '\n'
255            )+
256            {$channel=HIDDEN}
257        ;    
258  END
259  
260  example "parsing 'int foo;'" do
261    lexer = MoreComplicated::Lexer.new "int foo;"
262    parser = MoreComplicated::Parser.new lexer
263    parser.program
264    parser.reported_errors.should be_empty
265  end
266  
267  
268  example "catching badly formed input" do
269    lexer = MoreComplicated::Lexer.new "int foo() { 1+2 }"
270    parser = MoreComplicated::Parser.new lexer
271    parser.program
272    parser.reported_errors.should have( 1 ).thing
273  end
274  
275  example "two instances of badly formed input" do
276    lexer = MoreComplicated::Lexer.new "int foo() { 1+; 1+2 }"
277    parser = MoreComplicated::Parser.new lexer
278    parser.program
279    parser.reported_errors.should have( 2 ).things
280  end
281  
282end
283