antlr3 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/ANTLR-LICENSE.txt +26 -0
- data/History.txt +66 -0
- data/README.txt +139 -0
- data/bin/antlr4ruby +33 -0
- data/java/RubyTarget.java +524 -0
- data/java/antlr-full-3.2.1.jar +0 -0
- data/lib/antlr3.rb +176 -0
- data/lib/antlr3/constants.rb +88 -0
- data/lib/antlr3/debug.rb +701 -0
- data/lib/antlr3/debug/event-hub.rb +210 -0
- data/lib/antlr3/debug/record-event-listener.rb +25 -0
- data/lib/antlr3/debug/rule-tracer.rb +55 -0
- data/lib/antlr3/debug/socket.rb +360 -0
- data/lib/antlr3/debug/trace-event-listener.rb +92 -0
- data/lib/antlr3/dfa.rb +247 -0
- data/lib/antlr3/dot.rb +174 -0
- data/lib/antlr3/error.rb +657 -0
- data/lib/antlr3/main.rb +561 -0
- data/lib/antlr3/modes/ast-builder.rb +41 -0
- data/lib/antlr3/modes/filter.rb +56 -0
- data/lib/antlr3/profile.rb +322 -0
- data/lib/antlr3/recognizers.rb +1280 -0
- data/lib/antlr3/streams.rb +985 -0
- data/lib/antlr3/streams/interactive.rb +91 -0
- data/lib/antlr3/streams/rewrite.rb +412 -0
- data/lib/antlr3/test/call-stack.rb +57 -0
- data/lib/antlr3/test/config.rb +23 -0
- data/lib/antlr3/test/core-extensions.rb +269 -0
- data/lib/antlr3/test/diff.rb +165 -0
- data/lib/antlr3/test/functional.rb +207 -0
- data/lib/antlr3/test/grammar.rb +371 -0
- data/lib/antlr3/token.rb +592 -0
- data/lib/antlr3/tree.rb +1415 -0
- data/lib/antlr3/tree/debug.rb +163 -0
- data/lib/antlr3/tree/visitor.rb +84 -0
- data/lib/antlr3/tree/wizard.rb +481 -0
- data/lib/antlr3/util.rb +149 -0
- data/lib/antlr3/version.rb +27 -0
- data/samples/ANTLRv3Grammar.g +621 -0
- data/samples/Cpp.g +749 -0
- data/templates/AST.stg +335 -0
- data/templates/ASTDbg.stg +40 -0
- data/templates/ASTParser.stg +153 -0
- data/templates/ASTTreeParser.stg +272 -0
- data/templates/Dbg.stg +192 -0
- data/templates/Ruby.stg +1514 -0
- data/test/functional/ast-output/auto-ast.rb +797 -0
- data/test/functional/ast-output/construction.rb +555 -0
- data/test/functional/ast-output/hetero-nodes.rb +753 -0
- data/test/functional/ast-output/rewrites.rb +1327 -0
- data/test/functional/ast-output/tree-rewrite.rb +1662 -0
- data/test/functional/debugging/debug-mode.rb +689 -0
- data/test/functional/debugging/profile-mode.rb +165 -0
- data/test/functional/debugging/rule-tracing.rb +74 -0
- data/test/functional/delegation/import.rb +379 -0
- data/test/functional/lexer/basic.rb +559 -0
- data/test/functional/lexer/filter-mode.rb +245 -0
- data/test/functional/lexer/nuances.rb +47 -0
- data/test/functional/lexer/properties.rb +104 -0
- data/test/functional/lexer/syn-pred.rb +32 -0
- data/test/functional/lexer/xml.rb +206 -0
- data/test/functional/main/main-scripts.rb +245 -0
- data/test/functional/parser/actions.rb +224 -0
- data/test/functional/parser/backtracking.rb +244 -0
- data/test/functional/parser/basic.rb +282 -0
- data/test/functional/parser/calc.rb +98 -0
- data/test/functional/parser/ll-star.rb +143 -0
- data/test/functional/parser/nuances.rb +165 -0
- data/test/functional/parser/predicates.rb +103 -0
- data/test/functional/parser/properties.rb +242 -0
- data/test/functional/parser/rule-methods.rb +132 -0
- data/test/functional/parser/scopes.rb +274 -0
- data/test/functional/token-rewrite/basic.rb +318 -0
- data/test/functional/token-rewrite/via-parser.rb +100 -0
- data/test/functional/tree-parser/basic.rb +750 -0
- data/test/unit/sample-input/file-stream-1 +2 -0
- data/test/unit/sample-input/teststreams.input2 +2 -0
- data/test/unit/test-dfa.rb +52 -0
- data/test/unit/test-exceptions.rb +44 -0
- data/test/unit/test-recognizers.rb +55 -0
- data/test/unit/test-scheme.rb +62 -0
- data/test/unit/test-streams.rb +459 -0
- data/test/unit/test-tree-wizard.rb +535 -0
- data/test/unit/test-trees.rb +854 -0
- metadata +205 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3/test/functional'
|
5
|
+
|
6
|
+
class TestCalcParser < ANTLR3::Test::Functional
|
7
|
+
inline_grammar(<<-'END')
|
8
|
+
grammar TestCalc;
|
9
|
+
options { language = Ruby; }
|
10
|
+
|
11
|
+
@parser::init {
|
12
|
+
@reported_errors = []
|
13
|
+
}
|
14
|
+
|
15
|
+
@parser::members {
|
16
|
+
attr_reader :reported_errors
|
17
|
+
|
18
|
+
def emit_error_message(msg)
|
19
|
+
@reported_errors << msg
|
20
|
+
end
|
21
|
+
}
|
22
|
+
|
23
|
+
evaluate returns [result]: r=expression { $result = $r.result };
|
24
|
+
|
25
|
+
expression returns [result]:
|
26
|
+
r=mult { $result = $r.result }
|
27
|
+
(
|
28
|
+
'+' r2=mult { $result += $r2.result }
|
29
|
+
| '-' r2=mult { $result -= $r2.result }
|
30
|
+
)*
|
31
|
+
;
|
32
|
+
|
33
|
+
mult returns [result]:
|
34
|
+
r=log { $result = $r.result }
|
35
|
+
(
|
36
|
+
'*' r2=log {$result *= $r2.result}
|
37
|
+
| '/' r2=log {$result /= $r2.result}
|
38
|
+
| '%' r2=log {$result \%= $r2.result}
|
39
|
+
)*
|
40
|
+
;
|
41
|
+
|
42
|
+
log returns [result]: 'ln' r=exp {$result = Math.log($r.result)}
|
43
|
+
| r=exp {$result = $r.result}
|
44
|
+
;
|
45
|
+
|
46
|
+
exp returns [result]: r=atom { $result = $r.result } ('^' r2=atom { $result **= $r2.result } )?
|
47
|
+
;
|
48
|
+
|
49
|
+
atom returns [result]:
|
50
|
+
n=INTEGER {$result = Integer($n.text)}
|
51
|
+
| n=DECIMAL {$result = Float($n.text)}
|
52
|
+
| '(' r=expression {$result = $r.result} ')'
|
53
|
+
| 'PI' {$result = Math::PI}
|
54
|
+
| 'E' {$result = Math::E}
|
55
|
+
;
|
56
|
+
|
57
|
+
INTEGER: DIGIT+;
|
58
|
+
|
59
|
+
DECIMAL: DIGIT+ '.' DIGIT+;
|
60
|
+
|
61
|
+
fragment
|
62
|
+
DIGIT: '0'..'9';
|
63
|
+
|
64
|
+
WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN};
|
65
|
+
END
|
66
|
+
|
67
|
+
def evaluate(expression)
|
68
|
+
lexer = TestCalc::Lexer.new( expression )
|
69
|
+
parser = TestCalc::Parser.new lexer
|
70
|
+
value = parser.evaluate
|
71
|
+
errors = parser.reported_errors
|
72
|
+
return [value, errors]
|
73
|
+
end
|
74
|
+
|
75
|
+
tests = %[
|
76
|
+
1 + 2 = 3
|
77
|
+
1 + 2 * 3 = 7
|
78
|
+
10 / 2 = 5
|
79
|
+
6 + 2*(3+1) - 4 = 10
|
80
|
+
].strip!.split(/\n/).map { |line|
|
81
|
+
expr, val = line.strip.split(/\s+=\s+/, 2)
|
82
|
+
[expr, Integer(val)]
|
83
|
+
}
|
84
|
+
|
85
|
+
tests.each do |expression, true_value|
|
86
|
+
example "should parse '#{expression}'" do
|
87
|
+
parser_value, errors = evaluate(expression)
|
88
|
+
parser_value.should == true_value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
example "badly formed input" do
|
93
|
+
val, errors = evaluate "6 - (2*1"
|
94
|
+
|
95
|
+
errors.should have(1).thing
|
96
|
+
errors.first.should =~ /mismatched/
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3/test/functional'
|
5
|
+
|
6
|
+
class TestLLStarParser < ANTLR3::Test::Functional
|
7
|
+
inline_grammar(<<-'END')
|
8
|
+
grammar LLStar;
|
9
|
+
|
10
|
+
options { language = Ruby; }
|
11
|
+
@header { require 'stringio' }
|
12
|
+
@init { @output = StringIO.new() }
|
13
|
+
@members {
|
14
|
+
def output
|
15
|
+
@output.string
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
program
|
20
|
+
: declaration+
|
21
|
+
;
|
22
|
+
|
23
|
+
/** In this rule, the functionHeader left prefix on the last two
|
24
|
+
* alternatives is not LL(k) for a fixed k. However, it is
|
25
|
+
* LL(*). The LL(*) algorithm simply scans ahead until it sees
|
26
|
+
* either the ';' or the '{' of the block and then it picks
|
27
|
+
* the appropriate alternative. Lookhead can be arbitrarily
|
28
|
+
* long in theory, but is <=10 in most cases. Works great.
|
29
|
+
* Use ANTLRWorks to see the look use (step by Location)
|
30
|
+
* and look for blue tokens in the input window pane. :)
|
31
|
+
*/
|
32
|
+
declaration
|
33
|
+
: variable
|
34
|
+
| functionHeader ';'
|
35
|
+
{ @output.puts( $functionHeader.name + " is a declaration") }
|
36
|
+
| functionHeader block
|
37
|
+
{ @output.puts( $functionHeader.name + " is a definition") }
|
38
|
+
;
|
39
|
+
|
40
|
+
variable
|
41
|
+
: type declarator ';'
|
42
|
+
;
|
43
|
+
|
44
|
+
declarator
|
45
|
+
: ID
|
46
|
+
;
|
47
|
+
|
48
|
+
functionHeader returns [name]
|
49
|
+
: type ID '(' ( formalParameter ( ',' formalParameter )* )? ')'
|
50
|
+
{$name = $ID.text}
|
51
|
+
;
|
52
|
+
|
53
|
+
formalParameter
|
54
|
+
: type declarator
|
55
|
+
;
|
56
|
+
|
57
|
+
type
|
58
|
+
: 'int'
|
59
|
+
| 'char'
|
60
|
+
| 'void'
|
61
|
+
| ID
|
62
|
+
;
|
63
|
+
|
64
|
+
block
|
65
|
+
: '{'
|
66
|
+
variable*
|
67
|
+
stat*
|
68
|
+
'}'
|
69
|
+
;
|
70
|
+
|
71
|
+
stat: forStat
|
72
|
+
| expr ';'
|
73
|
+
| block
|
74
|
+
| assignStat ';'
|
75
|
+
| ';'
|
76
|
+
;
|
77
|
+
|
78
|
+
forStat
|
79
|
+
: 'for' '(' assignStat ';' expr ';' assignStat ')' block
|
80
|
+
;
|
81
|
+
|
82
|
+
assignStat
|
83
|
+
: ID '=' expr
|
84
|
+
;
|
85
|
+
|
86
|
+
expr: condExpr
|
87
|
+
;
|
88
|
+
|
89
|
+
condExpr
|
90
|
+
: aexpr ( ('==' | '<') aexpr )?
|
91
|
+
;
|
92
|
+
|
93
|
+
aexpr
|
94
|
+
: atom ( '+' atom )*
|
95
|
+
;
|
96
|
+
|
97
|
+
atom
|
98
|
+
: ID
|
99
|
+
| INT
|
100
|
+
| '(' expr ')'
|
101
|
+
;
|
102
|
+
|
103
|
+
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
|
104
|
+
;
|
105
|
+
|
106
|
+
INT : ('0'..'9')+
|
107
|
+
;
|
108
|
+
|
109
|
+
WS : ( ' '
|
110
|
+
| '\t'
|
111
|
+
| '\r'
|
112
|
+
| '\n'
|
113
|
+
)+
|
114
|
+
{$channel=HIDDEN}
|
115
|
+
;
|
116
|
+
END
|
117
|
+
|
118
|
+
|
119
|
+
example "parsing with a LL(*) grammar" do
|
120
|
+
lexer = LLStar::Lexer.new(<<-'END'.fixed_indent(0))
|
121
|
+
char c;
|
122
|
+
int x;
|
123
|
+
|
124
|
+
void bar(int x);
|
125
|
+
|
126
|
+
int foo(int y, char d) {
|
127
|
+
int i;
|
128
|
+
for (i=0; i<3; i=i+1) {
|
129
|
+
x=3;
|
130
|
+
y=5;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
END
|
134
|
+
parser = LLStar::Parser.new lexer
|
135
|
+
|
136
|
+
parser.program
|
137
|
+
parser.output.should == <<-'END'.fixed_indent(0)
|
138
|
+
bar is a declaration
|
139
|
+
foo is a definition
|
140
|
+
END
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3/test/functional'
|
5
|
+
|
6
|
+
class TestEmptyAlternative < ANTLR3::Test::Functional
|
7
|
+
|
8
|
+
inline_grammar(<<-'END')
|
9
|
+
grammar EmptyAlt;
|
10
|
+
options {
|
11
|
+
language = Ruby;
|
12
|
+
}
|
13
|
+
|
14
|
+
r
|
15
|
+
: NAME
|
16
|
+
( WS+ NAME
|
17
|
+
|
|
18
|
+
)
|
19
|
+
EOF
|
20
|
+
;
|
21
|
+
|
22
|
+
NAME: ('a'..'z') ('a'..'z' | '0'..'9')+;
|
23
|
+
NUMBER: ('0'..'9')+;
|
24
|
+
WS: ' '+;
|
25
|
+
END
|
26
|
+
|
27
|
+
example "rule with empty alternative" do
|
28
|
+
lexer = EmptyAlt::Lexer.new( 'foo' )
|
29
|
+
parser = EmptyAlt::Parser.new lexer
|
30
|
+
events = parser.r
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class TestSubrulePrediction < ANTLR3::Test::Functional
|
36
|
+
|
37
|
+
inline_grammar(<<-'END')
|
38
|
+
grammar Subrule;
|
39
|
+
options {
|
40
|
+
language = Ruby;
|
41
|
+
}
|
42
|
+
|
43
|
+
@parser::members {
|
44
|
+
def emit_error_message(msg)
|
45
|
+
# do nothing
|
46
|
+
end
|
47
|
+
def report_error(error)
|
48
|
+
raise error
|
49
|
+
end
|
50
|
+
}
|
51
|
+
|
52
|
+
a: 'BEGIN' b WS+ 'END';
|
53
|
+
b: ( WS+ 'A' )+;
|
54
|
+
WS: ' ';
|
55
|
+
END
|
56
|
+
|
57
|
+
example "make correct predictions involving subrules" do
|
58
|
+
lexer = Subrule::Lexer.new( 'BEGIN A END' )
|
59
|
+
parser = Subrule::Parser.new lexer
|
60
|
+
events = parser.a
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class TestSpecialStates < ANTLR3::Test::Functional
|
67
|
+
|
68
|
+
inline_grammar(<<-'END')
|
69
|
+
grammar SpecialStates;
|
70
|
+
options { language = Ruby; }
|
71
|
+
|
72
|
+
@init { @cond = true }
|
73
|
+
|
74
|
+
@members {
|
75
|
+
attr_accessor :cond
|
76
|
+
def recover(re)
|
77
|
+
raise re
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
r
|
82
|
+
: ( { @cond }? NAME
|
83
|
+
| {!@cond }? NAME WS+ NAME
|
84
|
+
)
|
85
|
+
( WS+ NAME )?
|
86
|
+
EOF
|
87
|
+
;
|
88
|
+
|
89
|
+
NAME: ('a'..'z') ('a'..'z' | '0'..'9')+;
|
90
|
+
NUMBER: ('0'..'9')+;
|
91
|
+
WS: ' '+;
|
92
|
+
END
|
93
|
+
|
94
|
+
example "parsing 'foo'" do
|
95
|
+
lexer = SpecialStates::Lexer.new 'foo'
|
96
|
+
parser = SpecialStates::Parser.new lexer
|
97
|
+
parser.r
|
98
|
+
end
|
99
|
+
|
100
|
+
example "parsing 'foo name1'" do
|
101
|
+
lexer = SpecialStates::Lexer.new 'foo name1'
|
102
|
+
parser = SpecialStates::Parser.new lexer
|
103
|
+
parser.r
|
104
|
+
end
|
105
|
+
|
106
|
+
example "parsing 'bar name1'" do
|
107
|
+
lexer = SpecialStates::Lexer.new 'bar name1'
|
108
|
+
parser = SpecialStates::Parser.new lexer
|
109
|
+
parser.cond = false
|
110
|
+
parser.r
|
111
|
+
end
|
112
|
+
|
113
|
+
example "parsing 'bar name1 name2'" do
|
114
|
+
lexer = SpecialStates::Lexer.new 'bar name1 name2'
|
115
|
+
parser = SpecialStates::Parser.new lexer
|
116
|
+
parser.cond = false
|
117
|
+
parser.r
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
class TestDFABug < ANTLR3::Test::Functional
|
123
|
+
|
124
|
+
inline_grammar(<<-'END')
|
125
|
+
grammar DFABug;
|
126
|
+
options {
|
127
|
+
language = Ruby;
|
128
|
+
output = AST;
|
129
|
+
}
|
130
|
+
|
131
|
+
|
132
|
+
// this rule used to generate an infinite loop in DFA.predict
|
133
|
+
r
|
134
|
+
options { backtrack=true; }
|
135
|
+
: (modifier+ INT)=> modifier+ expression
|
136
|
+
| modifier+ statement
|
137
|
+
;
|
138
|
+
|
139
|
+
expression
|
140
|
+
: INT '+' INT
|
141
|
+
;
|
142
|
+
|
143
|
+
statement
|
144
|
+
: 'fooze'
|
145
|
+
| 'fooze2'
|
146
|
+
;
|
147
|
+
|
148
|
+
modifier
|
149
|
+
: 'public'
|
150
|
+
| 'private'
|
151
|
+
;
|
152
|
+
|
153
|
+
ID : 'a'..'z' + ;
|
154
|
+
INT : '0'..'9' +;
|
155
|
+
WS: (' ' | '\n' | '\t')+ {$channel = HIDDEN;};
|
156
|
+
END
|
157
|
+
|
158
|
+
example "testing for DFA-based decision bug" do
|
159
|
+
lexer = DFABug::Lexer.new 'public fooze'
|
160
|
+
parser = DFABug::Parser.new lexer
|
161
|
+
parser.r
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
require 'antlr3/test/functional'
|
5
|
+
|
6
|
+
class TestPredicateHoist < ANTLR3::Test::Functional
|
7
|
+
|
8
|
+
inline_grammar(<<-'END')
|
9
|
+
grammar TestHoist;
|
10
|
+
options {
|
11
|
+
language = Ruby;
|
12
|
+
}
|
13
|
+
|
14
|
+
/* With this true, enum is seen as a keyword. False, it's an identifier */
|
15
|
+
@parser::init {
|
16
|
+
@enable_enum = false
|
17
|
+
}
|
18
|
+
@members {
|
19
|
+
attr_accessor :enable_enum
|
20
|
+
}
|
21
|
+
|
22
|
+
stat returns [enumIs]
|
23
|
+
: identifier {$enumIs = "ID"}
|
24
|
+
| enumAsKeyword {$enumIs = "keyword"}
|
25
|
+
;
|
26
|
+
|
27
|
+
identifier
|
28
|
+
: ID
|
29
|
+
| enumAsID
|
30
|
+
;
|
31
|
+
|
32
|
+
enumAsKeyword : {@enable_enum}? 'enum' ;
|
33
|
+
|
34
|
+
enumAsID : {!@enable_enum}? 'enum' ;
|
35
|
+
|
36
|
+
ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')*
|
37
|
+
;
|
38
|
+
|
39
|
+
INT : ('0'..'9')+
|
40
|
+
;
|
41
|
+
|
42
|
+
WS : ( ' '
|
43
|
+
| '\t'
|
44
|
+
| '\r'
|
45
|
+
| '\n'
|
46
|
+
)+
|
47
|
+
{$channel=HIDDEN}
|
48
|
+
;
|
49
|
+
END
|
50
|
+
|
51
|
+
|
52
|
+
example "'enum' is a keyword" do
|
53
|
+
lexer = TestHoist::Lexer.new 'enum'
|
54
|
+
parser = TestHoist::Parser.new lexer
|
55
|
+
parser.enable_enum = true
|
56
|
+
parser.stat.should == 'keyword'
|
57
|
+
end
|
58
|
+
|
59
|
+
example "'enum' is an ID" do
|
60
|
+
lexer = TestHoist::Lexer.new 'enum'
|
61
|
+
parser = TestHoist::Parser.new lexer
|
62
|
+
parser.enable_enum = false
|
63
|
+
parser.stat.should == 'ID'
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
class TestSyntacticPredicate < ANTLR3::Test::Functional
|
70
|
+
|
71
|
+
inline_grammar(<<-'END')
|
72
|
+
grammar SyntacticPredicate;
|
73
|
+
options {
|
74
|
+
language = Ruby;
|
75
|
+
}
|
76
|
+
|
77
|
+
@parser::members {
|
78
|
+
def emit_error_message(msg)
|
79
|
+
# do nothing
|
80
|
+
end
|
81
|
+
def report_error(error)
|
82
|
+
raise error
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
a: ((s+ P)=> s+ b)? E;
|
87
|
+
b: P 'foo';
|
88
|
+
|
89
|
+
s: S;
|
90
|
+
|
91
|
+
|
92
|
+
S: ' ';
|
93
|
+
P: '+';
|
94
|
+
E: '>';
|
95
|
+
END
|
96
|
+
|
97
|
+
example "rule with syntactic predicate" do
|
98
|
+
lexer = SyntacticPredicate::Lexer.new(' +foo>')
|
99
|
+
parser = SyntacticPredicate::Parser.new lexer
|
100
|
+
events = parser.a
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|