mirror of
https://github.com/fluencelabs/lalrpop
synced 2025-03-28 06:01:02 +00:00
Merge pull request #264 from fitzgen/where-clauses
Create an explicit structure for `where` clauses
This commit is contained in:
commit
ff250d40a0
@ -41,6 +41,10 @@ mod expr_generic;
|
|||||||
mod generics_issue_104;
|
mod generics_issue_104;
|
||||||
mod generics_issue_104_lib;
|
mod generics_issue_104_lib;
|
||||||
|
|
||||||
|
/// Grammar parameterized by `F` with where clause `where F: for<'a> FnMut(&'a
|
||||||
|
/// str)`.
|
||||||
|
mod where_clause_with_forall;
|
||||||
|
|
||||||
/// test of inlining
|
/// test of inlining
|
||||||
mod inline;
|
mod inline;
|
||||||
|
|
||||||
@ -428,6 +432,14 @@ fn generics_issue_104_test1() {
|
|||||||
assert!(generics_issue_104::parse_Schema::<()>("grammar { foo }").is_ok());
|
assert!(generics_issue_104::parse_Schema::<()>("grammar { foo }").is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn where_clause_with_forall_test1() {
|
||||||
|
assert!(where_clause_with_forall::parse_Term(
|
||||||
|
&mut |s: &str| println!("log: {}", s),
|
||||||
|
"(((((42)))))"
|
||||||
|
).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_match_section() {
|
fn test_match_section() {
|
||||||
assert!(match_section::parse_Query("SELECT foo").is_ok());
|
assert!(match_section::parse_Query("SELECT foo").is_ok());
|
||||||
|
21
lalrpop-test/src/where_clause_with_forall.lalrpop
Normal file
21
lalrpop-test/src/where_clause_with_forall.lalrpop
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
grammar<F>(logger: &mut F) where F: for<'a> FnMut(&'a str);
|
||||||
|
|
||||||
|
pub Term: i32 = {
|
||||||
|
<n:Num> => {
|
||||||
|
let msg = format!("just parsed {}", n);
|
||||||
|
logger(&msg);
|
||||||
|
n
|
||||||
|
},
|
||||||
|
"(" <t:Term> ")" => {
|
||||||
|
logger("propagating...");
|
||||||
|
{
|
||||||
|
let msg = format!("... {}", t);
|
||||||
|
logger(&msg);
|
||||||
|
}
|
||||||
|
t
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Num: i32 = <s:r"[0-9]+"> => i32::from_str(s).unwrap();
|
1003
lalrpop-test/src/where_clause_with_forall.rs
Normal file
1003
lalrpop-test/src/where_clause_with_forall.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,7 @@ pub struct Grammar {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub type_parameters: Vec<TypeParameter>,
|
pub type_parameters: Vec<TypeParameter>,
|
||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
pub where_clauses: Vec<String>,
|
pub where_clauses: Vec<WhereClause<TypeRef>>,
|
||||||
pub items: Vec<GrammarItem>,
|
pub items: Vec<GrammarItem>,
|
||||||
pub annotations: Vec<Annotation>,
|
pub annotations: Vec<Annotation>,
|
||||||
pub module_attributes: Vec<String>,
|
pub module_attributes: Vec<String>,
|
||||||
@ -222,6 +222,105 @@ pub enum TypeRef {
|
|||||||
OfSymbol(SymbolKind),
|
OfSymbol(SymbolKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum WhereClause<T> {
|
||||||
|
// 'a: 'b + 'c
|
||||||
|
Lifetime {
|
||||||
|
lifetime: InternedString,
|
||||||
|
bounds: Vec<InternedString>,
|
||||||
|
},
|
||||||
|
// where for<'a> &'a T: Debug + Into<usize>
|
||||||
|
Type {
|
||||||
|
forall: Option<Vec<InternedString>>,
|
||||||
|
ty: T,
|
||||||
|
bounds: Vec<TypeBound<T>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> WhereClause<T> {
|
||||||
|
pub fn map<F, U>(&self, mut f: F) -> WhereClause<U>
|
||||||
|
where F: FnMut(&T) -> U
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
WhereClause::Lifetime { lifetime, ref bounds } => WhereClause::Lifetime {
|
||||||
|
lifetime: lifetime,
|
||||||
|
bounds: bounds.clone(),
|
||||||
|
},
|
||||||
|
WhereClause::Type { ref forall, ref ty, ref bounds } => WhereClause::Type {
|
||||||
|
forall: forall.clone(),
|
||||||
|
ty: f(ty),
|
||||||
|
bounds: bounds.iter().map(|b| b.map(&mut f)).collect(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum TypeBound<T> {
|
||||||
|
// The `'a` in `T: 'a`.
|
||||||
|
Lifetime(InternedString),
|
||||||
|
// `for<'a> FnMut(&'a usize)`
|
||||||
|
Fn {
|
||||||
|
forall: Option<Vec<InternedString>>,
|
||||||
|
path: Path,
|
||||||
|
parameters: Vec<T>,
|
||||||
|
ret: Option<T>,
|
||||||
|
},
|
||||||
|
// `some::Trait` or `some::Trait<Param, ...>` or `some::Trait<Item = Assoc>`
|
||||||
|
// or `for<'a> Trait<'a, T>`
|
||||||
|
Trait {
|
||||||
|
forall: Option<Vec<InternedString>>,
|
||||||
|
path: Path,
|
||||||
|
parameters: Vec<TypeBoundParameter<T>>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TypeBound<T> {
|
||||||
|
pub fn map<F, U>(&self, mut f: F) -> TypeBound<U>
|
||||||
|
where F: FnMut(&T) -> U
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
TypeBound::Lifetime(l) => TypeBound::Lifetime(l),
|
||||||
|
TypeBound::Fn { ref forall, ref path, ref parameters, ref ret } => TypeBound::Fn {
|
||||||
|
forall: forall.clone(),
|
||||||
|
path: path.clone(),
|
||||||
|
parameters: parameters.iter().map(&mut f).collect(),
|
||||||
|
ret: ret.as_ref().map(f),
|
||||||
|
},
|
||||||
|
TypeBound::Trait { ref forall, ref path, ref parameters } => TypeBound::Trait {
|
||||||
|
forall: forall.clone(),
|
||||||
|
path: path.clone(),
|
||||||
|
parameters: parameters.iter().map(|p| p.map(&mut f)).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum TypeBoundParameter<T> {
|
||||||
|
// 'a
|
||||||
|
Lifetime(InternedString),
|
||||||
|
// `T` or `'a`
|
||||||
|
TypeParameter(T),
|
||||||
|
// `Item = T`
|
||||||
|
Associated(InternedString, T),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TypeBoundParameter<T> {
|
||||||
|
pub fn map<F, U>(&self, mut f: F) -> TypeBoundParameter<U>
|
||||||
|
where F: FnMut(&T) -> U
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
TypeBoundParameter::Lifetime(l) =>
|
||||||
|
TypeBoundParameter::Lifetime(l),
|
||||||
|
TypeBoundParameter::TypeParameter(ref t) =>
|
||||||
|
TypeBoundParameter::TypeParameter(f(t)),
|
||||||
|
TypeBoundParameter::Associated(id, ref t) =>
|
||||||
|
TypeBoundParameter::Associated(id, f(t)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub enum TypeParameter {
|
pub enum TypeParameter {
|
||||||
Lifetime(InternedString),
|
Lifetime(InternedString),
|
||||||
@ -342,7 +441,7 @@ pub enum SymbolKind {
|
|||||||
|
|
||||||
// @R
|
// @R
|
||||||
Lookbehind,
|
Lookbehind,
|
||||||
|
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,6 +646,123 @@ impl Symbol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for WhereClause<T> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
match *self {
|
||||||
|
WhereClause::Lifetime { lifetime, ref bounds } => {
|
||||||
|
write!(fmt, "{}:", lifetime)?;
|
||||||
|
for (i, b) in bounds.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, " +")?;
|
||||||
|
}
|
||||||
|
write!(fmt, " {}", b)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
WhereClause::Type { ref forall, ref ty, ref bounds } => {
|
||||||
|
if let Some(ref forall) = *forall {
|
||||||
|
write!(fmt, "for<")?;
|
||||||
|
for (i, l) in forall.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", l)?;
|
||||||
|
}
|
||||||
|
write!(fmt, "> ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(fmt, "{}: ", ty)?;
|
||||||
|
for (i, b) in bounds.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, " +")?;
|
||||||
|
}
|
||||||
|
write!(fmt, " {}", b)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for TypeBound<T> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
match *self {
|
||||||
|
TypeBound::Lifetime(l) => {
|
||||||
|
write!(fmt, "{}", l)
|
||||||
|
}
|
||||||
|
TypeBound::Fn { ref forall, ref path, ref parameters, ref ret } => {
|
||||||
|
if let Some(ref forall) = *forall {
|
||||||
|
write!(fmt, "for<")?;
|
||||||
|
for (i, l) in forall.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", l)?;
|
||||||
|
}
|
||||||
|
write!(fmt, "> ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(fmt, "{}(", path)?;
|
||||||
|
for (i, p) in parameters.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", p)?;
|
||||||
|
}
|
||||||
|
write!(fmt, ")")?;
|
||||||
|
|
||||||
|
if let Some(ref ret) = *ret {
|
||||||
|
write!(fmt, " -> {}", ret)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
TypeBound::Trait { ref forall, ref path, ref parameters } => {
|
||||||
|
if let Some(ref forall) = *forall {
|
||||||
|
write!(fmt, "for<")?;
|
||||||
|
for (i, l) in forall.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", l)?;
|
||||||
|
}
|
||||||
|
write!(fmt, "> ")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(fmt, "{}", path)?;
|
||||||
|
if parameters.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(fmt, "<")?;
|
||||||
|
for (i, p) in parameters.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}", p)?;
|
||||||
|
}
|
||||||
|
write!(fmt, ">")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Display> Display for TypeBoundParameter<T> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
match *self {
|
||||||
|
TypeBoundParameter::Lifetime(l) => {
|
||||||
|
write!(fmt, "{}", l)
|
||||||
|
}
|
||||||
|
TypeBoundParameter::TypeParameter(ref t) => {
|
||||||
|
write!(fmt, "{}", t)
|
||||||
|
}
|
||||||
|
TypeBoundParameter::Associated(id, ref t) => {
|
||||||
|
write!(fmt, "{} = {}", id, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for TerminalString {
|
impl Display for TerminalString {
|
||||||
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
match *self {
|
match *self {
|
||||||
@ -554,7 +770,7 @@ impl Display for TerminalString {
|
|||||||
write!(fmt, "{}", s),
|
write!(fmt, "{}", s),
|
||||||
TerminalString::Bare(s) =>
|
TerminalString::Bare(s) =>
|
||||||
write!(fmt, "{}", s),
|
write!(fmt, "{}", s),
|
||||||
TerminalString::Error =>
|
TerminalString::Error =>
|
||||||
write!(fmt, "error"),
|
write!(fmt, "error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ pub use grammar::parse_tree::{Annotation,
|
|||||||
NonterminalString,
|
NonterminalString,
|
||||||
Path,
|
Path,
|
||||||
Span,
|
Span,
|
||||||
TerminalLiteral, TerminalString, TypeParameter};
|
TerminalLiteral, TerminalString, TypeParameter, WhereClause};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Grammar {
|
pub struct Grammar {
|
||||||
@ -47,7 +47,7 @@ pub struct Grammar {
|
|||||||
pub parameters: Vec<Parameter>,
|
pub parameters: Vec<Parameter>,
|
||||||
|
|
||||||
// where clauses declared on the grammar, like `grammar<T> where T: Sized`
|
// where clauses declared on the grammar, like `grammar<T> where T: Sized`
|
||||||
pub where_clauses: Vec<String>,
|
pub where_clauses: Vec<WhereClause<TypeRepr>>,
|
||||||
|
|
||||||
// optional tokenizer DFA; this is only needed if the user did not supply
|
// optional tokenizer DFA; this is only needed if the user did not supply
|
||||||
// an extern token declaration
|
// an extern token declaration
|
||||||
|
@ -151,6 +151,11 @@ impl<'s> LowerState<'s> {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let where_clauses = grammar.where_clauses
|
||||||
|
.iter()
|
||||||
|
.map(|wc| wc.map(pt::TypeRef::type_repr))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut algorithm = r::Algorithm::default();
|
let mut algorithm = r::Algorithm::default();
|
||||||
|
|
||||||
// FIXME Error recovery only works for parse tables so temporarily only generate parse tables for
|
// FIXME Error recovery only works for parse tables so temporarily only generate parse tables for
|
||||||
@ -189,7 +194,7 @@ impl<'s> LowerState<'s> {
|
|||||||
token_span: token_span.unwrap(),
|
token_span: token_span.unwrap(),
|
||||||
type_parameters: grammar.type_parameters,
|
type_parameters: grammar.type_parameters,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
where_clauses: grammar.where_clauses,
|
where_clauses: where_clauses,
|
||||||
algorithm: algorithm,
|
algorithm: algorithm,
|
||||||
intern_token: self.intern_token,
|
intern_token: self.intern_token,
|
||||||
terminals: r::TerminalSet {
|
terminals: r::TerminalSet {
|
||||||
@ -324,7 +329,7 @@ impl<'s> LowerState<'s> {
|
|||||||
match norm_util::check_between_braces(&action) {
|
match norm_util::check_between_braces(&action) {
|
||||||
norm_util::Presence::None => {
|
norm_util::Presence::None => {
|
||||||
action
|
action
|
||||||
}
|
}
|
||||||
norm_util::Presence::Normal => {
|
norm_util::Presence::Normal => {
|
||||||
let name_str : String = intern::read(|interner| {
|
let name_str : String = intern::read(|interner| {
|
||||||
let name_strs: Vec<_> = names.iter().map(|&(_,name,_)| interner.data(name)).collect();
|
let name_strs: Vec<_> = names.iter().map(|&(_,name,_)| interner.data(name)).collect();
|
||||||
|
@ -14,21 +14,14 @@ pub Grammar: Grammar =
|
|||||||
<lo:@L> "grammar" <hi:@R>
|
<lo:@L> "grammar" <hi:@R>
|
||||||
<tps:GrammarTypeParameters?>
|
<tps:GrammarTypeParameters?>
|
||||||
<parameters:GrammarParameters?>
|
<parameters:GrammarParameters?>
|
||||||
<where_clauses:"where"?>
|
<where_clauses:GrammarWhereClauses?>
|
||||||
";"
|
";"
|
||||||
<items:GrammarItem*> => {
|
<items:GrammarItem*> => {
|
||||||
let where_clauses =
|
|
||||||
where_clauses.iter()
|
|
||||||
.flat_map(|wc| wc.iter())
|
|
||||||
.map(|s| strip(s))
|
|
||||||
.filter(|s| !s.is_empty())
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect();
|
|
||||||
Grammar { prefix: format!("__"), // adjusted by `parse_grammar`
|
Grammar { prefix: format!("__"), // adjusted by `parse_grammar`
|
||||||
span: Span(lo, hi),
|
span: Span(lo, hi),
|
||||||
type_parameters: tps.unwrap_or(vec![]),
|
type_parameters: tps.unwrap_or(vec![]),
|
||||||
parameters: parameters.unwrap_or(vec![]),
|
parameters: parameters.unwrap_or(vec![]),
|
||||||
where_clauses: where_clauses,
|
where_clauses: where_clauses.unwrap_or(vec![]),
|
||||||
items: uses.into_iter().chain(items).collect(),
|
items: uses.into_iter().chain(items).collect(),
|
||||||
annotations: annotations,
|
annotations: annotations,
|
||||||
module_attributes: module_attributes}
|
module_attributes: module_attributes}
|
||||||
@ -42,6 +35,48 @@ TypeParameter: TypeParameter = {
|
|||||||
<l:Id> => TypeParameter::Id(l)
|
<l:Id> => TypeParameter::Id(l)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub GrammarWhereClauses: Vec<WhereClause<TypeRef>> =
|
||||||
|
"where" <Comma<GrammarWhereClause>>;
|
||||||
|
|
||||||
|
GrammarWhereClause: WhereClause<TypeRef> = {
|
||||||
|
<l:Lifetime> ":" <bounds:Plus<Lifetime>> =>
|
||||||
|
WhereClause::Lifetime { lifetime: l, bounds: bounds },
|
||||||
|
<f:ForAll?> <ty:TypeRef> ":" <bounds:TypeBounds> =>
|
||||||
|
WhereClause::Type { forall: f, ty: ty, bounds: bounds }
|
||||||
|
};
|
||||||
|
|
||||||
|
ForAll: Vec<InternedString> =
|
||||||
|
"for" "<" <Comma<Lifetime>> ">";
|
||||||
|
|
||||||
|
TypeBounds: Vec<TypeBound<TypeRef>> =
|
||||||
|
<Plus<TypeBound>>;
|
||||||
|
|
||||||
|
TypeBound: TypeBound<TypeRef> = {
|
||||||
|
<l:Lifetime> =>
|
||||||
|
TypeBound::Lifetime(l),
|
||||||
|
<f:ForAll?> <p:Path> "(" <params:Comma<TypeRef>> ")" <ret:("->" <TypeRef>)?> =>
|
||||||
|
TypeBound::Fn { forall: f, path: p, parameters: params, ret: ret },
|
||||||
|
<f:ForAll?> <p:Path> <params:("<" <Comma<TypeBoundParameter>> ">")?> =>
|
||||||
|
TypeBound::Trait { forall: f, path: p, parameters: params.unwrap_or(vec![]) }
|
||||||
|
};
|
||||||
|
|
||||||
|
TypeBoundParameter: TypeBoundParameter<TypeRef> = {
|
||||||
|
<l:Lifetime> => TypeBoundParameter::Lifetime(l),
|
||||||
|
<ty:TypeRef> => TypeBoundParameter::TypeParameter(ty),
|
||||||
|
<id:Id> "=" <ty:TypeRef> => TypeBoundParameter::Associated(id, ty),
|
||||||
|
};
|
||||||
|
|
||||||
|
Plus<T>: Vec<T> = {
|
||||||
|
<v:(<T> "+")*> <e:T?> => match e {
|
||||||
|
None => v,
|
||||||
|
Some(e) => {
|
||||||
|
let mut v = v;
|
||||||
|
v.push(e);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
GrammarParameters: Vec<Parameter> =
|
GrammarParameters: Vec<Parameter> =
|
||||||
"(" <Comma<GrammarParameter>> ")";
|
"(" <Comma<GrammarParameter>> ")";
|
||||||
|
|
||||||
@ -176,7 +211,7 @@ SymbolKind1: SymbolKind = {
|
|||||||
|
|
||||||
"@R" =>
|
"@R" =>
|
||||||
SymbolKind::Lookbehind,
|
SymbolKind::Lookbehind,
|
||||||
|
|
||||||
"!" =>
|
"!" =>
|
||||||
SymbolKind::Error,
|
SymbolKind::Error,
|
||||||
};
|
};
|
||||||
@ -378,9 +413,10 @@ extern {
|
|||||||
"mut" => Tok::Mut,
|
"mut" => Tok::Mut,
|
||||||
"pub" => Tok::Pub,
|
"pub" => Tok::Pub,
|
||||||
"type" => Tok::Type,
|
"type" => Tok::Type,
|
||||||
|
"where" => Tok::Where,
|
||||||
|
"for" => Tok::For,
|
||||||
"!" => Tok::Bang,
|
"!" => Tok::Bang,
|
||||||
"use" => Tok::Use(<&'input str>),
|
"use" => Tok::Use(<&'input str>),
|
||||||
"where" => Tok::Where(<Vec<&'input str>>),
|
|
||||||
|
|
||||||
"Escape" => Tok::Escape(<&'input str>),
|
"Escape" => Tok::Escape(<&'input str>),
|
||||||
"Id" => Tok::Id(<&'input str>),
|
"Id" => Tok::Id(<&'input str>),
|
||||||
@ -412,6 +448,7 @@ extern {
|
|||||||
"<" => Tok::LessThan,
|
"<" => Tok::LessThan,
|
||||||
"@L" => Tok::Lookahead,
|
"@L" => Tok::Lookahead,
|
||||||
"@R" => Tok::Lookbehind,
|
"@R" => Tok::Lookbehind,
|
||||||
|
"->" => Tok::MinusGreaterThan,
|
||||||
"+" => Tok::Plus,
|
"+" => Tok::Plus,
|
||||||
"?" => Tok::Question,
|
"?" => Tok::Question,
|
||||||
"}" => Tok::RightBrace,
|
"}" => Tok::RightBrace,
|
||||||
|
@ -46,3 +46,11 @@ pub fn parse_type_ref<'input>(input: &'input str)
|
|||||||
let tokenizer = tok::Tokenizer::new(input, 0);
|
let tokenizer = tok::Tokenizer::new(input, 0);
|
||||||
lrgrammar::parse_TypeRef(input, tokenizer)
|
lrgrammar::parse_TypeRef(input, tokenizer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn parse_where_clauses<'input>(input: &'input str)
|
||||||
|
-> Result<Vec<WhereClause<TypeRef>>, ParseError<'input>>
|
||||||
|
{
|
||||||
|
let tokenizer = tok::Tokenizer::new(input, 0);
|
||||||
|
lrgrammar::parse_GrammarWhereClauses(input, tokenizer)
|
||||||
|
}
|
||||||
|
@ -94,3 +94,59 @@ fn match_complex() {
|
|||||||
_ => panic!("expected MatchToken, but was: {:?}", first_item)
|
_ => panic!("expected MatchToken, but was: {:?}", first_item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn where_clauses() {
|
||||||
|
let clauses = vec![
|
||||||
|
"where T: Debug",
|
||||||
|
"where T: Debug + Display",
|
||||||
|
"where T: std::ops::Add<usize>",
|
||||||
|
"where T: IntoIterator<Item = usize>",
|
||||||
|
"where T: 'a",
|
||||||
|
"where 'a: 'b",
|
||||||
|
"where for<'a> &'a T: Debug",
|
||||||
|
"where T: for<'a> Flobbles<'a>",
|
||||||
|
"where T: FnMut(usize)",
|
||||||
|
"where T: FnMut(usize, bool)",
|
||||||
|
"where T: FnMut() -> bool",
|
||||||
|
"where T: for<'a> FnMut(&'a usize)",
|
||||||
|
"where T: Debug, U: Display",
|
||||||
|
];
|
||||||
|
|
||||||
|
for santa in clauses {
|
||||||
|
assert!(
|
||||||
|
parser::parse_where_clauses(santa).is_ok(),
|
||||||
|
"should parse where clauses: {}",
|
||||||
|
santa
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn grammars_with_where_clauses() {
|
||||||
|
let grammars = vec![
|
||||||
|
r###"
|
||||||
|
grammar<T> where T: StaticMethods;
|
||||||
|
"###,
|
||||||
|
|
||||||
|
r###"
|
||||||
|
grammar<T>(methods: &mut T) where T: MutMethods;
|
||||||
|
"###,
|
||||||
|
|
||||||
|
r###"
|
||||||
|
grammar<'input, T>(methods: &mut T) where T: 'input + Debug + MutMethods;
|
||||||
|
"###,
|
||||||
|
|
||||||
|
r###"
|
||||||
|
grammar<F>(methods: &mut F) where F: for<'a> FnMut(&'a usize) -> bool;
|
||||||
|
"###,
|
||||||
|
|
||||||
|
r###"
|
||||||
|
grammar<F>(logger: &mut F) where F: for<'a> FnMut(&'a str);
|
||||||
|
"###,
|
||||||
|
];
|
||||||
|
|
||||||
|
for g in grammars {
|
||||||
|
assert!(parser::parse_grammar(g).is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -42,11 +42,12 @@ pub enum Tok<'input> {
|
|||||||
Mut,
|
Mut,
|
||||||
Pub,
|
Pub,
|
||||||
Type,
|
Type,
|
||||||
|
Where,
|
||||||
|
For,
|
||||||
|
|
||||||
// Special keywords: these are accompanied by a series of
|
// Special keywords: these are accompanied by a series of
|
||||||
// uninterpreted strings representing imports and stuff.
|
// uninterpreted strings representing imports and stuff.
|
||||||
Use(&'input str),
|
Use(&'input str),
|
||||||
Where(Vec<&'input str>),
|
|
||||||
|
|
||||||
// Identifiers of various kinds:
|
// Identifiers of various kinds:
|
||||||
Escape(&'input str),
|
Escape(&'input str),
|
||||||
@ -79,6 +80,7 @@ pub enum Tok<'input> {
|
|||||||
LessThan,
|
LessThan,
|
||||||
Lookahead, // @L
|
Lookahead, // @L
|
||||||
Lookbehind, // @R
|
Lookbehind, // @R
|
||||||
|
MinusGreaterThan,
|
||||||
Plus,
|
Plus,
|
||||||
Question,
|
Question,
|
||||||
RightBrace,
|
RightBrace,
|
||||||
@ -111,10 +113,12 @@ const KEYWORDS: &'static [(&'static str, Tok<'static>)] = &[
|
|||||||
("mut", Mut),
|
("mut", Mut),
|
||||||
("pub", Pub),
|
("pub", Pub),
|
||||||
("type", Type),
|
("type", Type),
|
||||||
|
("where", Where),
|
||||||
|
("for", For),
|
||||||
];
|
];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper for backtracking.
|
* Helper for backtracking.
|
||||||
*/
|
*/
|
||||||
macro_rules! first {
|
macro_rules! first {
|
||||||
($this:expr, $action:expr, $fallback:expr) => {
|
($this:expr, $action:expr, $fallback:expr) => {
|
||||||
@ -266,7 +270,7 @@ impl<'input> Tokenizer<'input> {
|
|||||||
}
|
}
|
||||||
Some((idx0, '#')) => {
|
Some((idx0, '#')) => {
|
||||||
self.bump();
|
self.bump();
|
||||||
first!(self,
|
first!(self,
|
||||||
{ self.shebang_attribute(idx0) },
|
{ self.shebang_attribute(idx0) },
|
||||||
{ Ok((idx0, Hash, idx0+1)) })
|
{ Ok((idx0, Hash, idx0+1)) })
|
||||||
}
|
}
|
||||||
@ -367,6 +371,17 @@ impl<'input> Tokenizer<'input> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some((idx0, '-')) => {
|
||||||
|
match self.bump() {
|
||||||
|
Some((idx1, '>')) => {
|
||||||
|
self.bump();
|
||||||
|
Some(Ok((idx0, MinusGreaterThan, idx1 + 1)))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Some(error(UnrecognizedToken, idx0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some((idx0, c)) if is_identifier_start(c) => {
|
Some((idx0, c)) if is_identifier_start(c) => {
|
||||||
if c == 'r' {
|
if c == 'r' {
|
||||||
// watch out for r"..." or r#"..."# strings
|
// watch out for r"..." or r#"..."# strings
|
||||||
@ -676,31 +691,6 @@ impl<'input> Tokenizer<'input> {
|
|||||||
return Ok((start, Tok::Use(code), code_end));
|
return Ok((start, Tok::Use(code), code_end));
|
||||||
}
|
}
|
||||||
|
|
||||||
if word == "where" {
|
|
||||||
let mut wcs = vec![];
|
|
||||||
let mut wc_start = end;
|
|
||||||
let mut wc_end;
|
|
||||||
loop {
|
|
||||||
// Note: do not include `{` as a delimeter here, as
|
|
||||||
// that is not legal in the trait/where-clause syntax,
|
|
||||||
// and in fact signals start of the fn body. But do
|
|
||||||
// include `<`.
|
|
||||||
wc_end = try!(self.code(wc_start, "([<", ">])"));
|
|
||||||
let wc = &self.text[wc_start..wc_end];
|
|
||||||
wcs.push(wc);
|
|
||||||
|
|
||||||
// if this ended in a comma, maybe expect another where-clause
|
|
||||||
if let Some((_, ',')) = self.lookahead {
|
|
||||||
self.bump();
|
|
||||||
wc_start = wc_end + 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok((start, Tok::Where(wcs), wc_end));
|
|
||||||
}
|
|
||||||
|
|
||||||
let tok =
|
let tok =
|
||||||
// search for a keyword first; if none are found, this is
|
// search for a keyword first; if none are found, this is
|
||||||
// either a MacroId or an Id, depending on whether there
|
// either a MacroId or an Id, depending on whether there
|
||||||
@ -799,4 +789,3 @@ fn is_identifier_start(c: char) -> bool {
|
|||||||
fn is_identifier_continue(c: char) -> bool {
|
fn is_identifier_continue(c: char) -> bool {
|
||||||
UnicodeXID::is_xid_continue(c) || c == '_'
|
UnicodeXID::is_xid_continue(c) || c == '_'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,11 +241,60 @@ fn equalsgreaterthancode_nested_function_with_lifetimes() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn where_with_lifetimes() {
|
fn where_with_lifetimes() {
|
||||||
test(r#"where <'a,bar<'b,'c>>,baz;"#, vec![
|
test(r#"where <'a,bar<'b,'c>>,baz;"#, vec![
|
||||||
(r#"~~~~~~~~~~~~~~~~~~~~~~~~~ "#, Where(vec![" <'a,bar<'b,'c>>", "baz"])),
|
(r#"~~~~~ "#, Where),
|
||||||
|
(r#" ~ "#, LessThan),
|
||||||
|
(r#" ~~ "#, Lifetime("'a")),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~~ "#, MacroId("bar")),
|
||||||
|
(r#" ~ "#, LessThan),
|
||||||
|
(r#" ~~ "#, Lifetime("'b")),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~ "#, Lifetime("'c")),
|
||||||
|
(r#" ~ "#, GreaterThan),
|
||||||
|
(r#" ~ "#, GreaterThan),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~~ "#, Id("baz")),
|
||||||
(r#" ~"#, Semi),
|
(r#" ~"#, Semi),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn forall() {
|
||||||
|
test(r#"for<'a, 'b, 'c> FnMut"#, vec![
|
||||||
|
(r#"~~~ "#, For),
|
||||||
|
(r#" ~ "#, LessThan),
|
||||||
|
(r#" ~~ "#, Lifetime("'a")),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~ "#, Lifetime("'b")),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~ "#, Lifetime("'c")),
|
||||||
|
(r#" ~ "#, GreaterThan),
|
||||||
|
(r#" ~~~~~"#, Id("FnMut")),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn where_forall_fnmut_with_return_type() {
|
||||||
|
test(r#"where F: for<'a> FnMut(&'a T) -> U;"#, vec![
|
||||||
|
(r#"~~~~~ "#, Where),
|
||||||
|
(r#" ~ "#, Id("F")),
|
||||||
|
(r#" ~ "#, Colon),
|
||||||
|
(r#" ~~~ "#, For),
|
||||||
|
(r#" ~ "#, LessThan),
|
||||||
|
(r#" ~~ "#, Lifetime("'a")),
|
||||||
|
(r#" ~ "#, GreaterThan),
|
||||||
|
(r#" ~~~~~ "#, Id("FnMut")),
|
||||||
|
(r#" ~ "#, LeftParen),
|
||||||
|
(r#" ~ "#, Ampersand),
|
||||||
|
(r#" ~~ "#, Lifetime("'a")),
|
||||||
|
(r#" ~ "#, Id("T")),
|
||||||
|
(r#" ~ "#, RightParen),
|
||||||
|
(r#" ~~ "#, MinusGreaterThan),
|
||||||
|
(r#" ~ "#, Id("U")),
|
||||||
|
(r#" ~"#, Semi),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn equalsgreaterthancode_error_unbalanced() {
|
fn equalsgreaterthancode_error_unbalanced() {
|
||||||
test_err(r#"=> (,"#,
|
test_err(r#"=> (,"#,
|
||||||
@ -371,7 +420,14 @@ fn use2() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn where1() {
|
fn where1() {
|
||||||
test(r#"where <foo,bar>,baz;"#, vec![
|
test(r#"where <foo,bar>,baz;"#, vec![
|
||||||
(r#"~~~~~~~~~~~~~~~~~~~ "#, Where(vec![" <foo,bar>", "baz"])),
|
(r#"~~~~~ "#, Where),
|
||||||
|
(r#" ~ "#, LessThan),
|
||||||
|
(r#" ~~~ "#, Id("foo")),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~~ "#, Id("bar")),
|
||||||
|
(r#" ~ "#, GreaterThan),
|
||||||
|
(r#" ~ "#, Comma),
|
||||||
|
(r#" ~~~ "#, Id("baz")),
|
||||||
(r#" ~"#, Semi),
|
(r#" ~"#, Semi),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@ -453,5 +509,3 @@ fn char_literals() {
|
|||||||
(r#" ~~"#, Lifetime("'c")),
|
(r#" ~~"#, Lifetime("'c")),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user