pest_vm-2.7.14/Cargo.toml0000644000000020100000000000100105730ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.61" name = "pest_vm" version = "2.7.14" authors = ["Dragoș Tiselice "] description = "pest grammar virtual machine" homepage = "https://pest.rs/" documentation = "https://docs.rs/pest" readme = "_README.md" keywords = [ "pest", "vm", ] categories = ["parsing"] license = "MIT OR Apache-2.0" repository = "https://github.com/pest-parser/pest" [dependencies.pest] version = "2.7.14" [dependencies.pest_meta] version = "2.7.14" [features] grammar-extras = ["pest_meta/grammar-extras"] pest_vm-2.7.14/Cargo.toml.orig000064400000000000000000000011121046102023000142560ustar 00000000000000[package] name = "pest_vm" description = "pest grammar virtual machine" version = "2.7.14" edition = "2021" authors = ["Dragoș Tiselice "] homepage = "https://pest.rs/" repository = "https://github.com/pest-parser/pest" documentation = "https://docs.rs/pest" keywords = ["pest", "vm"] categories = ["parsing"] license = "MIT OR Apache-2.0" readme = "_README.md" rust-version = "1.61" [dependencies] pest = { path = "../pest", version = "2.7.14" } pest_meta = { path = "../meta", version = "2.7.14" } [features] grammar-extras = ["pest_meta/grammar-extras"] pest_vm-2.7.14/_README.md000064400000000000000000000175261046102023000130250ustar 00000000000000

