├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── Dockerfile ├── README.md ├── docker-compose.yml └── source ├── ast.rs ├── internal.rs ├── lib.rs ├── macros.rs ├── rules ├── comments.rs ├── expressions │ ├── assignment.rs │ ├── constant.rs │ ├── mod.rs │ └── primaries.rs ├── literals.rs ├── mod.rs ├── skip.rs ├── statements │ ├── function.rs │ └── mod.rs ├── tokens.rs └── whitespaces.rs └── tokens.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | matrix: 4 | include: 5 | - rust: stable 6 | os: linux 7 | dist: trusty 8 | - rust: stable 9 | os: osx 10 | - rust: beta 11 | os: linux 12 | dist: trusty 13 | - rust: beta 14 | os: osx 15 | - rust: nightly 16 | os: linux 17 | dist: trusty 18 | - rust: nightly 19 | os: osx 20 | allow_failures: 21 | - rust: nightly 22 | fast_finish: true 23 | 24 | cache: cargo 25 | 26 | sudo: false 27 | 28 | addons: 29 | apt: 30 | packages: 31 | - binutils-dev 32 | - cmake 33 | - libcurl4-openssl-dev 34 | - libdw-dev 35 | - libelf-dev 36 | sources: 37 | - kalakris-cmake 38 | 39 | env: 40 | global: 41 | secure: nDy6lalxkiPOkM1zvCjkdPhi8OK+k8Ac6MlcIDWUyw/GccSda9BkOZ4nQuGx3xwjsMjaZ6M81r2p8jne6Fc89DfjtpgWNZ7BlShLHSQgT86vcuT3f92OjLySJawEGHQGpndfzoBbySVghTfTfiCQdphrniHdDw9vvo/f70L8L17/JeVVM5lUQmekDlbBLLugWg4e2U1aBi6czZwZhqHhuAvU2rXP6tDx3PNU97tTDFF2Xxt0FjdrcAtweVSr8jOIArtHpy8/EXFgJZYzkRxitVCMEb5MU1Ddpttsoj7Sdg2f0Cpv7r5PPvTc1qnGWcWKb+xWjo3GtHrpQFI/6InWVEF8ytEyqX1VwQSOMG+9W2Rwns35xGMuPzCaJusmIohFc0XRN9wB6+SeH1mbx2QXpdcdPrepzaHZ9s+Dj5VbcJ+5dYaRiI7sIQSZ337bxLuHIH79k9ztMU3kS2U0oTJFIE/CZAJ9mrgZJ6Dc2lI/p0b5HR7Avj83KTVU+OiPL4abcX5j8PQQpIoF+RWj62+eTPqU+X5vgFA2eQeX8NKWE9Gb/t9YrD5KTebhTnLIOCLOOyZKz41VRy9q4M2kdFj1NqGe2SQazSA558jtC1kEoDgfuVNiVDiak/Hn8Cc8+eI7/9Hr6ykU5mvgl9xTY/a+olbTN3AyesxXnJVU8mdvLgc= 42 | 43 | notifications: 44 | irc: "chat.freenode.net#taguavm" 45 | 46 | script: 47 | - cargo build 48 | - cargo test 49 | 50 | before_script: 51 | - | 52 | cargo install --force cargo-travis && 53 | export PATH=$HOME/.cargo/bin:$PATH 54 | 55 | script: 56 | - | 57 | cargo build && 58 | cargo test 59 | 60 | after_success: 61 | - | 62 | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then 63 | cargo coveralls; 64 | fi 65 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.1.0 - 2016-08-24 4 | 5 | ### Changed 6 | 7 | - Implement the lexical grammar of the PHP Language Specification. 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tagua-parser" 3 | version = "0.1.0" 4 | authors = ["Ivan Enderlin "] 5 | repository = "https://github.com/tagua-vm/parser" 6 | description = "Safe, fast and memory efficient PHP parser (lexical and syntactic analysers)." 7 | readme = "README.md" 8 | keywords = ["php", "parser", "lexical", "syntactics", "analyser"] 9 | license = "BSD-3-Clause" 10 | 11 | [lib] 12 | name = "tagua_parser" 13 | path = "source/lib.rs" 14 | test = true 15 | doctest = true 16 | bench = true 17 | doc = true 18 | harness = true 19 | 20 | [profile.dev] 21 | opt-level = 0 22 | debug = true 23 | rpath = false 24 | lto = false 25 | debug-assertions = true 26 | codegen-units = 1 27 | 28 | [profile.release] 29 | opt-level = 3 30 | debug = false 31 | rpath = false 32 | lto = true 33 | debug-assertions = false 34 | codegen-units = 1 35 | 36 | [profile.test] 37 | opt-level = 0 38 | debug = true 39 | rpath = false 40 | lto = false 41 | debug-assertions = true 42 | codegen-units = 1 43 | 44 | [profile.bench] 45 | opt-level = 3 46 | debug = false 47 | rpath = false 48 | lto = true 49 | debug-assertions = false 50 | codegen-units = 1 51 | 52 | [features] 53 | simd = ["bytecount/simd-accel"] 54 | avx = ["bytecount/avx-accel"] 55 | 56 | [dependencies] 57 | bytecount = "^0.1" 58 | lazy_static = "^0.2" 59 | memchr = "^1.0" 60 | regex = "^0.2" 61 | smallvec = "^0.4" 62 | nom = {"git" = "https://github.com/Geal/nom", "branch" = "master", "features" = ["regexp", "regexp_macros", "verbose-errors"]} 63 | 64 | [dev-dependencies] 65 | quickcheck = "^0.3" 66 | pretty_assertions = "^0.4" -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | ENV ARCH=x86_64-unknown-linux-gnu 4 | ENV RUST_RELEASE=1.17.0 5 | ENV CARGO_RELEASE=nightly 6 | ENV SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt 7 | 8 | RUN apt-get update && \ 9 | apt-get install -y curl vim gcc libssl-dev libedit-dev libstdc++-4.9-dev && \ 10 | find /usr/bin -executable -iname llvm* | xargs -n1 -I file echo ln -s file file | sed s/-$LLVM_RELEASE$// | bash 11 | 12 | RUN curl -sL https://static.rust-lang.org/dist/rust-$RUST_RELEASE-$ARCH.tar.gz | tar xvz -C /tmp && \ 13 | /tmp/rust-$RUST_RELEASE-$ARCH/install.sh && \ 14 | rm -rf /tmp/rust-$RUST_RELEASE-$ARCH 15 | 16 | RUN curl -sL https://static.rust-lang.org/cargo-dist/cargo-$CARGO_RELEASE-$ARCH.tar.gz | tar xvz -C /tmp && \ 17 | /tmp/cargo-$CARGO_RELEASE-$ARCH/install.sh && \ 18 | rm -rf /tmp/cargo-$CARGO_RELEASE-$ARCH 19 | 20 | RUN apt-get remove --purge -y curl && \ 21 | apt-get autoclean && apt-get clean && \ 22 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 23 | 24 | VOLUME /source 25 | WORKDIR /source 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tagua Parser 2 | 3 | [![Chat on Freenode](https://img.shields.io/badge/chat-on_%23taguavm-ff0066.svg)](https://webchat.freenode.net/?channels=#taguavm) 4 | [![Chat on Gitter](https://img.shields.io/badge/chat-on_gitter-ff0066.svg)](https://gitter.im/tagua-vm/tagua-vm) 5 | [![Build status](https://api.travis-ci.org/tagua-vm/parser.svg?branch=master)](https://travis-ci.org/tagua-vm/parser) 6 | [![Coverage](https://img.shields.io/coveralls/tagua-vm/parser/master.svg)](https://coveralls.io/github/tagua-vm/parser?branch=master) 7 | 8 | Tagua VM is an experimental [PHP](http://php.net/) Virtual Machine written with 9 | [the Rust language](https://www.rust-lang.org/) and [the LLVM Compiler 10 | Infrastructure](http://llvm.org/). 11 | 12 | The Virtual Machine is composed of several libraries. This library, 13 | Tagua Parser, contains the lexical and syntactic analysers, aka the parser, 14 | for the PHP language. 15 | 16 | ## Installing 17 | 18 | To install Tagua Parser, you must have Rust (see [the Rust installation 19 | page](https://www.rust-lang.org/downloads.html)) installed. 20 | [Cargo](http://doc.crates.io/guide.html) is the Rust package manager. 21 | 22 | To build a release version: 23 | 24 | ```sh 25 | $ cargo build --release 26 | ``` 27 | 28 | To build a development version: 29 | 30 | ```sh 31 | $ cargo build 32 | ``` 33 | 34 | If you have SIMD or AVX on your hardware, then, you can respectively 35 | use the `simd` or `avx` feature as follows: 36 | 37 | ```sh 38 | $ cargo build --release --features simd 39 | ``` 40 | 41 | ### Using Docker 42 | 43 | If installing Rust on your machine is too much, Docker might be an alternative: 44 | It provides everything needed to build, test and run the Tagua Parser. 45 | 46 | First, build the Docker image: 47 | 48 | ```sh 49 | $ docker build -t tagua-parser-dev . 50 | ``` 51 | 52 | Now, it is possible to run a container from this image: 53 | 54 | ```sh 55 | $ docker run --rm -it -v $(pwd):/source tagua-parser-dev 56 | ``` 57 | 58 | If this command succeeds, you are inside a fresh container. To see if 59 | everything is fine, you can start the test suite: 60 | 61 | ```sh 62 | $ cargo test 63 | ``` 64 | 65 | ## Contributing 66 | 67 | Do whatever you want. Just respect the license and the other contributors. Your 68 | favorite tool is going to be: 69 | 70 | ```sh 71 | $ cargo test 72 | ``` 73 | 74 | to run all the test suites (unit test suites, integration test suites and 75 | documentation test suites). 76 | 77 | ### カンバン ([Kanban](https://en.wikipedia.org/wiki/Kanban)) 78 | 79 | In order to get an overview of what needs to be done, what is in progress and 80 | what has been recently done, [a kanban board is 81 | available](https://waffle.io/tagua-vm/parser). 82 | 83 | ## Documentation and help 84 | 85 | The documentation is automatically uploaded online at the following address: 86 | https://docs.rs/tagua-parser/*/tagua_parser/. 87 | 88 | To generate it locally, please, run the following command: 89 | 90 | ```sh 91 | $ cargo doc --open 92 | ``` 93 | 94 | To get help on IRC, please join the official [`#taguavm` channel on 95 | Freenode](https://webchat.freenode.net/?channels=#taguavm). Alternatively, there 96 | is a [mirrored room on Gitter](https://gitter.im/tagua-vm/tagua-vm). 97 | 98 | ## License 99 | 100 | Tagua VM is under the New BSD License (BSD-3-Clause): 101 | 102 | ``` 103 | New BSD License 104 | 105 | 106 | 107 | Copyright © 2016-2017, Ivan Enderlin. 108 | All rights reserved. 109 | 110 | Redistribution and use in source and binary forms, with or without 111 | modification, are permitted provided that the following conditions are met: 112 | * Redistributions of source code must retain the above copyright 113 | notice, this list of conditions and the following disclaimer. 114 | * Redistributions in binary form must reproduce the above copyright 115 | notice, this list of conditions and the following disclaimer in the 116 | documentation and/or other materials provided with the distribution. 117 | * Neither the name of the Hoa nor the names of its contributors may be 118 | used to endorse or promote products derived from this software without 119 | specific prior written permission. 120 | 121 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 122 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 123 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 124 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 125 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 126 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 127 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 128 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 129 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 130 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 131 | POSSIBILITY OF SUCH DAMAGE. 132 | ``` 133 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | services: 4 | cli: 5 | build: . 6 | volumes: 7 | - ./:/source/:rw 8 | command: tail -f /dev/null 9 | 10 | test: 11 | build: . 12 | volumes: 13 | - ./:/source/:rw 14 | command: bash -c 'cargo build; cargo test' 15 | -------------------------------------------------------------------------------- /source/internal.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Internal utilities for the parser. 33 | 34 | use smallvec::VecLike; 35 | 36 | /// Hold the result of a parser. 37 | pub use nom::IResult as Result; 38 | 39 | /// Contain the error that a parser can return. 40 | pub use nom::Err as Error; 41 | 42 | /// Indicate which parser has returned an error. 43 | pub use nom::ErrorKind; 44 | 45 | /// Indicate the context of an error. 46 | pub use nom::Context; 47 | 48 | /// Contain information on needed data if a parser returned `Incomplete`. 49 | pub use nom::Needed; 50 | 51 | /// Represent the type of the input elements. 52 | pub type InputElement = u8; 53 | 54 | /// Represent the type of the input. 55 | pub type Input<'a> = &'a [InputElement]; 56 | 57 | /// Fold an item into a vector. 58 | /// This is useful when combined with the `fold_many0!` macro for instance. 59 | /// 60 | /// # Examples 61 | /// 62 | /// ``` 63 | /// # #[macro_use] 64 | /// # extern crate nom; 65 | /// # #[macro_use] 66 | /// # extern crate tagua_parser; 67 | /// use tagua_parser::Result; 68 | /// use tagua_parser::internal::fold_into_vector; 69 | /// 70 | /// # fn main() { 71 | /// named!( 72 | /// test<&[u8], Vec<&[u8]>>, 73 | /// fold_many0!( 74 | /// tag!("abc"), 75 | /// Vec::new(), 76 | /// fold_into_vector 77 | /// ) 78 | /// ); 79 | /// 80 | /// assert_eq!(test(&b"abcabc"[..]), Ok((&b""[..], vec![&b"abc"[..], &b"abc"[..]]))); 81 | /// # } 82 | /// ``` 83 | pub fn fold_into_vector>(mut accumulator: V, item: I) -> V { 84 | accumulator.push(item); 85 | 86 | accumulator 87 | } 88 | -------------------------------------------------------------------------------- /source/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "lib"] 2 | 3 | // Tagua VM 4 | // 5 | // 6 | // New BSD License 7 | // 8 | // Copyright © 2016-2017, Ivan Enderlin. 9 | // All rights reserved. 10 | // 11 | // Redistribution and use in source and binary forms, with or without 12 | // modification, are permitted provided that the following conditions are met: 13 | // * Redistributions of source code must retain the above copyright 14 | // notice, this list of conditions and the following disclaimer. 15 | // * Redistributions in binary form must reproduce the above copyright 16 | // notice, this list of conditions and the following disclaimer in the 17 | // documentation and/or other materials provided with the distribution. 18 | // * Neither the name of the Hoa nor the names of its contributors may be 19 | // used to endorse or promote products derived from this software without 20 | // specific prior written permission. 21 | // 22 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 26 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | // POSSIBILITY OF SUCH DAMAGE. 33 | 34 | //! Tagua VM 35 | //! 36 | //! Tagua VM is an experimental [PHP](http://php.net/) Virtual Machine written 37 | //! with [the Rust language](https://www.rust-lang.org/) and [the LLVM Compiler 38 | //! Infrastructure](http://llvm.org/). 39 | //! 40 | //! This library contains lexical and syntactic analysers, aka the parser, 41 | //! for the PHP language. 42 | //! 43 | //! This is a parser combinator. The immediate consequence is that the lexical 44 | //! and syntax analyzers form a monolithic algorithm. The organization is the 45 | //! following: 46 | //! 47 | //! * The `tokens` module declares all the lexemes, 48 | //! * The `rules` module declares the grammar as a set of rules, 49 | //! * The `ast` module contains the structure that will constitute the AST. 50 | //! 51 | //! The parser is based on [nom](https://github.com/Geal/nom). nom is a parser 52 | //! combinator library with a focus on safe parsing, streaming patterns, and as 53 | //! much as possible zero copy. We try to enforce the zero copy property to 54 | //! hold. 55 | 56 | // Force all public items to be documented. 57 | #![deny(missing_docs)] 58 | 59 | // Increase the macro recursion limit. 60 | #![recursion_limit="128"] 61 | 62 | extern crate bytecount; 63 | #[macro_use] 64 | extern crate lazy_static; 65 | extern crate memchr; 66 | #[macro_use] 67 | extern crate nom; 68 | extern crate regex; 69 | extern crate smallvec; 70 | #[cfg(test)] 71 | #[macro_use] 72 | extern crate quickcheck; 73 | #[cfg(test)] 74 | #[macro_use] 75 | extern crate pretty_assertions; 76 | 77 | pub mod internal; 78 | #[macro_use] 79 | pub mod macros; 80 | pub mod ast; 81 | pub mod rules; 82 | pub mod tokens; 83 | 84 | pub use self::internal::*; 85 | 86 | /// Complete parsing of a datum starting by the sentence symbol of the grammar. 87 | /// 88 | /// The grammar is a set of rules. By definition, it has a sentence symbol, 89 | /// also called the root rule. The `parse` function will lex, parse and produce 90 | /// the associated AST of the `input` datum. 91 | /// 92 | /// # Examples 93 | /// 94 | /// ``` 95 | /// use tagua_parser::parse; 96 | /// use tagua_parser::tokens::Span; 97 | /// 98 | /// let expression = Span::new(b"1+2"); 99 | /// tagua_parser::parse(expression); 100 | /// ``` 101 | pub fn parse(input: tokens::Span) -> ast::Expression { 102 | rules::root(input) 103 | } 104 | -------------------------------------------------------------------------------- /source/macros.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Extra macros helping to write parsers. 33 | 34 | /// Custom values for `ErrorKind::Custom`. 35 | pub enum ErrorKindCustom { 36 | /// Represent errors from the `exclude` macro. 37 | Exclude, 38 | /// Represent errors from the `itag` macro. 39 | ITag 40 | } 41 | 42 | /// `exclude!(I -> Result, I -> Result) => I -> Result` 43 | /// returns the result of the first parser if the second fails. Both parsers 44 | /// run on the same input. 45 | /// 46 | /// This is handy when the first parser accepts general values and the second 47 | /// parser denies a particular subset of values. 48 | /// 49 | /// # Examples 50 | /// 51 | /// ``` 52 | /// # #[macro_use] 53 | /// # extern crate nom; 54 | /// # #[macro_use] 55 | /// # extern crate tagua_parser; 56 | /// use tagua_parser::{ 57 | /// Context, 58 | /// Error, 59 | /// ErrorKind, 60 | /// Result 61 | /// }; 62 | /// use tagua_parser::macros::ErrorKindCustom; 63 | /// 64 | /// # fn main() { 65 | /// named!( 66 | /// test, 67 | /// exclude!( 68 | /// is_a!("abcdef"), 69 | /// alt!( 70 | /// tag!("abc") 71 | /// | tag!("ace") 72 | /// ) 73 | /// ) 74 | /// ); 75 | /// 76 | /// assert_eq!(test(&b"fedabc"[..]), Ok((&b""[..], &b"fedabc"[..]))); 77 | /// assert_eq!(test(&b"abcabc"[..]), Err(Error::Error(Context::Code(&b"abcabc"[..], ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 78 | /// # } 79 | /// ``` 80 | #[macro_export] 81 | macro_rules! exclude( 82 | ($input:expr, $submacro1:ident!($($arguments1:tt)*), $submacro2:ident!($($arguments2:tt)*)) => ( 83 | { 84 | match $submacro1!($input, $($arguments1)*) { 85 | Ok((i, o)) => 86 | match $submacro2!(o, $($arguments2)*) { 87 | Ok((_, _)) => 88 | Err($crate::Error::Error($crate::Context::Code($input, $crate::ErrorKind::Custom($crate::macros::ErrorKindCustom::Exclude as u32)))), 89 | 90 | Err(_) => 91 | Ok((i, o)) 92 | }, 93 | 94 | Err(e) => Err(e) 95 | } 96 | } 97 | ); 98 | 99 | ($input:expr, $submacro1:ident!($($arguments1:tt)*), $g:expr) => ( 100 | exclude!($input, $submacro1!($($arguments1)*), call!($g)); 101 | ); 102 | 103 | ($input:expr, $f:expr, $submacro2:ident!($($arguments2:tt)*)) => ( 104 | exclude!($input, call!($f), $submacro2!($($arguments2)*)); 105 | ); 106 | 107 | ($input:expr, $f:expr, $g:expr) => ( 108 | exclude!($input, call!($f), call!($g)); 109 | ); 110 | ); 111 | 112 | /// `first!(I -> Result) => I -> Result` 113 | /// is applying the `skip` rule before the first argument; it allows to skip 114 | /// tokens. 115 | /// 116 | /// # Examples 117 | /// 118 | /// ``` 119 | /// # #[macro_use] 120 | /// # extern crate nom; 121 | /// # #[macro_use] 122 | /// # extern crate tagua_parser; 123 | /// use tagua_parser::Result; 124 | /// use tagua_parser::tokens::Span; 125 | /// 126 | /// # fn main() { 127 | /// named!( 128 | /// test, 129 | /// first!(tag!(b"bar")) 130 | /// ); 131 | /// 132 | /// assert_eq!( 133 | /// test(Span::new(b"/* foo */bar")), 134 | /// Ok(( 135 | /// Span::new_at(b"", 12, 1, 13), 136 | /// Span::new_at(b"bar", 9, 1, 10) 137 | /// )) 138 | /// ); 139 | /// # } 140 | /// ``` 141 | #[macro_export] 142 | macro_rules! first( 143 | ($input:expr, $submacro:ident!($($arguments:tt)*)) => ( 144 | { 145 | preceded!( 146 | $input, 147 | call!($crate::rules::skip::skip), 148 | $submacro!($($arguments)*) 149 | ) 150 | } 151 | ); 152 | 153 | ($input:expr, $f:expr) => ( 154 | first!($input, call!($f)); 155 | ); 156 | ); 157 | 158 | /// `itag!(I -> Result) => I -> Result` 159 | /// declares a case-insensitive tag to recognize. 160 | /// 161 | /// It is pretty similar to the nom `tag!` macro except it is 162 | /// case-insensitive, and return the expected tag, not the consumed 163 | /// tag. 164 | /// 165 | /// # Examples 166 | /// 167 | /// ``` 168 | /// # #[macro_use] 169 | /// # extern crate nom; 170 | /// # #[macro_use] 171 | /// # extern crate tagua_parser; 172 | /// use tagua_parser::Result; 173 | /// use tagua_parser::tokens::Span; 174 | /// 175 | /// # fn main() { 176 | /// named!( 177 | /// test, 178 | /// itag!(b"foobar") 179 | /// ); 180 | /// 181 | /// let output = Ok((Span::new_at(b"", 6, 1, 7), Span::new(b"foobar"))); 182 | /// 183 | /// assert_eq!(test(Span::new(b"foobar")), output); 184 | /// assert_eq!(test(Span::new(b"FoObAr")), output); 185 | /// # } 186 | /// ``` 187 | #[macro_export] 188 | macro_rules! itag( 189 | ($input:expr, $tag:expr) => ( 190 | { 191 | use $crate::tokens::Span; 192 | use nom::{ 193 | Compare, 194 | CompareResult, 195 | InputLength, 196 | Slice 197 | }; 198 | 199 | let tag_length = $tag.input_len(); 200 | 201 | let output: $crate::Result<_, _> = match $input.compare_no_case($tag) { 202 | CompareResult::Ok => { 203 | let Span { offset, line, column, .. } = $input.slice(..tag_length); 204 | let consumed = Span::new_at($tag, offset, line, column); 205 | 206 | Ok(($input.slice(tag_length..), consumed)) 207 | }, 208 | 209 | CompareResult::Incomplete => { 210 | Err($crate::Error::Incomplete($crate::Needed::Size($tag.input_len()))) 211 | }, 212 | 213 | CompareResult::Error => { 214 | Err($crate::Error::Error($crate::Context::Code($input, $crate::ErrorKind::Custom($crate::macros::ErrorKindCustom::ITag as u32)))) 215 | } 216 | }; 217 | 218 | output 219 | } 220 | ); 221 | ); 222 | 223 | /// `keyword!(&[T]: nom::AsBytes) => &[T] -> Result<&[T], &[T]>` 224 | /// is an alias to the `itag` macro. 225 | /// 226 | /// The goal of this alias is twofold: 227 | /// 228 | /// 1. It avoids confusion and errors (a PHP keyword is always 229 | /// case-insensitive), 230 | /// 2. It ensures a better readability of parsers. 231 | /// 232 | /// # Examples 233 | /// 234 | /// ``` 235 | /// # #[macro_use] 236 | /// # extern crate nom; 237 | /// # #[macro_use] 238 | /// # extern crate tagua_parser; 239 | /// use tagua_parser::{ 240 | /// Result, 241 | /// tokens 242 | /// }; 243 | /// use tagua_parser::tokens::Span; 244 | /// 245 | /// # fn main() { 246 | /// named!( 247 | /// test, 248 | /// keyword!(tokens::CLASS) 249 | /// ); 250 | /// 251 | /// let output = Ok((Span::new_at(b"", 5, 1, 6), Span::new(tokens::CLASS))); 252 | /// 253 | /// assert_eq!(test(Span::new(b"class")), output); 254 | /// assert_eq!(test(Span::new(b"ClAsS")), output); 255 | /// # } 256 | /// ``` 257 | #[macro_export] 258 | macro_rules! keyword( 259 | ($input:expr, $keyword:expr) => ( 260 | { 261 | itag!($input, $keyword) 262 | } 263 | ); 264 | ); 265 | 266 | /// `fold_into_vector_many0!(I -> IResult, R) => I -> IResult` 267 | /// is a wrapper around `fold_many0!` specifically designed for vectors. 268 | /// 269 | /// This is strictly equivalent to `fold_many0!(submacro!(…), 270 | /// Vec::new(), fold_into_vector)` but it shrinks the capacity of the 271 | /// vector to fit the current length. 272 | /// 273 | /// # Examples 274 | /// 275 | /// ``` 276 | /// # #[macro_use] 277 | /// # extern crate nom; 278 | /// # #[macro_use] 279 | /// # extern crate tagua_parser; 280 | /// use tagua_parser::Result; 281 | /// 282 | /// # fn main() { 283 | /// named!( 284 | /// test>, 285 | /// fold_into_vector_many0!( 286 | /// tag!("abc"), 287 | /// Vec::new() 288 | /// ) 289 | /// ); 290 | /// 291 | /// if let Ok((_, vector)) = test(b"abcabcabc") { 292 | /// assert_eq!(vector.capacity(), vector.len()); 293 | /// } 294 | /// # } 295 | /// ``` 296 | #[macro_export] 297 | macro_rules! fold_into_vector_many0( 298 | ($input:expr, $submacro:ident!($($arguments:tt)*), $init:expr) => ( 299 | { 300 | let result = fold_many0!( 301 | $input, 302 | $submacro!($($arguments)*), 303 | $init, 304 | $crate::internal::fold_into_vector 305 | ); 306 | 307 | if let Ok((input, mut output)) = result { 308 | output.shrink_to_fit(); 309 | 310 | Ok((input, output)) 311 | } else { 312 | result 313 | } 314 | } 315 | ); 316 | 317 | ($input:expr, $function:expr, $init:expr) => ( 318 | fold_many0!($input, call!($function), $init); 319 | ); 320 | ); 321 | 322 | /// `regex!(regexp) => &[T] -> IResult<&[T], &[T]>` 323 | /// Return the first match. 324 | /// 325 | /// This is exactly like `re_bytes_find_static!` from nom, except that 326 | /// it works on `Span`. 327 | #[macro_export] 328 | macro_rules! regex ( 329 | ($input:expr, $regex:expr) => ( 330 | { 331 | use nom::Slice; 332 | 333 | regex_bytes!(RE, $regex); 334 | 335 | if let Some(first_match) = RE.find($input.as_slice()) { 336 | Ok(($input.slice(first_match.end()..), $input.slice(first_match.start()..first_match.end()))) 337 | } else { 338 | let output: $crate::Result<_, _> = Err($crate::Error::Error($crate::Context::Code($input, $crate::ErrorKind::RegexpFind))); 339 | 340 | output 341 | } 342 | } 343 | ) 344 | ); 345 | 346 | /// `map_res_and_input!(I -> IResult, O -> Result

) => I -> IResult` 347 | /// Map a function returning a `Result` on the output of a parser. The 348 | /// original parser input is accessible as the last argument of the 349 | /// mapper. 350 | /// 351 | /// This is exactly like `map_res!` from nom, except that the input is 352 | /// passed. 353 | #[macro_export] 354 | macro_rules! map_res_and_input ( 355 | // Internal parser, do not use directly 356 | (__impl $input:expr, $submacro:ident!($($arguments:tt)*), $submacro2:ident!($($arguments2:tt)*)) => ( 357 | { 358 | use ::std::result::Result::*; 359 | use $crate::Error; 360 | 361 | let i_ = $input.clone(); 362 | ($submacro!(i_, $($arguments)*)).and_then(|(i,o)| { 363 | match $submacro2!(o, $($arguments2)*) { 364 | Ok(output) => { 365 | Ok((i, output)) 366 | }, 367 | 368 | Err(_) => { 369 | let e = $crate::ErrorKind::MapRes; 370 | 371 | Err(Error::Error(error_position!($input, e))) 372 | }, 373 | } 374 | }) 375 | } 376 | ); 377 | ($input:expr, $submacro:ident!($($arguments:tt)* ), $mapper:expr) => ( 378 | map_res_and_input!(__impl $input, $submacro!($($arguments)*), call!($mapper, $input)); 379 | ); 380 | ($input:expr, $submacro:ident!($($arguments:tt)*), $submacro2:ident!($($arguments2:tt)*)) => ( 381 | map_res_and_input!(__impl $input, $submacro!($($arguments)*), $submacro2!($input, $($arguments2)*)); 382 | ); 383 | ($input:expr, $parser:expr, $mapper:expr) => ( 384 | map_res_and_input!(__impl $input, call!($parser), call!($mapper, $input)); 385 | ); 386 | ($input:expr, $parser:expr, $submacro:ident!($($arguments:tt)*)) => ( 387 | map_res_and_input!(__impl $input, call!($parser), $submacro!($input, $($arguments)*)); 388 | ); 389 | ); 390 | 391 | /// Create a `SmallVec` and push items into it. 392 | /// 393 | /// This macro almost works like `vec![]`. It allows to create a 394 | /// `SmallVec` and push items into it. This macro does not define the 395 | /// type of the data, so the type of items and capacity must be 396 | /// infered. 397 | /// 398 | /// # Examples 399 | /// 400 | /// The following example creates a small vector. Since its size is 401 | /// lower or equal to 3, it is stored on the stack (inline), i.e. it has not “spilled”. 402 | /// 403 | /// ``` 404 | /// # extern crate smallvec; 405 | /// use smallvec::SmallVec; 406 | /// # #[macro_use] 407 | /// # extern crate tagua_parser; 408 | /// 409 | /// # fn main() { 410 | /// let handle: SmallVec<[_; 3]> = smallvec![1i32, 2, 3]; 411 | /// 412 | /// assert_eq!(handle[0], 1); 413 | /// assert_eq!(handle.len(), 3); 414 | /// assert!(!handle.spilled()); 415 | /// # } 416 | /// ``` 417 | /// 418 | /// The following example is similar to the previous one, but the 419 | /// small vector will spilled into the heap after a new item is pushed 420 | /// because it overflows the initial capacity. Note that `handle` must 421 | /// be mutable to be able to push a new item. 422 | /// 423 | /// ``` 424 | /// # extern crate smallvec; 425 | /// use smallvec::SmallVec; 426 | /// # #[macro_use] 427 | /// # extern crate tagua_parser; 428 | /// 429 | /// # fn main() { 430 | /// let mut handle: SmallVec<[_; 3]> = smallvec![1i32, 2, 3]; 431 | /// 432 | /// assert!(!handle.spilled()); 433 | /// 434 | /// handle.push(4); 435 | /// 436 | /// assert!(handle.spilled()); 437 | /// # } 438 | /// ``` 439 | /// 440 | /// This example will **not** compile, because the type for `handle` cannot be infered. 441 | /// 442 | /// ```rust,ignore 443 | /// # extern crate smallvec; 444 | /// # #[macro_use] 445 | /// # extern crate tagua_parser; 446 | /// # 447 | /// # fn main() { 448 | /// let handle = smallvec![1i32, 2, 3]; 449 | /// # } 450 | /// ``` 451 | #[macro_export] 452 | macro_rules! smallvec [ 453 | ($($e:expr),+) => ({ 454 | let mut output = ::smallvec::SmallVec::new(); 455 | $(output.push($e);)+ 456 | 457 | output 458 | }); 459 | 460 | () => ( 461 | ::smallvec::SmallVec::new() 462 | ); 463 | ]; 464 | 465 | 466 | #[cfg(test)] 467 | mod tests { 468 | use smallvec::SmallVec; 469 | use super::ErrorKindCustom; 470 | use super::super::internal::{ 471 | Context, 472 | Error, 473 | ErrorKind, 474 | Needed 475 | }; 476 | use super::super::tokens::Span; 477 | 478 | #[test] 479 | fn case_exclude_empty_set() { 480 | named!( 481 | test, 482 | exclude!( 483 | is_a!("abcdef"), 484 | alt!( 485 | tag!("abc") 486 | | tag!("ace") 487 | ) 488 | ) 489 | ); 490 | 491 | assert_eq!(test(&b"fedabc"[..]), Ok((&b""[..], &b"fedabc"[..]))); 492 | } 493 | 494 | #[test] 495 | fn case_exclude_one_branch() { 496 | named!( 497 | test, 498 | exclude!( 499 | is_a!("abcdef"), 500 | alt!( 501 | tag!("abc") 502 | | tag!("ace") 503 | ) 504 | ) 505 | ); 506 | 507 | assert_eq!(test(&b"abcabc"[..]), Err(Error::Error(Context::Code(&b"abcabc"[..], ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 508 | } 509 | 510 | #[test] 511 | fn case_exclude_another_branch() { 512 | named!( 513 | test, 514 | exclude!( 515 | is_a!("abcdef"), 516 | alt!( 517 | tag!("abc") 518 | | tag!("ace") 519 | ) 520 | ) 521 | ); 522 | 523 | assert_eq!(test(&b"acebdf"[..]), Err(Error::Error(Context::Code(&b"acebdf"[..], ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 524 | } 525 | 526 | #[test] 527 | fn case_exclude_incomplete() { 528 | named!( 529 | test, 530 | exclude!( 531 | take!(3), 532 | alt!( 533 | tag!("abc") 534 | | tag!("ace") 535 | ) 536 | ) 537 | ); 538 | 539 | assert_eq!(test(&b"a"[..]), Err(Error::Incomplete(Needed::Size(3)))); 540 | } 541 | 542 | #[test] 543 | fn case_exclude_incomplete_submacro() { 544 | named!( 545 | test, 546 | exclude!( 547 | take!(3), 548 | take!(5) 549 | ) 550 | ); 551 | 552 | assert_eq!(test(&b"abcdef"[..]), Ok((&b"def"[..], &b"abc"[..]))); 553 | } 554 | 555 | #[test] 556 | fn case_first_with_whitespace() { 557 | named!(hello, tag!(b"hello")); 558 | named!(test1, first!(tag!(b"hello"))); 559 | named!(test2, first!(hello)); 560 | 561 | let input = Span::new(b" \nhello\t\r"); 562 | let output = Ok((Span::new_at(b"\t\r", 8, 2, 6), Span::new_at(b"hello", 3, 2, 1))); 563 | 564 | assert_eq!(test1(input), output); 565 | assert_eq!(test2(input), output); 566 | } 567 | 568 | #[test] 569 | fn case_first_with_comment() { 570 | named!(hello, tag!(b"hello")); 571 | named!(test1, first!(tag!(b"hello"))); 572 | named!(test2, first!(hello)); 573 | 574 | let input = Span::new(b"/* foo */hello/* bar */"); 575 | let output = Ok((Span::new_at(b"/* bar */", 14, 1, 15), Span::new_at(b"hello", 9, 1, 10))); 576 | 577 | assert_eq!(test1(input), output); 578 | assert_eq!(test2(input), output); 579 | } 580 | 581 | #[test] 582 | fn case_first_with_whitespace_and_comment() { 583 | named!(hello, tag!(b"hello")); 584 | named!(test1, first!(tag!(b"hello"))); 585 | named!(test2, first!(hello)); 586 | 587 | let input = Span::new(b"/* foo */ \nhello/* bar */\t"); 588 | let output = Ok((Span::new_at(b"/* bar */\t", 17, 2, 6), Span::new_at(b"hello", 12, 2, 1))); 589 | 590 | assert_eq!(test1(input), output); 591 | assert_eq!(test2(input), output); 592 | } 593 | 594 | #[test] 595 | fn case_itag() { 596 | named!(test1, itag!(b"foobar")); 597 | named!(test2, itag!(b"fOoBaR")); 598 | 599 | let input = Span::new(&b"FoObArBaZQuX"[..]); 600 | 601 | assert_eq!(test1(input), Ok((Span::new_at(&b"BaZQuX"[..], 6, 1, 7), Span::new_at(&b"foobar"[..], 0, 1, 1)))); 602 | assert_eq!(test2(input), Ok((Span::new_at(&b"BaZQuX"[..], 6, 1, 7), Span::new_at(&b"fOoBaR"[..], 0, 1, 1)))); 603 | } 604 | 605 | #[test] 606 | fn case_itag_incomplete() { 607 | named!(test1, itag!(b"foobar")); 608 | named!(test2, itag!(b"FoObAR")); 609 | 610 | let input = Span::new(&b"FoOb"[..]); 611 | let output = Err(Error::Incomplete(Needed::Size(6))); 612 | 613 | assert_eq!(test1(input), output); 614 | assert_eq!(test2(input), output); 615 | } 616 | 617 | #[test] 618 | fn case_itag_error() { 619 | named!(test, itag!(b"foobar")); 620 | 621 | assert_eq!(test(Span::new(&b"BaZQuX"[..])), Err(Error::Error(Context::Code(Span::new(&b"BaZQuX"[..]), ErrorKind::Custom(ErrorKindCustom::ITag as u32))))); 622 | } 623 | 624 | #[test] 625 | fn case_keyword() { 626 | named!(test1, keyword!(b"foobar")); 627 | named!(test2, keyword!(b"fOoBaR")); 628 | 629 | let input = Span::new(b"FoObArBaZQuX"); 630 | let output = Span::new_at(b"BaZQuX", 6, 1, 7); 631 | 632 | assert_eq!(test1(input), Ok((output, Span::new(b"foobar")))); 633 | assert_eq!(test2(input), Ok((output, Span::new(b"fOoBaR")))); 634 | } 635 | 636 | #[test] 637 | fn case_keyword_incomplete() { 638 | named!(test1, keyword!(b"foobar")); 639 | named!(test2, keyword!(b"FoObAR")); 640 | 641 | let input = Span::new(b"FoOb"); 642 | let output = Err(Error::Incomplete(Needed::Size(6))); 643 | 644 | assert_eq!(test1(input), output); 645 | assert_eq!(test2(input), output); 646 | } 647 | 648 | #[test] 649 | fn case_keyword_error() { 650 | named!(test, keyword!(b"foobar")); 651 | 652 | let input = Span::new(b"BaZQuX"); 653 | 654 | assert_eq!(test(input), Err(Error::Error(Context::Code(input, ErrorKind::Custom(ErrorKindCustom::ITag as u32))))); 655 | } 656 | 657 | #[test] 658 | fn case_fold_into_vector_many0() { 659 | named!( 660 | test>, 661 | fold_into_vector_many0!( 662 | tag!("abc"), 663 | Vec::new() 664 | ) 665 | ); 666 | 667 | if let Ok((_, vector)) = test(&b"abcabcabc"[..]) { 668 | assert_eq!(vector.capacity(), vector.len()); 669 | assert_eq!(vector.len(), 3); 670 | } else { 671 | assert!(false); 672 | } 673 | } 674 | 675 | #[test] 676 | fn case_smallvec_empty() { 677 | let input: SmallVec<[i32; 3]> = smallvec![]; 678 | 679 | assert_eq!(input.len(), 0); 680 | assert_eq!(input.capacity(), 3); 681 | } 682 | 683 | #[test] 684 | fn case_smallvec_capacity() { 685 | let input1: SmallVec<[i32; 3]> = smallvec![]; 686 | let input2: SmallVec<[i32; 5]> = smallvec![]; 687 | 688 | assert_eq!(input1.capacity(), 3); 689 | assert_eq!(input2.capacity(), 5); 690 | } 691 | 692 | #[test] 693 | fn case_smallvec_length() { 694 | let input: SmallVec<[_; 3]> = smallvec![1i32, 2]; 695 | 696 | assert_eq!(input.len(), 2); 697 | } 698 | 699 | #[test] 700 | fn case_smallvec_on_the_stack() { 701 | let input: SmallVec<[_; 3]> = smallvec![1i32, 2, 3]; 702 | 703 | assert_eq!(false, input.spilled()); 704 | } 705 | 706 | #[test] 707 | fn case_smallvec_on_the_heap() { 708 | let input: SmallVec<[_; 3]> = smallvec![1i32, 2, 3, 4]; 709 | 710 | assert_eq!(true, input.spilled()); 711 | } 712 | } 713 | -------------------------------------------------------------------------------- /source/rules/comments.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of comment rules. 33 | //! 34 | //! The list of all comments is provided by the PHP Language Specification in 35 | //! the [Grammar chapter, Comments 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#comments). 37 | 38 | use super::super::tokens::Span; 39 | use super::super::tokens; 40 | 41 | named_attr!( 42 | #[doc=" 43 | Recognize all kind of comments. 44 | 45 | A comment can be a single line (`//` or `#`) or a delimited block (`/* … */`). 46 | 47 | # Examples 48 | 49 | ``` 50 | use tagua_parser::Result; 51 | use tagua_parser::rules::comments::comment; 52 | use tagua_parser::tokens::Span; 53 | 54 | # fn main () { 55 | assert_eq!( 56 | comment(Span::new(b\"/* foo */ bar\")), 57 | Ok(( 58 | Span::new_at(b\" bar\", 9, 1, 10), 59 | Span::new_at(b\" foo \", 2, 1, 3) 60 | )) 61 | ); 62 | # } 63 | ``` 64 | "], 65 | pub comment, 66 | alt!( 67 | comment_delimited 68 | | comment_single_line 69 | ) 70 | ); 71 | 72 | named!( 73 | comment_delimited, 74 | preceded!( 75 | tag!(tokens::BLOCK_COMMENT_OPEN), 76 | take_until_and_consume!(tokens::BLOCK_COMMENT_CLOSE) 77 | ) 78 | ); 79 | 80 | named!( 81 | comment_single_line, 82 | preceded!( 83 | alt!(tag!(tokens::INLINE_COMMENT) | tag!(tokens::INLINE_COMMENT_HASH)), 84 | regex!(r"^(?-u).*?(\r\n|\n|$)") 85 | ) 86 | ); 87 | 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::{ 92 | comment, 93 | comment_delimited, 94 | comment_single_line 95 | }; 96 | use super::super::super::internal::{ 97 | Context, 98 | Error, 99 | ErrorKind, 100 | }; 101 | use super::super::super::tokens::Span; 102 | 103 | #[test] 104 | fn case_comment_single_line_double_slash_empty() { 105 | let input = Span::new(b"//"); 106 | let output = Ok((Span::new_at(b"", 2, 1, 3), Span::new_at(b"", 2, 1, 3))); 107 | 108 | assert_eq!(comment_single_line(input), output); 109 | assert_eq!(comment(input), output); 110 | } 111 | 112 | #[test] 113 | fn case_comment_single_line_double_slash_with_feed() { 114 | let input = Span::new(b"// foobar\nbazqux"); 115 | let output = Ok((Span::new_at(b"bazqux", 10, 2, 1), Span::new_at(b" foobar\n", 2, 1, 3))); 116 | 117 | assert_eq!(comment_single_line(input), output); 118 | assert_eq!(comment(input), output); 119 | } 120 | 121 | #[test] 122 | fn case_comment_single_line_double_slash_with_carriage_return_feed() { 123 | let input = Span::new(b"// foobar\r\nbazqux"); 124 | let output = Ok((Span::new_at(b"bazqux", 11, 2, 1), Span::new_at(b" foobar\r\n", 2, 1, 3))); 125 | 126 | assert_eq!(comment_single_line(input), output); 127 | assert_eq!(comment(input), output); 128 | } 129 | 130 | #[test] 131 | fn case_comment_single_line_double_slash_without_ending() { 132 | let input = Span::new(b"// foobar"); 133 | let output = Ok((Span::new_at(b"", 9, 1, 10), Span::new_at(b" foobar", 2, 1, 3))); 134 | 135 | assert_eq!(comment_single_line(input), output); 136 | assert_eq!(comment(input), output); 137 | } 138 | 139 | #[test] 140 | fn case_comment_single_line_double_slash_embedded() { 141 | let input = Span::new(b"//foo//bar"); 142 | let output = Ok((Span::new_at(b"", 10, 1, 11), Span::new_at(b"foo//bar", 2, 1, 3))); 143 | 144 | assert_eq!(comment_single_line(input), output); 145 | assert_eq!(comment(input), output); 146 | } 147 | 148 | #[test] 149 | fn case_comment_single_line_hash_empty() { 150 | let input = Span::new(b"#"); 151 | let output = Ok((Span::new_at(b"", 1, 1, 2), Span::new_at(b"", 1, 1, 2))); 152 | 153 | assert_eq!(comment_single_line(input), output); 154 | assert_eq!(comment(input), output); 155 | } 156 | 157 | #[test] 158 | fn case_comment_single_line_hash_with_line_feed() { 159 | let input = Span::new(b"# foobar\nbazqux"); 160 | let output = Ok((Span::new_at(b"bazqux", 9, 2, 1), Span::new_at(b" foobar\n", 1, 1, 2))); 161 | 162 | assert_eq!(comment_single_line(input), output); 163 | assert_eq!(comment(input), output); 164 | } 165 | 166 | #[test] 167 | fn case_comment_single_line_hash_with_carriage_return_line_feed() { 168 | let input = Span::new(b"# foobar\r\nbazqux"); 169 | let output = Ok((Span::new_at(b"bazqux", 10, 2, 1), Span::new_at(b" foobar\r\n", 1, 1, 2))); 170 | 171 | assert_eq!(comment_single_line(input), output); 172 | assert_eq!(comment(input), output); 173 | } 174 | 175 | #[test] 176 | fn case_comment_single_line_hash_without_line_ending() { 177 | let input = Span::new(b"# foobar"); 178 | let output = Ok((Span::new_at(b"", 8, 1, 9), Span::new_at(b" foobar", 1, 1, 2))); 179 | 180 | assert_eq!(comment_single_line(input), output); 181 | assert_eq!(comment(input), output); 182 | } 183 | 184 | #[test] 185 | fn case_comment_single_line_hash_embedded() { 186 | let input = Span::new(b"#foo#bar"); 187 | let output = Ok((Span::new_at(b"", 8, 1, 9), Span::new_at(b"foo#bar", 1, 1, 2))); 188 | 189 | assert_eq!(comment_single_line(input), output); 190 | assert_eq!(comment(input), output); 191 | } 192 | 193 | #[test] 194 | fn case_comment_delimited_empty() { 195 | let input = Span::new(b"/**/xyz"); 196 | let output = Ok((Span::new_at(b"xyz", 4, 1, 5), Span::new_at(b"", 2, 1, 3))); 197 | 198 | assert_eq!(comment_delimited(input), output); 199 | assert_eq!(comment(input), output); 200 | } 201 | 202 | #[test] 203 | fn case_comment_delimited_almost_nested() { 204 | let input = Span::new(b"/****/xyz"); 205 | let output = Ok((Span::new_at(b"xyz", 6, 1, 7), Span::new_at(b"**", 2, 1, 3))); 206 | 207 | assert_eq!(comment_delimited(input), output); 208 | assert_eq!(comment(input), output); 209 | } 210 | 211 | #[test] 212 | fn case_comment_delimited() { 213 | let input = Span::new(b"/* foo bar\nbaz\r\nqux // hello,\n /*world!*/xyz */"); 214 | let output = Ok((Span::new_at(b"xyz */", 41, 4, 12), Span::new_at(b" foo bar\nbaz\r\nqux // hello,\n /*world!", 2, 1, 3))); 215 | 216 | assert_eq!(comment_delimited(input), output); 217 | assert_eq!(comment(input), output); 218 | } 219 | 220 | #[test] 221 | fn case_invalid_comment_delimited_not_closed() { 222 | let input = Span::new(b"/*foobar"); 223 | 224 | assert_eq!(comment_delimited(input), Err(Error::Error(Context::Code(Span::new_at(b"foobar", 2, 1, 3), ErrorKind::TakeUntilAndConsume)))); 225 | assert_eq!(comment(input), Err(Error::Error(Context::Code(input, ErrorKind::Alt)))); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /source/rules/expressions/assignment.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of assignment operator rules. 33 | //! 34 | //! The list of all assignment operators is provided by the PHP 35 | //! Language Specification in the [Expressions chapter, Assignment 36 | //! operators 37 | //! section](https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#assignment-operators). 38 | 39 | use super::super::expressions::expression; 40 | use super::super::super::ast::{ 41 | NAryOperation, 42 | BinaryOperator, 43 | Expression, 44 | TernaryOperator, 45 | }; 46 | use super::super::super::tokens::Span; 47 | use super::super::super::tokens; 48 | use super::super::tokens::qualified_name; 49 | 50 | named_attr!( 51 | #[doc=" 52 | Recognize all assignment expressions. 53 | "], 54 | pub assignment, 55 | map_res!( 56 | conditional, 57 | nary_expression_mapper 58 | ) 59 | ); 60 | 61 | #[inline] 62 | fn nary_expression_mapper<'a>(nary_operation: NAryOperation<'a>) -> Result, ()> { 63 | Ok(Expression::NAryOperation(nary_operation)) 64 | } 65 | 66 | named!( 67 | conditional, 68 | do_parse!( 69 | left_operand: coalesce >> 70 | result: fold_many0!( 71 | do_parse!( 72 | first!(tag!(tokens::TERNARY_THEN)) >> 73 | middle_operand: opt!(first!(expression)) >> 74 | first!(tag!(tokens::TERNARY_ELSE)) >> 75 | right_operand: first!(coalesce) >> 76 | (middle_operand, right_operand) 77 | ), 78 | left_operand, 79 | |accumulator, (middle_operand, right_operand)| { 80 | match middle_operand { 81 | Some(middle_operand) => { 82 | NAryOperation::Ternary { 83 | operator : TernaryOperator::Conditional, 84 | left_operand : Box::new(accumulator), 85 | middle_operand: Box::new(middle_operand), 86 | right_operand : Box::new(right_operand) 87 | } 88 | }, 89 | 90 | None => { 91 | NAryOperation::Binary { 92 | operator : BinaryOperator::Conditional, 93 | left_operand : Box::new(accumulator), 94 | right_operand: Box::new(right_operand) 95 | } 96 | } 97 | 98 | } 99 | } 100 | ) >> 101 | (result) 102 | ) 103 | ); 104 | 105 | macro_rules! left_to_right_binary_operation { 106 | ( 107 | $parser_name:ident: 108 | $operand:ident with 109 | $operator_token:ident as $operator_representation:ident 110 | ) => ( 111 | named!( 112 | $parser_name, 113 | do_parse!( 114 | left_operand: $operand >> 115 | result: fold_many0!( 116 | preceded!( 117 | first!(tag!(tokens::$operator_token)), 118 | first!($operand) 119 | ), 120 | left_operand, 121 | |accumulator, right_operand| { 122 | NAryOperation::Binary { 123 | operator : BinaryOperator::$operator_representation, 124 | left_operand : Box::new(accumulator), 125 | right_operand: Box::new(right_operand) 126 | } 127 | } 128 | ) >> 129 | (result) 130 | ) 131 | ); 132 | ); 133 | 134 | ( 135 | $parser_name:ident: 136 | $operand:ident with 137 | ($($operator_token:ident as $operator_representation:ident),*) 138 | ) => ( 139 | named!( 140 | $parser_name, 141 | do_parse!( 142 | left_operand: $operand >> 143 | result: fold_many0!( 144 | do_parse!( 145 | operator: first!( 146 | alt_complete!( 147 | $( 148 | tag!(tokens::$operator_token) => { 149 | |_| { BinaryOperator::$operator_representation } 150 | } 151 | )|* 152 | ) 153 | ) >> 154 | right_operand: first!($operand) >> 155 | (operator, right_operand) 156 | ), 157 | left_operand, 158 | |accumulator, (operator, right_operand)| { 159 | NAryOperation::Binary { 160 | operator : operator, 161 | left_operand : Box::new(accumulator), 162 | right_operand: Box::new(right_operand) 163 | } 164 | } 165 | ) >> 166 | (result) 167 | ) 168 | ); 169 | ) 170 | } 171 | 172 | macro_rules! right_to_left_binary_operation { 173 | ( 174 | $parser_name:ident: 175 | $operand:ident with 176 | $operator_token:ident as $operator_representation:ident 177 | ) => ( 178 | named!( 179 | $parser_name, 180 | alt_complete!( 181 | do_parse!( 182 | left_operand: $operand >> 183 | right_operand: preceded!( 184 | first!(tag!(tokens::$operator_token)), 185 | first!($parser_name) 186 | ) >> 187 | ( 188 | NAryOperation::Binary { 189 | operator : BinaryOperator::$operator_representation, 190 | left_operand : Box::new(left_operand), 191 | right_operand: Box::new(right_operand) 192 | } 193 | ) 194 | ) 195 | | $operand 196 | ) 197 | ); 198 | ) 199 | } 200 | 201 | right_to_left_binary_operation!(coalesce : logical_or with COALESCE as Coalesce); 202 | left_to_right_binary_operation!(logical_or : logical_and with BOOLEAN_OR as LogicalOr); 203 | left_to_right_binary_operation!(logical_and: bitwise_or with BOOLEAN_AND as LogicalAnd); 204 | left_to_right_binary_operation!(bitwise_or : bitwise_xor with BITWISE_OR as BitwiseOr); 205 | left_to_right_binary_operation!(bitwise_xor: bitwise_and with BITWISE_XOR as BitwiseXor); 206 | left_to_right_binary_operation!(bitwise_and: equality with BITWISE_AND as BitwiseAnd); 207 | left_to_right_binary_operation!( 208 | equality: 209 | relational with 210 | ( 211 | IDENTICAL as Identical, 212 | NOT_IDENTICAL as NotIdentical, 213 | EQUAL as Equal, 214 | NOT_EQUAL as NotEqual, 215 | NOT_EQUAL_BIS as NotEqual 216 | ) 217 | ); 218 | left_to_right_binary_operation!( 219 | relational: 220 | shift with 221 | ( 222 | COMPARE as Comparison, 223 | LESS_THAN_OR_EQUAL_TO as LessThanOrEqualTo, 224 | GREATER_THAN_OR_EQUAL_TO as GreaterThanOrEqualTo, 225 | LESS_THAN as LessThan, 226 | GREATER_THAN as GreaterThan 227 | ) 228 | ); 229 | left_to_right_binary_operation!( 230 | shift: 231 | additive with 232 | ( 233 | BITWISE_LEFT_SHIFT as BitwiseShiftLeft, 234 | BITWISE_RIGHT_SHIFT as BitwiseShiftRight 235 | ) 236 | ); 237 | left_to_right_binary_operation!( 238 | additive: 239 | multiplicative with 240 | ( 241 | ADD as Plus, 242 | SUBSTRACT as Minus, 243 | CONCATENATE as Dot 244 | ) 245 | ); 246 | left_to_right_binary_operation!( 247 | multiplicative: 248 | instanceof with 249 | ( 250 | MULTIPLY as Multiplication, 251 | DIVIDE as Division, 252 | MODULO as Modulo 253 | ) 254 | ); 255 | 256 | named!( 257 | instanceof, 258 | alt_complete!( 259 | do_parse!( 260 | subject: expression >> 261 | type_designator: preceded!( 262 | first!(tag!(tokens::INSTANCEOF)), 263 | first!( 264 | alt!( 265 | qualified_name => { 266 | |qualified_name| { 267 | Expression::Name(qualified_name) 268 | } 269 | } 270 | | expression 271 | ) 272 | ) 273 | ) >> 274 | ( 275 | NAryOperation::Binary { 276 | operator : BinaryOperator::InstanceOf, 277 | left_operand : Box::new(NAryOperation::Nullary(Box::new(subject))), 278 | right_operand: Box::new(NAryOperation::Nullary(Box::new(type_designator))) 279 | } 280 | ) 281 | ) 282 | | unary_operation 283 | ) 284 | ); 285 | 286 | named!( 287 | unary_operation, 288 | call!(leaf) 289 | ); 290 | 291 | named!( 292 | leaf, 293 | map_res!( 294 | expression, 295 | |expression| -> Result { 296 | Ok(NAryOperation::Nullary(Box::new(expression))) 297 | } 298 | ) 299 | ); 300 | 301 | 302 | #[cfg(test)] 303 | mod tests { 304 | use super::assignment; 305 | use super::super::super::super::ast::{ 306 | BinaryOperator, 307 | Expression, 308 | Literal, 309 | NAryOperation, 310 | Name, 311 | TernaryOperator, 312 | Variable 313 | }; 314 | use super::super::super::super::tokens::{ 315 | Span, 316 | Token 317 | }; 318 | 319 | /// Build a nullary operation. 320 | macro_rules! nullary_operation { 321 | ($expression:expr) => ( 322 | NAryOperation::Nullary(Box::new($expression)) 323 | ) 324 | } 325 | 326 | /// Build a binary operation. 327 | macro_rules! binary_operation { 328 | ($operator:ident, $left_operand:expr, $right_operand:expr) => ( 329 | NAryOperation::Binary { 330 | operator : BinaryOperator::$operator, 331 | left_operand : Box::new($left_operand), 332 | right_operand: Box::new($right_operand) 333 | } 334 | ) 335 | } 336 | 337 | /// Build a ternary operation. 338 | macro_rules! ternary_operation { 339 | ($operator:ident, $left_operand:expr, $middle_operand:expr, $right_operand:expr) => ( 340 | NAryOperation::Ternary { 341 | operator : TernaryOperator::$operator, 342 | left_operand : Box::new($left_operand), 343 | middle_operand: Box::new($middle_operand), 344 | right_operand : Box::new($right_operand) 345 | } 346 | ) 347 | } 348 | 349 | /// Build a literal expression with an integer inside. 350 | macro_rules! integer { 351 | ($value:expr, $span:expr) => ( 352 | Expression::Literal(Literal::Integer(Token::new($value, $span))) 353 | ) 354 | } 355 | 356 | #[test] 357 | fn case_conditional() { 358 | let input = Span::new(b"1 ? 2 : 3 ? 4 : 5"); 359 | let output = Ok(( 360 | Span::new_at(b"", 17, 1, 18), 361 | Expression::NAryOperation( 362 | ternary_operation!( 363 | Conditional, 364 | ternary_operation!( 365 | Conditional, 366 | nullary_operation!(integer!(1, Span::new(b"1"))), 367 | integer!(2, Span::new_at(b"2", 4, 1, 5)), 368 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 369 | ), 370 | integer!(4, Span::new_at(b"4", 12, 1, 13)), 371 | nullary_operation!(integer!(5, Span::new_at(b"5", 16, 1, 17))) 372 | ) 373 | ) 374 | )); 375 | 376 | assert_eq!(assignment(input), output); 377 | } 378 | 379 | #[test] 380 | fn case_conditional_with_none_middle_operator() { 381 | let input = Span::new(b"1 ?: 2 ? : 3"); 382 | let output = Ok(( 383 | Span::new_at(b"", 12, 1, 13), 384 | Expression::NAryOperation( 385 | binary_operation!( 386 | Conditional, 387 | binary_operation!( 388 | Conditional, 389 | nullary_operation!(integer!(1, Span::new(b"1"))), 390 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 391 | ), 392 | nullary_operation!(integer!(3, Span::new_at(b"3", 11, 1, 12))) 393 | ) 394 | ) 395 | )); 396 | 397 | assert_eq!(assignment(input), output); 398 | } 399 | 400 | #[test] 401 | fn case_logical_or() { 402 | let input = Span::new(b"1 || 2 || 3"); 403 | let output = Ok(( 404 | Span::new_at(b"", 11, 1, 12), 405 | Expression::NAryOperation( 406 | binary_operation!( 407 | LogicalOr, 408 | binary_operation!( 409 | LogicalOr, 410 | nullary_operation!(integer!(1, Span::new(b"1"))), 411 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 412 | ), 413 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 414 | ) 415 | ) 416 | )); 417 | 418 | assert_eq!(assignment(input), output); 419 | } 420 | 421 | #[test] 422 | fn case_logical_and() { 423 | let input = Span::new(b"1 && 2 && 3"); 424 | let output = Ok(( 425 | Span::new_at(b"", 11, 1, 12), 426 | Expression::NAryOperation( 427 | binary_operation!( 428 | LogicalAnd, 429 | binary_operation!( 430 | LogicalAnd, 431 | nullary_operation!(integer!(1, Span::new(b"1"))), 432 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 433 | ), 434 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 435 | ) 436 | ) 437 | )); 438 | 439 | assert_eq!(assignment(input), output); 440 | } 441 | 442 | #[test] 443 | fn case_bitwise_or() { 444 | let input = Span::new(b"1 | 2 | 3"); 445 | let output = Ok(( 446 | Span::new_at(b"", 9, 1, 10), 447 | Expression::NAryOperation( 448 | binary_operation!( 449 | BitwiseOr, 450 | binary_operation!( 451 | BitwiseOr, 452 | nullary_operation!(integer!(1, Span::new(b"1"))), 453 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 454 | ), 455 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 456 | ) 457 | ) 458 | )); 459 | 460 | assert_eq!(assignment(input), output); 461 | } 462 | 463 | #[test] 464 | fn case_bitwise_xor() { 465 | let input = Span::new(b"1 ^ 2 ^ 3"); 466 | let output = Ok(( 467 | Span::new_at(b"", 9, 1, 10), 468 | Expression::NAryOperation( 469 | binary_operation!( 470 | BitwiseXor, 471 | binary_operation!( 472 | BitwiseXor, 473 | nullary_operation!(integer!(1, Span::new(b"1"))), 474 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 475 | ), 476 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 477 | ) 478 | ) 479 | )); 480 | 481 | assert_eq!(assignment(input), output); 482 | } 483 | 484 | #[test] 485 | fn case_bitwise_and() { 486 | let input = Span::new(b"1 & 2 & 3"); 487 | let output = Ok(( 488 | Span::new_at(b"", 9, 1, 10), 489 | Expression::NAryOperation( 490 | binary_operation!( 491 | BitwiseAnd, 492 | binary_operation!( 493 | BitwiseAnd, 494 | nullary_operation!(integer!(1, Span::new(b"1"))), 495 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 496 | ), 497 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 498 | ) 499 | ) 500 | )); 501 | 502 | assert_eq!(assignment(input), output); 503 | } 504 | 505 | #[test] 506 | fn case_equality_identical() { 507 | let input = Span::new(b"1 === 2 === 3"); 508 | let output = Ok(( 509 | Span::new_at(b"", 13, 1, 14), 510 | Expression::NAryOperation( 511 | binary_operation!( 512 | Identical, 513 | binary_operation!( 514 | Identical, 515 | nullary_operation!(integer!(1, Span::new(b"1"))), 516 | nullary_operation!(integer!(2, Span::new_at(b"2", 6, 1, 7))) 517 | ), 518 | nullary_operation!(integer!(3, Span::new_at(b"3", 12, 1, 13))) 519 | ) 520 | ) 521 | )); 522 | 523 | assert_eq!(assignment(input), output); 524 | } 525 | 526 | #[test] 527 | fn case_equality_not_identical() { 528 | let input = Span::new(b"1 !== 2 !== 3"); 529 | let output = Ok(( 530 | Span::new_at(b"", 13, 1, 14), 531 | Expression::NAryOperation( 532 | binary_operation!( 533 | NotIdentical, 534 | binary_operation!( 535 | NotIdentical, 536 | nullary_operation!(integer!(1, Span::new(b"1"))), 537 | nullary_operation!(integer!(2, Span::new_at(b"2", 6, 1, 7))) 538 | ), 539 | nullary_operation!(integer!(3, Span::new_at(b"3", 12, 1, 13))) 540 | ) 541 | ) 542 | )); 543 | 544 | assert_eq!(assignment(input), output); 545 | } 546 | 547 | #[test] 548 | fn case_equality_equal() { 549 | let input = Span::new(b"1 == 2 == 3"); 550 | let output = Ok(( 551 | Span::new_at(b"", 11, 1, 12), 552 | Expression::NAryOperation( 553 | binary_operation!( 554 | Equal, 555 | binary_operation!( 556 | Equal, 557 | nullary_operation!(integer!(1, Span::new(b"1"))), 558 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 559 | ), 560 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 561 | ) 562 | ) 563 | )); 564 | 565 | assert_eq!(assignment(input), output); 566 | } 567 | 568 | #[test] 569 | fn case_equality_not_equal() { 570 | let input = Span::new(b"1 != 2 != 3"); 571 | let output = Ok(( 572 | Span::new_at(b"", 11, 1, 12), 573 | Expression::NAryOperation( 574 | binary_operation!( 575 | NotEqual, 576 | binary_operation!( 577 | NotEqual, 578 | nullary_operation!(integer!(1, Span::new(b"1"))), 579 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 580 | ), 581 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 582 | ) 583 | ) 584 | )); 585 | 586 | assert_eq!(assignment(input), output); 587 | } 588 | 589 | #[test] 590 | fn case_equality_not_equal_bis() { 591 | let input = Span::new(b"1 <> 2 <> 3"); 592 | let output = Ok(( 593 | Span::new_at(b"", 11, 1, 12), 594 | Expression::NAryOperation( 595 | binary_operation!( 596 | NotEqual, 597 | binary_operation!( 598 | NotEqual, 599 | nullary_operation!(integer!(1, Span::new(b"1"))), 600 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 601 | ), 602 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 603 | ) 604 | ) 605 | )); 606 | 607 | assert_eq!(assignment(input), output); 608 | } 609 | 610 | #[test] 611 | fn case_relational_compare() { 612 | let input = Span::new(b"1 <=> 2 <=> 3"); 613 | let output = Ok(( 614 | Span::new_at(b"", 13, 1, 14), 615 | Expression::NAryOperation( 616 | binary_operation!( 617 | Comparison, 618 | binary_operation!( 619 | Comparison, 620 | nullary_operation!(integer!(1, Span::new(b"1"))), 621 | nullary_operation!(integer!(2, Span::new_at(b"2", 6, 1, 7))) 622 | ), 623 | nullary_operation!(integer!(3, Span::new_at(b"3", 12, 1, 13))) 624 | ) 625 | ) 626 | )); 627 | 628 | assert_eq!(assignment(input), output); 629 | } 630 | 631 | #[test] 632 | fn case_relational_less_than_or_equal_to() { 633 | let input = Span::new(b"1 <= 2 <= 3"); 634 | let output = Ok(( 635 | Span::new_at(b"", 11, 1, 12), 636 | Expression::NAryOperation( 637 | binary_operation!( 638 | LessThanOrEqualTo, 639 | binary_operation!( 640 | LessThanOrEqualTo, 641 | nullary_operation!(integer!(1, Span::new(b"1"))), 642 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 643 | ), 644 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 645 | ) 646 | ) 647 | )); 648 | 649 | assert_eq!(assignment(input), output); 650 | } 651 | 652 | #[test] 653 | fn case_relational_greater_than_or_equal_to() { 654 | let input = Span::new(b"1 >= 2 >= 3"); 655 | let output = Ok(( 656 | Span::new_at(b"", 11, 1, 12), 657 | Expression::NAryOperation( 658 | binary_operation!( 659 | GreaterThanOrEqualTo, 660 | binary_operation!( 661 | GreaterThanOrEqualTo, 662 | nullary_operation!(integer!(1, Span::new(b"1"))), 663 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 664 | ), 665 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 666 | ) 667 | ) 668 | )); 669 | 670 | assert_eq!(assignment(input), output); 671 | } 672 | 673 | #[test] 674 | fn case_relational_less_than() { 675 | let input = Span::new(b"1 < 2 < 3"); 676 | let output = Ok(( 677 | Span::new_at(b"", 9, 1, 10), 678 | Expression::NAryOperation( 679 | binary_operation!( 680 | LessThan, 681 | binary_operation!( 682 | LessThan, 683 | nullary_operation!(integer!(1, Span::new(b"1"))), 684 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 685 | ), 686 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 687 | ) 688 | ) 689 | )); 690 | 691 | assert_eq!(assignment(input), output); 692 | } 693 | 694 | #[test] 695 | fn case_relational_greater_than() { 696 | let input = Span::new(b"1 > 2 > 3"); 697 | let output = Ok(( 698 | Span::new_at(b"", 9, 1, 10), 699 | Expression::NAryOperation( 700 | binary_operation!( 701 | GreaterThan, 702 | binary_operation!( 703 | GreaterThan, 704 | nullary_operation!(integer!(1, Span::new(b"1"))), 705 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 706 | ), 707 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 708 | ) 709 | ) 710 | )); 711 | 712 | assert_eq!(assignment(input), output); 713 | } 714 | 715 | #[test] 716 | fn case_shift_left() { 717 | let input = Span::new(b"1 << 2 << 3"); 718 | let output = Ok(( 719 | Span::new_at(b"", 11, 1, 12), 720 | Expression::NAryOperation( 721 | binary_operation!( 722 | BitwiseShiftLeft, 723 | binary_operation!( 724 | BitwiseShiftLeft, 725 | nullary_operation!(integer!(1, Span::new(b"1"))), 726 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 727 | ), 728 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 729 | ) 730 | ) 731 | )); 732 | 733 | assert_eq!(assignment(input), output); 734 | } 735 | 736 | #[test] 737 | fn case_shift_right() { 738 | let input = Span::new(b"1 >> 2 >> 3"); 739 | let output = Ok(( 740 | Span::new_at(b"", 11, 1, 12), 741 | Expression::NAryOperation( 742 | binary_operation!( 743 | BitwiseShiftRight, 744 | binary_operation!( 745 | BitwiseShiftRight, 746 | nullary_operation!(integer!(1, Span::new(b"1"))), 747 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))) 748 | ), 749 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 750 | ) 751 | ) 752 | )); 753 | 754 | assert_eq!(assignment(input), output); 755 | } 756 | 757 | #[test] 758 | fn case_additive_plus() { 759 | let input = Span::new(b"1 + 2 + 3"); 760 | let output = Ok(( 761 | Span::new_at(b"", 9, 1, 10), 762 | Expression::NAryOperation( 763 | binary_operation!( 764 | Plus, 765 | binary_operation!( 766 | Plus, 767 | nullary_operation!(integer!(1, Span::new(b"1"))), 768 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 769 | ), 770 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 771 | ) 772 | ) 773 | )); 774 | 775 | assert_eq!(assignment(input), output); 776 | } 777 | 778 | #[test] 779 | fn case_additive_minus() { 780 | let input = Span::new(b"1 - 2 - 3"); 781 | let output = Ok(( 782 | Span::new_at(b"", 9, 1, 10), 783 | Expression::NAryOperation( 784 | binary_operation!( 785 | Minus, 786 | binary_operation!( 787 | Minus, 788 | nullary_operation!(integer!(1, Span::new(b"1"))), 789 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 790 | ), 791 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 792 | ) 793 | ) 794 | )); 795 | 796 | assert_eq!(assignment(input), output); 797 | } 798 | 799 | #[test] 800 | fn case_additive_dot() { 801 | let input = Span::new(b"1 . 2 . 3"); 802 | let output = Ok(( 803 | Span::new_at(b"", 9, 1, 10), 804 | Expression::NAryOperation( 805 | binary_operation!( 806 | Dot, 807 | binary_operation!( 808 | Dot, 809 | nullary_operation!(integer!(1, Span::new(b"1"))), 810 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 811 | ), 812 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 813 | ) 814 | ) 815 | )); 816 | 817 | assert_eq!(assignment(input), output); 818 | } 819 | 820 | #[test] 821 | fn case_multiplicative_multiply() { 822 | let input = Span::new(b"1 * 2 * 3"); 823 | let output = Ok(( 824 | Span::new_at(b"", 9, 1, 10), 825 | Expression::NAryOperation( 826 | binary_operation!( 827 | Multiplication, 828 | binary_operation!( 829 | Multiplication, 830 | nullary_operation!(integer!(1, Span::new(b"1"))), 831 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 832 | ), 833 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 834 | ) 835 | ) 836 | )); 837 | 838 | assert_eq!(assignment(input), output); 839 | } 840 | 841 | #[test] 842 | fn case_multiplicative_division() { 843 | let input = Span::new(b"1 / 2 / 3"); 844 | let output = Ok(( 845 | Span::new_at(b"", 9, 1, 10), 846 | Expression::NAryOperation( 847 | binary_operation!( 848 | Division, 849 | binary_operation!( 850 | Division, 851 | nullary_operation!(integer!(1, Span::new(b"1"))), 852 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 853 | ), 854 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 855 | ) 856 | ) 857 | )); 858 | 859 | assert_eq!(assignment(input), output); 860 | } 861 | 862 | #[test] 863 | fn case_multiplicative_modulo() { 864 | let input = Span::new(b"1 % 2 % 3"); 865 | let output = Ok(( 866 | Span::new_at(b"", 9, 1, 10), 867 | Expression::NAryOperation( 868 | binary_operation!( 869 | Modulo, 870 | binary_operation!( 871 | Modulo, 872 | nullary_operation!(integer!(1, Span::new(b"1"))), 873 | nullary_operation!(integer!(2, Span::new_at(b"2", 4, 1, 5))) 874 | ), 875 | nullary_operation!(integer!(3, Span::new_at(b"3", 8, 1, 9))) 876 | ) 877 | ) 878 | )); 879 | 880 | assert_eq!(assignment(input), output); 881 | } 882 | 883 | #[test] 884 | fn case_instanceof_with_qualified_name_type_designator() { 885 | let input = Span::new(b"1 instanceof C"); 886 | let output = Ok(( 887 | Span::new_at(b"", 14, 1, 15), 888 | Expression::NAryOperation( 889 | binary_operation!( 890 | InstanceOf, 891 | nullary_operation!(integer!(1, Span::new(b"1"))), 892 | nullary_operation!(Expression::Name(Name::Unqualified(Span::new_at(b"C", 13, 1, 14)))) 893 | ) 894 | ) 895 | )); 896 | 897 | assert_eq!(assignment(input), output); 898 | } 899 | 900 | #[test] 901 | fn case_instanceof_with_expression_type_designator() { 902 | let input = Span::new(b"1 instanceof $c"); 903 | let output = Ok(( 904 | Span::new_at(b"", 15, 1, 16), 905 | Expression::NAryOperation( 906 | binary_operation!( 907 | InstanceOf, 908 | nullary_operation!(integer!(1, Span::new(b"1"))), 909 | nullary_operation!(Expression::Variable(Variable(Span::new_at(b"c", 14, 1, 15)))) 910 | ) 911 | ) 912 | )); 913 | 914 | assert_eq!(assignment(input), output); 915 | } 916 | 917 | #[test] 918 | fn case_coalesce() { 919 | let input = Span::new(b"1 ?? 2 ?? 3"); 920 | let output = Ok(( 921 | Span::new_at(b"", 11, 1, 12), 922 | Expression::NAryOperation( 923 | binary_operation!( 924 | Coalesce, 925 | nullary_operation!(integer!(1, Span::new(b"1"))), 926 | binary_operation!( 927 | Coalesce, 928 | nullary_operation!(integer!(2, Span::new_at(b"2", 5, 1, 6))), 929 | nullary_operation!(integer!(3, Span::new_at(b"3", 10, 1, 11))) 930 | ) 931 | ) 932 | ) 933 | )); 934 | 935 | assert_eq!(assignment(input), output); 936 | } 937 | } 938 | -------------------------------------------------------------------------------- /source/rules/expressions/constant.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of constant expression rules. 33 | //! 34 | //! The list of all constant expressions is provided by the PHP Language 35 | //! Specification in the [Grammar chapter, Expressions 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#constant-expressions). 37 | 38 | use super::primaries::array; 39 | use super::super::literals::literal; 40 | use super::super::super::ast::{ 41 | Expression, 42 | Literal 43 | }; 44 | use super::super::super::tokens::Span; 45 | 46 | named_attr!( 47 | #[doc=" 48 | Recognize all kind of constant expressions. 49 | "], 50 | pub constant_expression, 51 | alt!( 52 | literal => { literal_mapper } 53 | | array 54 | ) 55 | ); 56 | 57 | #[inline] 58 | fn literal_mapper(literal: Literal) -> Expression { 59 | Expression::Literal(literal) 60 | } 61 | -------------------------------------------------------------------------------- /source/rules/expressions/mod.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of expression rules. 33 | //! 34 | //! The list of all expressions is provided by the PHP Language Specification 35 | //! in the [Grammar chapter, Expressions 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#expressions). 37 | 38 | pub mod assignment; 39 | pub mod constant; 40 | pub mod primaries; 41 | 42 | use super::super::ast::Expression; 43 | use super::super::tokens::Span; 44 | 45 | named_attr!( 46 | #[doc=" 47 | Recognize all kind of expressions. 48 | 49 | # Examples 50 | 51 | ``` 52 | use std::borrow::Cow; 53 | use tagua_parser::Result; 54 | use tagua_parser::ast::{Expression, Literal}; 55 | use tagua_parser::rules::expressions::expression; 56 | use tagua_parser::tokens::{ 57 | Span, 58 | Token 59 | }; 60 | 61 | # fn main () { 62 | assert_eq!( 63 | expression(Span::new(b\"echo 'Hello, World!'\")), 64 | Ok(( 65 | Span::new_at(b\"\", 20, 1, 21), 66 | Expression::Echo(vec![ 67 | Expression::Literal( 68 | Literal::String( 69 | Token::new( 70 | Cow::from(&b\"Hello, World!\"[..]), 71 | Span::new_at(b\"'Hello, World!'\", 5, 1, 6) 72 | ) 73 | ) 74 | ) 75 | ]) 76 | )) 77 | ); 78 | # } 79 | ``` 80 | "], 81 | pub expression, 82 | call!(primaries::primary) 83 | ); 84 | -------------------------------------------------------------------------------- /source/rules/mod.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! The grammar as a set of rules. 33 | //! 34 | //! The grammar is splitted into group of rules for the sake of clarity. 35 | 36 | pub mod comments; 37 | pub mod expressions; 38 | pub mod literals; 39 | pub mod skip; 40 | pub mod statements; 41 | pub mod tokens; 42 | pub mod whitespaces; 43 | 44 | use super::ast::Expression; 45 | use super::tokens::Span; 46 | 47 | /// The `root` parser is the axiom of the grammar, i.e. the entry 48 | /// point of all the parsers. 49 | /// 50 | /// # Examples 51 | /// 52 | /// ``` 53 | /// use std::borrow::Cow; 54 | /// use tagua_parser::ast::{ 55 | /// Expression, 56 | /// Literal 57 | /// }; 58 | /// use tagua_parser::tokens::{ 59 | /// Span, 60 | /// Token 61 | /// }; 62 | /// use tagua_parser::rules::root; 63 | /// 64 | /// # fn main() { 65 | /// let input = Span::new(b"'Hello, World!'"); 66 | /// let output = Expression::Literal(Literal::String(Token::new(Cow::from(&b"Hello, World!"[..]), input))); 67 | /// 68 | /// assert_eq!(root(input), output); 69 | /// # } 70 | /// ``` 71 | pub fn root(input: Span) -> Expression { 72 | match expressions::expression(input) { 73 | Ok((_, ast)) => ast, 74 | _ => panic!("Youhouuu") 75 | } 76 | } 77 | 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use std::borrow::Cow; 82 | use super::root; 83 | use super::super::ast::{ 84 | Expression, 85 | Literal 86 | }; 87 | use super::super::tokens::{ 88 | Span, 89 | Token 90 | }; 91 | 92 | #[test] 93 | fn case_root() { 94 | let input = Span::new(b"'Hello, World!'"); 95 | let output = Expression::Literal(Literal::String(Token::new(Cow::from(&b"Hello, World!"[..]), input))); 96 | 97 | assert_eq!(root(input), output); 98 | } 99 | 100 | #[test] 101 | #[should_panic(expected = "Youhouuu")] 102 | fn case_root_panic() { 103 | root(Span::new(b"!")); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /source/rules/skip.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! The skip rule. 33 | //! 34 | //! The skip rule is a special rule representing all the tokens that are not 35 | //! required. For instance, whitespaces and comments can most of the time be 36 | //! skipped. 37 | 38 | use super::comments::comment; 39 | use super::super::tokens::Span; 40 | use super::whitespaces::whitespace; 41 | 42 | named_attr!( 43 | #[doc=" 44 | Recognize all tokens to skip. 45 | 46 | A skip token is a token that is not relevant for the understanding of 47 | the language. It is present for comestic reasons only. 48 | 49 | # Examples 50 | 51 | ``` 52 | use tagua_parser::Result; 53 | use tagua_parser::rules::skip::skip; 54 | use tagua_parser::tokens::Span; 55 | 56 | # fn main () { 57 | assert_eq!( 58 | skip(Span::new(b\"/* foo */ \\n\\thello\")), 59 | Ok(( 60 | Span::new_at(b\"hello\", 12, 2, 2), 61 | vec![ 62 | Span::new_at(b\" foo \", 2, 1, 3), 63 | Span::new_at(b\" \\n\\t\", 9, 1, 10) 64 | ] 65 | )) 66 | ); 67 | # } 68 | ``` 69 | "], 70 | pub skip>, 71 | many0!( 72 | alt!( 73 | comment 74 | | whitespace 75 | ) 76 | ) 77 | ); 78 | 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use super::skip; 83 | use super::super::super::tokens::Span; 84 | 85 | #[test] 86 | fn case_skip_comment() { 87 | let input = Span::new(b"/* foo */hello"); 88 | let output = Ok((Span::new_at(b"hello", 9, 1, 10), vec![Span::new_at(b" foo ", 2, 1, 3)])); 89 | 90 | assert_eq!(skip(input), output); 91 | } 92 | 93 | #[test] 94 | fn case_skip_whitespace() { 95 | let input = Span::new(b" \nhello"); 96 | let output = Ok((Span::new_at(b"hello", 3, 2, 1), vec![Span::new_at(b" \n", 0, 1, 1)])); 97 | 98 | assert_eq!(skip(input), output); 99 | } 100 | 101 | #[test] 102 | fn case_skip_comment_whitespace() { 103 | let input = Span::new(b"/* foo */ \nhello"); 104 | let output = Ok(( 105 | Span::new_at(b"hello", 12, 2, 1), 106 | vec![ 107 | Span::new_at(b" foo ", 2, 1, 3), 108 | Span::new_at(b" \n", 9, 1, 10) 109 | ] 110 | )); 111 | 112 | assert_eq!(skip(input), output); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /source/rules/statements/function.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of function rules. 33 | //! 34 | //! The list of all function rules is provided by the PHP Language 35 | //! Specification in the [Chapter chapter, Function Definition 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#function-definition). 37 | 38 | use std::result::Result as StdResult; 39 | use super::compound_statement; 40 | use super::super::expressions::constant::constant_expression; 41 | use super::super::tokens::{ 42 | name, 43 | qualified_name, 44 | variable 45 | }; 46 | use super::super::super::ast::{ 47 | Arity, 48 | Expression, 49 | Function, 50 | Name, 51 | Parameter, 52 | Statement, 53 | Ty, 54 | Variable 55 | }; 56 | use super::super::super::internal::{ 57 | Context, 58 | Error, 59 | ErrorKind 60 | }; 61 | use super::super::super::tokens; 62 | use super::super::super::tokens::Span; 63 | 64 | /// Function errors. 65 | pub enum FunctionError { 66 | /// A variadic function has a `...parameter` at an invalid 67 | /// position. It must be the latest one. 68 | InvalidVariadicParameterPosition, 69 | 70 | /// A function has multiple parameters declared with the same name. 71 | MultipleParametersWithSameName 72 | } 73 | 74 | named_attr!( 75 | #[doc=" 76 | Recognize a function. 77 | 78 | # Examples 79 | 80 | A function with 3 inputs, aka parameters: 81 | 82 | 1. `$x`, untyped and passed by copy, 83 | 2. `$y`, typed with a fully-qualified name, and passed by 84 | an implicit reference (this is a copy type, but the type 85 | is an object, so this is always a reference), 86 | 3. `$z`, typed with a primite type, and passed by reference. 87 | 88 | The output is also typed with a unqualified name, and 89 | explicitly passed by reference. 90 | 91 | The arity of this function is finite. 92 | 93 | ``` 94 | # extern crate smallvec; 95 | # #[macro_use] 96 | # extern crate tagua_parser; 97 | use tagua_parser::Result; 98 | use tagua_parser::ast::{ 99 | Arity, 100 | Function, 101 | Name, 102 | Parameter, 103 | Statement, 104 | Ty, 105 | Variable 106 | }; 107 | use tagua_parser::rules::statements::function::function; 108 | use tagua_parser::tokens::{ 109 | Span, 110 | Token 111 | }; 112 | 113 | # fn main() { 114 | assert_eq!( 115 | function(Span::new(b\"function &f($x, \\\\I\\\\J $y, int &$z): O { return; }\")), 116 | Ok(( 117 | Span::new_at(b\"\", 48, 1, 49), 118 | Statement::Function( 119 | Function { 120 | name : Span::new_at(b\"f\", 10, 1, 11), 121 | inputs: Arity::Finite(vec![ 122 | Parameter { 123 | ty : Ty::Copy(None), 124 | name : Variable(Span::new_at(b\"x\", 13, 1, 14)), 125 | value: None 126 | }, 127 | Parameter { 128 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b\"I\", 17, 1, 18), Span::new_at(b\"J\", 19, 1, 20)]))), 129 | name : Variable(Span::new_at(b\"y\", 22, 1, 23)), 130 | value: None 131 | }, 132 | Parameter { 133 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b\"int\", 25, 1, 26)]))), 134 | name : Variable(Span::new_at(b\"z\", 31, 1, 32)), 135 | value: None 136 | } 137 | ]), 138 | output: Ty::Reference(Some(Name::Unqualified(Span::new_at(b\"O\", 35, 1, 36)))), 139 | body : vec![Statement::Return] 140 | } 141 | ) 142 | )) 143 | ); 144 | # } 145 | ``` 146 | 147 | This function has an infinite arity. This is also called a 148 | variadic function. The last parameter receives all extra 149 | arguments. 150 | 151 | ``` 152 | # extern crate smallvec; 153 | # #[macro_use] 154 | # extern crate tagua_parser; 155 | use tagua_parser::Result; 156 | use tagua_parser::ast::{ 157 | Arity, 158 | Function, 159 | Name, 160 | Parameter, 161 | Statement, 162 | Ty, 163 | Variable 164 | }; 165 | use tagua_parser::rules::statements::function::function; 166 | use tagua_parser::tokens::{ 167 | Span, 168 | Token 169 | }; 170 | 171 | # fn main() { 172 | assert_eq!( 173 | function(Span::new(b\"function f($x, int ...$y) { return; }\")), 174 | Ok(( 175 | Span::new_at(b\"\", 37, 1, 38), 176 | Statement::Function( 177 | Function { 178 | name : Span::new_at(b\"f\", 9, 1, 10), 179 | inputs: Arity::Infinite(vec![ 180 | Parameter { 181 | ty : Ty::Copy(None), 182 | name : Variable(Span::new_at(b\"x\", 12, 1, 13)), 183 | value: None 184 | }, 185 | Parameter { 186 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b\"int\", 15, 1, 16)]))), 187 | name : Variable(Span::new_at(b\"y\", 23, 1, 24)), 188 | value: None 189 | } 190 | ]), 191 | output: Ty::Copy(None), 192 | body : vec![Statement::Return] 193 | } 194 | ) 195 | )) 196 | ); 197 | # } 198 | ``` 199 | "], 200 | pub function, 201 | do_parse!( 202 | first!(keyword!(tokens::FUNCTION)) >> 203 | output_is_a_reference: opt!(first!(tag!(tokens::REFERENCE))) >> 204 | name: first!(name) >> 205 | inputs: first!(parameters) >> 206 | output: opt!( 207 | do_parse!( 208 | first!(tag!(tokens::FUNCTION_OUTPUT)) >> 209 | output_is_nullable: opt!(first!(tag!(tokens::NULLABLE))) >> 210 | output_type_name: alt!( 211 | first!(native_type) 212 | | first!(qualified_name) 213 | ) >> 214 | ( 215 | into_type( 216 | Some(output_type_name), 217 | output_is_nullable.is_some(), 218 | output_is_a_reference.is_some() 219 | ) 220 | ) 221 | ) 222 | ) >> 223 | body: first!(compound_statement) >> 224 | ( 225 | into_function( 226 | name, 227 | inputs, 228 | output.unwrap_or_else( 229 | || { 230 | if output_is_a_reference.is_some() { 231 | Ty::Reference(None) 232 | } else { 233 | Ty::Copy(None) 234 | } 235 | } 236 | ), 237 | body 238 | ) 239 | ) 240 | ) 241 | ); 242 | 243 | named_attr!( 244 | #[doc=" 245 | Recognize a list of function parameters. 246 | 247 | # Examples 248 | 249 | ``` 250 | # extern crate smallvec; 251 | # #[macro_use] 252 | # extern crate tagua_parser; 253 | use tagua_parser::Result; 254 | use tagua_parser::ast::{ 255 | Arity, 256 | Name, 257 | Parameter, 258 | Ty, 259 | Variable 260 | }; 261 | use tagua_parser::rules::statements::function::parameters; 262 | use tagua_parser::tokens::{ 263 | Span, 264 | Token 265 | }; 266 | 267 | # fn main() { 268 | assert_eq!( 269 | parameters(Span::new(b\"($x, \\\\I\\\\J $y, int &$z)\")), 270 | Ok(( 271 | Span::new_at(b\"\", 22, 1, 23), 272 | Arity::Finite(vec![ 273 | Parameter { 274 | ty : Ty::Copy(None), 275 | name : Variable(Span::new_at(b\"x\", 2, 1, 3)), 276 | value: None 277 | }, 278 | Parameter { 279 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b\"I\", 6, 1, 7), Span::new_at(b\"J\", 8, 1, 9)]))), 280 | name : Variable(Span::new_at(b\"y\", 11, 1, 12)), 281 | value: None 282 | }, 283 | Parameter { 284 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b\"int\", 14, 1, 15)]))), 285 | name : Variable(Span::new_at(b\"z\", 20, 1, 21)), 286 | value: None 287 | } 288 | ]) 289 | )) 290 | ); 291 | # } 292 | ``` 293 | "], 294 | pub parameters, 295 | map_res_and_input!( 296 | terminated!( 297 | preceded!( 298 | tag!(tokens::LEFT_PARENTHESIS), 299 | opt!( 300 | do_parse!( 301 | accumulator: map_res!( 302 | first!(parameter), 303 | into_vector_mapper 304 | ) >> 305 | result: fold_into_vector_many0!( 306 | preceded!( 307 | first!(tag!(tokens::COMMA)), 308 | first!(parameter) 309 | ), 310 | accumulator 311 | ) >> 312 | (result) 313 | ) 314 | ) 315 | ), 316 | first!(tag!(tokens::RIGHT_PARENTHESIS)) 317 | ), 318 | parameters_mapper 319 | ) 320 | ); 321 | 322 | #[inline] 323 | fn parameters_mapper<'a, 'b>(pairs: Option, bool)>>, input: Span<'b>) -> StdResult, Error>> { 324 | let mut pairs = match pairs { 325 | Some(pairs) => { 326 | pairs 327 | }, 328 | 329 | None => { 330 | return Ok(Arity::Constant); 331 | } 332 | }; 333 | 334 | let last_pair = pairs.pop(); 335 | let mut parameters = Vec::new(); 336 | 337 | for (parameter, is_variadic) in pairs { 338 | if is_variadic { 339 | return Err(Error::Error(Context::Code(input, ErrorKind::Custom(FunctionError::InvalidVariadicParameterPosition as u32)))); 340 | } 341 | 342 | if parameters.iter().any(|p: &Parameter<'a>| p.name == parameter.name) { 343 | return Err(Error::Error(Context::Code(input, ErrorKind::Custom(FunctionError::MultipleParametersWithSameName as u32)))); 344 | } 345 | 346 | parameters.push(parameter); 347 | } 348 | 349 | match last_pair { 350 | Some((last_parameter, is_variadic)) => { 351 | if parameters.iter().any(|p: &Parameter<'a>| p.name == last_parameter.name) { 352 | return Err(Error::Error(Context::Code(input, ErrorKind::Custom(FunctionError::MultipleParametersWithSameName as u32)))); 353 | } 354 | 355 | parameters.push(last_parameter); 356 | 357 | if is_variadic { 358 | Ok(Arity::Infinite(parameters)) 359 | } else { 360 | Ok(Arity::Finite(parameters)) 361 | } 362 | }, 363 | 364 | None => { 365 | Ok(Arity::Constant) 366 | } 367 | } 368 | } 369 | 370 | named!( 371 | parameter, 372 | do_parse!( 373 | type_pair: opt!( 374 | do_parse!( 375 | is_nullable: opt!(tag!(tokens::NULLABLE)) >> 376 | type_name: alt!( 377 | first!(native_type) 378 | | first!(qualified_name) 379 | ) >> 380 | (Some(type_name), is_nullable) 381 | ) 382 | ) >> 383 | is_a_reference: opt!(first!(tag!(tokens::REFERENCE))) >> 384 | is_variadic: opt!(first!(tag!(tokens::ELLIPSIS))) >> 385 | name: first!(variable) >> 386 | default_value: opt!( 387 | preceded!( 388 | first!(tag!(tokens::ASSIGN)), 389 | first!(constant_expression) 390 | ) 391 | ) >> 392 | ({ 393 | let (type_name, is_nullable) = type_pair.unwrap_or_else(|| (None, None)); 394 | 395 | into_parameter( 396 | into_type( 397 | type_name, 398 | is_nullable.is_some(), 399 | is_a_reference.is_some() 400 | ), 401 | is_variadic.is_some(), 402 | name, 403 | default_value 404 | ) 405 | }) 406 | ) 407 | ); 408 | 409 | #[inline] 410 | fn into_vector_mapper(item: T) -> StdResult, ()> { 411 | Ok(vec![item]) 412 | } 413 | 414 | #[inline] 415 | fn into_type<'a>(ty: Option>, is_nullable: bool, is_a_reference: bool) -> Ty<'a> { 416 | match (ty, is_nullable, is_a_reference) { 417 | (Some(ty) , true, false) => { 418 | Ty::NullableCopy(ty) 419 | }, 420 | 421 | (Some(ty), true, true) => { 422 | Ty::NullableReference(ty) 423 | }, 424 | 425 | (ty, false, false) => { 426 | Ty::Copy(ty) 427 | }, 428 | 429 | (ty, false, true) => { 430 | Ty::Reference(ty) 431 | }, 432 | 433 | _ => { 434 | unreachable!(); 435 | } 436 | } 437 | } 438 | 439 | #[inline] 440 | fn into_parameter<'a>( 441 | ty : Ty<'a>, 442 | is_variadic : bool, 443 | name : Variable<'a>, 444 | default_value: Option> 445 | ) -> (Parameter<'a>, bool) { 446 | ( 447 | Parameter { 448 | ty : ty, 449 | name : name, 450 | value: default_value 451 | }, 452 | is_variadic 453 | ) 454 | } 455 | 456 | named_attr!( 457 | #[doc=" 458 | Recognize all native types. 459 | 460 | # Examples 461 | 462 | ``` 463 | # extern crate smallvec; 464 | # #[macro_use] 465 | # extern crate tagua_parser; 466 | use tagua_parser::Result; 467 | use tagua_parser::ast::Name; 468 | use tagua_parser::rules::statements::function::native_type; 469 | use tagua_parser::tokens::{ 470 | Span, 471 | Token 472 | }; 473 | 474 | # fn main() { 475 | assert_eq!( 476 | native_type(Span::new(b\"int\")), 477 | Ok(( 478 | Span::new_at(b\"\", 3, 1, 4), 479 | Name::FullyQualified(smallvec![Span::new(b\"int\")]) 480 | )) 481 | ); 482 | # } 483 | ``` 484 | "], 485 | pub native_type, 486 | map_res!( 487 | alt_complete!( 488 | tag!(tokens::ARRAY) 489 | | tag!(tokens::BOOL) 490 | | tag!(tokens::CALLABLE) 491 | | tag!(tokens::FLOAT) 492 | | tag!(tokens::INT) 493 | | tag!(tokens::ITERABLE) 494 | | tag!(tokens::STRING) 495 | ), 496 | native_type_mapper 497 | ) 498 | ); 499 | 500 | #[inline] 501 | fn native_type_mapper(native_type_name: Span) -> Result { 502 | Ok(Name::FullyQualified(smallvec![native_type_name])) 503 | } 504 | 505 | #[inline] 506 | fn into_function<'a>( 507 | name : Span<'a>, 508 | inputs: Arity<'a>, 509 | output: Ty<'a>, 510 | body : Vec> 511 | ) -> Statement<'a> { 512 | Statement::Function( 513 | Function { 514 | name : name, 515 | inputs: inputs, 516 | output: output, 517 | body : body 518 | } 519 | ) 520 | } 521 | 522 | 523 | #[cfg(test)] 524 | mod tests { 525 | use std::borrow::Cow; 526 | use super::{ 527 | function, 528 | native_type, 529 | parameters 530 | }; 531 | use super::super::statement; 532 | use super::super::super::super::ast::{ 533 | Arity, 534 | Expression, 535 | Function, 536 | Literal, 537 | Name, 538 | Parameter, 539 | Statement, 540 | Ty, 541 | Variable 542 | }; 543 | use super::super::super::super::internal::{ 544 | Context, 545 | Error, 546 | ErrorKind 547 | }; 548 | use super::super::super::super::tokens::{ 549 | Span, 550 | Token 551 | }; 552 | 553 | #[test] 554 | fn case_function() { 555 | let input = Span::new(b"function f(I $x, J &$y): O { return; }"); 556 | let output = Ok(( 557 | Span::new_at(b"", 38, 1, 39), 558 | Statement::Function( 559 | Function { 560 | name : Span::new_at(b"f", 9, 1, 10), 561 | inputs: Arity::Finite(vec![ 562 | Parameter { 563 | ty : Ty::Copy(Some(Name::Unqualified(Span::new_at(b"I", 11, 1, 12)))), 564 | name : Variable(Span::new_at(b"x", 14, 1, 15)), 565 | value: None 566 | }, 567 | Parameter { 568 | ty : Ty::Reference(Some(Name::Unqualified(Span::new_at(b"J", 17, 1, 18)))), 569 | name : Variable(Span::new_at(b"y", 21, 1, 22)), 570 | value: None 571 | } 572 | ]), 573 | output: Ty::Copy(Some(Name::Unqualified(Span::new_at(b"O", 25, 1, 26)))), 574 | body : vec![Statement::Return] 575 | } 576 | ) 577 | )); 578 | 579 | assert_eq!(function(input), output); 580 | assert_eq!(statement(input), output); 581 | } 582 | 583 | #[test] 584 | fn case_function_arity_zero() { 585 | let input = Span::new(b"function f() {}"); 586 | let output = Ok(( 587 | Span::new_at(b"", 15, 1, 16), 588 | Statement::Function( 589 | Function { 590 | name : Span::new_at(b"f", 9, 1, 10), 591 | inputs: Arity::Constant, 592 | output: Ty::Copy(None), 593 | body : vec![Statement::Return] 594 | } 595 | ) 596 | )); 597 | 598 | assert_eq!(function(input), output); 599 | assert_eq!(statement(input), output); 600 | } 601 | 602 | #[test] 603 | fn case_function_arity_many() { 604 | let input = Span::new(b"function f($a, I\\J $b, int &$c, \\K $d) {}"); 605 | let output = Ok(( 606 | Span::new_at(b"", 41, 1, 42), 607 | Statement::Function( 608 | Function { 609 | name : Span::new_at(b"f", 9, 1, 10), 610 | inputs: Arity::Finite(vec![ 611 | Parameter { 612 | ty : Ty::Copy(None), 613 | name : Variable(Span::new_at(b"a", 12, 1, 13)), 614 | value: None 615 | }, 616 | Parameter { 617 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"I", 15, 1, 16), Span::new_at(b"J", 17, 1, 18)]))), 618 | name : Variable(Span::new_at(b"b", 20, 1, 21)), 619 | value: None 620 | }, 621 | Parameter { 622 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 23, 1, 24)]))), 623 | name : Variable(Span::new_at(b"c", 29, 1, 30)), 624 | value: None 625 | }, 626 | Parameter { 627 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b"K", 33, 1, 34)]))), 628 | name : Variable(Span::new_at(b"d", 36, 1, 37)), 629 | value: None 630 | } 631 | ]), 632 | output: Ty::Copy(None), 633 | body : vec![Statement::Return] 634 | } 635 | ) 636 | )); 637 | 638 | assert_eq!(function(input), output); 639 | assert_eq!(statement(input), output); 640 | } 641 | 642 | #[test] 643 | fn case_variadic_function_arity_many() { 644 | let input = Span::new(b"function f($a, I\\J $b, int &...$c) {}"); 645 | let output = Ok(( 646 | Span::new_at(b"", 37, 1, 38), 647 | Statement::Function( 648 | Function { 649 | name : Span::new_at(b"f", 9, 1, 10), 650 | inputs: Arity::Infinite(vec![ 651 | Parameter { 652 | ty : Ty::Copy(None), 653 | name : Variable(Span::new_at(b"a", 12, 1, 13)), 654 | value: None 655 | }, 656 | Parameter { 657 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"I", 15, 1, 16), Span::new_at(b"J", 17, 1, 18)]))), 658 | name : Variable(Span::new_at(b"b", 20, 1, 21)), 659 | value: None 660 | }, 661 | Parameter { 662 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 23, 1, 24)]))), 663 | name : Variable(Span::new_at(b"c", 32, 1, 33)), 664 | value: None 665 | } 666 | ]), 667 | output: Ty::Copy(None), 668 | body : vec![Statement::Return] 669 | } 670 | ) 671 | )); 672 | 673 | assert_eq!(function(input), output); 674 | assert_eq!(statement(input), output); 675 | } 676 | 677 | #[test] 678 | fn case_function_output_by_none_copy() { 679 | let input = Span::new(b"function f() {}"); 680 | let output = Ok(( 681 | Span::new_at(b"", 15, 1, 16), 682 | Statement::Function( 683 | Function { 684 | name : Span::new_at(b"f", 9, 1, 10), 685 | inputs: Arity::Constant, 686 | output: Ty::Copy(None), 687 | body : vec![Statement::Return] 688 | } 689 | ) 690 | )); 691 | 692 | assert_eq!(function(input), output); 693 | assert_eq!(statement(input), output); 694 | } 695 | 696 | #[test] 697 | fn case_function_output_by_some_copy() { 698 | let input = Span::new(b"function f(): \\O {}"); 699 | let output = Ok(( 700 | Span::new_at(b"", 19, 1, 20), 701 | Statement::Function( 702 | Function { 703 | name : Span::new_at(b"f", 9, 1, 10), 704 | inputs: Arity::Constant, 705 | output: Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b"O", 15, 1, 16)]))), 706 | body : vec![Statement::Return] 707 | } 708 | ) 709 | )); 710 | 711 | assert_eq!(function(input), output); 712 | assert_eq!(statement(input), output); 713 | } 714 | 715 | #[test] 716 | fn case_function_output_by_nullable_copy() { 717 | let input = Span::new(b"function f(): ?\\O {}"); 718 | let output = Ok(( 719 | Span::new_at(b"", 20, 1, 21), 720 | Statement::Function( 721 | Function { 722 | name : Span::new_at(b"f", 9, 1, 10), 723 | inputs: Arity::Constant, 724 | output: Ty::NullableCopy(Name::FullyQualified(smallvec![Span::new_at(b"O", 16, 1, 17)])), 725 | body : vec![Statement::Return] 726 | } 727 | ) 728 | )); 729 | 730 | assert_eq!(function(input), output); 731 | assert_eq!(statement(input), output); 732 | } 733 | 734 | #[test] 735 | fn case_function_output_by_none_reference() { 736 | let input = Span::new(b"function &f() {}"); 737 | let output = Ok(( 738 | Span::new_at(b"", 16, 1, 17), 739 | Statement::Function( 740 | Function { 741 | name : Span::new_at(b"f", 10, 1, 11), 742 | inputs: Arity::Constant, 743 | output: Ty::Reference(None), 744 | body : vec![Statement::Return] 745 | } 746 | ) 747 | )); 748 | 749 | assert_eq!(function(input), output); 750 | assert_eq!(statement(input), output); 751 | } 752 | 753 | #[test] 754 | fn case_function_output_by_some_reference() { 755 | let input = Span::new(b"function &f(): int {}"); 756 | let output = Ok(( 757 | Span::new_at(b"", 21, 1, 22), 758 | Statement::Function( 759 | Function { 760 | name : Span::new_at(b"f", 10, 1, 11), 761 | inputs: Arity::Constant, 762 | output: Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 15, 1, 16)]))), 763 | body : vec![Statement::Return] 764 | } 765 | ) 766 | )); 767 | 768 | assert_eq!(function(input), output); 769 | assert_eq!(statement(input), output); 770 | } 771 | 772 | #[test] 773 | fn case_function_output_by_nullable_reference() { 774 | let input = Span::new(b"function &f(): ?int {}"); 775 | let output = Ok(( 776 | Span::new_at(b"", 22, 1, 23), 777 | Statement::Function( 778 | Function { 779 | name : Span::new_at(b"f", 10, 1, 11), 780 | inputs: Arity::Constant, 781 | output: Ty::NullableReference(Name::FullyQualified(smallvec![Span::new_at(b"int", 16, 1, 17)])), 782 | body : vec![Statement::Return] 783 | } 784 | ) 785 | )); 786 | 787 | assert_eq!(function(input), output); 788 | assert_eq!(statement(input), output); 789 | } 790 | 791 | #[test] 792 | fn case_invalid_variadic_function_parameter_position() { 793 | let input = Span::new(b"function f(...$x, $y) {}"); 794 | 795 | assert_eq!(function(input), Err(Error::Error(Context::Code(Span::new_at(b"(...$x, $y) {}", 10, 1, 11), ErrorKind::MapRes)))); 796 | assert_eq!(statement(input), Err(Error::Error(Context::Code(input, ErrorKind::Alt)))); 797 | } 798 | 799 | #[test] 800 | fn case_parameters_one_by_none_copy() { 801 | let input = Span::new(b"($x)"); 802 | let output = Ok(( 803 | Span::new_at(b"", 4, 1, 5), 804 | Arity::Finite(vec![ 805 | Parameter { 806 | ty : Ty::Copy(None), 807 | name : Variable(Span::new_at(b"x", 2, 1, 3)), 808 | value: None 809 | } 810 | ]) 811 | )); 812 | 813 | assert_eq!(parameters(input), output); 814 | } 815 | 816 | #[test] 817 | fn case_parameters_one_by_some_copy() { 818 | let input = Span::new(b"(A\\B\\C $x)"); 819 | let output = Ok(( 820 | Span::new_at(b"", 10, 1, 11), 821 | Arity::Finite(vec![ 822 | Parameter { 823 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"A", 1, 1, 2), Span::new_at(b"B", 3, 1, 4), Span::new_at(b"C", 5, 1, 6)]))), 824 | name : Variable(Span::new_at(b"x", 8, 1, 9)), 825 | value: None 826 | } 827 | ]) 828 | )); 829 | 830 | assert_eq!(parameters(input), output); 831 | } 832 | 833 | #[test] 834 | fn case_parameters_one_by_nullable_copy() { 835 | let input = Span::new(b"(?A\\B\\C $x)"); 836 | let output = Ok(( 837 | Span::new_at(b"", 11, 1, 12), 838 | Arity::Finite(vec![ 839 | Parameter { 840 | ty : Ty::NullableCopy(Name::Qualified(smallvec![Span::new_at(b"A", 2, 1, 3), Span::new_at(b"B", 4, 1, 5), Span::new_at(b"C", 6, 1, 7)])), 841 | name : Variable(Span::new_at(b"x", 9, 1, 10)), 842 | value: None 843 | } 844 | ]) 845 | )); 846 | 847 | assert_eq!(parameters(input), output); 848 | } 849 | 850 | #[test] 851 | fn case_parameters_one_by_none_reference() { 852 | let input = Span::new(b"(&$x)"); 853 | let output = Ok(( 854 | Span::new_at(b"", 5, 1, 6), 855 | Arity::Finite(vec![ 856 | Parameter { 857 | ty : Ty::Reference(None), 858 | name : Variable(Span::new_at(b"x", 3, 1, 4)), 859 | value: None 860 | } 861 | ]) 862 | )); 863 | 864 | assert_eq!(parameters(input), output); 865 | } 866 | 867 | #[test] 868 | fn case_parameters_one_by_some_reference() { 869 | let input = Span::new(b"(int &$x)"); 870 | let output = Ok(( 871 | Span::new_at(b"", 9, 1, 10), 872 | Arity::Finite(vec![ 873 | Parameter { 874 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 1, 1, 2)]))), 875 | name : Variable(Span::new_at(b"x", 7, 1, 8)), 876 | value: None 877 | } 878 | ]) 879 | )); 880 | 881 | assert_eq!(parameters(input), output); 882 | } 883 | 884 | #[test] 885 | fn case_parameters_one_by_nullable_reference() { 886 | let input = Span::new(b"(?int &$x)"); 887 | let output = Ok(( 888 | Span::new_at(b"", 10, 1, 11), 889 | Arity::Finite(vec![ 890 | Parameter { 891 | ty : Ty::NullableReference(Name::FullyQualified(smallvec![Span::new_at(b"int", 2, 1, 3)])), 892 | name : Variable(Span::new_at(b"x", 8, 1, 9)), 893 | value: None 894 | } 895 | ]) 896 | )); 897 | 898 | assert_eq!(parameters(input), output); 899 | } 900 | 901 | #[test] 902 | fn case_parameters_one_variadic_by_none_copy() { 903 | let input = Span::new(b"(...$x)"); 904 | let output = Ok(( 905 | Span::new_at(b"", 7, 1, 8), 906 | Arity::Infinite(vec![ 907 | Parameter { 908 | ty : Ty::Copy(None), 909 | name : Variable(Span::new_at(b"x", 5, 1, 6)), 910 | value: None 911 | } 912 | ]) 913 | )); 914 | 915 | assert_eq!(parameters(input), output); 916 | } 917 | 918 | #[test] 919 | fn case_parameters_one_variadic_by_some_reference() { 920 | let input = Span::new(b"(I &...$x)"); 921 | let output = Ok(( 922 | Span::new_at(b"", 10, 1, 11), 923 | Arity::Infinite(vec![ 924 | Parameter { 925 | ty : Ty::Reference(Some(Name::Unqualified(Span::new_at(b"I", 1, 1, 2)))), 926 | name : Variable(Span::new_at(b"x", 8, 1, 9)), 927 | value: None 928 | } 929 | ]) 930 | )); 931 | 932 | assert_eq!(parameters(input), output); 933 | } 934 | 935 | #[test] 936 | fn case_parameters_one_by_none_copy_with_a_default_value() { 937 | let input = Span::new(b"($x = 42)"); 938 | let output = Ok(( 939 | Span::new_at(b"", 9, 1, 10), 940 | Arity::Finite(vec![ 941 | Parameter { 942 | ty : Ty::Copy(None), 943 | name : Variable(Span::new_at(b"x", 2, 1, 3)), 944 | value: Some(Expression::Literal(Literal::Integer(Token::new(42i64, Span::new_at(b"42", 6, 1, 7))))) 945 | } 946 | ]) 947 | )); 948 | 949 | assert_eq!(parameters(input), output); 950 | } 951 | 952 | #[test] 953 | fn case_parameters_one_by_some_copy_and_a_default_value() { 954 | let input = Span::new(b"(float $x = 4.2)"); 955 | let output = Ok(( 956 | Span::new_at(b"", 16, 1, 17), 957 | Arity::Finite(vec![ 958 | Parameter { 959 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b"float", 1, 1, 2)]))), 960 | name : Variable(Span::new_at(b"x", 8, 1, 9)), 961 | value: Some(Expression::Literal(Literal::Real(Token::new(4.2f64, Span::new_at(b"4.2", 12, 1, 13))))) 962 | } 963 | ]) 964 | )); 965 | 966 | assert_eq!(parameters(input), output); 967 | } 968 | 969 | #[test] 970 | fn case_parameters_one_by_nullable_copy_and_a_default_value() { 971 | let input = Span::new(b"(?float $x = 4.2)"); 972 | let output = Ok(( 973 | Span::new_at(b"", 17, 1, 18), 974 | Arity::Finite(vec![ 975 | Parameter { 976 | ty : Ty::NullableCopy(Name::FullyQualified(smallvec![Span::new_at(b"float", 2, 1, 3)])), 977 | name : Variable(Span::new_at(b"x", 9, 1, 10)), 978 | value: Some(Expression::Literal(Literal::Real(Token::new(4.2f64, Span::new_at(b"4.2", 13, 1, 14))))) 979 | } 980 | ]) 981 | )); 982 | 983 | assert_eq!(parameters(input), output); 984 | } 985 | 986 | #[test] 987 | fn case_parameters_one_by_none_reference_with_a_default_value() { 988 | let input = Span::new(b"(&$x = 'foo')"); 989 | let output = Ok(( 990 | Span::new_at(b"", 13, 1, 14), 991 | Arity::Finite(vec![ 992 | Parameter { 993 | ty : Ty::Reference(None), 994 | name : Variable(Span::new_at(b"x", 3, 1, 4)), 995 | value: Some(Expression::Literal(Literal::String(Token::new(Cow::from(&b"foo"[..]), Span::new_at(b"'foo'", 7, 1, 8))))) 996 | } 997 | ]) 998 | )); 999 | 1000 | assert_eq!(parameters(input), output); 1001 | } 1002 | 1003 | #[test] 1004 | fn case_parameters_one_by_some_reference_and_a_default_value() { 1005 | let input = Span::new(b"(array &$x = ['foo' => true])"); 1006 | let output = Ok(( 1007 | Span::new_at(b"", 29, 1, 30), 1008 | Arity::Finite(vec![ 1009 | Parameter { 1010 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"array", 1, 1, 2)]))), 1011 | name : Variable(Span::new_at(b"x", 9, 1, 10)), 1012 | value: Some( 1013 | Expression::Array(vec![ 1014 | ( 1015 | Some(Expression::Literal(Literal::String(Token::new(Cow::from(&b"foo"[..]), Span::new_at(b"'foo'", 14, 1, 15))))), 1016 | Expression::Name(Name::Unqualified(Span::new_at(b"true", 23, 1, 24))) 1017 | ) 1018 | ]) 1019 | ) 1020 | } 1021 | ]) 1022 | )); 1023 | 1024 | assert_eq!(parameters(input), output); 1025 | } 1026 | 1027 | #[test] 1028 | fn case_parameters_one_by_nullable_reference_with_a_default_value() { 1029 | let input = Span::new(b"(?string &$x = 'foo')"); 1030 | let output = Ok(( 1031 | Span::new_at(b"", 21, 1, 22), 1032 | Arity::Finite(vec![ 1033 | Parameter { 1034 | ty : Ty::NullableReference(Name::FullyQualified(smallvec![Span::new_at(b"string", 2, 1, 3)])), 1035 | name : Variable(Span::new_at(b"x", 11, 1, 12)), 1036 | value: Some(Expression::Literal(Literal::String(Token::new(Cow::from(&b"foo"[..]), Span::new_at(b"'foo'", 15, 1, 16))))) 1037 | } 1038 | ]) 1039 | )); 1040 | 1041 | assert_eq!(parameters(input), output); 1042 | } 1043 | 1044 | #[test] 1045 | fn case_parameters_variadic_arity_one_by_none_copy() { 1046 | let input = Span::new(b"(...$x)"); 1047 | let output = Ok(( 1048 | Span::new_at(b"", 7, 1, 8), 1049 | Arity::Infinite(vec![ 1050 | Parameter { 1051 | ty : Ty::Copy(None), 1052 | name : Variable(Span::new_at(b"x", 5, 1, 6)), 1053 | value: None 1054 | } 1055 | ]) 1056 | )); 1057 | 1058 | assert_eq!(parameters(input), output); 1059 | } 1060 | 1061 | #[test] 1062 | fn case_parameters_variadic_arity_one_by_some_copy() { 1063 | let input = Span::new(b"(A\\B\\C ...$x)"); 1064 | let output = Ok(( 1065 | Span::new_at(b"", 13, 1, 14), 1066 | Arity::Infinite(vec![ 1067 | Parameter { 1068 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"A", 1, 1, 2), Span::new_at(b"B", 3, 1, 4), Span::new_at(b"C", 5, 1, 6)]))), 1069 | name : Variable(Span::new_at(b"x", 11, 1, 12)), 1070 | value: None 1071 | } 1072 | ]) 1073 | )); 1074 | 1075 | assert_eq!(parameters(input), output); 1076 | } 1077 | 1078 | #[test] 1079 | fn case_parameters_variadic_arity_one_by_nullable_copy() { 1080 | let input = Span::new(b"(?A\\B\\C ...$x)"); 1081 | let output = Ok(( 1082 | Span::new_at(b"", 14, 1, 15), 1083 | Arity::Infinite(vec![ 1084 | Parameter { 1085 | ty : Ty::NullableCopy(Name::Qualified(smallvec![Span::new_at(b"A", 2, 1, 3), Span::new_at(b"B", 4, 1, 5), Span::new_at(b"C", 6, 1, 7)])), 1086 | name : Variable(Span::new_at(b"x", 12, 1, 13)), 1087 | value: None 1088 | } 1089 | ]) 1090 | )); 1091 | 1092 | assert_eq!(parameters(input), output); 1093 | } 1094 | 1095 | #[test] 1096 | fn case_parameters_variadic_arity_one_by_none_reference() { 1097 | let input = Span::new(b"(&...$x)"); 1098 | let output = Ok(( 1099 | Span::new_at(b"", 8, 1, 9), 1100 | Arity::Infinite(vec![ 1101 | Parameter { 1102 | ty : Ty::Reference(None), 1103 | name : Variable(Span::new_at(b"x", 6, 1, 7)), 1104 | value: None 1105 | } 1106 | ]) 1107 | )); 1108 | 1109 | assert_eq!(parameters(input), output); 1110 | } 1111 | 1112 | #[test] 1113 | fn case_parameters_variadic_arity_one_by_some_reference() { 1114 | let input = Span::new(b"(int &...$x)"); 1115 | let output = Ok(( 1116 | Span::new_at(b"", 12, 1, 13), 1117 | Arity::Infinite(vec![ 1118 | Parameter { 1119 | ty : Ty::Reference(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 1, 1, 2)]))), 1120 | name : Variable(Span::new_at(b"x", 10, 1, 11)), 1121 | value: None 1122 | } 1123 | ]) 1124 | )); 1125 | 1126 | assert_eq!(parameters(input), output); 1127 | } 1128 | 1129 | #[test] 1130 | fn case_parameters_variadic_arity_one_by_nullable_reference() { 1131 | let input = Span::new(b"(?int &...$x)"); 1132 | let output = Ok(( 1133 | Span::new_at(b"", 13, 1, 14), 1134 | Arity::Infinite(vec![ 1135 | Parameter { 1136 | ty : Ty::NullableReference(Name::FullyQualified(smallvec![Span::new_at(b"int", 2, 1, 3)])), 1137 | name : Variable(Span::new_at(b"x", 11, 1, 12)), 1138 | value: None 1139 | } 1140 | ]) 1141 | )); 1142 | 1143 | assert_eq!(parameters(input), output); 1144 | } 1145 | 1146 | #[test] 1147 | fn case_parameters_many() { 1148 | let input = Span::new(b"(&$x, int $y, I\\J $z)"); 1149 | let output = Ok(( 1150 | Span::new_at(b"", 21, 1, 22), 1151 | Arity::Finite(vec![ 1152 | Parameter { 1153 | ty : Ty::Reference(None), 1154 | name : Variable(Span::new_at(b"x", 3, 1, 4)), 1155 | value: None 1156 | }, 1157 | Parameter { 1158 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 6, 1, 7)]))), 1159 | name : Variable(Span::new_at(b"y", 11, 1, 12)), 1160 | value: None 1161 | }, 1162 | Parameter { 1163 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"I", 14, 1, 15), Span::new_at(b"J", 16, 1, 17)]))), 1164 | name : Variable(Span::new_at(b"z", 19, 1, 20)), 1165 | value: None 1166 | } 1167 | ]) 1168 | )); 1169 | 1170 | assert_eq!(parameters(input), output); 1171 | } 1172 | 1173 | #[test] 1174 | fn case_parameters_many_variadic() { 1175 | let input = Span::new(b"(&$x, int $y, I\\J ...$z)"); 1176 | let output = Ok(( 1177 | Span::new_at(b"", 24, 1, 25), 1178 | Arity::Infinite(vec![ 1179 | Parameter { 1180 | ty : Ty::Reference(None), 1181 | name : Variable(Span::new_at(b"x", 3, 1, 4)), 1182 | value: None 1183 | }, 1184 | Parameter { 1185 | ty : Ty::Copy(Some(Name::FullyQualified(smallvec![Span::new_at(b"int", 6, 1, 7)]))), 1186 | name : Variable(Span::new_at(b"y", 11, 1, 12)), 1187 | value: None 1188 | }, 1189 | Parameter { 1190 | ty : Ty::Copy(Some(Name::Qualified(smallvec![Span::new_at(b"I", 14, 1, 15), Span::new_at(b"J", 16, 1, 17)]))), 1191 | name : Variable(Span::new_at(b"z", 22, 1, 23)), 1192 | value: None 1193 | } 1194 | ]) 1195 | )); 1196 | 1197 | assert_eq!(parameters(input), output); 1198 | } 1199 | 1200 | #[test] 1201 | fn case_invalid_parameters_variadic_position() { 1202 | let input = Span::new(b"(...$x, $y)"); 1203 | let output = Err(Error::Error(Context::Code(input, ErrorKind::MapRes))); 1204 | 1205 | assert_eq!(parameters(input), output); 1206 | } 1207 | 1208 | #[test] 1209 | fn case_invalid_parameters_two_not_unique() { 1210 | let input = Span::new(b"($x, $x)"); 1211 | let output = Err(Error::Error(Context::Code(input, ErrorKind::MapRes))); 1212 | 1213 | assert_eq!(parameters(input), output); 1214 | } 1215 | 1216 | #[test] 1217 | fn case_invalid_parameters_many_not_unique() { 1218 | let input = Span::new(b"($x, $y, $x, $z)"); 1219 | let output = Err(Error::Error(Context::Code(input, ErrorKind::MapRes))); 1220 | 1221 | assert_eq!(parameters(input), output); 1222 | } 1223 | 1224 | macro_rules! test_native_type { 1225 | ($test:ident: $name:expr) => { 1226 | #[test] 1227 | fn $test() { 1228 | let input = Span::new($name); 1229 | let output = Ok(( 1230 | Span::new_at(b"", $name.len(), 1, $name.len() as u32 + 1), 1231 | Name::FullyQualified(smallvec![input]) 1232 | )); 1233 | 1234 | assert_eq!(native_type(input), output); 1235 | } 1236 | } 1237 | } 1238 | 1239 | test_native_type!(case_native_type_array: b"array"); 1240 | test_native_type!(case_native_type_bool: b"bool"); 1241 | test_native_type!(case_native_type_callable: b"callable"); 1242 | test_native_type!(case_native_type_float: b"float"); 1243 | test_native_type!(case_native_type_int: b"int"); 1244 | test_native_type!(case_native_type_iterable: b"iterable"); 1245 | test_native_type!(case_native_type_string: b"string"); 1246 | } 1247 | -------------------------------------------------------------------------------- /source/rules/statements/mod.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of statement rules. 33 | //! 34 | //! The list of all statements is provided by the PHP Language Specification 35 | //! in the [Grammar chapter, Statements 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#statements). 37 | 38 | pub mod function; 39 | 40 | use super::super::ast::Statement; 41 | use super::super::tokens; 42 | use super::super::tokens::Span; 43 | 44 | named_attr!( 45 | #[doc=" 46 | Recognize a group of statements. 47 | "], 48 | pub compound_statement>, 49 | map_res!( 50 | terminated!( 51 | preceded!( 52 | tag!(tokens::LEFT_CURLY_BRACKET), 53 | opt!(first!(tag!(b"return;"))) 54 | ), 55 | first!(tag!(tokens::RIGHT_CURLY_BRACKET)) 56 | ), 57 | |_| -> Result, ()> { 58 | Ok(vec![Statement::Return]) 59 | } 60 | ) 61 | ); 62 | 63 | named!( 64 | #[doc=" 65 | Recognize a statement. 66 | "], 67 | pub statement, 68 | alt!( 69 | call!(function::function) 70 | ) 71 | ); 72 | -------------------------------------------------------------------------------- /source/rules/tokens.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of token rules. 33 | //! 34 | //! The list of all tokens is provided by the PHP Language Specification in 35 | //! the [Grammar chapter, Tokens 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#tokens). 37 | 38 | use smallvec::SmallVec; 39 | use super::super::ast::{ 40 | Name, 41 | Variable 42 | }; 43 | use super::super::tokens; 44 | use super::super::tokens::Span; 45 | 46 | named_attr!( 47 | #[doc=" 48 | Recognize a variable. 49 | 50 | # Examples 51 | 52 | ``` 53 | use tagua_parser::Result; 54 | use tagua_parser::ast::Variable; 55 | use tagua_parser::rules::tokens::variable; 56 | use tagua_parser::tokens::Span; 57 | 58 | # fn main () { 59 | assert_eq!( 60 | variable(Span::new(b\"$foo\")), 61 | Ok(( 62 | Span::new_at(b\"\", 4, 1, 5), 63 | Variable(Span::new_at(b\"foo\", 1, 1, 2)) 64 | )) 65 | ); 66 | # } 67 | ``` 68 | 69 | Note that the variable prefix `$` is not present in the output 70 | of the parser. 71 | "], 72 | pub variable, 73 | map_res!( 74 | preceded!( 75 | tag!(tokens::VARIABLE), 76 | name 77 | ), 78 | variable_mapper 79 | ) 80 | ); 81 | 82 | #[inline] 83 | fn variable_mapper(span: Span) -> Result { 84 | Ok(Variable(span)) 85 | } 86 | 87 | named_attr!( 88 | #[doc=" 89 | Recognize a qualified name. 90 | 91 | # Examples 92 | 93 | ``` 94 | # extern crate smallvec; 95 | # #[macro_use] 96 | # extern crate tagua_parser; 97 | use tagua_parser::Result; 98 | use tagua_parser::ast::Name; 99 | use tagua_parser::rules::tokens::qualified_name; 100 | use tagua_parser::tokens::Span; 101 | 102 | # fn main () { 103 | assert_eq!( 104 | qualified_name(Span::new(b\"Foo\\\\Bar\\\\Baz\")), 105 | Ok(( 106 | Span::new_at(b\"\", 11, 1, 12), 107 | Name::Qualified(smallvec![ 108 | Span::new_at(b\"Foo\", 0, 1, 1), 109 | Span::new_at(b\"Bar\", 4, 1, 5), 110 | Span::new_at(b\"Baz\", 8, 1, 9) 111 | ]) 112 | )) 113 | ); 114 | # } 115 | ``` 116 | "], 117 | pub qualified_name, 118 | do_parse!( 119 | head: opt!( 120 | alt!( 121 | tag!(tokens::NAMESPACE_SEPARATOR) 122 | | terminated!( 123 | keyword!(tokens::NAMESPACE), 124 | first!(tag!(tokens::NAMESPACE_SEPARATOR)) 125 | ) 126 | ) 127 | ) >> 128 | accumulator: map_res!( 129 | exclude!(first!(name), tokens::keywords), 130 | wrap_into_vector_mapper 131 | ) >> 132 | result: fold_into_vector_many0!( 133 | preceded!( 134 | first!(tag!(tokens::NAMESPACE_SEPARATOR)), 135 | exclude!(first!(name), tokens::keywords) 136 | ), 137 | accumulator 138 | ) >> 139 | ( 140 | match head { 141 | Some(handle) => { 142 | if handle.as_slice() == tokens::NAMESPACE_SEPARATOR { 143 | Name::FullyQualified(result) 144 | } else { 145 | Name::RelativeQualified(result) 146 | } 147 | }, 148 | 149 | None => { 150 | if result.len() > 1 { 151 | Name::Qualified(result) 152 | } else { 153 | Name::Unqualified(result[0]) 154 | } 155 | } 156 | } 157 | ) 158 | ) 159 | ); 160 | 161 | #[inline] 162 | fn wrap_into_vector_mapper(span: Span) -> Result, ()> { 163 | Ok(smallvec![span]) 164 | } 165 | 166 | named_attr!( 167 | #[doc=" 168 | Recognize a name. 169 | 170 | # Examples 171 | 172 | ``` 173 | use tagua_parser::Result; 174 | use tagua_parser::rules::tokens::name; 175 | use tagua_parser::tokens::Span; 176 | 177 | # fn main () { 178 | assert_eq!( 179 | name(Span::new(b\"foo\")), 180 | Ok(( 181 | Span::new_at(b\"\", 3, 1, 4), 182 | Span::new(b\"foo\") 183 | )) 184 | ); 185 | # } 186 | ``` 187 | "], 188 | pub name, 189 | regex!(r"(?-u)^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*") 190 | ); 191 | 192 | 193 | #[cfg(test)] 194 | mod tests { 195 | use nom::Slice; 196 | use super::{ 197 | name, 198 | qualified_name, 199 | variable 200 | }; 201 | use super::super::super::ast::{ 202 | Name, 203 | Variable 204 | }; 205 | use super::super::super::internal::{ 206 | Context, 207 | Error, 208 | ErrorKind 209 | }; 210 | use super::super::super::macros::ErrorKindCustom; 211 | use super::super::super::tokens::Span; 212 | 213 | #[test] 214 | fn case_variable() { 215 | let input = Span::new(b"$foo"); 216 | let output = Ok((Span::new_at(b"", 4, 1, 5), Variable(input.slice(1..)))); 217 | 218 | assert_eq!(variable(input), output); 219 | } 220 | 221 | #[test] 222 | fn case_variable_shortest() { 223 | let input = Span::new(b"$x"); 224 | let output = Ok((Span::new_at(b"", 2, 1, 3), Variable(input.slice(1..)))); 225 | 226 | assert_eq!(variable(input), output); 227 | } 228 | 229 | #[test] 230 | fn case_invalid_variable_prefix() { 231 | let input = Span::new(b"x"); 232 | 233 | assert_eq!(variable(input), Err(Error::Error(Context::Code(input, ErrorKind::Tag)))); 234 | } 235 | 236 | #[test] 237 | fn case_invalid_variable_name() { 238 | let input = Span::new(b"$0"); 239 | 240 | assert_eq!(variable(input), Err(Error::Error(Context::Code(Span::new_at(b"0", 1, 1, 2), ErrorKind::RegexpFind)))); 241 | } 242 | 243 | #[test] 244 | fn case_unqualified_name() { 245 | let input = Span::new(b"Foo"); 246 | let output = Ok((Span::new_at(b"", 3, 1, 4), Name::Unqualified(input))); 247 | 248 | assert_eq!(qualified_name(input), output); 249 | } 250 | 251 | #[test] 252 | fn case_invalid_unqualified_name() { 253 | let input1 = Span::new(b"class"); 254 | let input2 = Span::new(b"ClAsS"); 255 | 256 | assert_eq!(qualified_name(input1), Err(Error::Error(Context::Code(input1, ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 257 | assert_eq!(qualified_name(input2), Err(Error::Error(Context::Code(input2, ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 258 | } 259 | 260 | #[test] 261 | fn case_qualified_name() { 262 | let input = Span::new(b"Foo\\Bar\\Baz"); 263 | let output = Ok(( 264 | Span::new_at(b"", 11, 1, 12), 265 | Name::Qualified(smallvec![ 266 | Span::new(b"Foo"), 267 | Span::new_at(b"Bar", 4, 1, 5), 268 | Span::new_at(b"Baz", 8, 1, 9) 269 | ]) 270 | )); 271 | 272 | assert_eq!(qualified_name(input), output); 273 | } 274 | 275 | #[test] 276 | fn case_qualified_name_vector_capacity() { 277 | if let Ok((_, Name::Qualified(vector))) = qualified_name(Span::new(b"Foo\\Bar\\Baz")) { 278 | assert_eq!(vector.capacity(), 5); 279 | assert_eq!(vector.len(), 3); 280 | } else { 281 | assert!(false); 282 | } 283 | } 284 | 285 | #[test] 286 | fn case_qualified_name_with_skip_tokens() { 287 | let input = Span::new(b"Foo\n/* baz */ \\ Bar /* qux */\\"); 288 | let output = Ok(( 289 | Span::new_at(b" /* qux */\\", 19, 2, 16), 290 | Name::Qualified(smallvec![ 291 | Span::new(b"Foo"), 292 | Span::new_at(b"Bar", 16, 2, 13) 293 | ]) 294 | )); 295 | 296 | assert_eq!(qualified_name(input), output); 297 | } 298 | 299 | #[test] 300 | fn case_invalid_qualified_name() { 301 | assert_eq!( 302 | qualified_name(Span::new(b"Foo\\class\\Baz")), 303 | Ok(( 304 | Span::new_at(b"\\class\\Baz", 3, 1, 4), 305 | Name::Unqualified(Span::new(b"Foo")) 306 | )) 307 | ); 308 | assert_eq!( 309 | qualified_name(Span::new(b"Foo\\ClAsS\\Baz")), 310 | Ok(( 311 | Span::new_at(b"\\ClAsS\\Baz", 3, 1, 4), 312 | Name::Unqualified(Span::new(b"Foo")) 313 | )) 314 | ); 315 | } 316 | 317 | #[test] 318 | fn case_relative_qualified_name() { 319 | let input = Span::new(b"namespace\\Foo\\Bar\\Baz"); 320 | let output = Ok(( 321 | Span::new_at(b"", 21, 1, 22), 322 | Name::RelativeQualified(smallvec![ 323 | Span::new_at(b"Foo", 10, 1, 11), 324 | Span::new_at(b"Bar", 14, 1, 15), 325 | Span::new_at(b"Baz", 18, 1, 19) 326 | ]) 327 | )); 328 | 329 | assert_eq!(qualified_name(input), output); 330 | } 331 | 332 | #[test] 333 | fn case_relative_qualified_name_vector_capacity() { 334 | if let Ok((_, Name::RelativeQualified(vector))) = qualified_name(Span::new(b"namespace\\Foo\\Bar\\Baz")) { 335 | assert!(vector.capacity() >= vector.len()); 336 | assert!(vector.len() >= 3); 337 | } else { 338 | assert!(false); 339 | } 340 | } 341 | 342 | #[test] 343 | fn case_relative_qualified_name_case_insensitive() { 344 | let input = Span::new(b"NaMeSpAcE\\Foo\\Bar\\Baz"); 345 | let output = Ok(( 346 | Span::new_at(b"", 21, 1, 22), 347 | Name::RelativeQualified(smallvec![ 348 | Span::new_at(b"Foo", 10, 1, 11), 349 | Span::new_at(b"Bar", 14, 1, 15), 350 | Span::new_at(b"Baz", 18, 1, 19) 351 | ]) 352 | )); 353 | 354 | assert_eq!(qualified_name(input), output); 355 | } 356 | 357 | #[test] 358 | fn case_relative_qualified_name_with_skip_tokens() { 359 | let input = Span::new(b"namespace/* baz */ \\ Foo\n/* qux */ \\ Bar /* hello */\\"); 360 | let output = Ok(( 361 | Span::new_at(b" /* hello */\\", 40, 2, 16), 362 | Name::RelativeQualified(smallvec![ 363 | Span::new_at(b"Foo", 21, 1, 22), 364 | Span::new_at(b"Bar", 37, 2, 13) 365 | ]) 366 | )); 367 | 368 | assert_eq!(qualified_name(input), output); 369 | } 370 | 371 | #[test] 372 | fn case_invalid_relative_qualified_name_with_namespace() { 373 | let input1 = Span::new(b"namespace\\Foo\\namespace\\Baz"); 374 | let input2 = Span::new(b"namespace\\Foo\\NaMeSpAcE\\Baz"); 375 | 376 | assert_eq!( 377 | qualified_name(input1), 378 | Ok(( 379 | Span::new_at(b"\\namespace\\Baz", 13, 1, 14), 380 | Name::RelativeQualified(smallvec![Span::new_at(b"Foo", 10, 1, 11)]) 381 | )) 382 | ); 383 | assert_eq!( 384 | qualified_name(input2), 385 | Ok(( 386 | Span::new_at(b"\\NaMeSpAcE\\Baz", 13, 1, 14), 387 | Name::RelativeQualified(smallvec![Span::new_at(b"Foo", 10, 1, 11)]) 388 | )) 389 | ); 390 | } 391 | 392 | #[test] 393 | fn case_invalid_relative_qualified_name_with_any_keyword() { 394 | let input1 = Span::new(b"namespace\\class"); 395 | let input2 = Span::new(b"namespace\\ClAsS"); 396 | 397 | assert_eq!(qualified_name(input1), Err(Error::Error(Context::Code(Span::new_at(b"class", 10, 1, 11), ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 398 | assert_eq!(qualified_name(input2), Err(Error::Error(Context::Code(Span::new_at(b"ClAsS", 10, 1, 11), ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 399 | } 400 | 401 | #[test] 402 | fn case_fully_qualified_name() { 403 | let input = Span::new(b"\\Foo\\Bar\\Baz"); 404 | let output = Ok(( 405 | Span::new_at(b"", 12, 1, 13), 406 | Name::FullyQualified(smallvec![ 407 | Span::new_at(b"Foo", 1, 1, 2), 408 | Span::new_at(b"Bar", 5, 1, 6), 409 | Span::new_at(b"Baz", 9, 1, 10) 410 | ]) 411 | )); 412 | 413 | assert_eq!(qualified_name(input), output); 414 | } 415 | 416 | #[test] 417 | fn case_fully_qualified_name_vector_capacity() { 418 | if let Ok((_, Name::FullyQualified(vector))) = qualified_name(Span::new(b"\\Foo\\Bar\\Baz")) { 419 | assert_eq!(vector.capacity(), 5); 420 | assert_eq!(vector.len(), 3); 421 | } else { 422 | assert!(false); 423 | } 424 | } 425 | 426 | #[test] 427 | fn case_fully_qualified_shortest_name() { 428 | let input = Span::new(b"\\Foo"); 429 | let output = Ok(( 430 | Span::new_at(b"", 4, 1, 5), 431 | Name::FullyQualified(smallvec![Span::new_at(b"Foo", 1, 1, 2)]) 432 | )); 433 | 434 | assert_eq!(qualified_name(input), output); 435 | } 436 | 437 | #[test] 438 | fn case_invalid_fully_and_relative_qualified_name_with_namespace() { 439 | let input = Span::new(b"\\namespace\\Foo\\Bar"); 440 | 441 | assert_eq!(qualified_name(input), Err(Error::Error(Context::Code(input.slice(1..), ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 442 | } 443 | 444 | #[test] 445 | fn case_invalid_fully_and_relative_qualified_name_with_any_keyword() { 446 | let input1 = Span::new(b"\\class\\Foo\\Bar"); 447 | let input2 = Span::new(b"\\ClAsS\\Foo\\Bar"); 448 | 449 | assert_eq!(qualified_name(input1), Err(Error::Error(Context::Code(input1.slice(1..), ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 450 | assert_eq!(qualified_name(input2), Err(Error::Error(Context::Code(input2.slice(1..), ErrorKind::Custom(ErrorKindCustom::Exclude as u32))))); 451 | } 452 | 453 | #[test] 454 | fn case_invalid_qualified_name_ending_with_a_separator() { 455 | let input = Span::new(b"Foo\\Bar\\"); 456 | let output = Ok(( 457 | Span::new_at(b"\\", 7, 1, 8), 458 | Name::Qualified(smallvec![ 459 | Span::new(b"Foo"), 460 | Span::new_at(b"Bar", 4, 1, 5) 461 | ]) 462 | )); 463 | 464 | assert_eq!(qualified_name(input), output); 465 | } 466 | 467 | #[test] 468 | fn case_name() { 469 | let input = Span::new(b"_fooBar42"); 470 | let output = Ok((Span::new_at(b"", 9, 1, 10), input)); 471 | 472 | assert_eq!(name(input), output); 473 | } 474 | 475 | #[test] 476 | fn case_name_shortest() { 477 | let input = Span::new(b"x"); 478 | let output = Ok((Span::new_at(b"", 1, 1, 2), input)); 479 | 480 | assert_eq!(name(input), output); 481 | } 482 | 483 | #[test] 484 | fn case_name_only_head() { 485 | let input = Span::new(b"aB_\x80"); 486 | let output = Ok((Span::new_at(b"", 4, 1, 5), input)); 487 | 488 | assert_eq!(name(input), output); 489 | } 490 | 491 | #[test] 492 | fn case_name_head_and_tail() { 493 | let input = Span::new(b"aB_\x80aB7\xff"); 494 | let output = Ok((Span::new_at(b"", 8, 1, 9), input)); 495 | 496 | assert_eq!(name(input), output); 497 | } 498 | 499 | #[test] 500 | fn case_name_copyright() { 501 | // © = 0xa9 502 | let input = Span::new(b"\xa9"); 503 | let output = Ok((Span::new_at(b"", 1, 1, 2), input)); 504 | 505 | assert_eq!(name(input), output); 506 | } 507 | 508 | #[test] 509 | fn case_name_non_breaking_space() { 510 | //   = 0xa0 511 | let input = Span::new(b"\xa0"); 512 | let output = Ok((Span::new_at(b"", 1, 1, 2), input)); 513 | 514 | assert_eq!(name(input), output); 515 | } 516 | 517 | #[test] 518 | fn case_invalid_name() { 519 | let input = Span::new(b"0x"); 520 | 521 | assert_eq!(name(input), Err(Error::Error(Context::Code(input, ErrorKind::RegexpFind)))); 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /source/rules/whitespaces.rs: -------------------------------------------------------------------------------- 1 | // Tagua VM 2 | // 3 | // 4 | // New BSD License 5 | // 6 | // Copyright © 2016-2017, Ivan Enderlin. 7 | // All rights reserved. 8 | // 9 | // Redistribution and use in source and binary forms, with or without 10 | // modification, are permitted provided that the following conditions are met: 11 | // * Redistributions of source code must retain the above copyright 12 | // notice, this list of conditions and the following disclaimer. 13 | // * Redistributions in binary form must reproduce the above copyright 14 | // notice, this list of conditions and the following disclaimer in the 15 | // documentation and/or other materials provided with the distribution. 16 | // * Neither the name of the Hoa nor the names of its contributors may be 17 | // used to endorse or promote products derived from this software without 18 | // specific prior written permission. 19 | // 20 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE 24 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | // POSSIBILITY OF SUCH DAMAGE. 31 | 32 | //! Group of white space rules. 33 | //! 34 | //! The list of all white spaces is provided by the PHP Language Specification 35 | //! in the [Grammar chapter, White Space 36 | //! section](https://github.com/php/php-langspec/blob/master/spec/19-grammar.md#white-space). 37 | 38 | use super::super::tokens::Span; 39 | 40 | named_attr!( 41 | #[doc=" 42 | Recognize all whitespaces. 43 | 44 | # Examples 45 | 46 | ``` 47 | use tagua_parser::Result; 48 | use tagua_parser::rules::whitespaces::whitespace; 49 | use tagua_parser::tokens::Span; 50 | 51 | # fn main () { 52 | assert_eq!( 53 | whitespace(Span::new(b\"\\n \\r\\tabc\")), 54 | Ok(( 55 | Span::new_at(b\"abc\", 4, 2, 4), 56 | Span::new(b\"\\n \\r\\t\") 57 | )) 58 | ); 59 | # } 60 | ``` 61 | "], 62 | pub whitespace, 63 | is_a!(" \t\n\r") 64 | ); 65 | 66 | #[cfg(test)] 67 | mod tests { 68 | use super::super::super::internal::{ 69 | Context, 70 | Error, 71 | ErrorKind 72 | }; 73 | use super::super::super::tokens::Span; 74 | use super::whitespace; 75 | 76 | #[test] 77 | fn case_whitespace_space() { 78 | let input = Span::new(b" "); 79 | let output = Ok((Span::new_at(b"", 3, 1, 4), input)); 80 | 81 | assert_eq!(whitespace(input), output); 82 | } 83 | 84 | #[test] 85 | fn case_whitespace_horizontal_tabulation() { 86 | let input = Span::new(b"\t\t\t"); 87 | let output = Ok((Span::new_at(b"", 3, 1, 4), input)); 88 | 89 | assert_eq!(whitespace(input), output); 90 | } 91 | 92 | #[test] 93 | fn case_whitespace_carriage_return_line_feed() { 94 | let input = Span::new(b"\r\n\r\n\r\n"); 95 | let output = Ok((Span::new_at(b"", 6, 4, 1), input)); 96 | 97 | assert_eq!(whitespace(input), output); 98 | } 99 | 100 | #[test] 101 | fn case_whitespace_carriage_return() { 102 | let input = Span::new(b"\r\r\r"); 103 | let output = Ok((Span::new_at(b"", 3, 1, 4), input)); 104 | 105 | assert_eq!(whitespace(input), output); 106 | } 107 | 108 | #[test] 109 | fn case_whitespace_line_feed() { 110 | let input = Span::new(b"\n\n\n"); 111 | let output = Ok((Span::new_at(b"", 3, 4, 1), input)); 112 | 113 | assert_eq!(whitespace(input), output); 114 | } 115 | 116 | #[test] 117 | fn case_whitespace_mixed() { 118 | let input = Span::new(b"\n \n \r\t \t\r\n\t \t\t"); 119 | let output = Ok((Span::new_at(b"", 15, 4, 5), input)); 120 | 121 | assert_eq!(whitespace(input), output); 122 | } 123 | 124 | #[test] 125 | fn case_whitespace_with_a_tail() { 126 | let input = Span::new(b"\n \n \r\t \t\r\n\t \t\tabc "); 127 | let output = Ok((Span::new_at(b"abc ", 15, 4, 5), Span::new(b"\n \n \r\t \t\r\n\t \t\t"))); 128 | 129 | assert_eq!(whitespace(input), output); 130 | } 131 | 132 | #[test] 133 | fn case_whitespace_too_short() { 134 | let input = Span::new(b""); 135 | let output = Ok((Span::new_at(b"", 0, 1, 1), input)); 136 | 137 | assert_eq!(whitespace(input), output); 138 | } 139 | 140 | #[test] 141 | fn case_invalid_whitespace_not_a_valid_whitespace() { 142 | let input = Span::new(b"\xa0 "); 143 | let output = Err(Error::Error(Context::Code(input, ErrorKind::IsA))); 144 | 145 | assert_eq!(whitespace(input), output); 146 | } 147 | 148 | #[test] 149 | fn case_invalid_whitespace_not_a_valid_character() { 150 | let input = Span::new(b"abc\n \t"); 151 | let output = Err(Error::Error(Context::Code(input, ErrorKind::IsA))); 152 | 153 | assert_eq!(whitespace(input), output); 154 | } 155 | } 156 | --------------------------------------------------------------------------------