Add case-insensitive matching
This commit is contained in:
+25
-4
@@ -25,11 +25,13 @@ enum Term {
|
|||||||
Choice(Vec<Term>),
|
Choice(Vec<Term>),
|
||||||
EOI,
|
EOI,
|
||||||
Literal(String),
|
Literal(String),
|
||||||
|
LiteralI(String),
|
||||||
NegLookahead(Box<Term>),
|
NegLookahead(Box<Term>),
|
||||||
Optional(Box<Term>),
|
Optional(Box<Term>),
|
||||||
Plus(Box<Term>),
|
Plus(Box<Term>),
|
||||||
PosLookahead(Box<Term>),
|
PosLookahead(Box<Term>),
|
||||||
Range(RangeInclusive<char>),
|
Range(RangeInclusive<char>),
|
||||||
|
RangeI(RangeInclusive<char>),
|
||||||
Rule(Ident),
|
Rule(Ident),
|
||||||
Sequence(Vec<Term>),
|
Sequence(Vec<Term>),
|
||||||
Star(Box<Term>),
|
Star(Box<Term>),
|
||||||
@@ -60,10 +62,15 @@ impl Parse for Rule {
|
|||||||
impl Parse for Term {
|
impl Parse for Term {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
fn parse_range(input: ParseStream) -> syn::Result<Term> {
|
fn parse_range(input: ParseStream) -> syn::Result<Term> {
|
||||||
let start = input.parse::<LitChar>()?.value();
|
let start_lit = input.parse::<LitChar>()?;
|
||||||
input.parse::<Token![..]>()?;
|
input.parse::<Token![..]>()?;
|
||||||
let end = input.parse::<LitChar>()?.value();
|
let end_lit = input.parse::<LitChar>()?;
|
||||||
Ok(Term::Range(start..=end))
|
let range = start_lit.value()..=end_lit.value();
|
||||||
|
if end_lit.suffix() == "i" {
|
||||||
|
Ok(Term::RangeI(range))
|
||||||
|
} else {
|
||||||
|
Ok(Term::Range(range))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_atom(input: ParseStream) -> syn::Result<Term> {
|
fn parse_atom(input: ParseStream) -> syn::Result<Term> {
|
||||||
@@ -77,7 +84,12 @@ impl Parse for Term {
|
|||||||
input.parse().map(Term::Rule)
|
input.parse().map(Term::Rule)
|
||||||
}
|
}
|
||||||
} else if look.peek(LitStr) {
|
} else if look.peek(LitStr) {
|
||||||
input.parse::<LitStr>().map(|x| Term::Literal(x.value()))
|
let lit = input.parse::<LitStr>()?;
|
||||||
|
if lit.suffix() == "i" {
|
||||||
|
Ok(Term::LiteralI(lit.value()))
|
||||||
|
} else {
|
||||||
|
Ok(Term::Literal(lit.value()))
|
||||||
|
}
|
||||||
} else if look.peek(LitChar) {
|
} else if look.peek(LitChar) {
|
||||||
parse_range(input)
|
parse_range(input)
|
||||||
} else if look.peek(syn::token::Paren) {
|
} else if look.peek(syn::token::Paren) {
|
||||||
@@ -159,6 +171,9 @@ impl Term {
|
|||||||
Term::Literal(lit_str) => quote! {
|
Term::Literal(lit_str) => quote! {
|
||||||
p.literal(#lit_str)
|
p.literal(#lit_str)
|
||||||
},
|
},
|
||||||
|
Term::LiteralI(lit_str) => quote! {
|
||||||
|
p.literal_i(#lit_str)
|
||||||
|
},
|
||||||
Term::Sequence(terms) => {
|
Term::Sequence(terms) => {
|
||||||
let expr = terms
|
let expr = terms
|
||||||
.iter()
|
.iter()
|
||||||
@@ -218,6 +233,12 @@ impl Term {
|
|||||||
p.range(#lo..=#hi)
|
p.range(#lo..=#hi)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Term::RangeI(range) => {
|
||||||
|
let (lo, hi) = (range.start(), range.end());
|
||||||
|
quote! {
|
||||||
|
p.range_i(#lo..=#hi)
|
||||||
|
}
|
||||||
|
}
|
||||||
Term::NegLookahead(term) => {
|
Term::NegLookahead(term) => {
|
||||||
let code = term.generate_code();
|
let code = term.generate_code();
|
||||||
quote! {
|
quote! {
|
||||||
|
|||||||
@@ -36,6 +36,15 @@ impl ParseState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn literal_i(&mut self, s: &str) -> bool {
|
||||||
|
if self.input[self.offset..(self.offset + s.len())].eq_ignore_ascii_case(s) {
|
||||||
|
self.offset += s.len();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn range(&mut self, r: RangeInclusive<char>) -> bool {
|
pub fn range(&mut self, r: RangeInclusive<char>) -> bool {
|
||||||
if let Some(next) = self.input[self.offset..].chars().next() {
|
if let Some(next) = self.input[self.offset..].chars().next() {
|
||||||
if r.contains(&next) {
|
if r.contains(&next) {
|
||||||
@@ -46,6 +55,16 @@ impl ParseState {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn range_i(&mut self, r: RangeInclusive<char>) -> bool {
|
||||||
|
if let Some(next) = self.input[self.offset..].chars().next() {
|
||||||
|
if r.contains(&next.to_ascii_lowercase()) || r.contains(&next.to_ascii_uppercase()) {
|
||||||
|
self.offset += next.len_utf8();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
pub fn any(&mut self) -> bool {
|
pub fn any(&mut self) -> bool {
|
||||||
if let Some(c) = self.input[self.offset..].chars().next() {
|
if let Some(c) = self.input[self.offset..].chars().next() {
|
||||||
self.offset += c.len_utf8();
|
self.offset += c.len_utf8();
|
||||||
@@ -85,6 +104,8 @@ mod tests {
|
|||||||
|
|
||||||
dq = "\"";
|
dq = "\"";
|
||||||
quoted = dq ("\\" ANY / !dq ANY)* dq;
|
quoted = dq ("\\" ANY / !dq ANY)* dq;
|
||||||
|
|
||||||
|
case_sensitivity = "Strict " ('a'..'z')+ ", loose "i ('a'..'z'i)+;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse<T: Fn(&mut ParseState) -> bool>(rule: T, s: &str) -> bool {
|
fn parse<T: Fn(&mut ParseState) -> bool>(rule: T, s: &str) -> bool {
|
||||||
@@ -163,4 +184,14 @@ mod tests {
|
|||||||
assert!(!parse(quoted, r#""foo " bar""#));
|
assert!(!parse(quoted, r#""foo " bar""#));
|
||||||
assert!(!parse(quoted, r#""false end \""#));
|
assert!(!parse(quoted, r#""false end \""#));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_i_suffix() {
|
||||||
|
assert!(parse(case_sensitivity, "Strict abc, loose xyz"));
|
||||||
|
assert!(!parse(case_sensitivity, "STRICT abc, loose xyz"));
|
||||||
|
assert!(!parse(case_sensitivity, "Strict ABC, loose xyz"));
|
||||||
|
assert!(parse(case_sensitivity, "Strict abc, LOOSE xyz"));
|
||||||
|
assert!(parse(case_sensitivity, "Strict abc, loose XYZ"));
|
||||||
|
assert!(parse(case_sensitivity, "Strict abc, LoOsE XyZ"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user