# pest. The Elegant Parser [![Join the chat at https://gitter.im/pest-parser/pest](https://badges.gitter.im/dragostis/pest.svg)](https://gitter.im/pest-parser/pest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Book](https://img.shields.io/badge/book-WIP-4d76ae.svg)](https://pest.rs/book) [![Docs](https://docs.rs/pest/badge.svg)](https://docs.rs/pest) [![pest Continuous Integration](https://github.com/pest-parser/pest/actions/workflows/ci.yml/badge.svg)](https://github.com/pest-parser/pest/actions/workflows/ci.yml) [![codecov](https://codecov.io/gh/pest-parser/pest/branch/master/graph/badge.svg)](https://codecov.io/gh/pest-parser/pest) Rustc Version 1.61.0+ [![Crates.io](https://img.shields.io/crates/d/pest.svg)](https://crates.io/crates/pest) [![Crates.io](https://img.shields.io/crates/v/pest.svg)](https://crates.io/crates/pest) pest is a general purpose parser written in Rust with a focus on accessibility, correctness, and performance. It uses parsing expression grammars (or [PEG]) as input, which are similar in spirit to regular expressions, but which offer the enhanced expressivity needed to parse complex languages. [PEG]: https://en.wikipedia.org/wiki/Parsing_expression_grammar ## Getting started The recommended way to start parsing with pest is to read the official [book]. Other helpful resources: * API reference on [docs.rs] * play with grammars and share them on our [fiddle] * find previous common questions answered or ask questions on [GitHub Discussions] * leave feedback, ask questions, or greet us on [Gitter] or [Discord] [book]: https://pest.rs/book [docs.rs]: https://docs.rs/pest [fiddle]: https://pest.rs/#editor [Gitter]: https://gitter.im/pest-parser/pest [Discord]: https://discord.gg/XEGACtWpT2 [GitHub Discussions]: https://github.com/pest-parser/pest/discussions ## Example The following is an example of a grammar for a list of alphanumeric identifiers where all identifiers don't start with a digit: ```rust alpha = { 'a'..'z' | 'A'..'Z' } digit = { '0'..'9' } ident = { !digit ~ (alpha | digit)+ } ident_list = _{ ident ~ (" " ~ ident)* } // ^ // ident_list rule is silent which means it produces no tokens ``` Grammars are saved in separate .pest files which are never mixed with procedural code. This results in an always up-to-date formalization of a language that is easy to read and maintain. ## Meaningful error reporting Based on the grammar definition, the parser also includes automatic error reporting. For the example above, the input `"123"` will result in: ``` thread 'main' panicked at ' --> 1:1 | 1 | 123 | ^--- | = unexpected digit', src/main.rs:12 ``` while `"ab *"` will result in: ``` thread 'main' panicked at ' --> 1:1 | 1 | ab * | ^--- | = expected ident', src/main.rs:12 ``` These error messages can be obtained from their default `Display` implementation, e.g. `panic!("{}", parser_result.unwrap_err())` or `println!("{}", e)`. ## Pairs API The grammar can be used to derive a `Parser` implementation automatically. Parsing returns an iterator of nested token pairs: ```rust use pest_derive::Parser; use pest::Parser; #[derive(Parser)] #[grammar = "ident.pest"] struct IdentParser; fn main() { let pairs = IdentParser::parse(Rule::ident_list, "a1 b2").unwrap_or_else(|e| panic!("{}", e)); // Because ident_list is silent, the iterator will contain idents for pair in pairs { // A pair is a combination of the rule which matched and a span of input println!("Rule: {:?}", pair.as_rule()); println!("Span: {:?}", pair.as_span()); println!("Text: {}", pair.as_str()); // A pair can be converted to an iterator of the tokens which make it up: for inner_pair in pair.into_inner() { match inner_pair.as_rule() { Rule::alpha => println!("Letter: {}", inner_pair.as_str()), Rule::digit => println!("Digit: {}", inner_pair.as_str()), _ => unreachable!() }; } } } ``` This produces the following output: ``` Rule: ident Span: Span { start: 0, end: 2 } Text: a1 Letter: a Digit: 1 Rule: ident Span: Span { start: 3, end: 5 } Text: b2 Letter: b Digit: 2 ``` ### Defining multiple parsers in a single file The current automatic `Parser` derivation will produce the `Rule` enum which would have name conflicts if one tried to define multiple such structs that automatically derive `Parser`. One possible way around it is to put each parser struct in a separate namespace: ```rust mod a { #[derive(Parser)] #[grammar = "a.pest"] pub struct ParserA; } mod b { #[derive(Parser)] #[grammar = "b.pest"] pub struct ParserB; } ``` ## Other features * Precedence climbing * Input handling * Custom errors * Runs on stable Rust ## Projects using pest You can find more projects and ecosystem tools in the [awesome-pest](https://github.com/pest-parser/awesome-pest) repo. * [pest_meta](https://github.com/pest-parser/pest/blob/master/meta/src/grammar.pest) (bootstrapped) * [AshPaper](https://github.com/shnewto/ashpaper) * [brain](https://github.com/brain-lang/brain) * [cicada](https://github.com/mitnk/cicada) * [comrak](https://github.com/kivikakk/comrak) * [elastic-rs](https://github.com/cch123/elastic-rs) * [graphql-parser](https://github.com/Keats/graphql-parser) * [handlebars-rust](https://github.com/sunng87/handlebars-rust) * [hexdino](https://github.com/Luz/hexdino) * [Huia](https://gitlab.com/jimsy/huia/) * [insta](https://github.com/mitsuhiko/insta) * [jql](https://github.com/yamafaktory/jql) * [json5-rs](https://github.com/callum-oakley/json5-rs) * [mt940](https://github.com/svenstaro/mt940-rs) * [Myoxine](https://github.com/d3bate/myoxine) * [py_literal](https://github.com/jturner314/py_literal) * [rouler](https://github.com/jarcane/rouler) * [RuSh](https://github.com/lwandrebeck/RuSh) * [rs_pbrt](https://github.com/wahn/rs_pbrt) * [stache](https://github.com/dgraham/stache) * [tera](https://github.com/Keats/tera) * [ui_gen](https://github.com/emoon/ui_gen) * [ukhasnet-parser](https://github.com/adamgreig/ukhasnet-parser) * [ZoKrates](https://github.com/ZoKrates/ZoKrates) * [Vector](https://github.com/timberio/vector) * [AutoCorrect](https://github.com/huacnlee/autocorrect) * [yaml-peg](https://github.com/aofdev/yaml-peg) * [qubit](https://github.com/abhimanyu003/qubit) * [caith](https://github.com/Geobert/caith) (a dice roller crate) * [Melody](https://github.com/yoav-lavi/melody) * [json5-nodes](https://github.com/jlyonsmith/json5-nodes) * [prisma](https://github.com/prisma/prisma) ## Minimum Supported Rust Version (MSRV) This library should always compile with default features on **Rust 1.61.0**. ## no_std support The `pest` and `pest_derive` crates can be built without the Rust standard library and target embedded environments. To do so, you need to disable their default features. In your `Cargo.toml`, you can specify it as follows: ```toml [dependencies] # ... pest = { version = "2", default-features = false } pest_derive = { version = "2", default-features = false } ``` If you want to build these crates in the pest repository's workspace, you can pass the `--no-default-features` flag to `cargo` and specify these crates using the `--package` (`-p`) flag. For example: ```bash $ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest $ cargo bootstrap $ cargo build --target thumbv7em-none-eabihf --no-default-features -p pest_derive ``` ## Special thanks A special round of applause goes to prof. Marius Minea for his guidance and all pest contributors, some of which being none other than my friends. pest_vm-2.7.14/src/lib.rs000064400000000000000000000306261046102023000133060ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. //! # pest vm //! //! This crate run ASTs on-the-fly and is used by the fiddle and debugger. #![doc( html_logo_url = "https://raw.githubusercontent.com/pest-parser/pest/master/pest-logo.svg", html_favicon_url = "https://raw.githubusercontent.com/pest-parser/pest/master/pest-logo.svg" )] #![warn(missing_docs, rust_2018_idioms, unused_qualifications)] use pest::error::Error; use pest::iterators::Pairs; use pest::{unicode, Position}; use pest::{Atomicity, MatchDir, ParseResult, ParserState}; use pest_meta::ast::RuleType; use pest_meta::optimizer::{OptimizedExpr, OptimizedRule}; use std::collections::HashMap; use std::panic::{RefUnwindSafe, UnwindSafe}; mod macros; /// A callback function that is called when a rule is matched. /// The first argument is the name of the rule and the second is the span of the rule. /// The function should return `true` if parsing should be terminated /// (if the new parsing session was started) or `false` otherwise. type ListenerFn = Box) -> bool + Sync + Send + RefUnwindSafe + UnwindSafe>; /// A virtual machine-like construct that runs an AST on-the-fly pub struct Vm { rules: HashMap, listener: Option, } impl Vm { /// Creates a new `Vm` from optimized rules pub fn new(rules: Vec) -> Vm { let rules = rules.into_iter().map(|r| (r.name.clone(), r)).collect(); Vm { rules, listener: None, } } /// Creates a new `Vm` from optimized rules /// and a listener function that is called when a rule is matched. /// (used by the `pest_debugger` crate) pub fn new_with_listener(rules: Vec, listener: ListenerFn) -> Vm { let rules = rules.into_iter().map(|r| (r.name.clone(), r)).collect(); Vm { rules, listener: Some(listener), } } /// Runs a parser rule on an input #[allow(clippy::perf)] pub fn parse<'a>( &'a self, rule: &'a str, input: &'a str, ) -> Result, Error<&str>> { pest::state(input, |state| self.parse_rule(rule, state)) } #[allow(clippy::suspicious)] fn parse_rule<'a>( &'a self, rule: &'a str, state: Box>, ) -> ParseResult>> { if let Some(ref listener) = self.listener { if listener(rule.to_owned(), state.position()) { return Err(ParserState::new(state.position().line_of())); } } match rule { "ANY" => return state.skip(1), "EOI" => return state.rule("EOI", |state| state.end_of_input()), "SOI" => return state.start_of_input(), "PEEK" => return state.stack_peek(), "PEEK_ALL" => return state.stack_match_peek(), "POP" => return state.stack_pop(), "POP_ALL" => return state.stack_match_pop(), "DROP" => return state.stack_drop(), "ASCII_DIGIT" => return state.match_range('0'..'9'), "ASCII_NONZERO_DIGIT" => return state.match_range('1'..'9'), "ASCII_BIN_DIGIT" => return state.match_range('0'..'1'), "ASCII_OCT_DIGIT" => return state.match_range('0'..'7'), "ASCII_HEX_DIGIT" => { return state .match_range('0'..'9') .or_else(|state| state.match_range('a'..'f')) .or_else(|state| state.match_range('A'..'F')); } "ASCII_ALPHA_LOWER" => return state.match_range('a'..'z'), "ASCII_ALPHA_UPPER" => return state.match_range('A'..'Z'), "ASCII_ALPHA" => { return state .match_range('a'..'z') .or_else(|state| state.match_range('A'..'Z')); } "ASCII_ALPHANUMERIC" => { return state .match_range('a'..'z') .or_else(|state| state.match_range('A'..'Z')) .or_else(|state| state.match_range('0'..'9')); } "ASCII" => return state.match_range('\x00'..'\x7f'), "NEWLINE" => { return state .match_string("\n") .or_else(|state| state.match_string("\r\n")) .or_else(|state| state.match_string("\r")); } _ => (), }; if let Some(rule) = self.rules.get(rule) { if rule.name == "WHITESPACE" || rule.name == "COMMENT" { match rule.ty { RuleType::Normal => state.rule(&rule.name, |state| { state.atomic(Atomicity::Atomic, |state| { self.parse_expr(&rule.expr, state) }) }), RuleType::Silent => state.atomic(Atomicity::Atomic, |state| { self.parse_expr(&rule.expr, state) }), RuleType::Atomic => state.rule(&rule.name, |state| { state.atomic(Atomicity::Atomic, |state| { self.parse_expr(&rule.expr, state) }) }), RuleType::CompoundAtomic => state.atomic(Atomicity::CompoundAtomic, |state| { state.rule(&rule.name, |state| self.parse_expr(&rule.expr, state)) }), RuleType::NonAtomic => state.atomic(Atomicity::Atomic, |state| { state.rule(&rule.name, |state| self.parse_expr(&rule.expr, state)) }), } } else { match rule.ty { RuleType::Normal => { state.rule(&rule.name, move |state| self.parse_expr(&rule.expr, state)) } RuleType::Silent => self.parse_expr(&rule.expr, state), RuleType::Atomic => state.rule(&rule.name, move |state| { state.atomic(Atomicity::Atomic, move |state| { self.parse_expr(&rule.expr, state) }) }), RuleType::CompoundAtomic => { state.atomic(Atomicity::CompoundAtomic, move |state| { state.rule(&rule.name, |state| self.parse_expr(&rule.expr, state)) }) } RuleType::NonAtomic => state.atomic(Atomicity::NonAtomic, move |state| { state.rule(&rule.name, |state| self.parse_expr(&rule.expr, state)) }), } } } else { if let Some(property) = unicode::by_name(rule) { // std::boxed::Box bool> is not FnOnce(char)->bool return state.match_char_by(property); } panic!("undefined rule {}", rule); } } fn parse_expr<'a>( &'a self, expr: &'a OptimizedExpr, state: Box>, ) -> ParseResult>> { match *expr { OptimizedExpr::Str(ref string) => state.match_string(string), OptimizedExpr::Insens(ref string) => state.match_insensitive(string), OptimizedExpr::Range(ref start, ref end) => { let start = start.chars().next().expect("empty char literal"); let end = end.chars().next().expect("empty char literal"); state.match_range(start..end) } OptimizedExpr::Ident(ref name) => self.parse_rule(name, state), OptimizedExpr::PeekSlice(start, end) => { state.stack_match_peek_slice(start, end, MatchDir::BottomToTop) } OptimizedExpr::PosPred(ref expr) => { state.lookahead(true, |state| self.parse_expr(expr, state)) } OptimizedExpr::NegPred(ref expr) => { state.lookahead(false, |state| self.parse_expr(expr, state)) } OptimizedExpr::Seq(ref lhs, ref rhs) => state.sequence(|state| { self.parse_expr(lhs, state) .and_then(|state| self.skip(state)) .and_then(|state| self.parse_expr(rhs, state)) }), OptimizedExpr::Choice(ref lhs, ref rhs) => self .parse_expr(lhs, state) .or_else(|state| self.parse_expr(rhs, state)), OptimizedExpr::Opt(ref expr) => state.optional(|state| self.parse_expr(expr, state)), OptimizedExpr::Rep(ref expr) => state.sequence(|state| { state.optional(|state| { self.parse_expr(expr, state).and_then(|state| { state.repeat(|state| { state.sequence(|state| { self.skip(state) .and_then(|state| self.parse_expr(expr, state)) }) }) }) }) }), #[cfg(feature = "grammar-extras")] OptimizedExpr::RepOnce(ref expr) => state.sequence(|state| { self.parse_expr(expr, state).and_then(|state| { state.repeat(|state| { state.sequence(|state| { self.skip(state) .and_then(|state| self.parse_expr(expr, state)) }) }) }) }), OptimizedExpr::Push(ref expr) => state.stack_push(|state| self.parse_expr(expr, state)), OptimizedExpr::Skip(ref strings) => state.skip_until( &strings .iter() .map(|state| state.as_str()) .collect::>(), ), #[cfg(feature = "grammar-extras")] OptimizedExpr::NodeTag(ref expr, ref tag) => self .parse_expr(expr, state) .and_then(|state| state.tag_node(tag)), OptimizedExpr::RestoreOnErr(ref expr) => { state.restore_on_err(|state| self.parse_expr(expr, state)) } } } fn skip<'a>( &'a self, state: Box>, ) -> ParseResult>> { match ( self.rules.contains_key("WHITESPACE"), self.rules.contains_key("COMMENT"), ) { (false, false) => Ok(state), (true, false) => { if state.atomicity() == Atomicity::NonAtomic { state.repeat(|state| self.parse_rule("WHITESPACE", state)) } else { Ok(state) } } (false, true) => { if state.atomicity() == Atomicity::NonAtomic { state.repeat(|state| self.parse_rule("COMMENT", state)) } else { Ok(state) } } (true, true) => { if state.atomicity() == Atomicity::NonAtomic { state.sequence(|state| { state .repeat(|state| self.parse_rule("WHITESPACE", state)) .and_then(|state| { state.repeat(|state| { state.sequence(|state| { self.parse_rule("COMMENT", state).and_then(|state| { state.repeat(|state| { self.parse_rule("WHITESPACE", state) }) }) }) }) }) }) } else { Ok(state) } } } } } pest_vm-2.7.14/src/macros.rs000064400000000000000000000213151046102023000140170ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. #[doc(hidden)] #[macro_export] macro_rules! consumes_to { ( $tokens:expr, [] ) => (); ( $tokens:expr, [ $name:ident ( $start:expr, $end:expr ) ] ) => { let expected = format!("expected Start {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $start); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::Start { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $start, "{} but found Start {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; let expected = format!("expected End {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $end); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::End { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $end, "{} but found End {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; }; ( $tokens:expr, [ $name:ident ( $start:expr, $end:expr ), $( $names:ident $calls:tt ),* $(,)* ] ) => { let expected = format!("expected Start {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $start); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::Start { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $start, "{} but found Start {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; let expected = format!("expected End {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $end); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::End { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $end, "{} but found End {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; consumes_to!($tokens, [ $( $names $calls ),* ]); }; ( $tokens:expr, [ $name:ident ( $start:expr, $end:expr, [ $( $names:ident $calls:tt ),* $(,)* ] ) ] ) => { let expected = format!("expected Start {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $start); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::Start { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $start, "{} but found Start {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; consumes_to!($tokens, [ $( $names $calls ),* ]); let expected = format!("expected End {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $end); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::End { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $end, "{} but found End {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; }; ( $tokens:expr, [ $name:ident ( $start:expr, $end:expr, [ $( $nested_names:ident $nested_calls:tt ),* $(,)* ] ), $( $names:ident $calls:tt ),* ] ) => { let expected = format!("expected Start {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $start); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::Start { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $start, "{} but found Start {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; consumes_to!($tokens, [ $( $nested_names $nested_calls ),* ]); let expected = format!("expected End {{ rule: {}, pos: Position {{ pos: {} }} }}", stringify!($name), $end); match $tokens.next().expect(&format!("{} but found nothing", expected)) { ::pest::Token::End { rule, pos } => { assert!(rule == stringify!($name) && pos.pos() == $end, "{} but found End {{ rule: {}, pos: Position {{ {} }} }}", expected, rule, pos.pos(), ); }, token => panic!("{}", format!("{} but found {:?}", expected, token)) }; consumes_to!($tokens, [ $( $names $calls ),* ]); }; } /// A macro that tests input parses to the expected tokens. #[macro_export] macro_rules! parses_to { ( parser: $parser:expr, input: $string:expr, rule: $rule:expr, tokens: [ $( $names:ident $calls:tt ),* $(,)* ] ) => { #[allow(unused_mut)] { let vm = $parser; let mut tokens = vm.parse($rule, $string).unwrap().tokens(); consumes_to!(&mut tokens, [ $( $names $calls ),* ]); let rest: Vec<_> = tokens.collect(); match rest.len() { 0 => (), 2 => { let (first, second) = (&rest[0], &rest[1]); match (first, second) { ( &::pest::Token::Start { rule: ref first_rule, .. }, &::pest::Token::End { rule: ref second_rule, .. } ) => { assert!( format!("{}", first_rule) == "EOI", "expected end of input, but found {:?}", rest ); assert!( format!("{}", second_rule) == "EOI", "expected end of input, but found {:?}", rest ); } _ => panic!("expected end of input, but found {:?}", rest) } } _ => panic!("expected end of input, but found {:?}", rest) }; } }; } /// A macro that tests input fails to parse. #[macro_export] macro_rules! fails_with { ( parser: $parser:expr, input: $string:expr, rule: $rule:expr, positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => { #[allow(unused_mut)] #[allow(unused_variables)] { let vm = $parser; let error = vm.parse($rule, $string).unwrap_err(); match error.variant { ::pest::error::ErrorVariant::ParsingError { positives, negatives, } => { let quoted_positives: Vec<&str> = $positives; let quoted_negatives: Vec<&str> = $negatives; assert_eq!(quoted_positives, positives); assert_eq!(quoted_negatives, negatives); } _ => unreachable!(), }; match error.location { ::pest::error::InputLocation::Pos(pos) => assert_eq!(pos, $pos), _ => unreachable!(), } } }; } pest_vm-2.7.14/tests/grammar.pest000064400000000000000000000055471046102023000150740ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. string = { "abc" } insensitive = { ^"abc" } range = { '0'..'9' } ident = { string } pos_pred = { &string } neg_pred = { !string } double_neg_pred = { !!string } sequence = !{ string ~ string } sequence_compound = ${ string ~ string } sequence_atomic = @{ string ~ string } sequence_non_atomic = @{ sequence } sequence_atomic_compound = @{ sequence_compound } sequence_nested = { string ~ string } sequence_compound_nested = ${ sequence_nested } node_tag = { #string = string } choice = { string | range } choice_prefix = { | string | range } optional = { string? } repeat = { string* } repeat_atomic = @{ string* } repeat_once = { string+ } repeat_once_atomic = @{ string+ } repeat_min_max = { string{2, 3} } repeat_min_max_atomic = @{ string{2, 3} } repeat_exact = { string{2} } repeat_min = { string{2,} } repeat_min_atomic = @{ string{2,} } repeat_max = { string{, 2} } repeat_max_atomic = @{ string{, 2} } soi_at_start = { SOI ~ string } repeat_mutate_stack = { (PUSH('a'..'c') ~ ",")* ~ POP ~ POP ~ POP } repeat_mutate_stack_pop_all = { (PUSH('a'..'c') ~ ",")* ~ POP_ALL } will_fail = { repeat_mutate_stack_pop_all ~ "FAIL" } stack_resume_after_fail = { will_fail | repeat_mutate_stack_pop_all } peek_ = { PUSH(range) ~ PUSH(range) ~ PEEK ~ PEEK } peek_all = { PUSH(range) ~ PUSH(range) ~ PEEK_ALL } peek_slice_23 = { PUSH(range) ~ PUSH(range) ~ PUSH(range) ~ PUSH(range) ~ PUSH(range) ~ PEEK[1..-2] } pop_ = { PUSH(range) ~ PUSH(range) ~ POP ~ POP } pop_all = { PUSH(range) ~ PUSH(range) ~ POP_ALL } pop_fail = { PUSH(range) ~ !POP ~ range ~ POP } checkpoint_restore = ${ PUSH("") ~ (PUSH("a") ~ "b" ~ POP | DROP ~ "b" | POP ~ "a") ~ EOI } ascii_digits = { ASCII_DIGIT+ } ascii_nonzero_digits = { ASCII_NONZERO_DIGIT+ } ascii_bin_digits = { ASCII_BIN_DIGIT+ } ascii_oct_digits = { ASCII_OCT_DIGIT+ } ascii_hex_digits = { ASCII_HEX_DIGIT+ } ascii_alpha_lowers = { ASCII_ALPHA_LOWER+ } ascii_alpha_uppers = { ASCII_ALPHA_UPPER+ } ascii_alphas = { ASCII_ALPHA+ } ascii_alphanumerics = { ASCII_ALPHANUMERIC+ } asciis = { ASCII+ } newline = { NEWLINE+ } unicode = { XID_START ~ XID_CONTINUE* } SYMBOL = { "shadows builtin" } han = { HAN+ } hangul = { HANGUL+ } hiragana = { HIRAGANA+ } arabic = { ARABIC+ } emoji = { EMOJI+ } WHITESPACE = _{ " " } COMMENT = _{ "$"+ } // Line comment /* 1-line multiline comment */ /* N-line multiline comment */ /* // Line comment inside multiline /* (Multiline inside) multiline */ Invalid segment of grammar below (repeated rule) WHITESPACE = _{ "hi" } */ pest_vm-2.7.14/tests/grammar.rs000064400000000000000000000423031046102023000145340ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. extern crate pest; extern crate pest_meta; #[macro_use] extern crate pest_vm; use pest_meta::parser::Rule; use pest_meta::{optimizer, parser}; use pest_vm::Vm; const GRAMMAR: &str = include_str!("grammar.pest"); fn vm() -> Vm { let pairs = parser::parse(Rule::grammar_rules, GRAMMAR).unwrap(); let ast = parser::consume_rules(pairs).unwrap(); Vm::new(optimizer::optimize(ast)) } #[test] fn string() { parses_to! { parser: vm(), input: "abc", rule: "string", tokens: [ string(0, 3) ] }; } #[test] fn insensitive() { parses_to! { parser: vm(), input: "aBC", rule: "insensitive", tokens: [ insensitive(0, 3) ] }; } #[test] fn range() { parses_to! { parser: vm(), input: "6", rule: "range", tokens: [ range(0, 1) ] }; } #[test] fn ident() { parses_to! { parser: vm(), input: "abc", rule: "ident", tokens: [ ident(0, 3, [ string(0, 3) ]) ] }; } #[test] fn pos_pred() { parses_to! { parser: vm(), input: "abc", rule: "pos_pred", tokens: [ pos_pred(0, 0) ] }; } #[test] fn neg_pred() { parses_to! { parser: vm(), input: "", rule: "neg_pred", tokens: [ neg_pred(0, 0) ] }; } #[test] fn double_neg_pred() { parses_to! { parser: vm(), input: "abc", rule: "double_neg_pred", tokens: [ double_neg_pred(0, 0) ] }; } #[test] fn sequence() { parses_to! { parser: vm(), input: "abc abc", rule: "sequence", tokens: [ sequence(0, 9, [ string(0, 3), string(6, 9) ]) ] }; } #[test] fn sequence_compound() { parses_to! { parser: vm(), input: "abcabc", rule: "sequence_compound", tokens: [ sequence_compound(0, 6, [ string(0, 3), string(3, 6) ]) ] }; } #[test] fn sequence_atomic() { parses_to! { parser: vm(), input: "abcabc", rule: "sequence_atomic", tokens: [ sequence_atomic(0, 6) ] }; } #[test] fn sequence_non_atomic() { parses_to! { parser: vm(), input: "abc abc", rule: "sequence_non_atomic", tokens: [ sequence_non_atomic(0, 9, [ sequence(0, 9, [ string(0, 3), string(6, 9) ]) ]) ] }; } #[test] #[should_panic] fn sequence_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "sequence_atomic", tokens: [] }; } #[test] fn sequence_atomic_compound() { parses_to! { parser: vm(), input: "abcabc", rule: "sequence_atomic_compound", tokens: [ sequence_atomic_compound(0, 6, [ sequence_compound(0, 6, [ string(0, 3), string(3, 6) ]) ]) ] }; } #[test] fn sequence_compound_nested() { parses_to! { parser: vm(), input: "abcabc", rule: "sequence_compound_nested", tokens: [ sequence_compound_nested(0, 6, [ sequence_nested(0, 6, [ string(0, 3), string(3, 6) ]) ]) ] }; } #[test] #[should_panic] fn sequence_compound_nested_space() { parses_to! { parser: vm(), input: "abc abc", rule: "sequence_compound_nested", tokens: [] }; } #[test] fn choice_string() { parses_to! { parser: vm(), input: "abc", rule: "choice", tokens: [ choice(0, 3, [ string(0, 3) ]) ] }; } #[test] fn choice_range() { parses_to! { parser: vm(), input: "0", rule: "choice", tokens: [ choice(0, 1, [ range(0, 1) ]) ] }; } #[test] fn optional_string() { parses_to! { parser: vm(), input: "abc", rule: "optional", tokens: [ optional(0, 3, [ string(0, 3) ]) ] }; } #[test] fn optional_empty() { parses_to! { parser: vm(), input: "", rule: "optional", tokens: [ optional(0, 0) ] }; } #[test] fn repeat_empty() { parses_to! { parser: vm(), input: "", rule: "repeat", tokens: [ repeat(0, 0) ] }; } #[test] fn repeat_strings() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat", tokens: [ repeat(0, 9, [ string(0, 3), string(6, 9) ]) ] }; } #[test] fn repeat_atomic_empty() { parses_to! { parser: vm(), input: "", rule: "repeat_atomic", tokens: [ repeat_atomic(0, 0) ] }; } #[test] fn repeat_atomic_strings() { parses_to! { parser: vm(), input: "abcabc", rule: "repeat_atomic", tokens: [ repeat_atomic(0, 6) ] }; } #[test] #[should_panic] fn repeat_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_atomic", tokens: [] }; } #[test] #[should_panic] fn repeat_once_empty() { parses_to! { parser: vm(), input: "", rule: "repeat_once", tokens: [] }; } #[test] fn repeat_once_strings() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_once", tokens: [ repeat_once(0, 9, [ string(0, 3), string(6, 9) ]) ] }; } #[test] #[should_panic] fn repeat_once_atomic_empty() { parses_to! { parser: vm(), input: "", rule: "repeat_once_atomic", tokens: [] }; } #[test] fn repeat_once_atomic_strings() { parses_to! { parser: vm(), input: "abcabc", rule: "repeat_once_atomic", tokens: [ repeat_once_atomic(0, 6) ] }; } #[test] #[should_panic] fn repeat_once_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_once_atomic", tokens: [] }; } #[test] fn repeat_min_max_twice() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_min_max", tokens: [ repeat_min_max(0, 7, [ string(0, 3), string(4, 7) ]) ] }; } #[test] fn repeat_min_max_thrice() { parses_to! { parser: vm(), input: "abc abc abc", rule: "repeat_min_max", tokens: [ repeat_min_max(0, 11, [ string(0, 3), string(4, 7), string(8, 11) ]) ] }; } #[test] fn repeat_min_max_atomic_twice() { parses_to! { parser: vm(), input: "abcabc", rule: "repeat_min_max_atomic", tokens: [ repeat_min_max_atomic(0, 6) ] }; } #[test] fn repeat_min_max_atomic_thrice() { parses_to! { parser: vm(), input: "abcabcabc", rule: "repeat_min_max_atomic", tokens: [ repeat_min_max_atomic(0, 9) ] }; } #[test] #[should_panic] fn repeat_min_max_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_min_max_atomic", tokens: [] }; } #[test] fn repeat_exact() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_exact", tokens: [ repeat_exact(0, 7, [ string(0, 3), string(4, 7) ]) ] }; } #[test] #[should_panic] fn repeat_min_once() { parses_to! { parser: vm(), input: "abc", rule: "repeat_min", tokens: [] }; } #[test] fn repeat_min_twice() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_min", tokens: [ repeat_min(0, 7, [ string(0, 3), string(4, 7) ]) ] }; } #[test] fn repeat_min_thrice() { parses_to! { parser: vm(), input: "abc abc abc", rule: "repeat_min", tokens: [ repeat_min(0, 12, [ string(0, 3), string(4, 7), string(9, 12) ]) ] }; } #[test] #[should_panic] fn repeat_min_atomic_once() { parses_to! { parser: vm(), input: "abc", rule: "repeat_min_atomic", tokens: [] }; } #[test] fn repeat_min_atomic_twice() { parses_to! { parser: vm(), input: "abcabc", rule: "repeat_min_atomic", tokens: [ repeat_min_atomic(0, 6) ] }; } #[test] fn repeat_min_atomic_thrice() { parses_to! { parser: vm(), input: "abcabcabc", rule: "repeat_min_atomic", tokens: [ repeat_min_atomic(0, 9) ] }; } #[test] #[should_panic] fn repeat_min_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_min_atomic", tokens: [] }; } #[test] fn repeat_max_once() { parses_to! { parser: vm(), input: "abc", rule: "repeat_max", tokens: [ repeat_max(0, 3, [ string(0, 3) ]) ] }; } #[test] fn repeat_max_twice() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_max", tokens: [ repeat_max(0, 7, [ string(0, 3), string(4, 7) ]) ] }; } #[test] #[should_panic] fn repeat_max_thrice() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_max", tokens: [] }; } #[test] fn repeat_max_atomic_once() { parses_to! { parser: vm(), input: "abc", rule: "repeat_max_atomic", tokens: [ repeat_max_atomic(0, 3) ] }; } #[test] fn repeat_max_atomic_twice() { parses_to! { parser: vm(), input: "abcabc", rule: "repeat_max_atomic", tokens: [ repeat_max_atomic(0, 6) ] }; } #[test] #[should_panic] fn repeat_max_atomic_thrice() { parses_to! { parser: vm(), input: "abcabcabc", rule: "repeat_max_atomic", tokens: [] }; } #[test] #[should_panic] fn repeat_max_atomic_space() { parses_to! { parser: vm(), input: "abc abc", rule: "repeat_max_atomic", tokens: [] }; } #[test] fn repeat_comment() { parses_to! { parser: vm(), input: "abc$$$ $$$abc", rule: "repeat_once", tokens: [ repeat_once(0, 13, [ string(0, 3), string(10, 13) ]) ] }; } #[test] fn soi_at_start() { parses_to! { parser: vm(), input: "abc", rule: "soi_at_start", tokens: [ soi_at_start(0, 3, [ string(0, 3) ]) ] }; } #[test] fn peek() { parses_to! { parser: vm(), input: "0111", rule: "peek_", tokens: [ peek_(0, 4, [ range(0, 1), range(1, 2) ]) ] }; } #[test] fn peek_all() { parses_to! { parser: vm(), input: "0110", rule: "peek_all", tokens: [ peek_all(0, 4, [ range(0, 1), range(1, 2) ]) ] }; } #[test] fn peek_slice_23() { parses_to! { parser: vm(), input: "0123412", rule: "peek_slice_23", tokens: [ peek_slice_23(0, 7, [ range(0, 1), range(1, 2), range(2, 3), range(3, 4), range(4, 5), ]) ] }; } #[test] fn pop() { parses_to! { parser: vm(), input: "0110", rule: "pop_", tokens: [ pop_(0, 4, [ range(0, 1), range(1, 2) ]) ] }; } #[test] fn pop_all() { parses_to! { parser: vm(), input: "0110", rule: "pop_all", tokens: [ pop_all(0, 4, [ range(0, 1), range(1, 2) ]) ] }; } #[test] fn pop_fail() { parses_to! { parser: vm(), input: "010", rule: "pop_fail", tokens: [ pop_fail(0, 3, [ range(0, 1), range(1, 2) ]) ] }; } #[test] fn repeat_mutate_stack() { parses_to! { parser: vm(), input: "a,b,c,cba", rule: "repeat_mutate_stack", tokens: [ repeat_mutate_stack(0, 9) ] }; } #[test] fn checkpoint_restore() { parses_to! { parser: vm(), input: "a", rule: "checkpoint_restore", tokens: [ checkpoint_restore(0, 1, [EOI(1, 1)]) ] }; } #[test] fn ascii_digits() { parses_to! { parser: vm(), input: "6", rule: "ascii_digits", tokens: [ ascii_digits(0, 1) ] }; } #[test] fn ascii_nonzero_digits() { parses_to! { parser: vm(), input: "5", rule: "ascii_nonzero_digits", tokens: [ ascii_nonzero_digits(0, 1) ] }; } #[test] fn ascii_bin_digits() { parses_to! { parser: vm(), input: "1", rule: "ascii_bin_digits", tokens: [ ascii_bin_digits(0, 1) ] }; } #[test] fn ascii_oct_digits() { parses_to! { parser: vm(), input: "3", rule: "ascii_oct_digits", tokens: [ ascii_oct_digits(0, 1) ] }; } #[test] fn ascii_hex_digits() { parses_to! { parser: vm(), input: "6bC", rule: "ascii_hex_digits", tokens: [ ascii_hex_digits(0, 3) ] }; } #[test] fn ascii_alpha_lowers() { parses_to! { parser: vm(), input: "a", rule: "ascii_alpha_lowers", tokens: [ ascii_alpha_lowers(0, 1) ] }; } #[test] fn ascii_alpha_uppers() { parses_to! { parser: vm(), input: "K", rule: "ascii_alpha_uppers", tokens: [ ascii_alpha_uppers(0, 1) ] }; } #[test] fn ascii_alphas() { parses_to! { parser: vm(), input: "wF", rule: "ascii_alphas", tokens: [ ascii_alphas(0, 2) ] }; } #[test] fn ascii_alphanumerics() { parses_to! { parser: vm(), input: "4jU", rule: "ascii_alphanumerics", tokens: [ ascii_alphanumerics(0, 3) ] }; } #[test] fn asciis() { parses_to! { parser: vm(), input: "x02", rule: "asciis", tokens: [ asciis(0, 3) ] }; } #[test] fn newline() { parses_to! { parser: vm(), input: "\n\r\n\r", rule: "newline", tokens: [ newline(0, 4) ] }; } #[test] fn unicode() { parses_to! { parser: vm(), input: "نامهای", rule: "unicode", tokens: [ unicode(0, 12) ] } } #[test] fn shadowing() { parses_to! { parser: vm(), input: "shadows builtin", rule: "SYMBOL", tokens: [ SYMBOL(0, 15) ] } } #[test] fn test_han() { parses_to! { parser: vm(), input: "你好", rule: "han", tokens: [ han(0, 6) ] }; } #[test] fn test_hangul() { parses_to! { parser: vm(), input: "여보세요", rule: "hangul", tokens: [ hangul(0, 12) ] }; } #[test] fn test_hiragana() { parses_to! { parser: vm(), input: "こんにちは", rule: "hiragana", tokens: [ hiragana(0, 15) ] }; } #[test] fn arabic() { parses_to! { parser: vm(), input: "نامهای", rule: "arabic", tokens: [ arabic(0, 12) ] } } #[test] fn emoji() { parses_to! { parser: vm(), input: "👶", rule: "emoji", tokens: [ emoji(0, 4) ] } } pest_vm-2.7.14/tests/lists.pest000064400000000000000000000013371046102023000145750ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. item = { (!"\n" ~ ANY)* } lists = _{ lines ~ EOI } lines = _{ top_first ~ ("\n" ~ top_continue)* } top_first = _{ "- " ~ item ~ ("\n" ~ children)? } top_continue = _{ PEEK_ALL ~ "- " ~ item ~ ("\n" ~ children)? } indentation = _{ (" " | "\t")+ } children = { PEEK_ALL ~ PUSH(indentation) ~ lines ~ DROP } pest_vm-2.7.14/tests/lists.rs000064400000000000000000000050031046102023000142400ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. extern crate pest; extern crate pest_meta; #[macro_use] extern crate pest_vm; use pest_meta::parser::Rule; use pest_meta::{optimizer, parser}; use pest_vm::Vm; const GRAMMAR: &str = include_str!("lists.pest"); fn vm() -> Vm { let pairs = parser::parse(Rule::grammar_rules, GRAMMAR).unwrap(); let ast = parser::consume_rules(pairs).unwrap(); Vm::new(optimizer::optimize(ast)) } #[test] fn item() { parses_to! { parser: vm(), input: "- a", rule: "lists", tokens: [ item(2, 3) ] }; } #[test] fn items() { parses_to! { parser: vm(), input: "- a\n- b", rule: "lists", tokens: [ item(2, 3), item(6, 7) ] }; } #[test] fn children() { parses_to! { parser: vm(), input: " - b", rule: "children", tokens: [ children(0, 5, [ item(4, 5) ]) ] }; } #[test] fn nested_item() { parses_to! { parser: vm(), input: "- a\n - b", rule: "lists", tokens: [ item(2, 3), children(4, 9, [ item(8, 9) ]) ] }; } #[test] fn nested_items() { parses_to! { parser: vm(), input: "- a\n - b\n - c", rule: "lists", tokens: [ item(2, 3), children(4, 15, [ item(8, 9), item(14, 15) ]) ] }; } #[test] fn nested_two_levels() { parses_to! { parser: vm(), input: "- a\n - b\n - c", rule: "lists", tokens: [ item(2, 3), children(4, 17, [ item(8, 9), children(10, 17, [ item(16, 17) ]) ]) ] }; } #[test] fn nested_then_not() { parses_to! { parser: vm(), input: "- a\n - b\n- c", rule: "lists", tokens: [ item(2, 3), children(4, 9, [ item(8, 9) ]), item(12, 13) ] }; } pest_vm-2.7.14/tests/reporting.pest000064400000000000000000000013561046102023000154510ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. a = { "a" } b = { "b" } c = { "c" } d = { ANY } choices = _{ a | b | c } choices_no_progress = { a | b | c } choices_a_progress = { a ~ a | b | c } choices_b_progress = { a | b ~ b | c } level1 = _{ level2 } level2 = _{ a | b | c } negative = _{ !d } negative_match = _{ !a ~ b } mixed = _{ !d | a } mixed_progress = _{ (!d | a | b) ~ a } pest_vm-2.7.14/tests/reporting.rs000064400000000000000000000051261046102023000151210ustar 00000000000000// pest. The Elegant Parser // Copyright (c) 2018 Dragoș Tiselice // // Licensed under the Apache License, Version 2.0 // or the MIT // license , at your // option. All files in the project carrying such notice may not be copied, // modified, or distributed except according to those terms. extern crate pest; extern crate pest_meta; #[macro_use] extern crate pest_vm; use pest_meta::parser::Rule; use pest_meta::{optimizer, parser}; use pest_vm::Vm; const GRAMMAR: &str = include_str!("reporting.pest"); fn vm() -> Vm { let pairs = parser::parse(Rule::grammar_rules, GRAMMAR).unwrap(); let ast = parser::consume_rules(pairs).unwrap(); Vm::new(optimizer::optimize(ast)) } #[test] fn choices() { fails_with! { parser: vm(), input: "x", rule: "choices", positives: vec!["a", "b", "c"], negatives: vec![], pos: 0 }; } #[test] fn choices_no_progress() { fails_with! { parser: vm(), input: "x", rule: "choices_no_progress", positives: vec!["choices_no_progress"], negatives: vec![], pos: 0 }; } #[test] fn choices_a_progress() { fails_with! { parser: vm(), input: "a", rule: "choices_a_progress", positives: vec!["a"], negatives: vec![], pos: 1 }; } #[test] fn choices_b_progress() { fails_with! { parser: vm(), input: "b", rule: "choices_b_progress", positives: vec!["b"], negatives: vec![], pos: 1 }; } #[test] fn nested() { fails_with! { parser: vm(), input: "x", rule: "level1", positives: vec!["a", "b", "c"], negatives: vec![], pos: 0 }; } #[test] fn negative() { fails_with! { parser: vm(), input: "x", rule: "negative", positives: vec![], negatives: vec!["d"], pos: 0 }; } #[test] fn negative_match() { fails_with! { parser: vm(), input: "x", rule: "negative_match", positives: vec!["b"], negatives: vec![], pos: 0 }; } #[test] fn mixed() { fails_with! { parser: vm(), input: "x", rule: "mixed", positives: vec!["a"], negatives: vec!["d"], pos: 0 }; } #[test] fn mixed_progress() { fails_with! { parser: vm(), input: "b", rule: "mixed_progress", positives: vec!["a"], negatives: vec![], pos: 1 }; }