@@ -16,6 +16,8 @@ use pest::iterators::Pair;
16
16
use pest:: { Parser , RuleType } ;
17
17
use std:: borrow:: Cow ;
18
18
19
+ // TODO turn operator/delimiter into enums of their own (nested or otherwise)
20
+
19
21
/// The parsed content associated with a [`Token`] that has been scanned.
20
22
#[ derive( Clone , Debug , Eq , PartialEq ) ]
21
23
pub enum Content < ' val > {
@@ -36,7 +38,22 @@ pub enum Content<'val> {
36
38
37
39
/// A string literal. Contains the slice for the content of the literal.
38
40
StringLiteral ( Cow < ' val , str > ) ,
39
- // TODO things like literals, punctuation, etc.
41
+
42
+ /// The `.` punctuation
43
+ Dot ,
44
+
45
+ /// The `*` operator and wildcard.
46
+ Star ,
47
+
48
+ /// The `?` placeholder for a query parameter.
49
+ Parameter ,
50
+
51
+ /// An operator represented by punctuation (as opposed to a keyword based operator).
52
+ /// Contains the slice for the operator.
53
+ Operator ( Cow < ' val , str > ) ,
54
+
55
+ /// A separator character. Contains the slice for the delimiter character.
56
+ Delimiter ( Cow < ' val , str > ) ,
40
57
}
41
58
42
59
/// Convenience constructor for a [`Content::Keyword`].
@@ -64,6 +81,16 @@ pub fn string_literal<'val, S: Into<Cow<'val, str>>>(text: S) -> Content<'val> {
64
81
Content :: StringLiteral ( text. into ( ) )
65
82
}
66
83
84
+ /// Convenience constructor for a [`Content::Operator`].
85
+ pub fn operator < ' val , S : Into < Cow < ' val , str > > > ( text : S ) -> Content < ' val > {
86
+ Content :: Operator ( text. into ( ) )
87
+ }
88
+
89
+ /// Convenience constructor for a [`Content::Operator`].
90
+ pub fn delimiter < ' val , S : Into < Cow < ' val , str > > > ( text : S ) -> Content < ' val > {
91
+ Content :: Delimiter ( text. into ( ) )
92
+ }
93
+
67
94
/// Internal type to keep track of remaining input and relative line/column information.
68
95
///
69
96
/// This is used to leverage the PEG to do continuation parsing and calculating the line/offset
@@ -174,6 +201,14 @@ where
174
201
}
175
202
}
176
203
204
+ fn normalize_operator ( raw_text : & str ) -> Cow < str > {
205
+ match raw_text {
206
+ "!=" => "<>" ,
207
+ _ => raw_text,
208
+ }
209
+ . into ( )
210
+ }
211
+
177
212
impl < ' val > PartiQLScanner < ' val > {
178
213
fn do_next_token ( & mut self ) -> ParserResult < Token < ' val > > {
179
214
// the scanner rule is expected to return a single node
@@ -186,14 +221,14 @@ impl<'val> PartiQLScanner<'val> {
186
221
self . remainder = self . remainder . consume ( start_off + text. len ( ) , pair. end ( ) ?) ;
187
222
188
223
let content = match pair. as_rule ( ) {
189
- Rule :: Keyword => Content :: Keyword ( text. to_uppercase ( ) . into ( ) ) ,
190
- Rule :: String => Content :: StringLiteral ( normalize_string_lit ( pair. as_str ( ) ) ) ,
224
+ Rule :: Keyword => keyword ( text. to_uppercase ( ) ) ,
225
+ Rule :: String => string_literal ( normalize_string_lit ( pair. as_str ( ) ) ) ,
191
226
Rule :: Identifier => {
192
227
let ident_pair = pair. into_inner ( ) . exactly_one ( ) ?;
193
228
match ident_pair. as_rule ( ) {
194
- Rule :: NonQuotedIdentifier => Content :: Identifier ( ident_pair. as_str ( ) . into ( ) ) ,
229
+ Rule :: NonQuotedIdentifier => identifier ( ident_pair. as_str ( ) ) ,
195
230
Rule :: QuotedIdentifier => {
196
- Content :: Identifier ( normalize_quoted_ident ( ident_pair. as_str ( ) ) )
231
+ identifier ( normalize_quoted_ident ( ident_pair. as_str ( ) ) )
197
232
}
198
233
_ => return ident_pair. unexpected ( ) ,
199
234
}
@@ -208,6 +243,11 @@ impl<'val> PartiQLScanner<'val> {
208
243
_ => return number_pair. unexpected ( ) ,
209
244
}
210
245
}
246
+ Rule :: Dot_ => Content :: Dot ,
247
+ Rule :: Star_ => Content :: Star ,
248
+ Rule :: Parameter => Content :: Parameter ,
249
+ Rule :: Operator => operator ( normalize_operator ( text) ) ,
250
+ Rule :: Delimiter => delimiter ( text) ,
211
251
_ => return pair. unexpected ( ) ,
212
252
} ;
213
253
@@ -533,6 +573,85 @@ mod test {
533
573
"0.0e000" => decimal_literal_from_str( "0.0" )
534
574
]
535
575
) ]
576
+ #[ case:: no_trailing_zeros( scanner_test_case![ "1231231." => decimal_literal_from_str( "1231231" ) ] ) ]
577
+ #[ case:: delimiters(
578
+ scanner_test_case![
579
+ "[" => delimiter( "[" ) ,
580
+ "]" => delimiter( "]" ) ,
581
+ "(" => delimiter( "(" ) ,
582
+ ")" => delimiter( ")" ) ,
583
+ "{" => delimiter( "{" ) ,
584
+ "}" => delimiter( "}" ) ,
585
+ "<<" => delimiter( "<<" ) ,
586
+ ">>" => delimiter( ">>" ) ,
587
+ "," => delimiter( "," ) ,
588
+ ":" => delimiter( ":" ) ,
589
+ ";" => delimiter( ";" ) ,
590
+ ]
591
+ ) ]
592
+ #[ case:: operators(
593
+ scanner_test_case![
594
+ "@" => operator( "@" ) ,
595
+ "+" => operator( "+" ) ,
596
+ "-" => operator( "-" ) ,
597
+ "/" => operator( "/" ) ,
598
+ "%" => operator( "%" ) ,
599
+ "<" => operator( "<" ) ,
600
+ " " ,
601
+ "<=" => operator( "<=" ) ,
602
+ ">" => operator( ">" ) ,
603
+ " " ,
604
+ ">=" => operator( ">=" ) ,
605
+ "=" => operator( "=" ) ,
606
+ "<>" => operator( "<>" ) ,
607
+ "!=" => operator( "<>" ) ,
608
+ ]
609
+ ) ]
610
+ #[ case:: left_angles(
611
+ scanner_test_case![
612
+ "<<" => delimiter( "<<" ) ,
613
+ "<<" => delimiter( "<<" ) ,
614
+ "<" => operator( "<" ) ,
615
+ ]
616
+ ) ]
617
+ #[ case:: right_angles(
618
+ scanner_test_case![
619
+ ">>" => delimiter( ">>" ) ,
620
+ ">>" => delimiter( ">>" ) ,
621
+ ">" => operator( ">" ) ,
622
+ ]
623
+ ) ]
624
+ #[ case:: balanced_angles(
625
+ scanner_test_case![
626
+ "<<" => delimiter( "<<" ) ,
627
+ "<<" => delimiter( "<<" ) ,
628
+ "<>" => operator( "<>" ) ,
629
+ ">>" => delimiter( ">>" ) ,
630
+ ">>" => delimiter( ">>" ) ,
631
+ " " ,
632
+ "<<" => delimiter( "<<" ) ,
633
+ "<=" => operator( "<=" ) ,
634
+ ">>" => delimiter( ">>" ) ,
635
+ ">" => operator( ">" ) ,
636
+ ]
637
+ ) ]
638
+ #[ case:: dot( scanner_test_case![ "." => Content :: Dot ] ) ]
639
+ #[ case:: star( scanner_test_case![ "*" => Content :: Star ] ) ]
640
+ #[ case:: parameter( scanner_test_case![ "?" => Content :: Parameter ] ) ]
641
+ #[ case:: comment_no_minus(
642
+ scanner_test_case![
643
+ "-------- a line comment with no minus...\n "
644
+ ]
645
+ ) ]
646
+ #[ case:: divide_block_comment(
647
+ scanner_test_case![
648
+ "/" => operator( "/" ) ,
649
+ "/" => operator( "/" ) ,
650
+ "/**/" ,
651
+ "/" => operator( "/" ) ,
652
+ "/" => operator( "/" ) ,
653
+ ]
654
+ ) ]
536
655
#[ case:: select_from(
537
656
scanner_test_case![
538
657
"SelEct" => keyword( "SELECT" ) ,
0 commit comments