You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
793 lines
23 KiB
793 lines
23 KiB
#!/usr/bin/ruby
|
|
# encoding: utf-8
|
|
|
|
require 'antlr3/test/functional'
|
|
|
|
class TestAutoAST < ANTLR3::Test::Functional
|
|
|
|
def parse( grammar, rule, input, expect_errors = false )
|
|
@grammar = inline_grammar( grammar )
|
|
compile_and_load @grammar
|
|
grammar_module = self.class.const_get( @grammar.name )
|
|
|
|
grammar_module::Lexer.send( :include, ANTLR3::Test::CollectErrors )
|
|
grammar_module::Lexer.send( :include, ANTLR3::Test::CaptureOutput )
|
|
grammar_module::Parser.send( :include, ANTLR3::Test::CollectErrors )
|
|
grammar_module::Parser.send( :include, ANTLR3::Test::CaptureOutput )
|
|
|
|
lexer = grammar_module::Lexer.new( input )
|
|
parser = grammar_module::Parser.new( lexer )
|
|
|
|
r = parser.send( rule )
|
|
parser.reported_errors.should be_empty unless expect_errors
|
|
result = ''
|
|
|
|
unless r.nil?
|
|
result += r.result if r.respond_to?( :result )
|
|
result += r.tree.inspect if r.tree
|
|
end
|
|
return( expect_errors ? [ result, parser.reported_errors ] : result )
|
|
end
|
|
|
|
def tree_parse( grammar, tree_grammar, rule, tree_rule, input )
|
|
@grammar = inline_grammar( grammar )
|
|
@tree_grammar = inline_grammar( tree_grammar )
|
|
compile_and_load @grammar
|
|
compile_and_load @tree_grammar
|
|
|
|
grammar_module = self.class.const_get( @grammar.name )
|
|
tree_grammar_module = self.class.const_get( @tree_grammar.name )
|
|
|
|
grammar_module::Lexer.send( :include, ANTLR3::Test::CollectErrors )
|
|
grammar_module::Lexer.send( :include, ANTLR3::Test::CaptureOutput )
|
|
grammar_module::Parser.send( :include, ANTLR3::Test::CollectErrors )
|
|
grammar_module::Parser.send( :include, ANTLR3::Test::CaptureOutput )
|
|
tree_grammar_module::TreeParser.send( :include, ANTLR3::Test::CollectErrors )
|
|
tree_grammar_module::TreeParser.send( :include, ANTLR3::Test::CaptureOutput )
|
|
|
|
lexer = grammar_module::Lexer.new( input )
|
|
parser = grammar.module::Parser.new( lexer )
|
|
r = parser.send( rule )
|
|
nodes = ANTLR3::CommonTreeNodeStream( r.tree )
|
|
nodes.token_stream = parser.input
|
|
walker = tree_grammar_module::TreeParser.new( nodes )
|
|
r = walker.send( tree_rule )
|
|
|
|
return( r ? r.tree.inspect : '' )
|
|
end
|
|
|
|
|
|
example 'flat token list' do
|
|
result = parse( <<-'END', :a, 'abc 34' )
|
|
grammar TokenList;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;};
|
|
END
|
|
result.should == 'abc 34'
|
|
end
|
|
|
|
example 'token list in a single-alternative subrule' do
|
|
result = parse( <<-'END', :a, 'abc 34' )
|
|
grammar TokenListInSingleAltBlock;
|
|
options {language=Ruby;output=AST;}
|
|
a : (ID INT) ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == 'abc 34'
|
|
end
|
|
|
|
example "simple root at the outer level via the `^' operator" do
|
|
result = parse( <<-'END', :a, 'abc 34' )
|
|
grammar SimpleRootAtOuterLevel;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID^ INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(abc 34)'
|
|
end
|
|
|
|
example "outer-level root changing token order from the `^' operator" do
|
|
result = parse( <<-'END', :a, '34 abc' )
|
|
grammar SimpleRootAtOuterLevelReverse;
|
|
options {language=Ruby;output=AST;}
|
|
a : INT ID^ ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(abc 34)'
|
|
end
|
|
|
|
example "leaving out tokens using the `!' operator" do
|
|
result = parse( <<-'END', :a, 'abc 34 dag 4532' )
|
|
grammar Bang;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID INT! ID! INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
|
|
result.should == 'abc 4532'
|
|
end
|
|
|
|
example "tokens in `(...)?' optional subrule" do
|
|
result = parse( <<-'END', :a, 'a 1 b' )
|
|
grammar OptionalThenRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : ( ID INT )? ID^ ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(b a 1)'
|
|
end
|
|
|
|
example "labeled literal-string root token" do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar LabeledStringRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void'^ ID ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(void foo ;)'
|
|
end
|
|
|
|
example 'rule with token wildcard' do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar Wildcard;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void'^ . ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(void foo ;)'
|
|
end
|
|
|
|
example "token wildcard as root via the `^' operator" do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar WildcardRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void' .^ ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(foo void ;)'
|
|
end
|
|
|
|
example "labeled token wildcard as root via the `^' operator" do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar WildcardRootWithLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void' x=.^ ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(foo void ;)'
|
|
end
|
|
|
|
|
|
example "token wildcard as root (with list label)" do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar WildcardRootWithListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void' x=.^ ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(foo void ;)'
|
|
end
|
|
|
|
example "trashed token wildcard" do
|
|
result = parse( <<-'END', :a, 'void foo;' )
|
|
grammar WildcardBangWithListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : v='void' x=.! ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == 'void ;'
|
|
end
|
|
|
|
example "multiple occurences of the `^' operator in a list of tokens" do
|
|
result = parse( <<-'END', :a, 'a 34 c' )
|
|
grammar RootRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID^ INT^ ID ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(34 a c)'
|
|
end
|
|
|
|
example "another case of multiple occurences of the `^' operator" do
|
|
result = parse( <<-'END', :a, 'a 34 c' )
|
|
grammar RootRoot2;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID INT^ ID^ ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(c (34 a))'
|
|
end
|
|
|
|
example "root-hoist using `^' from within a (...)+ block" do
|
|
result = parse( <<-'END', :a, 'a 34 * b 9 * c' )
|
|
grammar RootThenRootInLoop;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID^ (INT '*'^ ID)+ ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(* (* (a 34) b 9) c)'
|
|
end
|
|
|
|
example "nested subrules without any AST ops resulting in a flat list" do
|
|
result = parse( <<-'END', :a, 'void a b;' )
|
|
grammar NestedSubrule;
|
|
options {language=Ruby;output=AST;}
|
|
a : 'void' (({
|
|
#do nothing
|
|
} ID|INT) ID | 'null' ) ';' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == 'void a b ;'
|
|
end
|
|
|
|
example "invoking another rule without any AST ops, resulting in a flat list" do
|
|
result = parse( <<-'END', :a, 'int a' )
|
|
grammar InvokeRule;
|
|
options {language=Ruby;output=AST;}
|
|
a : type ID ;
|
|
type : {
|
|
# do nothing
|
|
}'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == 'int a'
|
|
end
|
|
|
|
example "hoisting the results of another rule as root using the `^' operator" do
|
|
result = parse( <<-'END', :a, 'int a' )
|
|
grammar InvokeRuleAsRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : type^ ID ;
|
|
type : {
|
|
# do nothing
|
|
}'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(int a)'
|
|
end
|
|
|
|
example "hoisting another rule's true as root using the `^' operator (with a label)" do
|
|
result = parse( <<-'END', :a, 'int a' )
|
|
grammar InvokeRuleAsRootWithLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x=type^ ID ;
|
|
type : {
|
|
# do nothing
|
|
}'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(int a)'
|
|
end
|
|
|
|
example "hoisting another rule's result tree as root using the `^' operator (with a list += label)" do
|
|
result = parse( <<-'END', :a, 'int a' )
|
|
grammar InvokeRuleAsRootWithListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x+=type^ ID ;
|
|
type : {
|
|
# do nothing
|
|
}'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(int a)'
|
|
end
|
|
|
|
example "root-hoist via `^' within a (...)* loop resulting in a deeply-nested tree" do
|
|
result = parse( <<-'END', :a, 'a+b+c+d' )
|
|
grammar RuleRootInLoop;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID ('+'^ ID)* ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(+ (+ (+ a b) c) d)'
|
|
end
|
|
|
|
example "hoisting another rule's result tree as root from within a (...)* loop resulting in a deeply nested tree" do
|
|
result = parse( <<-'END', :a, 'a+b+c-d' )
|
|
grammar RuleInvocationRuleRootInLoop;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID (op^ ID)* ;
|
|
op : {
|
|
# do nothing
|
|
}'+' | '-' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(- (+ (+ a b) c) d)'
|
|
end
|
|
|
|
example "using tail recursion to build deeply-nested expression trees" do
|
|
result = parse( <<-'END', :s, '3 exp 4 exp 5' )
|
|
grammar TailRecursion;
|
|
options {language=Ruby;output=AST;}
|
|
s : a ;
|
|
a : atom ('exp'^ a)? ;
|
|
atom : INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(exp 3 (exp 4 5))'
|
|
end
|
|
|
|
example "simple token node from a token type set" do
|
|
result = parse( <<-'END', :a, 'abc' )
|
|
grammar TokenSet;
|
|
options {language=Ruby; output=AST;}
|
|
a : ID|INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == 'abc'
|
|
end
|
|
|
|
example "hoisting a token-type set token as root with `^'" do
|
|
result = parse( <<-'END', :a, '+abc' )
|
|
grammar SetRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : ('+' | '-')^ ID ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(+ abc)'
|
|
end
|
|
|
|
example "hoisting a token-type set token as root with `^' (with a label)" do
|
|
result = parse( <<-'END', :a, '+abc' )
|
|
grammar SetRootWithLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : (x=('+' | '-'))^ ID ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '+ abc'
|
|
end
|
|
|
|
example "hoisting a token-type set token as root from within a (...)* loop" do
|
|
result = parse( <<-'END', :a, 'a+b-c' )
|
|
grammar SetAsRuleRootInLoop;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID (('+'|'-')^ ID)* ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(- (+ a b) c)'
|
|
end
|
|
|
|
example "an `~' inverted token-type set element" do
|
|
result = parse( <<-'END', :a, '34+2' )
|
|
grammar NotSet;
|
|
options {language=Ruby;output=AST;}
|
|
a : ~ID '+' INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '34 + 2'
|
|
end
|
|
|
|
example "a `~' inverted token-type set in a rule (with a label)" do
|
|
result = parse( <<-'END', :a, '34+2' )
|
|
grammar NotSetWithLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x=~ID '+' INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '34 + 2'
|
|
end
|
|
|
|
example "a `~' inverted token-type set element in a rule (with a list += label)" do
|
|
result = parse( <<-'END', :a, '34+2' )
|
|
grammar NotSetWithListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x=~ID '+' INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '34 + 2'
|
|
end
|
|
|
|
example "a `~' inverted token-type set element hoisted to root via `^'" do
|
|
result = parse( <<-'END', :a, '34 55' )
|
|
grammar NotSetRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : ~'+'^ INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(34 55)'
|
|
end
|
|
|
|
example "hoisting a `~' inverted token-type set to root using `^' (with label)" do
|
|
result = parse( <<-'END', :a, '34 55' )
|
|
grammar NotSetRootWithLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x=~'+'^ INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(34 55)'
|
|
end
|
|
|
|
example "hoisting a `~' inverted token-type set to root using `^' (with list += label)" do
|
|
result = parse( <<-'END', :a, '34 55' )
|
|
grammar NotSetRootWithListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a : x+=~'+'^ INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == '(34 55)'
|
|
end
|
|
|
|
example "hoisting a `~' inverted token-type set to root from within a (...)* loop" do
|
|
result = parse( <<-'END', :a, '3+4+5' )
|
|
grammar NotSetRuleRootInLoop;
|
|
options {language=Ruby;output=AST;}
|
|
a : INT (~INT^ INT)* ;
|
|
blort : '+' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '(+ (+ 3 4) 5)'
|
|
end
|
|
|
|
example "multiple tokens with the same label in a rule" do
|
|
result = parse( <<-'END', :a, 'a b' )
|
|
grammar TokenLabelReuse;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result] : id=ID id=ID {
|
|
$result = "2nd id=\%s," \% $id.text
|
|
} ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '2nd id=b,a b'
|
|
end
|
|
|
|
example "multiple tokens with the same label in a rule (with a `^' root hoist)" do
|
|
result = parse( <<-'END', :a, 'a b' )
|
|
grammar TokenLabelReuse2;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result]: id=ID id=ID^ {$result = "2nd id=#{$id.text},"} ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '2nd id=b,(b a)'
|
|
end
|
|
|
|
example "extra token in a simple declaration" do
|
|
result, errors = parse( <<-'END', :decl, 'int 34 x=1;', true )
|
|
grammar ExtraTokenInSimpleDecl;
|
|
options {language=Ruby;output=AST;}
|
|
decl : type^ ID '='! INT ';'! ;
|
|
type : 'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 1:4 extraneous input \"34\" expecting ID" ]
|
|
result.should == '(int x 1)'
|
|
end
|
|
|
|
example "missing ID in a simple declaration" do
|
|
result, errors = parse( <<-'END', :decl, 'int =1;', true )
|
|
grammar MissingIDInSimpleDecl;
|
|
options {language=Ruby;output=AST;}
|
|
tokens {EXPR;}
|
|
decl : type^ ID '='! INT ';'! ;
|
|
type : 'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
errors.should == [ "line 1:4 missing ID at \"=\"" ]
|
|
result.should == '(int <missing ID> 1)'
|
|
end
|
|
|
|
example "missing token of a token-type set in a simple declaration" do
|
|
result, errors = parse( <<-'END', :decl, 'x=1;', true )
|
|
grammar MissingSetInSimpleDecl;
|
|
options {language=Ruby;output=AST;}
|
|
tokens {EXPR;}
|
|
decl : type^ ID '='! INT ';'! ;
|
|
type : 'int' | 'float' ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 1:0 mismatched input \"x\" expecting set nil" ]
|
|
result.should == '(<error: x> x 1)'
|
|
end
|
|
|
|
example "missing INT token simulated with a `<missing INT>' error node" do
|
|
result, errors = parse( <<-'END', :a, 'abc', true )
|
|
grammar MissingTokenGivesErrorNode;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID INT ; // follow is EOF
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 0:-1 missing INT at \"<EOF>\"" ]
|
|
result.should == 'abc <missing INT>'
|
|
end
|
|
|
|
example "missing token from invoked rule results in error node with a resync attribute" do
|
|
result, errors = parse( <<-'END', :a, 'abc', true )
|
|
grammar MissingTokenGivesErrorNodeInInvokedRule;
|
|
options {language=Ruby;output=AST;}
|
|
a : b ;
|
|
b : ID INT ; // follow should see EOF
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 0:-1 mismatched input \"<EOF>\" expecting INT" ]
|
|
result.should == '<mismatched token: <EOF>, resync = abc>'
|
|
end
|
|
|
|
example "extraneous ID token displays error and is ignored in AST output" do
|
|
result, errors = parse( <<-'END', :a, 'abc ick 34', true )
|
|
grammar ExtraTokenGivesErrorNode;
|
|
options {language=Ruby;output=AST;}
|
|
a : b c ;
|
|
b : ID ;
|
|
c : INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 1:4 extraneous input \"ick\" expecting INT" ]
|
|
result.should == 'abc 34'
|
|
end
|
|
|
|
example "missing ID token simulated with a `<missing ID>' error node" do
|
|
result, errors = parse( <<-'END', :a, '34', true )
|
|
grammar MissingFirstTokenGivesErrorNode;
|
|
options {language=Ruby;output=AST;}
|
|
a : ID INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 1:0 missing ID at \"34\"" ]
|
|
result.should == '<missing ID> 34'
|
|
end
|
|
|
|
example "another case where a missing ID token is simulated with a `<missing ID>' error node" do
|
|
result, errors = parse( <<-'END', :a, '34', true )
|
|
grammar MissingFirstTokenGivesErrorNode2;
|
|
options {language=Ruby;output=AST;}
|
|
a : b c ;
|
|
b : ID ;
|
|
c : INT ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
errors.should == [ "line 1:0 missing ID at \"34\"" ]
|
|
result.should == '<missing ID> 34'
|
|
end
|
|
|
|
example "no viable alternative for rule is represented as a single `<unexpected: ...>' error node" do
|
|
result, errors = parse( <<-'END', :a, '*', true )
|
|
grammar NoViableAltGivesErrorNode;
|
|
options {language=Ruby;output=AST;}
|
|
a : b | c ;
|
|
b : ID ;
|
|
c : INT ;
|
|
ID : 'a'..'z'+ ;
|
|
S : '*' ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
errors.should == [ "line 1:0 no viable alternative at input \"*\"" ]
|
|
result.should == "<unexpected: 0 S[\"*\"] @ line 1 col 0 (0..0), resync = *>"
|
|
end
|
|
|
|
example "token with a `+=' list label hoisted to root with `^'" do
|
|
result = parse( <<-'END', :a, 'a' )
|
|
grammar TokenListLabelRuleRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a : id+=ID^ ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == 'a'
|
|
end
|
|
|
|
example "token with a list `+=' label trashed with `!'" do
|
|
result = parse( <<-'END', :a, 'a' )
|
|
grammar TokenListLabelBang;
|
|
options {language=Ruby;output=AST;}
|
|
a : id+=ID! ;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == ''
|
|
end
|
|
|
|
example "using list `+=' labels to collect trees of invoked rules" do
|
|
result = parse( <<-'END', :a, 'a b' )
|
|
grammar RuleListLabel;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result]: x+=b x+=b {
|
|
t = $x[1]
|
|
$result = "2nd x=#{t.inspect},"
|
|
};
|
|
b : ID;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '2nd x=b,a b'
|
|
end
|
|
|
|
example "using a list `+=' label to collect the trees of invoked rules within a (...)+ block" do
|
|
result = parse( <<-'END', :a, 'a b' )
|
|
grammar RuleListLabelRuleRoot;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result] : ( x+=b^ )+ {
|
|
$result = "x=\%s," \% $x[1].inspect
|
|
} ;
|
|
b : ID;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == 'x=(b a),(b a)'
|
|
end
|
|
|
|
example "trashing the tree of an invoked rule with `!' while collecting the tree with a list `+=' label" do
|
|
result = parse( <<-'END', :a, 'a b' )
|
|
grammar RuleListLabelBang;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result] : x+=b! x+=b {
|
|
$result = "1st x=#{$x[0].inspect},"
|
|
} ;
|
|
b : ID;
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == '1st x=a,b'
|
|
end
|
|
|
|
example "a whole bunch of different elements" do
|
|
result = parse( <<-'END', :a, 'a b b c c d' )
|
|
grammar ComplicatedMelange;
|
|
options {language=Ruby;output=AST;}
|
|
a : A b=B b=B c+=C c+=C D {s = $D.text} ;
|
|
A : 'a' ;
|
|
B : 'b' ;
|
|
C : 'c' ;
|
|
D : 'd' ;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
END
|
|
result.should == 'a b b c c d'
|
|
end
|
|
|
|
example "rule return values in addition to AST output" do
|
|
result = parse( <<-'END', :a, 'abc 34' )
|
|
grammar ReturnValueWithAST;
|
|
options {language=Ruby;output=AST;}
|
|
a returns [result] : ID b { $result = $b.i.to_s + "\n" } ;
|
|
b returns [i] : INT {$i=$INT.text.to_i};
|
|
ID : 'a'..'z'+ ;
|
|
INT : '0'..'9'+;
|
|
WS : (' '|'\n') {$channel=HIDDEN;} ;
|
|
|
|
END
|
|
result.should == "34\nabc 34"
|
|
end
|
|
|
|
example "a (...)+ loop containing a token-type set" do
|
|
result = parse( <<-'END', :r, 'abc 34 d' )
|
|
grammar SetLoop;
|
|
options { language=Ruby;output=AST; }
|
|
r : (INT|ID)+ ;
|
|
ID : 'a'..'z' + ;
|
|
INT : '0'..'9' +;
|
|
WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN;};
|
|
|
|
END
|
|
result.should == 'abc 34 d'
|
|
end
|
|
|
|
end
|