-
Notifications
You must be signed in to change notification settings - Fork 69
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cucumber expression AST and parser (#124) #153
Closed
Closed
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
7228d03
Bootstrap basic parser
ilslv eea0c30
Start implementing custom errors
ilslv d681997
WIP [skip ci]
ilslv 180b95d
More custom errors [skip ci]
ilslv bd7c640
More custom errors [skip ci]
ilslv 9bae945
More custom errors [skip ci]
ilslv 959cdd5
Add docs and spec for escaped0 [skip ci]
ilslv a774ffd
Separate parser combinators and document them
ilslv ef7b8cf
Document and add spec for parameter()
ilslv 3403951
Document and add spec for parameter() [skip ci]
ilslv f7a2851
Document and add spec for parameter() [skip ci]
ilslv fbeb537
Document and add spec for optional() [skip ci]
ilslv f466e18
Document and add spec for optional() [skip ci]
ilslv 5775823
Document and add spec for alternative() [skip ci]
ilslv b2dfabf
Document and add spec for alternation() and fix alternative() [skip ci]
ilslv 3470bf9
Document and add spec for expression() [skip ci]
ilslv a883b52
Document and add spec for expression() [skip ci]
ilslv e633df6
Tidy up [skip ci]
ilslv c2042c1
Add README [skip ci]
ilslv 290144b
Add CHANGELOG [skip ci]
ilslv 85ae536
Corrections
ilslv 2b11992
Merge branch 'main' into 124-cucumber-expression
ilslv 12b35ac
Corrections
ilslv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// Copyright (c) 2020 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// Copyright (c) 2020-2021 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// Copyright (c) 2020 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// Copyright (c) 2020-2021 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
// Copyright (c) 2020 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// Copyright (c) 2020-2021 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
`cucumber-expression` changelog | ||
============================ | ||
|
||
All user visible changes to `cucumber-expression` crate will be documented in this file. This project uses [Semantic Versioning 2.0.0]. | ||
|
||
|
||
|
||
|
||
## [0.11.0] · 2021-??-?? | ||
[0.11.0]: /../../tree/v0.11.0/expression | ||
|
||
[Milestone](/../../milestone/3) | ||
|
||
### Added | ||
|
||
- Cucumber expression AST and parser. ([#153]) | ||
|
||
[#153]: /../../pull/153 | ||
|
||
|
||
|
||
|
||
[Semantic Versioning 2.0.0]: https://semver.org |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
[package] | ||
name = "cucumber-expression" | ||
version = "0.11.0-dev" # should be the same as main crate version | ||
edition = "2021" | ||
rust-version = "1.56" | ||
description = "Cucumber expression parser." | ||
license = "MIT OR Apache-2.0" | ||
authors = [ | ||
"Ilya Solovyiov <[email protected]>", | ||
"Kai Ren <[email protected]>", | ||
] | ||
documentation = "https://docs.rs/cucumber-expression" | ||
homepage = "https://github.com/cucumber-rs/cucumber/tree/main/expression" | ||
repository = "https://github.com/cucumber-rs/cucumber" | ||
readme = "README.md" | ||
categories = ["development-tools::testing", "parser-implementations"] | ||
keywords = ["cucumber", "codegen", "macros"] | ||
exclude = ["/tests/"] | ||
|
||
[dependencies] | ||
derive_more = { version = "0.99.16", features = ["as_ref", "deref", "deref_mut", "display", "error"], default_features = false } | ||
nom = "7.0" | ||
nom_locate = "4.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
`cucumber-expression` crate | ||
======================== | ||
|
||
[![Documentation](https://docs.rs/cucumber-expression/badge.svg)](https://docs.rs/cucumber-expression) | ||
[![CI](https://github.com/cucumber-rs/cucumber/workflows/CI/badge.svg?branch=master "CI")](https://github.com/cucumber-rs/cucumber/actions?query=workflow%3ACI+branch%3Amaster) | ||
[![Rust 1.56+](https://img.shields.io/badge/rustc-1.56+-lightgray.svg "Rust 1.56+")](https://blog.rust-lang.org/2021/10/21/Rust-1.56.0.html) | ||
[![Unsafe Forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance) | ||
|
||
- [Changelog](https://github.com/cucumber-rs/cucumber/blob/main/expression/CHANGELOG.md) | ||
|
||
Parser for [`cucumber expression`]. | ||
|
||
|
||
|
||
|
||
## License | ||
|
||
This project is licensed under either of | ||
|
||
* Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/cucumber-rs/cucumber/blob/main/LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>) | ||
* MIT license ([LICENSE-MIT](https://github.com/cucumber-rs/cucumber/blob/main/LICENSE-MIT) or <http://opensource.org/licenses/MIT>) | ||
|
||
at your option. | ||
|
||
|
||
|
||
|
||
[`cucumber`]: https://docs.rs/cucumber | ||
[`cucumber expression`]: https://github.com/cucumber/cucumber-expressions#readme |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
// Copyright (c) 2021 Brendan Molloy <[email protected]>, | ||
// Ilya Solovyiov <[email protected]>, | ||
// Kai Ren <[email protected]> | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
//! [Cucumber expression][1] [AST][2] definitions. | ||
//! | ||
//! [1]: https://github.com/cucumber/cucumber-expressions#readme | ||
//! [2]: https://en.wikipedia.org/wiki/Abstract_syntax_tree | ||
|
||
use derive_more::{AsRef, Deref, DerefMut}; | ||
use nom::{error::ErrorKind, Err, InputLength}; | ||
use nom_locate::LocatedSpan; | ||
|
||
use crate::{parse, Error}; | ||
|
||
/// A set of meta information about the location of a token. | ||
pub type Spanned<'s> = LocatedSpan<&'s str>; | ||
|
||
/// [Cucumber expression][1]. | ||
/// | ||
/// See [`parse::expression()`] for detailed syntax and examples. | ||
/// | ||
/// [1]: https://github.com/cucumber/cucumber-expressions#readme | ||
#[derive(AsRef, Clone, Debug, Deref, DerefMut, Eq, PartialEq)] | ||
pub struct Expression<Input>(pub Vec<SingleExpression<Input>>); | ||
|
||
impl<'s> TryFrom<&'s str> for Expression<Spanned<'s>> { | ||
type Error = Error<Spanned<'s>>; | ||
|
||
fn try_from(value: &'s str) -> Result<Self, Self::Error> { | ||
parse::expression(Spanned::new(value)) | ||
.map_err(|e| match e { | ||
Err::Error(e) | Err::Failure(e) => e, | ||
Err::Incomplete(n) => Error::Needed(n), | ||
}) | ||
.and_then(|(rest, parsed)| { | ||
if rest.is_empty() { | ||
Ok(parsed) | ||
} else { | ||
Err(Error::Other(rest, ErrorKind::Verify)) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
impl<'s> Expression<Spanned<'s>> { | ||
/// Tries to `input` into [`Expression`]. | ||
/// | ||
/// # Errors | ||
/// | ||
/// See [`Error`] for more details. | ||
pub fn parse<I: AsRef<str>>( | ||
input: &'s I, | ||
) -> Result<Self, Error<Spanned<'s>>> { | ||
Self::try_from(input.as_ref()) | ||
} | ||
} | ||
|
||
/// Building block of an [`Expression`]. | ||
/// | ||
/// See [`parse::single_expression()`] for detailed syntax and examples. | ||
#[derive(Clone, Debug, Eq, PartialEq)] | ||
pub enum SingleExpression<Input> { | ||
/// [`Alternation`]. | ||
Alternation(Alternation<Input>), | ||
|
||
/// [`Optional`]. | ||
Optional(Optional<Input>), | ||
|
||
/// [`Parameter`]. | ||
Parameter(Parameter<Input>), | ||
|
||
/// Text without whitespaces. | ||
Text(Input), | ||
|
||
/// Whitespaces are treated as special case to avoid lookaheads and | ||
/// lookbehinds described in the [`architecture`][1]. This allows parser to | ||
/// have `O(n)` complexity. | ||
/// | ||
/// [1]: https://bit.ly/3k8DfcW | ||
Whitespace, | ||
} | ||
|
||
/// Allows to match one of [`SingleAlternation`]s. | ||
/// | ||
/// See [`parse::alternation()`] for detailed syntax and examples. | ||
#[derive(AsRef, Clone, Debug, Deref, DerefMut, Eq, PartialEq)] | ||
pub struct Alternation<Input>(pub Vec<SingleAlternation<Input>>); | ||
|
||
/// Building block an [`Alternation`]. | ||
pub type SingleAlternation<Input> = Vec<Alternative<Input>>; | ||
|
||
impl<Input: InputLength> Alternation<Input> { | ||
/// Returns length of capture from `Input`. | ||
pub(crate) fn span_len(&self) -> usize { | ||
self.0 | ||
.iter() | ||
.flatten() | ||
.map(|alt| match alt { | ||
Alternative::Text(t) => t.input_len(), | ||
Alternative::Optional(opt) => opt.input_len() + 2, | ||
}) | ||
.sum::<usize>() | ||
+ self.len() | ||
- 1 | ||
} | ||
|
||
/// Indicates whether one of [`SingleAlternation`]s consists only from | ||
/// [`Optional`]s. | ||
pub(crate) fn contains_only_optional(&self) -> bool { | ||
for single_alt in &**self { | ||
if single_alt | ||
.iter() | ||
.all(|alt| matches!(alt, Alternative::Optional(_))) | ||
{ | ||
return true; | ||
} | ||
} | ||
false | ||
} | ||
} | ||
|
||
/// Building block of a [`SingleAlternation`]. | ||
/// | ||
/// See [`parse::alternative()`] for detailed syntax and examples. | ||
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
pub enum Alternative<Input> { | ||
/// [`Optional`]. | ||
Optional(Optional<Input>), | ||
|
||
/// Text. | ||
Text(Input), | ||
} | ||
|
||
/// Allows to match optional `Input`. | ||
/// | ||
/// See [`parse::optional()`] for detailed syntax and examples. | ||
#[derive(AsRef, Clone, Copy, Debug, Deref, DerefMut, Eq, PartialEq)] | ||
pub struct Optional<Input>(pub Input); | ||
|
||
/// Allows to match some special `Input` descried by a [`Parameter`] name. | ||
/// | ||
/// See [`parse::parameter()`] for detailed syntax and examples. | ||
#[derive(AsRef, Clone, Copy, Debug, Deref, DerefMut, Eq, PartialEq)] | ||
pub struct Parameter<Input>(pub Input); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this crate can be versioned separately.