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