9
9
//! ```
10
10
//! use pestion::*;
11
11
//! use ion_rs::value::*;
12
+ //! use ion_rs::value::reader::*;
12
13
//!
13
14
//! fn main() -> PestionResult<()> {
14
15
//! // parse a Pest grammar and convert it to Ion element
15
- //! let element = r#"a = @{ "a" | "b" ~ "c" }"#.pest_to_element ()?;
16
+ //! let actual = r#"a = @{ "a" | "b" ~ "c" }"#.try_pest_to_element ()?;
16
17
//!
17
- //! // the grammar is a struct containing a field for each rule
18
- //! let a_rule = element
19
- //! .as_struct()
20
- //! .and_then(|s| s.get("a"))
21
- //! .and_then(|r| r.as_struct()).unwrap();
18
+ //! // here is the equivalent Ion element
19
+ //! let ion_text = r#"{
20
+ //! a: {
21
+ //! type: atomic,
22
+ //! expression:
23
+ //! (choice
24
+ //! (string exact "a")
25
+ //! (sequence
26
+ //! (string exact "b")
27
+ //! (string exact "c")
28
+ //! )
29
+ //! )
30
+ //! }
31
+ //! }"#;
32
+ //! let expected = element_reader().read_one(ion_text.as_bytes())?;
22
33
//!
23
- //! // The '@' in the start of the rule means it is atomic
24
- //! assert_eq!("atomic", a_rule.get("type").and_then(|t| t.as_str()).unwrap());
25
- //!
26
- //! // the first node in the expression tree is a `choice` operator
27
- //! assert_eq!(
28
- //! "choice",
29
- //! a_rule
30
- //! .get("expression")
31
- //! .and_then(|e| e.as_sequence())
32
- //! .and_then(|s| s.get(0))
33
- //! .and_then(|h| h.as_str()).unwrap()
34
- //! );
34
+ //! // these should be equivalent
35
+ //! assert_eq!(expected, actual);
35
36
//!
36
37
//! Ok(())
37
38
//! }
@@ -49,20 +50,35 @@ use ion_rs::value::{Builder, Element};
49
50
use pest:: Parser ;
50
51
use pest_meta:: ast:: { Expr , Rule as AstRule , RuleType as AstRuleType , RuleType } ;
51
52
use pest_meta:: parser:: { consume_rules, PestParser , Rule } ;
53
+ use smallvec:: { smallvec, SmallVec } ;
52
54
53
- /// Converts a representation of a Pest grammar (or part of a grammar) into Ion [`Element`].
54
- pub trait PestToElement {
55
+ /// Converts representation of a Pest grammar (or part of a grammar) into Ion [`Element`].
56
+ pub trait TryPestToElement {
55
57
type Element : Element ;
56
58
57
59
/// Converts this into [`Element`] which may imply parsing Pest syntax.
58
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > ;
60
+ ///
61
+ /// This returns `Err` if the the conversion fails. For example, this can happen if the
62
+ /// source is not a valid Pest grammar.
63
+ fn try_pest_to_element ( & self ) -> PestionResult < Self :: Element > ;
64
+ }
65
+
66
+ /// Infallible conversion of a Pest grammar (or part of a grammar) into Ion [`Element`].
67
+ pub trait PestToElement {
68
+ type Element : Element ;
69
+
70
+ /// Converts this into an [`Element`] representation.
71
+ ///
72
+ /// This operation cannot fail and therefore it is implied that it represents some
73
+ /// well formed Pest grammar or component thereof.
74
+ fn pest_to_element ( & self ) -> Self :: Element ;
59
75
}
60
76
61
- impl PestToElement for & str {
77
+ impl TryPestToElement for & str {
62
78
type Element = OwnedElement ;
63
79
64
80
/// Parses a `str` slice as a Pest grammar and serializes the AST into [`Element`].
65
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > {
81
+ fn try_pest_to_element ( & self ) -> PestionResult < Self :: Element > {
66
82
let pairs = PestParser :: parse ( Rule :: grammar_rules, * self ) ?;
67
83
let ast = match consume_rules ( pairs) {
68
84
Ok ( ast) => ast,
@@ -77,22 +93,21 @@ impl PestToElement for &str {
77
93
}
78
94
} ;
79
95
80
- ast. pest_to_element ( )
96
+ Ok ( ast. pest_to_element ( ) )
81
97
}
82
98
}
83
99
84
100
impl PestToElement for Vec < AstRule > {
85
101
type Element = OwnedElement ;
86
102
87
103
/// Converts a body of rules into a `struct` that has a rule for each field.
88
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > {
89
- let mut fields = vec ! [ ] ;
90
- for rule in self . iter ( ) {
104
+ fn pest_to_element ( & self ) -> Self :: Element {
105
+ let fields = self . iter ( ) . map ( |rule| {
91
106
let rule_name = text_token ( rule. name . clone ( ) ) ;
92
- let rule_value = rule. pest_to_element ( ) ? ;
93
- fields . push ( ( rule_name, rule_value) ) ;
94
- }
95
- Ok ( Self :: Element :: new_struct ( fields) )
107
+ let rule_value = rule. pest_to_element ( ) ;
108
+ ( rule_name, rule_value)
109
+ } ) ;
110
+ Self :: Element :: new_struct ( fields)
96
111
}
97
112
}
98
113
@@ -101,20 +116,20 @@ impl PestToElement for AstRule {
101
116
102
117
/// Converts a Pest Rule into a `struct` that has the field for [`RuleType`] as a symbol
103
118
/// and a field for the [`Expr`].
104
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > {
105
- let fields = vec ! [
106
- ( text_token( "type" ) , self . ty. pest_to_element( ) ? ) ,
107
- ( text_token( "expression" ) , self . expr. pest_to_element( ) ? ) ,
108
- ] ;
109
- Ok ( Self :: Element :: new_struct ( fields) )
119
+ fn pest_to_element ( & self ) -> Self :: Element {
120
+ let fields = std :: array :: IntoIter :: new ( [
121
+ ( text_token ( "type" ) , self . ty . pest_to_element ( ) ) ,
122
+ ( text_token ( "expression" ) , self . expr . pest_to_element ( ) ) ,
123
+ ] ) ;
124
+ Self :: Element :: new_struct ( fields)
110
125
}
111
126
}
112
127
113
128
impl PestToElement for AstRuleType {
114
129
type Element = OwnedElement ;
115
130
116
131
/// Serializes the enum into a symbolic value.
117
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > {
132
+ fn pest_to_element ( & self ) -> Self :: Element {
118
133
let sym_tok = text_token ( match self {
119
134
RuleType :: Normal => "normal" ,
120
135
RuleType :: Silent => "silent" ,
@@ -123,94 +138,100 @@ impl PestToElement for AstRuleType {
123
138
RuleType :: NonAtomic => "non_atomic" ,
124
139
} ) ;
125
140
126
- Ok ( sym_tok. into ( ) )
141
+ sym_tok. into ( )
127
142
}
128
143
}
129
144
130
145
impl PestToElement for Expr {
131
146
type Element = OwnedElement ;
132
147
133
148
/// Generates a `sexp` representation of the rule expression.
134
- fn pest_to_element ( & self ) -> PestionResult < Self :: Element > {
149
+ fn pest_to_element ( & self ) -> Self :: Element {
135
150
use OwnedValue :: * ;
136
151
137
- let element = Self :: Element :: new_sexp ( match self . clone ( ) {
138
- Expr :: Str ( text) => vec ! [
152
+ const STACK_LEN : usize = 4 ;
153
+ let values: SmallVec < [ _ ; STACK_LEN ] > = match self . clone ( ) {
154
+ Expr :: Str ( text) => smallvec ! [
139
155
text_token( "string" ) . into( ) ,
140
156
text_token( "exact" ) . into( ) ,
141
157
String ( text) . into( ) ,
142
158
] ,
143
- Expr :: Insens ( text) => vec ! [
159
+ Expr :: Insens ( text) => smallvec ! [
144
160
text_token( "string" ) . into( ) ,
145
161
text_token( "insensitive" ) . into( ) ,
146
162
String ( text) . into( ) ,
147
163
] ,
148
- Expr :: Range ( begin, end) => vec ! [
164
+ Expr :: Range ( begin, end) => smallvec ! [
149
165
text_token( "character_range" ) . into( ) ,
150
166
String ( begin) . into( ) ,
151
167
String ( end) . into( ) ,
152
168
] ,
153
- Expr :: Ident ( name) => vec ! [ text_token( "identifier" ) . into( ) , String ( name) . into( ) ] ,
154
- Expr :: PosPred ( expr) => vec ! [
169
+ Expr :: Ident ( name) => smallvec ! [ text_token( "identifier" ) . into( ) , String ( name) . into( ) ] ,
170
+ Expr :: PosPred ( expr) => smallvec ! [
155
171
text_token( "predicate" ) . into( ) ,
156
172
text_token( "positive" ) . into( ) ,
157
- expr. pest_to_element( ) ? ,
173
+ expr. pest_to_element( ) ,
158
174
] ,
159
- Expr :: NegPred ( expr) => vec ! [
175
+ Expr :: NegPred ( expr) => smallvec ! [
160
176
text_token( "predicate" ) . into( ) ,
161
177
text_token( "negative" ) . into( ) ,
162
- expr. pest_to_element( ) ? ,
178
+ expr. pest_to_element( ) ,
163
179
] ,
164
- Expr :: Seq ( left, right) => vec ! [
180
+ Expr :: Seq ( left, right) => smallvec ! [
165
181
text_token( "sequence" ) . into( ) ,
166
- left. pest_to_element( ) ? ,
167
- right. pest_to_element( ) ? ,
182
+ left. pest_to_element( ) ,
183
+ right. pest_to_element( ) ,
168
184
] ,
169
- Expr :: Choice ( left, right) => vec ! [
185
+ Expr :: Choice ( left, right) => smallvec ! [
170
186
text_token( "choice" ) . into( ) ,
171
- left. pest_to_element( ) ? ,
172
- right. pest_to_element( ) ? ,
187
+ left. pest_to_element( ) ,
188
+ right. pest_to_element( ) ,
173
189
] ,
174
- Expr :: Opt ( expr) => vec ! [ text_token( "optional" ) . into( ) , expr. pest_to_element( ) ?] ,
175
- Expr :: Rep ( expr) => vec ! [
190
+ Expr :: Opt ( expr) => {
191
+ smallvec ! [ text_token( "optional" ) . into( ) , expr. pest_to_element( ) ]
192
+ }
193
+ Expr :: Rep ( expr) => smallvec ! [
176
194
text_token( "repeat_min" ) . into( ) ,
177
195
0 . into( ) ,
178
- expr. pest_to_element( ) ? ,
196
+ expr. pest_to_element( ) ,
179
197
] ,
180
- Expr :: RepOnce ( expr) => vec ! [
198
+ Expr :: RepOnce ( expr) => smallvec ! [
181
199
text_token( "repeat_min" ) . into( ) ,
182
200
1 . into( ) ,
183
- expr. pest_to_element( ) ? ,
201
+ expr. pest_to_element( ) ,
184
202
] ,
185
- Expr :: RepMin ( expr, min) => vec ! [
203
+ Expr :: RepMin ( expr, min) => smallvec ! [
186
204
text_token( "repeat_min" ) . into( ) ,
187
205
( min as i64 ) . into( ) ,
188
- expr. pest_to_element( ) ? ,
206
+ expr. pest_to_element( ) ,
189
207
] ,
190
- Expr :: RepMax ( expr, max) => vec ! [
208
+ Expr :: RepMax ( expr, max) => smallvec ! [
191
209
text_token( "repeat_max" ) . into( ) ,
192
210
( max as i64 ) . into( ) ,
193
- expr. pest_to_element( ) ? ,
211
+ expr. pest_to_element( ) ,
194
212
] ,
195
- Expr :: RepExact ( expr, exact) => vec ! [
213
+ Expr :: RepExact ( expr, exact) => smallvec ! [
196
214
text_token( "repeat_range" ) . into( ) ,
197
215
( exact as i64 ) . into( ) ,
198
216
( exact as i64 ) . into( ) ,
199
- expr. pest_to_element( ) ? ,
217
+ expr. pest_to_element( ) ,
200
218
] ,
201
- Expr :: RepMinMax ( expr, min, max) => vec ! [
219
+ Expr :: RepMinMax ( expr, min, max) => smallvec ! [
202
220
text_token( "repeat_range" ) . into( ) ,
203
221
( min as i64 ) . into( ) ,
204
222
( max as i64 ) . into( ) ,
205
- expr. pest_to_element( ) ? ,
223
+ expr. pest_to_element( ) ,
206
224
] ,
207
225
// TODO implement these
208
226
Expr :: Skip ( _) => unimplemented ! ( ) ,
209
227
Expr :: Push ( _) => unimplemented ! ( ) ,
210
228
Expr :: PeekSlice ( _, _) => unimplemented ! ( ) ,
211
- } ) ;
229
+ } ;
230
+ assert ! ( values. len( ) <= STACK_LEN ) ;
231
+
232
+ let element = Self :: Element :: new_sexp ( values) ;
212
233
213
- Ok ( element)
234
+ element
214
235
}
215
236
}
216
237
@@ -403,10 +424,10 @@ mod tests {
403
424
) ]
404
425
fn good < T , S > ( #[ case] input : T , #[ case] ion_literal : S ) -> PestionResult < ( ) >
405
426
where
406
- T : PestToElement < Element = OwnedElement > + Debug ,
427
+ T : TryPestToElement < Element = OwnedElement > + Debug ,
407
428
S : AsRef < str > ,
408
429
{
409
- let actual = input. pest_to_element ( ) ?;
430
+ let actual = input. try_pest_to_element ( ) ?;
410
431
let expected = element_reader ( ) . read_one ( ion_literal. as_ref ( ) . as_bytes ( ) ) ?;
411
432
412
433
const BUF_SIZE : usize = 16 * 1024 * 1024 ;
@@ -431,8 +452,8 @@ mod tests {
431
452
#[ case:: empty_rule( r#"a = {}"# ) ]
432
453
#[ case:: self_reference( r#"a = { a }"# ) ]
433
454
#[ case:: double_rule( r#"a = { "a" }\n a = { "b" }"# ) ]
434
- fn pest_errors < T : PestToElement > ( #[ case] input : T ) -> PestionResult < ( ) > {
435
- match input. pest_to_element ( ) {
455
+ fn pest_errors < T : TryPestToElement > ( #[ case] input : T ) -> PestionResult < ( ) > {
456
+ match input. try_pest_to_element ( ) {
436
457
Err ( PestionError :: Pest ( _) ) => { }
437
458
something => {
438
459
unreachable ! ( "Got result we did not expect: {:?}" , something) ;
0 commit comments