├── .github └── workflows │ └── build.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── default.nix └── src ├── ast.rs ├── bin └── git-revs.rs ├── error.rs ├── eval.rs ├── ext.rs ├── lib.rs ├── mutation.rs ├── parser.rs ├── parser ├── grammar.lalrpop └── grammar.rs ├── repo.rs ├── testrepo.rs └── tests.rs /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Test 22 | run: cargo test --verbose 23 | - name: Test with features 24 | run: cargo test --verbose --features testutil 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "gitrevset" 3 | version = "0.2.0" 4 | authors = ["Jun Wu "] 5 | edition = "2018" 6 | license = "GPL-2.0-only" 7 | description = "A domain-specific-language to select commits in a git repo. Similar to Mercurial's revset." 8 | repository = "https://github.com/quark-zju/gitrevset" 9 | 10 | [dependencies] 11 | drawdag = { package = "esl01-drawdag", version = "0.1", optional = true } 12 | gitdag = "0.1.2" 13 | globset = "0.4" 14 | hgtime = { package = "esl01-hgtime", version = "0.1" } 15 | lalrpop-util = { version = "0.19", features = ["lexer"] } 16 | once_cell = "1.4" 17 | tempfile = { version = "3", optional = true } 18 | thiserror = "1" 19 | 20 | [dev-dependencies] 21 | drawdag = { package = "esl01-drawdag", version = "0.1" } 22 | tempfile = "3" 23 | 24 | [features] 25 | default = [] 26 | testutil = ["drawdag", "tempfile"] 27 | 28 | [[bin]] 29 | name = "git-revs" 30 | path = "src/bin/git-revs.rs" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gitrevset 2 | 3 | [![Documentation](https://docs.rs/gitrevset/badge.svg)](https://docs.rs/gitrevset) 4 | ![Build Status](https://github.com/quark-zju/gitrevset/workflows/build/badge.svg) 5 | 6 | A domain-specific-language to select commits in a git repo. Similar to 7 | [Mercurial's revset](https://www.mercurial-scm.org/repo/hg/help/revsets). 8 | 9 | See [the crate documentation](https://docs.rs/gitrevset/#language-specification) for supported functions and operators. More functions might be added over time. 10 | 11 | `gitrevset` provides the Rust library interface. There is also a simple command-line utility `git-revs`. It takes revset expressions as arguments, and outputs commit hashes. 12 | 13 | ## Examples 14 | 15 | ### Revset Expressions 16 | 17 | The current commit (HEAD) and its parent: 18 | 19 | . + .^ 20 | 21 | Merge base (common ancestor) of HEAD and origin/master: 22 | 23 | gca(., origin/master) 24 | 25 | The bottom of the current local (draft) branch: 26 | 27 | roots(draft() & ::.) 28 | 29 | Tagged commits since 100 days ago: 30 | 31 | tag() & date("since 100 days ago") 32 | 33 | Commits by "alice" or "bob" in the "dev" but not "master" branch: 34 | 35 | (dev % master) & (author(alice) | author(bob)) 36 | 37 | ### Using `gitrevset` Library 38 | 39 | Parse revset from a string at runtime. Execute it and iterate through the `Oid`s: 40 | 41 | ```rust 42 | use gitrevset::{Repo, SetExt}; 43 | 44 | let repo = Repo::open_from_env()?; 45 | let set = repo.revs("(draft() & ::.)^ + .")?; 46 | for oid in set.to_oids()? { 47 | dbg!(oid?) 48 | } 49 | ``` 50 | 51 | Parse at compile time. Interact with local variables like strings, or calculated set: 52 | 53 | ```rust 54 | use gitrevset::{ast, Repo}; 55 | 56 | let repo = Repo::open_from_env()?; 57 | let master = "origin/master"; 58 | let stack = repo.revs(ast!(only(".", ref({ master }))))?; 59 | let head = repo.revs(ast!(heads({ stack })))?; 60 | ``` 61 | 62 | ### Using `git-revs` CLI 63 | 64 | ```bash 65 | git revs "(draft() & ::.)^ + ." 66 | ``` 67 | 68 | ### Configuration 69 | 70 | Customized revset aliases or functions can be defined in git config: 71 | 72 | ```ini 73 | [revsetalias] 74 | d = draft() 75 | f = ancestor($1, origin/master):$1 76 | ``` 77 | 78 | Then they can be used in `git-revs` or using the `repo.anyrevs` API. 79 | 80 | ```bash 81 | git revs "f(d)" 82 | ``` 83 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | stdenv.mkDerivation { 3 | name = "gitrevset"; 4 | # used by the git2 crate 5 | buildInputs = [ openssl pkg-config ]; 6 | } 7 | -------------------------------------------------------------------------------- /src/ast.rs: -------------------------------------------------------------------------------- 1 | use crate::Error; 2 | use crate::Result; 3 | use gitdag::dag::Set; 4 | use gitdag::git2::Oid; 5 | use std::borrow::Cow; 6 | use std::fmt; 7 | 8 | /// A node in the parsed AST. 9 | #[derive(Clone)] 10 | pub enum Expr { 11 | /// A plain string name. 12 | Name(String), 13 | 14 | /// A function call. 15 | Fn(Cow<'static, str>, Vec), 16 | 17 | /// An inlined Set. 18 | Inlined(Set), 19 | } 20 | 21 | impl fmt::Debug for Expr { 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | match self { 24 | Expr::Name(s) => f.write_str(&s)?, 25 | Expr::Fn(name, args) => { 26 | if args.is_empty() { 27 | f.write_str(name)?; 28 | f.write_str("()")?; 29 | } else { 30 | let mut list = f.debug_tuple(&name); 31 | for arg in args { 32 | list.field(arg); 33 | } 34 | list.finish()?; 35 | } 36 | } 37 | Expr::Inlined(set) => set.fmt(f)?, 38 | } 39 | Ok(()) 40 | } 41 | } 42 | 43 | impl fmt::Display for Expr { 44 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 45 | ::fmt(self, f) 46 | } 47 | } 48 | 49 | impl Expr { 50 | /// Replace a name (ex. `$1`) to another name. 51 | /// Useful to support user-defined aliases. 52 | pub(crate) fn replace(&mut self, from: &str, to: &Expr) { 53 | match self { 54 | Expr::Name(s) => { 55 | if &s[..] == from { 56 | *self = to.clone(); 57 | } 58 | } 59 | Expr::Fn(name, args) => { 60 | // Special case: hold the first argument of "apply" unchanged. 61 | if name == "apply" && args.len() > 1 { 62 | for arg in &mut args[1..] { 63 | arg.replace(from, to); 64 | } 65 | } else { 66 | for arg in args { 67 | arg.replace(from, to); 68 | } 69 | } 70 | } 71 | Expr::Inlined(_) => (), 72 | } 73 | } 74 | 75 | /// Parse AST from a string. 76 | pub fn parse(s: &str) -> Result { 77 | crate::parser::parse(s).map_err(|e| Error::ParseError(e.to_string())) 78 | } 79 | } 80 | 81 | /// Convert to `Expr` by parsing. 82 | pub trait ParseToExpr { 83 | /// Convert to `Expr` by parsing. 84 | fn parse_to_expr(self) -> Result; 85 | } 86 | 87 | impl ParseToExpr for &str { 88 | fn parse_to_expr(self) -> Result { 89 | Expr::parse(self) 90 | } 91 | } 92 | 93 | impl ParseToExpr for Expr { 94 | fn parse_to_expr(self) -> Result { 95 | Ok(self) 96 | } 97 | } 98 | 99 | impl From<&str> for Expr { 100 | fn from(s: &str) -> Expr { 101 | Expr::Name(s.to_string()) 102 | } 103 | } 104 | 105 | impl From for Expr { 106 | fn from(s: Set) -> Expr { 107 | Expr::Inlined(s) 108 | } 109 | } 110 | 111 | impl From for Expr { 112 | fn from(oid: Oid) -> Expr { 113 | use crate::ext::OidExt; 114 | Expr::Inlined(oid.to_vertex().into()) 115 | } 116 | } 117 | 118 | /// Construct an AST statically. This can be useful to avoid escaping issues 119 | /// parsing strings. 120 | /// 121 | /// # Example 122 | /// 123 | /// ``` 124 | /// # use gitrevset::ast; 125 | /// let expr = ast!(union(draft(), desc("foo"))); 126 | /// assert_eq!(expr.to_string(), "union(draft(), desc(foo))"); 127 | /// ``` 128 | /// 129 | /// Use `{ ... }` to refer to local variables: 130 | /// 131 | /// ``` 132 | /// # use gitrevset::ast; 133 | /// let name = "origin/master"; 134 | /// let expr = ast!(ref({ name })); 135 | /// assert_eq!(expr.to_string(), "ref(origin/master)"); 136 | /// let nested = ast!(parents({ expr })); 137 | /// assert_eq!(nested.to_string(), "parents(ref(origin/master))"); 138 | /// ``` 139 | /// 140 | /// `Set` can also be referred: 141 | /// 142 | /// ``` 143 | /// # fn main() -> gitrevset::Result<()> { 144 | /// # #[cfg(feature = "testutil")] 145 | /// # { 146 | /// # use gitrevset::ast; 147 | /// # use gitrevset::dag::DagAlgorithm; 148 | /// # let mut repo = gitrevset::TestRepo::new(); 149 | /// # repo.drawdag("A--B"); 150 | /// let head = repo.revs(ast!(head()))?; 151 | /// let set = repo.revs(ast!(parents({ head })))?; 152 | /// 153 | /// // The above is similar to using raw `dag` APIs: 154 | /// let set2 = { 155 | /// let dag = repo.dag(); 156 | /// dag.parents(dag.heads(dag.all()?)?)? 157 | /// }; 158 | /// # assert_eq!((set2.clone() - set.clone()).count()?, 0); 159 | /// # assert_eq!((set.clone() - set2.clone()).count()?, 0); 160 | /// # } 161 | /// # Ok(()) 162 | /// # } 163 | #[macro_export] 164 | macro_rules! ast { 165 | ($v:literal) => { $crate::Expr::Name($v.to_string()) }; 166 | ($fname:ident ( $($arg:tt $( ( $($subargs:tt)* ) )? ),* )) => {{ 167 | let args = vec![ $(ast!($arg $( ( $($subargs)* ) )?),)* ]; 168 | $crate::Expr::Fn(stringify!($fname).into(), args) 169 | }}; 170 | {$v:expr} => { $crate::Expr::from($v) }; 171 | } 172 | -------------------------------------------------------------------------------- /src/bin/git-revs.rs: -------------------------------------------------------------------------------- 1 | use gitrevset::Expr; 2 | use gitrevset::Repo; 3 | use gitrevset::Result; 4 | use std::env; 5 | 6 | fn try_main() -> Result<()> { 7 | let repo = Repo::open_from_env()?; 8 | let mut print_ast = false; 9 | for arg in env::args().skip(1) { 10 | let arg: &str = &arg; 11 | if arg == "--ast" { 12 | print_ast = true; 13 | continue; 14 | } 15 | if print_ast { 16 | let ast = Expr::parse(arg)?; 17 | println!("{:?}", ast); 18 | } else { 19 | let set = repo.anyrevs(arg)?; 20 | for v in set.iter()? { 21 | println!("{}", v?.to_hex()); 22 | } 23 | } 24 | } 25 | Ok(()) 26 | } 27 | 28 | fn main() { 29 | match try_main() { 30 | Ok(()) => (), 31 | Err(e) => eprintln!("{}", e), 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use gitdag::dag::Vertex; 2 | use std::convert::Infallible; 3 | use thiserror::Error; 4 | 5 | /// Error type used by `gitrevset`. 6 | #[derive(Error, Debug)] 7 | pub enum Error { 8 | /// Error caused by the commit graph indexing layer. 9 | #[error(transparent)] 10 | Dag(#[from] gitdag::dag::Error), 11 | 12 | /// Error caused by libgit2. 13 | #[error(transparent)] 14 | Git2(#[from] gitdag::git2::Error), 15 | 16 | /// A short commit hash can be resolved to multiple commits. 17 | #[error("ambiguous prefix: {0:?}")] 18 | AmbiguousPrefix(Vec), 19 | 20 | /// A name cannot be resolved. 21 | #[error("name {0:?} cannot be resolved")] 22 | UnresolvedName(String), 23 | 24 | /// A function call with wrong number of arguments. 25 | #[error("function {0} requires {1} arguments, but got {2} arguments")] 26 | MismatchedArguments(String, usize, usize), 27 | 28 | /// String is expected in the AST but got something different. 29 | #[error("expect string, got {0}")] 30 | ExpectString(String), 31 | 32 | /// An expression cannot be parsed into an AST. 33 | #[error("{0}")] 34 | ParseError(String), 35 | } 36 | 37 | impl From for Error { 38 | fn from(_e: Infallible) -> Self { 39 | unreachable!() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/eval.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::Expr; 2 | use crate::repo::Repo; 3 | use crate::Error; 4 | use crate::Result; 5 | use dag::ops::DagAlgorithm; 6 | use dag::ops::PrefixLookup; 7 | use dag::Set; 8 | use dag::Vertex; 9 | use gitdag::dag; 10 | use gitdag::git2; 11 | use globset::Glob; 12 | use hgtime::HgTime; 13 | use std::collections::HashMap; 14 | use std::ops::Deref; 15 | use std::path::Path; 16 | use std::sync::Arc; 17 | use std::sync::Mutex; 18 | 19 | type EvalFn = Box Result>; 20 | 21 | /// Extra context for `eval`. It can define customized aliases. 22 | #[derive(Default)] 23 | pub struct Context { 24 | /// Extra pre-calculated sets. For example, if "foo" is defined here, 25 | /// "foo + x" will use the defined "foo" set. 26 | pub names: HashMap, 27 | 28 | /// Extra functions. For example, if "foo" is defined here, "foo(x)" will 29 | /// use the "foo" function. 30 | pub fns: HashMap, 31 | } 32 | 33 | /// Evaluate an AST. Return the resulting set. 34 | /// `context` can be used to define customized names or functions. 35 | pub fn eval(repo: &Repo, expr: &Expr, context: &Context) -> Result { 36 | match expr { 37 | Expr::Name(name) => lookup(repo, name, context), 38 | Expr::Fn(name, args) => { 39 | let func = get_function(name, context)?; 40 | func(name, repo, args, context) 41 | } 42 | Expr::Inlined(set) => Ok(set.clone()), 43 | } 44 | } 45 | 46 | /// Resolve a name. 47 | fn lookup(repo: &Repo, name: &str, context: &Context) -> Result { 48 | // User alias. 49 | if let Some(set) = context.names.get(name) { 50 | return Ok(set.clone()); 51 | } 52 | if let Some(func) = context.fns.get(name) { 53 | return func(name, repo, &[], context); 54 | } 55 | 56 | let args = [Expr::Name(name.to_string())]; 57 | 58 | // Resolve references. 59 | if let Ok(set) = r#ref("lookup", repo, &args, context) { 60 | return Ok(set); 61 | } 62 | 63 | // Resolve as commit hash. 64 | id("lookup", repo, &args, context) 65 | } 66 | 67 | /// Resolve a function name. 68 | fn get_function<'a>( 69 | name: &str, 70 | context: &'a Context, 71 | ) -> Result<&'a dyn Fn(&str, &Repo, &[Expr], &Context) -> Result> { 72 | if let Some(func) = context.fns.get(name) { 73 | return Ok(func); 74 | } 75 | match name { 76 | "parents" => Ok(&parents), 77 | "children" => Ok(&children), 78 | "ancestors" => Ok(&ancestors), 79 | "descendants" => Ok(&descendants), 80 | "heads" => Ok(&heads), 81 | "roots" => Ok(&roots), 82 | "range" => Ok(&range), 83 | "only" => Ok(&only), 84 | "ancestor" => Ok(&gca), 85 | "gca" => Ok(&gca), 86 | "intersection" => Ok(&intersection), 87 | "union" => Ok(&union), 88 | "difference" => Ok(&difference), 89 | "negate" => Ok(&negate), 90 | "first" => Ok(&first), 91 | "last" => Ok(&last), 92 | "head" => Ok(&head), 93 | "all" => Ok(&all), 94 | "publichead" => Ok(&publichead), 95 | "drafthead" => Ok(&drafthead), 96 | "public" => Ok(&public), 97 | "draft" => Ok(&draft), 98 | "author" => Ok(&author), 99 | "date" => Ok(&date), 100 | "committer" => Ok(&committer), 101 | "committerdate" => Ok(&committer_date), 102 | "desc" => Ok(&desc), 103 | "modifies" => Ok(&modifies), 104 | "predecessors" => Ok(&predecessors), 105 | "successors" => Ok(&successors), 106 | "obsolete" => Ok(&obsolete), 107 | "id" => Ok(&id), 108 | "ref" => Ok(&r#ref), 109 | "tag" => Ok(&tag), 110 | "present" => Ok(&present), 111 | "none" => Ok(&none), 112 | "apply" => Ok(&apply), 113 | _ => Err(Error::UnresolvedName(name.to_string())), 114 | } 115 | } 116 | 117 | /// Ensure the number of arguments. 118 | fn ensure_arg_count(func_name: &str, args: &[Expr], n: usize, context: &Context) -> Result<()> { 119 | let _ = context; 120 | if args.len() != n { 121 | Err(Error::MismatchedArguments( 122 | func_name.to_string(), 123 | n, 124 | args.len(), 125 | )) 126 | } else { 127 | Ok(()) 128 | } 129 | } 130 | 131 | /// Expr -> Set 132 | fn resolve_set(repo: &Repo, expr: &Expr, context: &Context) -> Result { 133 | eval(repo, expr, context) 134 | } 135 | 136 | /// Expr -> String 137 | fn resolve_string(expr: &Expr) -> Result { 138 | match expr { 139 | Expr::Name(name) => Ok(name.clone()), 140 | _ => Err(Error::UnresolvedName(format!("{:?}", expr))), 141 | } 142 | } 143 | 144 | /// Resolve args to a single set. 145 | fn resolve_single_set( 146 | func_name: &str, 147 | repo: &Repo, 148 | args: &[Expr], 149 | context: &Context, 150 | ) -> Result { 151 | ensure_arg_count(func_name, args, 1, context)?; 152 | eval(repo, &args[0], context) 153 | } 154 | 155 | /// Resolve 2 args to 2 sets. 156 | fn resolve_double_sets( 157 | func_name: &str, 158 | repo: &Repo, 159 | args: &[Expr], 160 | context: &Context, 161 | ) -> Result<(Set, Set)> { 162 | ensure_arg_count(func_name, args, 2, context)?; 163 | Ok(( 164 | eval(repo, &args[0], context)?, 165 | eval(repo, &args[1], context)?, 166 | )) 167 | } 168 | 169 | fn parents(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 170 | let set = resolve_single_set(func_name, repo, args, context)?; 171 | Ok(repo.dag().parents(set)?) 172 | } 173 | 174 | fn children(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 175 | let set = resolve_single_set(func_name, repo, args, context)?; 176 | let dag = repo.dag(); 177 | let roots = set.clone(); 178 | let visible = dag.ancestors(dag.git_heads())?; 179 | Ok(dag.children(roots)? & visible) 180 | } 181 | 182 | fn ancestors(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 183 | let set = resolve_single_set(func_name, repo, args, context)?; 184 | Ok(repo.dag().ancestors(set)?) 185 | } 186 | 187 | fn descendants(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 188 | let set = resolve_single_set(func_name, repo, args, context)?; 189 | let dag = repo.dag(); 190 | let roots = set.clone(); 191 | Ok(dag.range(roots, dag.git_heads())? | set) 192 | } 193 | 194 | fn heads(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 195 | let set = resolve_single_set(func_name, repo, args, context)?; 196 | Ok(repo.dag().heads(set)?) 197 | } 198 | 199 | fn roots(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 200 | let set = resolve_single_set(func_name, repo, args, context)?; 201 | Ok(repo.dag().roots(set)?) 202 | } 203 | 204 | fn range(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 205 | let (roots, heads) = resolve_double_sets(func_name, repo, args, context)?; 206 | Ok(repo.dag().range(roots, heads)?) 207 | } 208 | 209 | fn only(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 210 | let (reachable, unreachable) = resolve_double_sets(func_name, repo, args, context)?; 211 | Ok(repo.dag().only(reachable, unreachable)?) 212 | } 213 | 214 | fn gca(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 215 | let _ = func_name; 216 | let mut set = repo.to_set(std::iter::empty())?; 217 | for arg in args { 218 | let subset = resolve_set(repo, arg, context)?; 219 | set = set | subset; 220 | } 221 | Ok(repo.dag().gca_all(set)?) 222 | } 223 | 224 | fn intersection(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 225 | let (a, b) = resolve_double_sets(func_name, repo, args, context)?; 226 | Ok(a & b) 227 | } 228 | 229 | fn union(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 230 | let (a, b) = resolve_double_sets(func_name, repo, args, context)?; 231 | Ok(a | b) 232 | } 233 | 234 | fn difference(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 235 | let (a, b) = resolve_double_sets(func_name, repo, args, context)?; 236 | Ok(a - b) 237 | } 238 | 239 | fn negate(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 240 | let set = resolve_single_set(func_name, repo, args, context)?; 241 | let dag = repo.dag(); 242 | Ok(dag.all()? - set) 243 | } 244 | 245 | fn first(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 246 | let _ = func_name; 247 | for arg in args { 248 | let subset = resolve_set(repo, arg, context)?; 249 | if let Some(v) = subset.first()? { 250 | return repo.to_set(std::iter::once(v)); 251 | } 252 | } 253 | repo.to_set(std::iter::empty()) 254 | } 255 | 256 | fn last(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 257 | let set = resolve_single_set(func_name, repo, args, context)?; 258 | if let Some(v) = set.last()? { 259 | return repo.to_set(std::iter::once(v)); 260 | } 261 | repo.to_set(std::iter::empty()) 262 | } 263 | 264 | fn head(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 265 | ensure_arg_count(func_name, args, 0, context)?; 266 | Ok(repo.dag().git_heads()) 267 | } 268 | 269 | fn all(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 270 | ensure_arg_count(func_name, args, 0, context)?; 271 | repo.cached_set("all", |repo| { 272 | let heads = head("head", repo, &[], context)?; 273 | Ok(repo.dag().ancestors(heads)?) 274 | }) 275 | } 276 | 277 | fn publichead(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 278 | ensure_arg_count(func_name, args, 0, context)?; 279 | repo.cached_set("publichead", |repo| { 280 | r#ref( 281 | "refglob", 282 | repo, 283 | &[Expr::Name("remotes/**".to_string())], 284 | context, 285 | ) 286 | }) 287 | } 288 | 289 | fn drafthead(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 290 | ensure_arg_count(func_name, args, 0, context)?; 291 | repo.cached_set("drafthead", |repo| { 292 | Ok(head("head", repo, &[], context)? - publichead("publichead", repo, args, context)?) 293 | }) 294 | } 295 | 296 | fn public(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 297 | ensure_arg_count(func_name, args, 0, context)?; 298 | repo.cached_set("public", |repo| { 299 | let dag = repo.dag(); 300 | Ok(dag.ancestors(publichead("publichead", repo, &[], context)?)?) 301 | }) 302 | } 303 | 304 | fn draft(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 305 | ensure_arg_count(func_name, args, 0, context)?; 306 | repo.cached_set("draft", |repo| { 307 | let dag = repo.dag(); 308 | Ok(dag.ancestors(head("drafthead", repo, &[], context)?)? 309 | - public("public", repo, &[], context)?) 310 | }) 311 | } 312 | 313 | fn author(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 314 | ensure_arg_count(func_name, args, 1, context)?; 315 | let name = resolve_string(&args[0])?; 316 | filter_set(repo, move |commit| { 317 | let author = commit.author(); 318 | author.name().unwrap_or("").contains(&name) || author.email().unwrap_or("").contains(&name) 319 | }) 320 | } 321 | 322 | fn date(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 323 | ensure_arg_count(func_name, args, 1, context)?; 324 | let date_str = resolve_string(&args[0])?; 325 | let date_range = match HgTime::parse_range(&date_str) { 326 | Some(range) => range.start.unixtime..=range.end.unixtime, 327 | None => { 328 | return Err(crate::Error::ParseError(format!( 329 | "invalid date: {}", 330 | date_str 331 | ))) 332 | } 333 | }; 334 | filter_set(repo, move |commit| { 335 | let author = commit.author(); 336 | let epoch = author.when().seconds(); 337 | date_range.contains(&epoch) 338 | }) 339 | } 340 | 341 | fn committer_date(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 342 | ensure_arg_count(func_name, args, 1, context)?; 343 | let date_str = resolve_string(&args[0])?; 344 | let date_range = match HgTime::parse_range(&date_str) { 345 | Some(range) => range.start.unixtime..=range.end.unixtime, 346 | None => { 347 | return Err(crate::Error::ParseError(format!( 348 | "invalid date: {}", 349 | date_str 350 | ))) 351 | } 352 | }; 353 | filter_set(repo, move |commit| { 354 | let committer = commit.committer(); 355 | let epoch = committer.when().seconds(); 356 | date_range.contains(&epoch) 357 | }) 358 | } 359 | 360 | fn committer(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 361 | ensure_arg_count(func_name, args, 1, context)?; 362 | let name = resolve_string(&args[0])?; 363 | filter_set(repo, move |commit| { 364 | let author = commit.committer(); 365 | author.name().unwrap_or("").contains(&name) || author.email().unwrap_or("").contains(&name) 366 | }) 367 | } 368 | 369 | fn desc(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 370 | ensure_arg_count(func_name, args, 1, context)?; 371 | let text = resolve_string(&args[0])?; 372 | filter_set(repo, move |commit| { 373 | commit.summary().unwrap_or("").contains(&text) 374 | }) 375 | } 376 | 377 | fn modifies(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 378 | ensure_arg_count(func_name, args, 1, context)?; 379 | let path = resolve_string(&args[0])?; 380 | filter_set(repo, move |commit| { 381 | let path = Path::new(&path); 382 | (|| -> Result { 383 | let tree = commit.tree()?; 384 | let entry = tree.get_path(&path)?; 385 | let mut parent_not_found_count = 0; 386 | let parents = commit.parents(); 387 | let parents_len = parents.len(); 388 | for parent in parents { 389 | let parent_tree = parent.tree()?; 390 | let parent_entry = match parent_tree.get_path(&path) { 391 | Err(e) => match e.code() { 392 | git2::ErrorCode::NotFound => { 393 | parent_not_found_count += 1; 394 | if parent_not_found_count == parents_len { 395 | return Ok(true); 396 | } 397 | continue; 398 | } 399 | _ => return Ok(true), 400 | }, 401 | Ok(entry) => entry, 402 | }; 403 | if parent_entry.id() != entry.id() || parent_entry.filemode() != entry.filemode() { 404 | return Ok(true); 405 | } 406 | } 407 | Ok(false) 408 | })() 409 | .unwrap_or(false) 410 | }) 411 | } 412 | 413 | fn predecessors(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 414 | let set = resolve_single_set(func_name, repo, args, context)?; 415 | let dag = repo.dag(); 416 | let mutdag = repo.mutation_dag()?; 417 | let set = set.clone() | (mutdag.ancestors(set & mutdag.all()?)? & dag.all()?); 418 | Ok(dag.sort(&set)?.flatten()?) 419 | } 420 | 421 | fn successors(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 422 | let set = resolve_single_set(func_name, repo, args, context)?; 423 | let dag = repo.dag(); 424 | let mutdag = repo.mutation_dag()?; 425 | let set = set.clone() | (mutdag.descendants(set & mutdag.all()?)? & dag.all()?); 426 | Ok(dag.sort(&set)?.flatten()?) 427 | } 428 | 429 | fn obsolete(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 430 | ensure_arg_count(func_name, args, 0, context)?; 431 | let mutdag = repo.mutation_dag()?; 432 | let draft = draft("draft", repo, &[], context)?; 433 | let obsoleted = mutdag.parents(draft.clone() & mutdag.all()?)?; 434 | let set = obsoleted & draft; 435 | Ok(repo.dag().sort(&set)?.flatten()?) 436 | } 437 | 438 | fn id(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 439 | ensure_arg_count(func_name, args, 1, context)?; 440 | let name = resolve_string(&args[0])?; 441 | match name.as_ref() { 442 | "." | "@" | "HEAD" => { 443 | let id = repo.git_repo().head()?.peel_to_commit()?.id(); 444 | let v = Vertex::copy_from(id.as_bytes()); 445 | repo.to_set(std::iter::once(v)) 446 | } 447 | _ => { 448 | if let Some(bin_hex) = normalize_hex(&name) { 449 | let matched = repo.dag().vertexes_by_hex_prefix(&bin_hex, 3)?; 450 | match matched.len() { 451 | 0 => Err(Error::UnresolvedName(name)), 452 | 1 => repo.to_set(matched), 453 | _ => Err(Error::AmbiguousPrefix(matched)), 454 | } 455 | } else { 456 | Err(Error::UnresolvedName(name)) 457 | } 458 | } 459 | } 460 | } 461 | 462 | fn resolve_precise_name(repo: &Repo, candidates: &[String]) -> Result> { 463 | let refs = repo.dag().git_references(); 464 | for name in candidates.iter() { 465 | if let Some(v) = refs.get(name) { 466 | return Ok(Some(repo.to_set(std::iter::once(v.clone()))?)); 467 | } 468 | } 469 | Ok(None) 470 | } 471 | 472 | fn resolve_glob_name(repo: &Repo, glob: &str) -> Result> { 473 | let refs = repo.dag().git_references(); 474 | if let Ok(glob) = Glob::new(&format!("refs/{}", glob)) { 475 | let matcher = glob.compile_matcher(); 476 | let iter = refs 477 | .iter() 478 | .filter(|(k, _)| matcher.is_match(k)) 479 | .map(|(_, v)| v.clone()); 480 | return Ok(Some(repo.to_set(iter)?)); 481 | } 482 | Ok(None) 483 | } 484 | 485 | fn r#ref(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 486 | let refs = repo.dag().git_references(); 487 | // No arguments: all references. 488 | if args.len() == 0 { 489 | return repo.to_set(refs.values().cloned()); 490 | } 491 | ensure_arg_count(func_name, args, 1, context)?; 492 | let name = resolve_string(&args[0])?; 493 | // Try precise lookup. 494 | if func_name != "refglob" { 495 | let candidates = [ 496 | format!("refs/{}", name), 497 | format!("refs/heads/{}", name), 498 | format!("refs/tags/{}", name), 499 | format!("refs/remotes/{}", name), 500 | ]; 501 | if let Some(set) = resolve_precise_name(repo, &candidates)? { 502 | return Ok(set); 503 | } 504 | } 505 | // Try glob pattern lookup. 506 | if func_name != "lookup" && name.contains('*') { 507 | if let Some(set) = resolve_glob_name(repo, &name)? { 508 | return Ok(set); 509 | } 510 | } 511 | Err(Error::UnresolvedName(name)) 512 | } 513 | 514 | fn tag(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 515 | let refs = repo.dag().git_references(); 516 | // No arguments: all tags. 517 | if args.len() == 0 { 518 | return repo.to_set(refs.iter().filter_map(|(name, vertex)| { 519 | if name.starts_with("refs/tags/") { 520 | Some(vertex.clone()) 521 | } else { 522 | None 523 | } 524 | })); 525 | } 526 | ensure_arg_count(func_name, args, 1, context)?; 527 | let name = resolve_string(&args[0])?; 528 | // Try precise lookup. 529 | let ref_name = format!("refs/tags/{}", name); 530 | if let Some(vertex) = refs.get(&ref_name) { 531 | return Ok(repo.to_set(std::iter::once(vertex.clone()))?); 532 | } 533 | // Try glob pattern lookup. 534 | if func_name != "lookup" && name.contains('*') { 535 | let tag_glob = format!("tags/{}", name); 536 | if let Some(set) = resolve_glob_name(repo, &tag_glob)? { 537 | return Ok(set); 538 | } 539 | } 540 | Err(Error::UnresolvedName(name)) 541 | } 542 | 543 | fn present(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 544 | ensure_arg_count(func_name, args, 1, context)?; 545 | match resolve_set(repo, &args[0], context) { 546 | Err(Error::UnresolvedName(_)) => Ok(Set::empty()), 547 | Err(e) => Err(e), 548 | Ok(set) => Ok(set), 549 | } 550 | } 551 | 552 | fn none(func_name: &str, _repo: &Repo, args: &[Expr], context: &Context) -> Result { 553 | ensure_arg_count(func_name, args, 0, context)?; 554 | Ok(Set::empty()) 555 | } 556 | 557 | fn apply(func_name: &str, repo: &Repo, args: &[Expr], context: &Context) -> Result { 558 | if args.len() < 1 { 559 | return Err(Error::MismatchedArguments( 560 | func_name.to_string(), 561 | 1, 562 | args.len(), 563 | )); 564 | } 565 | 566 | let arg_sets = args[1..] 567 | .iter() 568 | .map(|arg| resolve_set(repo, arg, context)) 569 | .collect::>>()?; 570 | 571 | let mut expr = args[0].clone(); 572 | for (i, set) in arg_sets.into_iter().enumerate() { 573 | let to_replace = format!("${}", i + 1); 574 | expr.replace(&to_replace, &Expr::Inlined(set)) 575 | } 576 | 577 | resolve_set(repo, &expr, context) 578 | } 579 | 580 | fn normalize_hex(s: &str) -> Option> { 581 | let mut result = Vec::with_capacity(s.len()); 582 | for &b in s.as_bytes() { 583 | match b { 584 | b'0'..=b'9' => result.push(b), 585 | b'a'..=b'f' => result.push(b), 586 | b'A'..=b'F' => result.push(b - b'A' + b'a'), 587 | _ => return None, 588 | } 589 | } 590 | Some(result) 591 | } 592 | 593 | fn filter_set( 594 | repo: &Repo, 595 | func: impl Fn(&git2::Commit) -> bool + Send + Sync + 'static, 596 | ) -> Result { 597 | #[derive(Clone)] 598 | struct State { 599 | git_repo: Arc>, 600 | func: Arc bool + Send + Sync + 'static>, 601 | } 602 | 603 | impl State { 604 | fn contains(&self, name: &Vertex) -> bool { 605 | if let Ok(oid) = git2::Oid::from_bytes(name.as_ref()) { 606 | if let Ok(commit) = self.git_repo.lock().unwrap().find_commit(oid) { 607 | return self.func.deref()(&commit); 608 | } 609 | } 610 | false 611 | } 612 | } 613 | 614 | let state = State { 615 | git_repo: Arc::new(Mutex::new(git2::Repository::open(repo.git_repo().path())?)), 616 | func: Arc::new(func), 617 | }; 618 | 619 | let evaluate = { 620 | let all = all("all", repo, &[], &Default::default())?; 621 | let state = state.clone(); 622 | move || -> dag::Result { 623 | let iter = all 624 | .iter()? 625 | .filter(|name| match name { 626 | Ok(name) => state.contains(name), 627 | Err(_) => false, 628 | }) 629 | .map(|name| name.unwrap()); 630 | Ok(Set::from_static_names(iter.into_iter())) 631 | } 632 | }; 633 | 634 | Ok(Set::from_evaluate_contains(evaluate, move |_, name| { 635 | Ok(state.contains(name)) 636 | })) 637 | } 638 | -------------------------------------------------------------------------------- /src/ext.rs: -------------------------------------------------------------------------------- 1 | use crate::Error; 2 | use crate::Result; 3 | use gitdag::dag::Set; 4 | use gitdag::dag::Vertex; 5 | use gitdag::git2::Oid; 6 | use std::collections::HashMap; 7 | 8 | /// Extended methods on `Oid`. 9 | pub trait OidExt { 10 | /// Convert to `Vertex`. 11 | fn to_vertex(&self) -> Vertex; 12 | } 13 | 14 | /// Extended methods on `Vertex`. 15 | pub trait VertexExt { 16 | /// Convert to `Oid`. 17 | fn to_oid(&self) -> Result; 18 | } 19 | 20 | /// Extended methods on `Oid` iterator. 21 | pub trait OidIterExt { 22 | /// Convert to `Set`. 23 | fn to_set(self) -> Set; 24 | } 25 | 26 | impl OidExt for Oid { 27 | fn to_vertex(&self) -> Vertex { 28 | Vertex::copy_from(self.as_bytes()) 29 | } 30 | } 31 | 32 | impl VertexExt for Vertex { 33 | fn to_oid(&self) -> Result { 34 | Ok(Oid::from_bytes(self.as_ref())?) 35 | } 36 | } 37 | 38 | impl> OidIterExt for T { 39 | fn to_set(self) -> Set { 40 | Set::from_static_names(self.into_iter().map(|oid| oid.to_vertex())) 41 | } 42 | } 43 | 44 | pub(crate) trait Merge { 45 | fn merge(&mut self, other: Self); 46 | } 47 | 48 | impl Merge for HashMap { 49 | fn merge(&mut self, other: Self) { 50 | for (k, v) in other { 51 | self.insert(k, v); 52 | } 53 | } 54 | } 55 | 56 | /// Extended methods on `Set` struct. 57 | pub trait SetExt { 58 | /// Convert to a convenient iterator of `Oid`s. 59 | fn to_oids(&self) -> Result>>>; 60 | } 61 | 62 | impl SetExt for Set { 63 | fn to_oids(&self) -> Result>>> { 64 | let iter = self.iter()?.map(|v| match v { 65 | Ok(v) => match Oid::from_bytes(v.as_ref()) { 66 | Ok(oid) => Ok(oid), 67 | Err(e) => Err(Error::from(e)), 68 | }, 69 | Err(e) => Err(Error::from(e)), 70 | }); 71 | Ok(Box::new(iter)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A domain-specific-language to select commits in a git repo. Similar to 2 | //! [Mercurial's revset](https://www.mercurial-scm.org/repo/hg/help/revsets). 3 | //! 4 | //! ## Language Specification 5 | //! 6 | //! Specifying a commit: 7 | //! - Reference names like `master`, `release-foo`, or `origin/master`. 8 | //! - Hex commit hashes or hash prefixes. 9 | //! - A dot `.`, or the at sign `@` refers to `HEAD`. 10 | //! 11 | //! Operators: 12 | //! - `x + y`, `x | y`, `x or y`, `union(x, y)`: Union of `x` and `y` (1). 13 | //! - `x & y`, `x and y`, `intersection(x, y)`: Intersection of `x` and `y`. 14 | //! - `x - y`, `difference(x, y)`: Commits in `x` but not `y`. 15 | //! - `!x`, `not x`, `negate(x)`: Commits not in `x`. 16 | //! - `::x`, `ancestors(x)`: Ancestors of `x`, including `x`. 17 | //! - `x::`, `descendants(x)`: Descendants of `x`, including `x`. 18 | //! - `x^`, `parents(x)`: Parents of `x` (2). 19 | //! - `x % y`, `only(x, y)`: Reachable from `x`, not `y`, or `::x - ::y`. 20 | //! - `x:y`, `range(x, y)`: A DAG range, descendants of `x` and ancestors of 21 | //! `y`, or `x:: & ::y` (3). 22 | //! 23 | //! Functions: 24 | //! - `children(x)`: Commits with at least one parent in the `x` set. 25 | //! - `heads(x)`: Heads of a set, `x - parents(x)`. 26 | //! - `roots(x)`: Roots of a set, `x - children(x)`. 27 | //! - `gca(x, y, ...)`, `ancestor(x, y, ...)`: Heads of common ancestors (4). 28 | //! - `first(x, ...)`: First item in `x`, or `first(...)` if `x` is empty. 29 | //! - `last(x)`: Last item in `x`, or empty. 30 | //! - `head()`: Visible heads (references). 31 | //! - `all()`: Visible commits, aka. `::head()`. 32 | //! - `publichead()`: Heads referred by remotes, `ref("remotes/**")`. 33 | //! - `drafthead()`: Heads not referred by remotes, `head() - publichead()`. 34 | //! - `public()`: Commits reachable from `publichead()`, `::publichead()`. 35 | //! - `draft()`: Commits only reachable from draft heads, `all() - public()`. 36 | //! - `author(name)`: Filter by author name or email. 37 | //! - `committer(name)`: Filter by committer name or email. 38 | //! - `date(date)`: Filter by author date. 39 | //! - `committerdate(date)`: Filter by committer date. 40 | //! - `desc(text)`: Filter by commit message. 41 | //! - `modifies(path)`: Filter by modified path. 42 | //! - `predecessors(x)`: Previous versions of `x`, including `x`. 43 | //! - `successors(x)`: Newer versions of `x`, including `x`. 44 | //! - `obsolete()`: Commits with at least one newer versions. 45 | //! - `id(hexhash)`: Resolve a commit explicitly by a hex hash string. 46 | //! - `ref()`: All references. 47 | //! - `ref(name)`: Resolve commits by a reference name or glob. 48 | //! - `tag()`: All tags. 49 | //! - `tag(name)`: Resolve commits by a tag name or glob. 50 | //! - `none()`: Empty set. 51 | //! - `present(set)`: Empty set on "unresolved name" error. Otherwise just `set`. 52 | //! - `apply(expr, $1, $2, ...)`: Replace `$1`, `$2` in `expr` with evaluated 53 | //! sets. Then evaluate `expr`. Useful to avoid evaluate same sets multiple 54 | //! times. 55 | //! 56 | //! Differences from Mercurial: 57 | //! 1. `x + y` does not make sure `y` comes after `x`. For example, 58 | //! `first(x + y)` might be `first(x)` or `first(y)`. In Mercurial, 59 | //! `first(x + y)` would be `first(x)`. 60 | //! 2. `x^` selects all parents, not just first parents. 61 | //! 3. `x:y` selects DAG range `x` to `y`, not revision number range. 62 | //! `x::y` is invalid syntax, for easier parsing. 63 | //! 4. `ancestor(x, y)` can return multiple commits for criss-cross merges. 64 | //! 65 | //! ## Quick Start 66 | //! 67 | //! First, construct a [`gitrevset::Repo`](struct.Repo.html): 68 | //! 69 | //! ``` 70 | //! # fn main() -> gitrevset::Result<()> { 71 | //! # #[cfg(feature = "testutil")] 72 | //! # { 73 | //! # use gitrevset::git2; 74 | //! # let repo = gitrevset::TestRepo::new(); 75 | //! # repo.set_env(); 76 | //! # let path = repo.git_repo().path(); 77 | //! // Open from the current directory. 78 | //! let repo = gitrevset::Repo::open_from_env()?; 79 | //! 80 | //! // Open from a libgit2 repository. 81 | //! let git_repo = git2::Repository::open(path)?; 82 | //! let repo = gitrevset::Repo::open_from_repo(Box::new(git_repo))?; 83 | //! # } 84 | //! # Ok(()) 85 | //! # } 86 | //! ``` 87 | //! 88 | //! Then, use [`revs`](struct.Repo.html#method.revs) or 89 | //! [`anyrevs`](struct.Repo.html#method.anyrevs) to perform queries. 90 | //! 91 | //! ``` 92 | //! # fn main() -> gitrevset::Result<()> { 93 | //! # #[cfg(feature = "testutil")] 94 | //! # { 95 | //! # let mut repo = gitrevset::TestRepo::new(); 96 | //! # repo.drawdag("A"); 97 | //! # repo.add_ref("refs/heads/master", repo.query_single_oid("A")); 98 | //! # repo.git_repo().config().unwrap().set_str("revsetalias.foo", "parents($1)").unwrap(); 99 | //! let set = repo.revs("(draft() & ::.)^ + .")?; 100 | //! 101 | //! // With user-defined aliases considered. 102 | //! // ex. git config revsetalias.foo "parents($1)" 103 | //! let set = repo.anyrevs("foo(.)")?; 104 | //! # } 105 | //! # Ok(()) 106 | //! # } 107 | //! ``` 108 | //! 109 | //! Finally, use [`to_oids`](trait.SetExt.html#tymethod.to_oids) to extract Git 110 | //! object IDs from the resulting set: 111 | //! 112 | //! ``` 113 | //! # fn main() -> gitrevset::Result<()> { 114 | //! # #[cfg(feature = "testutil")] 115 | //! # { 116 | //! # let repo = gitrevset::TestRepo::new(); 117 | //! # let set = repo.revs("all()")?; 118 | //! use gitrevset::SetExt; 119 | //! for oid in set.to_oids()? { 120 | //! dbg!(oid?); 121 | //! } 122 | //! # } 123 | //! # Ok(()) 124 | //! # } 125 | //! ``` 126 | //! 127 | //! To parse the revset expression at compile time, to avoid issues about 128 | //! string escaping or injection, use the [`ast!`](macro.ast.html) macro. 129 | //! 130 | //! ## Note on Commit Graph Index 131 | //! 132 | //! `gitrevset` takes advantage of the commit graph index from the 133 | //! [EdenSCM](https://github.com/facebookexperimental/eden) project that is 134 | //! designed to support very large repositories. The index is built on demand 135 | //! under the `.git/dag` directory during the construction of the 136 | //! [`Repo`](struct.Repo.html) struct. 137 | //! 138 | //! The index is highly optimized for ancestors-related queries. For example, 139 | //! `gca(x, y)`, `x & ::y`, `x % y` usually complete under 1 millisecond 140 | //! regardless of the distance between `x` and `y`. 141 | //! 142 | //! The index is not optimized for many visible heads. Having too many 143 | //! references might have a visible performance penalty on 144 | //! [`Repo`](struct.Repo.html) construction. 145 | //! 146 | //! The index can be accessed by [`repo.dag()`](struct.Repo.html#method.dag) 147 | //! and the re-exported `dag` crate. 148 | 149 | #![allow(dead_code)] 150 | #![deny(missing_docs)] 151 | 152 | /// Extended methods on types defined in other crates. 153 | pub mod ext; 154 | 155 | mod ast; 156 | mod error; 157 | mod eval; 158 | mod mutation; 159 | mod parser; 160 | mod repo; 161 | 162 | #[cfg(any(test, feature = "testutil"))] 163 | mod testrepo; 164 | 165 | #[cfg(feature = "testutil")] 166 | pub use testrepo::TestRepo; 167 | 168 | #[cfg(test)] 169 | mod tests; 170 | 171 | pub use error::Error; 172 | pub use gitdag::dag; 173 | pub use gitdag::git2; 174 | 175 | /// `Result` type used by `gitrevset`. 176 | pub type Result = std::result::Result; 177 | 178 | pub use ast::Expr; 179 | pub use eval::Context as EvalContext; 180 | pub use ext::SetExt; 181 | pub use repo::Repo; 182 | -------------------------------------------------------------------------------- /src/mutation.rs: -------------------------------------------------------------------------------- 1 | use crate::ext::Merge; 2 | use crate::ext::OidExt; 3 | use crate::repo::Repo; 4 | use crate::Result; 5 | use dag::namedag::MemNameDag; 6 | use dag::ops::DagAddHeads; 7 | use dag::Vertex; 8 | use gitdag::dag; 9 | use gitdag::git2; 10 | use std::collections::HashMap; 11 | use std::collections::HashSet; 12 | 13 | pub(crate) fn infer_mutation_from_reflog(repo: &Repo) -> Result { 14 | let refs = repo.dag().git_references(); 15 | let mut replaces: HashMap = Default::default(); 16 | for name in refs.keys() { 17 | if !name.starts_with("refs/remotes/") && name.starts_with("refs/heads/") { 18 | replaces.merge(analyse_reflog_name(repo, name).unwrap_or_default()); 19 | } 20 | } 21 | 22 | let parent_func = |v: Vertex| -> dag::Result> { 23 | match replaces.get(&v) { 24 | None => Ok(Vec::new()), 25 | Some(old) => Ok(vec![old.clone()]), 26 | } 27 | }; 28 | let parent_func = dag::utils::break_parent_func_cycle(parent_func); 29 | let mut heads: Vec = replaces 30 | .keys() 31 | .collect::>() 32 | .difference(&replaces.values().collect::>()) 33 | .cloned() 34 | .cloned() 35 | .collect(); 36 | heads.sort_unstable(); 37 | 38 | let mut dag = MemNameDag::new(); 39 | dag.add_heads(parent_func, &heads)?; 40 | Ok(dag) 41 | } 42 | 43 | fn analyse_reflog_name(repo: &Repo, name: &str) -> Result> { 44 | // Check reflog for the given reference name. 45 | let reflog = repo.git_repo().reflog(name)?; 46 | let mut replaces: HashMap = Default::default(); 47 | for entry in reflog.iter() { 48 | let message: &str = match entry.message() { 49 | Some(m) => m, 50 | None => continue, 51 | }; 52 | if message.starts_with("commit (amend):") || message.starts_with("rebase -i (finish):") { 53 | replaces.merge( 54 | analyse_head_rewrite(repo.git_repo(), entry.id_old(), entry.id_new()) 55 | .unwrap_or_default(), 56 | ); 57 | } 58 | } 59 | Ok(replaces) 60 | } 61 | 62 | fn analyse_head_rewrite( 63 | git_repo: &git2::Repository, 64 | mut old: git2::Oid, 65 | mut new: git2::Oid, 66 | ) -> Result> { 67 | const MAX_DEPTH: usize = 50; 68 | 69 | // Find the old and new stack. Not using "dag" APIs as "dag" could be 70 | // incomplete (i.e. not indexing the reflog heads). 71 | // 72 | // Because "dag" API is not used, this is an approximate. For example, 73 | // `old_stack` might be ancestors of `new_stack`, and common ancestors 74 | // might be inserted to either `old_stack` or `new_stack`. 75 | let mut old_stack = Vec::new(); 76 | let mut new_stack = Vec::new(); 77 | let mut seen = HashSet::new(); 78 | for _ in 0..MAX_DEPTH { 79 | if old == new { 80 | break; 81 | } 82 | if seen.insert(old) { 83 | old_stack.push(old); 84 | if let Some(next_old) = git_repo.find_commit(old)?.parent_ids().next() { 85 | old = next_old; 86 | } 87 | } 88 | if seen.insert(new) { 89 | new_stack.push(new); 90 | if let Some(next_new) = git_repo.find_commit(old)?.parent_ids().next() { 91 | new = next_new; 92 | } 93 | } 94 | } 95 | 96 | // If tree id is not changed, then it's considered as a "commit message" 97 | // rewrite. To detect that, keep {tree: [commit]} map. 98 | // If commit message and author date is not changed, then it's consider 99 | // as a "content" rewrite. To detect that, keep {(msg, date): [commit]} 100 | // map. 101 | 102 | #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] 103 | enum Side { 104 | Old, 105 | New, 106 | } 107 | let mut tree_map = HashMap::>::new(); 108 | let mut msg_map = HashMap::<(i64, Vec), Vec<(Side, git2::Oid)>>::new(); 109 | 110 | for (side, stack) in vec![(Side::Old, old_stack), (Side::New, new_stack)] { 111 | for oid in stack.into_iter().take(MAX_DEPTH) { 112 | let commit = match git_repo.find_commit(oid) { 113 | Err(_) => continue, 114 | Ok(commit) => commit, 115 | }; 116 | let tree_id = commit.tree_id(); 117 | tree_map.entry(tree_id).or_default().push((side, oid)); 118 | let msg = commit.message_bytes().to_vec(); 119 | let time = commit.author().when().seconds(); 120 | msg_map.entry((time, msg)).or_default().push((side, oid)); 121 | } 122 | } 123 | 124 | let result = tree_map 125 | .values() 126 | .chain(msg_map.values()) 127 | .filter_map(|pair| { 128 | if let [(Side::Old, old), (Side::New, new)] = &pair[..] { 129 | if old == new { 130 | None 131 | } else { 132 | Some((new.to_vertex(), old.to_vertex())) 133 | } 134 | } else { 135 | None 136 | } 137 | }) 138 | .collect(); 139 | 140 | Ok(result) 141 | } 142 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::Expr; 2 | use lalrpop_util::lexer::Token; 3 | use lalrpop_util::ParseError; 4 | 5 | #[rustfmt::skip] 6 | mod grammar; 7 | 8 | /// Parse a string into an AST. 9 | pub fn parse(s: &str) -> Result> { 10 | grammar::ExprParser::new().parse(s) 11 | } 12 | -------------------------------------------------------------------------------- /src/parser/grammar.lalrpop: -------------------------------------------------------------------------------- 1 | use crate::ast::Expr; 2 | 3 | // Binary Operator Precedence (highest to lowest): 4 | // BinOp1: : .. 5 | // BinOp2: & and % - 6 | // BinOp3: | + or 7 | 8 | grammar; 9 | 10 | pub Expr = Expr4; 11 | 12 | Expr4: Expr = { 13 | => Expr::Fn(op.into(), vec![a, b]), 14 | Expr3, 15 | }; 16 | 17 | BinOp3: &'static str = { 18 | "|" => "union", 19 | "+" => "union", 20 | "or" => "union", 21 | }; 22 | 23 | Expr3: Expr = { 24 | => Expr::Fn(op.into(), vec![a, b]), 25 | Expr2, 26 | }; 27 | 28 | BinOp2: &'static str = { 29 | "&" => "intersection", 30 | "and" => "intersection", 31 | "-" => "difference", 32 | "%" => "only", 33 | }; 34 | 35 | Expr2: Expr = { 36 | => Expr::Fn(op.into(), vec![a, b]), 37 | Expr15, 38 | }; 39 | 40 | BinOp1: &'static str = { 41 | ":" => "range", 42 | ".." => "range", 43 | }; 44 | 45 | Expr15: Expr = { 46 | )*> => { 47 | let mut e = e; 48 | for v in pre { e = Expr::Fn(v.into(), vec![e]); } 49 | e 50 | }, 51 | } 52 | 53 | Expr1: Expr = { 54 | )*> => { 55 | let mut e = e; 56 | for v in post { e = Expr::Fn(v.into(), vec![e]); } 57 | e 58 | }, 59 | } 60 | 61 | Expr0: Expr = { 62 | Symbol2 => Expr::Name(<>), 63 | "(" ")" => { 64 | // Function call. 65 | let mut arg_list: Vec = args.into_iter().map(|(e, _)| e).collect(); 66 | if let Some(last_arg) = last { arg_list.push(last_arg); } 67 | Expr::Fn(f.into(), arg_list) 68 | }, 69 | "(" ")" 70 | }; 71 | 72 | Prefix: &'static str = { 73 | "!" => "negate", 74 | "not " => "negate", 75 | "::" => "ancestors", 76 | } 77 | 78 | Postfix: &'static str = { 79 | "::" => "descendants", 80 | "^" => "parents", 81 | } 82 | 83 | Symbol2: String = { 84 | Symbol1 => <>, 85 | => { 86 | // Escaped string. 87 | let mut result = String::with_capacity(escaped.len()); 88 | let mut prev = '_'; 89 | for ch in escaped[1..escaped.len()-1].chars() { 90 | match (prev, ch) { 91 | ('\\', 'n') => result.push('\n'), 92 | ('\\', _) => result.push(ch), 93 | (_, '\\') => (), 94 | (_, _) => result.push(ch), 95 | } 96 | prev = ch; 97 | } 98 | result 99 | }, 100 | } 101 | 102 | Symbol1: String = { 103 | r"[a-zA-Z0-9/_$@.]+" => <>.to_string(), 104 | } -------------------------------------------------------------------------------- /src/parser/grammar.rs: -------------------------------------------------------------------------------- 1 | // auto-generated: "lalrpop 0.19.0" 2 | // sha256: 3540bb378dbf388a39df6d0e134ad2ce99d7372b8f086ef5da1d2a4be4f38c9 3 | use crate::ast::Expr; 4 | #[allow(unused_extern_crates)] 5 | extern crate lalrpop_util as __lalrpop_util; 6 | #[allow(unused_imports)] 7 | use self::__lalrpop_util::state_machine as __state_machine; 8 | 9 | #[cfg_attr(rustfmt, rustfmt_skip)] 10 | mod __parse__Expr { 11 | #![allow(non_snake_case, non_camel_case_types, unused_mut, unused_variables, unused_imports, unused_parens)] 12 | 13 | use crate::ast::Expr; 14 | #[allow(unused_extern_crates)] 15 | extern crate lalrpop_util as __lalrpop_util; 16 | #[allow(unused_imports)] 17 | use self::__lalrpop_util::state_machine as __state_machine; 18 | use self::__lalrpop_util::lexer::Token; 19 | #[allow(dead_code)] 20 | pub enum __Symbol<'input> 21 | { 22 | Variant0(&'input str), 23 | Variant1(&'static str), 24 | Variant2(::std::vec::Vec<&'static str>), 25 | Variant3((Expr, &'input str)), 26 | Variant4(::std::vec::Vec<(Expr, &'input str)>), 27 | Variant5(Expr), 28 | Variant6(::std::option::Option), 29 | Variant7(String), 30 | } 31 | const __ACTION: &[i8] = &[ 32 | // State 0 33 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 34 | // State 1 35 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 36 | // State 2 37 | 0, -32, -32, 0, -32, -32, -32, -32, -32, -32, 30, 31, -32, 0, -32, -32, 0, 0, 38 | // State 3 39 | 0, -39, -39, 0, -39, -39, -39, -39, 32, 33, 0, 0, -39, 0, -39, -39, 0, 0, 40 | // State 4 41 | 0, 34, 35, 0, -41, -41, -41, 36, 0, 0, 0, 0, 37, 0, -41, -41, 0, 0, 42 | // State 5 43 | 0, 0, 0, 0, -25, 38, -25, 0, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 44 | // State 6 45 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 46 | // State 7 47 | 0, -33, -33, 0, -33, -33, -33, -33, -33, -33, 30, 31, -33, 0, -33, -33, 0, 0, 48 | // State 8 49 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 50 | // State 9 51 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 52 | // State 10 53 | 22, 0, 0, 7, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 54 | // State 11 55 | 22, 0, 0, 7, 45, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 56 | // State 12 57 | 0, -38, -38, 0, -38, -38, -38, -38, 32, 33, 0, 0, -38, 0, -38, -38, 0, 0, 58 | // State 13 59 | 0, 34, 35, 0, -40, -40, -40, 36, 0, 0, 0, 0, 37, 0, -40, -40, 0, 0, 60 | // State 14 61 | 22, 0, 0, 7, 48, 0, 0, 0, 0, 0, 23, 0, 0, 24, 0, 0, 25, 26, 62 | // State 15 63 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 | // State 16 65 | 0, -34, -34, 0, -34, -34, -34, -34, -34, -34, 0, 0, -34, 0, -34, -34, 0, 0, 66 | // State 17 67 | 0, -37, -37, 0, -37, -37, -37, -37, -37, -37, 0, 0, -37, 0, -37, -37, 0, 0, 68 | // State 18 69 | -9, 0, 0, -9, 0, 0, 0, 0, 0, 0, -9, 0, 0, -9, 0, 0, -9, -9, 70 | // State 19 71 | 0, -50, -50, 12, -50, -50, -50, -50, -50, -50, -50, -50, -50, 0, -50, -50, 0, 0, 72 | // State 20 73 | 0, -26, -26, 0, -26, -26, -26, -26, -26, -26, -26, -26, -26, 0, -26, -26, 0, 0, 74 | // State 21 75 | -46, 0, 0, -46, 0, 0, 0, 0, 0, 0, -46, 0, 0, -46, 0, 0, -46, -46, 76 | // State 22 77 | -48, 0, 0, -48, 0, 0, 0, 0, 0, 0, -48, 0, 0, -48, 0, 0, -48, -48, 78 | // State 23 79 | -47, 0, 0, -47, 0, 0, 0, 0, 0, 0, -47, 0, 0, -47, 0, 0, -47, -47, 80 | // State 24 81 | 0, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, -49, 0, -49, -49, 0, 0, 82 | // State 25 83 | 0, -51, -51, 0, -51, -51, -51, -51, -51, -51, -51, -51, -51, 0, -51, -51, 0, 0, 84 | // State 26 85 | 0, -35, -35, 0, -35, -35, -35, -35, -35, -35, 0, 0, -35, 0, -35, -35, 0, 0, 86 | // State 27 87 | -10, 0, 0, -10, 0, 0, 0, 0, 0, 0, -10, 0, 0, -10, 0, 0, -10, -10, 88 | // State 28 89 | 0, -4, -4, 0, -4, -4, -4, -4, -4, -4, -4, -4, -4, 0, -4, -4, 0, 0, 90 | // State 29 91 | 0, -44, -44, 0, -44, -44, -44, -44, -44, -44, -44, -44, -44, 0, -44, -44, 0, 0, 92 | // State 30 93 | 0, -45, -45, 0, -45, -45, -45, -45, -45, -45, -45, -45, -45, 0, -45, -45, 0, 0, 94 | // State 31 95 | -17, 0, 0, -17, 0, 0, 0, 0, 0, 0, -17, 0, 0, -17, 0, 0, -17, -17, 96 | // State 32 97 | -16, 0, 0, -16, 0, 0, 0, 0, 0, 0, -16, 0, 0, -16, 0, 0, -16, -16, 98 | // State 33 99 | -21, 0, 0, -21, 0, 0, 0, 0, 0, 0, -21, 0, 0, -21, 0, 0, -21, -21, 100 | // State 34 101 | -18, 0, 0, -18, 0, 0, 0, 0, 0, 0, -18, 0, 0, -18, 0, 0, -18, -18, 102 | // State 35 103 | -20, 0, 0, -20, 0, 0, 0, 0, 0, 0, -20, 0, 0, -20, 0, 0, -20, -20, 104 | // State 36 105 | -19, 0, 0, -19, 0, 0, 0, 0, 0, 0, -19, 0, 0, -19, 0, 0, -19, -19, 106 | // State 37 107 | -23, 0, 0, -23, 0, 0, 0, 0, 0, 0, -23, 0, 0, -23, 0, 0, -23, -23, 108 | // State 38 109 | -24, 0, 0, -24, 0, 0, 0, 0, 0, 0, -24, 0, 0, -24, 0, 0, -24, -24, 110 | // State 39 111 | -22, 0, 0, -22, 0, 0, 0, 0, 0, 0, -22, 0, 0, -22, 0, 0, -22, -22, 112 | // State 40 113 | 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114 | // State 41 115 | 0, -5, -5, 0, -5, -5, -5, -5, -5, -5, -5, -5, -5, 0, -5, -5, 0, 0, 116 | // State 42 117 | 0, -36, -36, 0, -36, -36, -36, -36, -36, -36, 0, 0, -36, 0, -36, -36, 0, 0, 118 | // State 43 119 | 0, 0, 0, 0, 49, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 | // State 44 121 | 0, -28, -28, 0, -28, -28, -28, -28, -28, -28, -28, -28, -28, 0, -28, -28, 0, 0, 122 | // State 45 123 | 0, -31, -31, 0, -31, -31, -31, -31, -31, -31, -31, -31, -31, 0, -31, -31, 0, 0, 124 | // State 46 125 | 0, 0, 0, 0, 51, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126 | // State 47 127 | 0, -30, -30, 0, -30, -30, -30, -30, -30, -30, -30, -30, -30, 0, -30, -30, 0, 0, 128 | // State 48 129 | 0, -27, -27, 0, -27, -27, -27, -27, -27, -27, -27, -27, -27, 0, -27, -27, 0, 0, 130 | // State 49 131 | -14, 0, 0, -14, -14, 0, 0, 0, 0, 0, -14, 0, 0, -14, 0, 0, -14, -14, 132 | // State 50 133 | 0, -29, -29, 0, -29, -29, -29, -29, -29, -29, -29, -29, -29, 0, -29, -29, 0, 0, 134 | // State 51 135 | -15, 0, 0, -15, -15, 0, 0, 0, 0, 0, -15, 0, 0, -15, 0, 0, -15, -15, 136 | ]; 137 | fn __action(state: i8, integer: usize) -> i8 { 138 | __ACTION[(state as usize) * 18 + integer] 139 | } 140 | const __EOF_ACTION: &[i8] = &[ 141 | // State 0 142 | 0, 143 | // State 1 144 | 0, 145 | // State 2 146 | -32, 147 | // State 3 148 | -39, 149 | // State 4 150 | -41, 151 | // State 5 152 | -25, 153 | // State 6 154 | 0, 155 | // State 7 156 | -33, 157 | // State 8 158 | 0, 159 | // State 9 160 | 0, 161 | // State 10 162 | 0, 163 | // State 11 164 | 0, 165 | // State 12 166 | -38, 167 | // State 13 168 | -40, 169 | // State 14 170 | 0, 171 | // State 15 172 | -52, 173 | // State 16 174 | -34, 175 | // State 17 176 | -37, 177 | // State 18 178 | 0, 179 | // State 19 180 | -50, 181 | // State 20 182 | -26, 183 | // State 21 184 | 0, 185 | // State 22 186 | 0, 187 | // State 23 188 | 0, 189 | // State 24 190 | -49, 191 | // State 25 192 | -51, 193 | // State 26 194 | -35, 195 | // State 27 196 | 0, 197 | // State 28 198 | -4, 199 | // State 29 200 | -44, 201 | // State 30 202 | -45, 203 | // State 31 204 | 0, 205 | // State 32 206 | 0, 207 | // State 33 208 | 0, 209 | // State 34 210 | 0, 211 | // State 35 212 | 0, 213 | // State 36 214 | 0, 215 | // State 37 216 | 0, 217 | // State 38 218 | 0, 219 | // State 39 220 | 0, 221 | // State 40 222 | 0, 223 | // State 41 224 | -5, 225 | // State 42 226 | -36, 227 | // State 43 228 | 0, 229 | // State 44 230 | -28, 231 | // State 45 232 | -31, 233 | // State 46 234 | 0, 235 | // State 47 236 | -30, 237 | // State 48 238 | -27, 239 | // State 49 240 | 0, 241 | // State 50 242 | -29, 243 | // State 51 244 | 0, 245 | ]; 246 | fn __goto(state: i8, nt: usize) -> i8 { 247 | match nt { 248 | 2 => 7, 249 | 5 => 1, 250 | 8 => 14, 251 | 9 => 8, 252 | 10 => 9, 253 | 11 => 10, 254 | 12 => match state { 255 | 6 => 40, 256 | 11 => 43, 257 | 14 => 46, 258 | _ => 15, 259 | }, 260 | 13 => 2, 261 | 14 => match state { 262 | 1 => 26, 263 | _ => 16, 264 | }, 265 | 15 => match state { 266 | 8 => 42, 267 | _ => 17, 268 | }, 269 | 16 => match state { 270 | 9 => 12, 271 | _ => 3, 272 | }, 273 | 17 => match state { 274 | 10 => 13, 275 | _ => 4, 276 | }, 277 | 18 => 5, 278 | 20 => match state { 279 | 7 => 41, 280 | _ => 28, 281 | }, 282 | 21 => match state { 283 | 1 => 27, 284 | _ => 18, 285 | }, 286 | 22 => 19, 287 | 23 => 20, 288 | _ => 0, 289 | } 290 | } 291 | fn __expected_tokens(__state: i8) -> Vec<::std::string::String> { 292 | const __TERMINAL: &[&str] = &[ 293 | r###""!""###, 294 | r###""%""###, 295 | r###""&""###, 296 | r###""(""###, 297 | r###"")""###, 298 | r###""+""###, 299 | r###"",""###, 300 | r###""-""###, 301 | r###""..""###, 302 | r###"":""###, 303 | r###""::""###, 304 | r###""^""###, 305 | r###""and""###, 306 | r###""not ""###, 307 | r###""or""###, 308 | r###""|""###, 309 | r###"r#"[a-zA-Z0-9/_$@.]+"#"###, 310 | r###"r#"\\x22([^\\x22\\x5c]|\\x5c.)*\\x22"#"###, 311 | ]; 312 | __TERMINAL.iter().enumerate().filter_map(|(index, terminal)| { 313 | let next_state = __action(__state, index); 314 | if next_state == 0 { 315 | None 316 | } else { 317 | Some(terminal.to_string()) 318 | } 319 | }).collect() 320 | } 321 | pub struct __StateMachine<'input> 322 | where 323 | { 324 | input: &'input str, 325 | __phantom: ::std::marker::PhantomData<(&'input ())>, 326 | } 327 | impl<'input> __state_machine::ParserDefinition for __StateMachine<'input> 328 | where 329 | { 330 | type Location = usize; 331 | type Error = &'static str; 332 | type Token = Token<'input>; 333 | type TokenIndex = usize; 334 | type Symbol = __Symbol<'input>; 335 | type Success = Expr; 336 | type StateIndex = i8; 337 | type Action = i8; 338 | type ReduceIndex = i8; 339 | type NonterminalIndex = usize; 340 | 341 | #[inline] 342 | fn start_location(&self) -> Self::Location { 343 | Default::default() 344 | } 345 | 346 | #[inline] 347 | fn start_state(&self) -> Self::StateIndex { 348 | 0 349 | } 350 | 351 | #[inline] 352 | fn token_to_index(&self, token: &Self::Token) -> Option { 353 | __token_to_integer(token, ::std::marker::PhantomData::<(&())>) 354 | } 355 | 356 | #[inline] 357 | fn action(&self, state: i8, integer: usize) -> i8 { 358 | __action(state, integer) 359 | } 360 | 361 | #[inline] 362 | fn error_action(&self, state: i8) -> i8 { 363 | __action(state, 18 - 1) 364 | } 365 | 366 | #[inline] 367 | fn eof_action(&self, state: i8) -> i8 { 368 | __EOF_ACTION[state as usize] 369 | } 370 | 371 | #[inline] 372 | fn goto(&self, state: i8, nt: usize) -> i8 { 373 | __goto(state, nt) 374 | } 375 | 376 | fn token_to_symbol(&self, token_index: usize, token: Self::Token) -> Self::Symbol { 377 | __token_to_symbol(token_index, token, ::std::marker::PhantomData::<(&())>) 378 | } 379 | 380 | fn expected_tokens(&self, state: i8) -> Vec { 381 | __expected_tokens(state) 382 | } 383 | 384 | #[inline] 385 | fn uses_error_recovery(&self) -> bool { 386 | false 387 | } 388 | 389 | #[inline] 390 | fn error_recovery_symbol( 391 | &self, 392 | recovery: __state_machine::ErrorRecovery, 393 | ) -> Self::Symbol { 394 | panic!("error recovery not enabled for this grammar") 395 | } 396 | 397 | fn reduce( 398 | &mut self, 399 | action: i8, 400 | start_location: Option<&Self::Location>, 401 | states: &mut Vec, 402 | symbols: &mut Vec<__state_machine::SymbolTriple>, 403 | ) -> Option<__state_machine::ParseResult> { 404 | __reduce( 405 | self.input, 406 | action, 407 | start_location, 408 | states, 409 | symbols, 410 | ::std::marker::PhantomData::<(&())>, 411 | ) 412 | } 413 | 414 | fn simulate_reduce(&self, action: i8) -> __state_machine::SimulatedReduce { 415 | panic!("error recovery not enabled for this grammar") 416 | } 417 | } 418 | fn __token_to_integer< 419 | 'input, 420 | >( 421 | __token: &Token<'input>, 422 | _: ::std::marker::PhantomData<(&'input ())>, 423 | ) -> Option 424 | { 425 | match *__token { 426 | Token(2, _) if true => Some(0), 427 | Token(3, _) if true => Some(1), 428 | Token(4, _) if true => Some(2), 429 | Token(5, _) if true => Some(3), 430 | Token(6, _) if true => Some(4), 431 | Token(7, _) if true => Some(5), 432 | Token(8, _) if true => Some(6), 433 | Token(9, _) if true => Some(7), 434 | Token(10, _) if true => Some(8), 435 | Token(11, _) if true => Some(9), 436 | Token(12, _) if true => Some(10), 437 | Token(13, _) if true => Some(11), 438 | Token(14, _) if true => Some(12), 439 | Token(15, _) if true => Some(13), 440 | Token(16, _) if true => Some(14), 441 | Token(17, _) if true => Some(15), 442 | Token(0, _) if true => Some(16), 443 | Token(1, _) if true => Some(17), 444 | _ => None, 445 | } 446 | } 447 | fn __token_to_symbol< 448 | 'input, 449 | >( 450 | __token_index: usize, 451 | __token: Token<'input>, 452 | _: ::std::marker::PhantomData<(&'input ())>, 453 | ) -> __Symbol<'input> 454 | { 455 | match __token_index { 456 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 => match __token { 457 | Token(2, __tok0) | Token(3, __tok0) | Token(4, __tok0) | Token(5, __tok0) | Token(6, __tok0) | Token(7, __tok0) | Token(8, __tok0) | Token(9, __tok0) | Token(10, __tok0) | Token(11, __tok0) | Token(12, __tok0) | Token(13, __tok0) | Token(14, __tok0) | Token(15, __tok0) | Token(16, __tok0) | Token(17, __tok0) | Token(0, __tok0) | Token(1, __tok0) if true => __Symbol::Variant0(__tok0), 458 | _ => unreachable!(), 459 | }, 460 | _ => unreachable!(), 461 | } 462 | } 463 | pub struct ExprParser { 464 | builder: __lalrpop_util::lexer::MatcherBuilder, 465 | _priv: (), 466 | } 467 | 468 | impl ExprParser { 469 | pub fn new() -> ExprParser { 470 | let __builder = super::__intern_token::new_builder(); 471 | ExprParser { 472 | builder: __builder, 473 | _priv: (), 474 | } 475 | } 476 | 477 | #[allow(dead_code)] 478 | pub fn parse< 479 | 'input, 480 | >( 481 | &self, 482 | input: &'input str, 483 | ) -> Result, &'static str>> 484 | { 485 | let mut __tokens = self.builder.matcher(input); 486 | __state_machine::Parser::drive( 487 | __StateMachine { 488 | input, 489 | __phantom: ::std::marker::PhantomData::<(&())>, 490 | }, 491 | __tokens, 492 | ) 493 | } 494 | } 495 | pub(crate) fn __reduce< 496 | 'input, 497 | >( 498 | input: &'input str, 499 | __action: i8, 500 | __lookahead_start: Option<&usize>, 501 | __states: &mut ::std::vec::Vec, 502 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 503 | _: ::std::marker::PhantomData<(&'input ())>, 504 | ) -> Option, &'static str>>> 505 | { 506 | let (__pop_states, __nonterminal) = match __action { 507 | 0 => { 508 | __reduce0(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 509 | } 510 | 1 => { 511 | __reduce1(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 512 | } 513 | 2 => { 514 | __reduce2(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 515 | } 516 | 3 => { 517 | __reduce3(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 518 | } 519 | 4 => { 520 | __reduce4(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 521 | } 522 | 5 => { 523 | __reduce5(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 524 | } 525 | 6 => { 526 | __reduce6(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 527 | } 528 | 7 => { 529 | __reduce7(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 530 | } 531 | 8 => { 532 | __reduce8(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 533 | } 534 | 9 => { 535 | __reduce9(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 536 | } 537 | 10 => { 538 | __reduce10(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 539 | } 540 | 11 => { 541 | __reduce11(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 542 | } 543 | 12 => { 544 | __reduce12(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 545 | } 546 | 13 => { 547 | __reduce13(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 548 | } 549 | 14 => { 550 | __reduce14(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 551 | } 552 | 15 => { 553 | __reduce15(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 554 | } 555 | 16 => { 556 | __reduce16(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 557 | } 558 | 17 => { 559 | __reduce17(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 560 | } 561 | 18 => { 562 | __reduce18(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 563 | } 564 | 19 => { 565 | __reduce19(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 566 | } 567 | 20 => { 568 | __reduce20(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 569 | } 570 | 21 => { 571 | __reduce21(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 572 | } 573 | 22 => { 574 | __reduce22(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 575 | } 576 | 23 => { 577 | __reduce23(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 578 | } 579 | 24 => { 580 | __reduce24(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 581 | } 582 | 25 => { 583 | __reduce25(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 584 | } 585 | 26 => { 586 | __reduce26(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 587 | } 588 | 27 => { 589 | __reduce27(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 590 | } 591 | 28 => { 592 | __reduce28(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 593 | } 594 | 29 => { 595 | __reduce29(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 596 | } 597 | 30 => { 598 | __reduce30(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 599 | } 600 | 31 => { 601 | __reduce31(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 602 | } 603 | 32 => { 604 | __reduce32(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 605 | } 606 | 33 => { 607 | __reduce33(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 608 | } 609 | 34 => { 610 | __reduce34(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 611 | } 612 | 35 => { 613 | __reduce35(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 614 | } 615 | 36 => { 616 | __reduce36(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 617 | } 618 | 37 => { 619 | __reduce37(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 620 | } 621 | 38 => { 622 | __reduce38(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 623 | } 624 | 39 => { 625 | __reduce39(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 626 | } 627 | 40 => { 628 | __reduce40(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 629 | } 630 | 41 => { 631 | __reduce41(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 632 | } 633 | 42 => { 634 | __reduce42(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 635 | } 636 | 43 => { 637 | __reduce43(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 638 | } 639 | 44 => { 640 | __reduce44(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 641 | } 642 | 45 => { 643 | __reduce45(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 644 | } 645 | 46 => { 646 | __reduce46(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 647 | } 648 | 47 => { 649 | __reduce47(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 650 | } 651 | 48 => { 652 | __reduce48(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 653 | } 654 | 49 => { 655 | __reduce49(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 656 | } 657 | 50 => { 658 | __reduce50(input, __lookahead_start, __symbols, ::std::marker::PhantomData::<(&())>) 659 | } 660 | 51 => { 661 | // __Expr = Expr => ActionFn(0); 662 | let __sym0 = __pop_Variant5(__symbols); 663 | let __start = __sym0.0.clone(); 664 | let __end = __sym0.2.clone(); 665 | let __nt = super::__action0::<>(input, __sym0); 666 | return Some(Ok(__nt)); 667 | } 668 | _ => panic!("invalid action code {}", __action) 669 | }; 670 | let __states_len = __states.len(); 671 | __states.truncate(__states_len - __pop_states); 672 | let __state = *__states.last().unwrap(); 673 | let __next_state = __goto(__state, __nonterminal); 674 | __states.push(__next_state); 675 | None 676 | } 677 | #[inline(never)] 678 | fn __symbol_type_mismatch() -> ! { 679 | panic!("symbol type mismatch") 680 | } 681 | fn __pop_Variant3< 682 | 'input, 683 | >( 684 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 685 | ) -> (usize, (Expr, &'input str), usize) 686 | { 687 | match __symbols.pop().unwrap() { 688 | (__l, __Symbol::Variant3(__v), __r) => (__l, __v, __r), 689 | _ => __symbol_type_mismatch() 690 | } 691 | } 692 | fn __pop_Variant5< 693 | 'input, 694 | >( 695 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 696 | ) -> (usize, Expr, usize) 697 | { 698 | match __symbols.pop().unwrap() { 699 | (__l, __Symbol::Variant5(__v), __r) => (__l, __v, __r), 700 | _ => __symbol_type_mismatch() 701 | } 702 | } 703 | fn __pop_Variant7< 704 | 'input, 705 | >( 706 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 707 | ) -> (usize, String, usize) 708 | { 709 | match __symbols.pop().unwrap() { 710 | (__l, __Symbol::Variant7(__v), __r) => (__l, __v, __r), 711 | _ => __symbol_type_mismatch() 712 | } 713 | } 714 | fn __pop_Variant6< 715 | 'input, 716 | >( 717 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 718 | ) -> (usize, ::std::option::Option, usize) 719 | { 720 | match __symbols.pop().unwrap() { 721 | (__l, __Symbol::Variant6(__v), __r) => (__l, __v, __r), 722 | _ => __symbol_type_mismatch() 723 | } 724 | } 725 | fn __pop_Variant4< 726 | 'input, 727 | >( 728 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 729 | ) -> (usize, ::std::vec::Vec<(Expr, &'input str)>, usize) 730 | { 731 | match __symbols.pop().unwrap() { 732 | (__l, __Symbol::Variant4(__v), __r) => (__l, __v, __r), 733 | _ => __symbol_type_mismatch() 734 | } 735 | } 736 | fn __pop_Variant2< 737 | 'input, 738 | >( 739 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 740 | ) -> (usize, ::std::vec::Vec<&'static str>, usize) 741 | { 742 | match __symbols.pop().unwrap() { 743 | (__l, __Symbol::Variant2(__v), __r) => (__l, __v, __r), 744 | _ => __symbol_type_mismatch() 745 | } 746 | } 747 | fn __pop_Variant0< 748 | 'input, 749 | >( 750 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 751 | ) -> (usize, &'input str, usize) 752 | { 753 | match __symbols.pop().unwrap() { 754 | (__l, __Symbol::Variant0(__v), __r) => (__l, __v, __r), 755 | _ => __symbol_type_mismatch() 756 | } 757 | } 758 | fn __pop_Variant1< 759 | 'input, 760 | >( 761 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)> 762 | ) -> (usize, &'static str, usize) 763 | { 764 | match __symbols.pop().unwrap() { 765 | (__l, __Symbol::Variant1(__v), __r) => (__l, __v, __r), 766 | _ => __symbol_type_mismatch() 767 | } 768 | } 769 | pub(crate) fn __reduce0< 770 | 'input, 771 | >( 772 | input: &'input str, 773 | __lookahead_start: Option<&usize>, 774 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 775 | _: ::std::marker::PhantomData<(&'input ())>, 776 | ) -> (usize, usize) 777 | { 778 | // () = Postfix => ActionFn(37); 779 | let __sym0 = __pop_Variant1(__symbols); 780 | let __start = __sym0.0.clone(); 781 | let __end = __sym0.2.clone(); 782 | let __nt = super::__action37::<>(input, __sym0); 783 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 784 | (1, 0) 785 | } 786 | pub(crate) fn __reduce1< 787 | 'input, 788 | >( 789 | input: &'input str, 790 | __lookahead_start: Option<&usize>, 791 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 792 | _: ::std::marker::PhantomData<(&'input ())>, 793 | ) -> (usize, usize) 794 | { 795 | // ()* = => ActionFn(35); 796 | let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); 797 | let __end = __start.clone(); 798 | let __nt = super::__action35::<>(input, &__start, &__end); 799 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 800 | (0, 1) 801 | } 802 | pub(crate) fn __reduce2< 803 | 'input, 804 | >( 805 | input: &'input str, 806 | __lookahead_start: Option<&usize>, 807 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 808 | _: ::std::marker::PhantomData<(&'input ())>, 809 | ) -> (usize, usize) 810 | { 811 | // ()* = ()+ => ActionFn(36); 812 | let __sym0 = __pop_Variant2(__symbols); 813 | let __start = __sym0.0.clone(); 814 | let __end = __sym0.2.clone(); 815 | let __nt = super::__action36::<>(input, __sym0); 816 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 817 | (1, 1) 818 | } 819 | pub(crate) fn __reduce3< 820 | 'input, 821 | >( 822 | input: &'input str, 823 | __lookahead_start: Option<&usize>, 824 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 825 | _: ::std::marker::PhantomData<(&'input ())>, 826 | ) -> (usize, usize) 827 | { 828 | // ()+ = Postfix => ActionFn(47); 829 | let __sym0 = __pop_Variant1(__symbols); 830 | let __start = __sym0.0.clone(); 831 | let __end = __sym0.2.clone(); 832 | let __nt = super::__action47::<>(input, __sym0); 833 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 834 | (1, 2) 835 | } 836 | pub(crate) fn __reduce4< 837 | 'input, 838 | >( 839 | input: &'input str, 840 | __lookahead_start: Option<&usize>, 841 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 842 | _: ::std::marker::PhantomData<(&'input ())>, 843 | ) -> (usize, usize) 844 | { 845 | // ()+ = ()+, Postfix => ActionFn(48); 846 | assert!(__symbols.len() >= 2); 847 | let __sym1 = __pop_Variant1(__symbols); 848 | let __sym0 = __pop_Variant2(__symbols); 849 | let __start = __sym0.0.clone(); 850 | let __end = __sym1.2.clone(); 851 | let __nt = super::__action48::<>(input, __sym0, __sym1); 852 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 853 | (2, 2) 854 | } 855 | pub(crate) fn __reduce5< 856 | 'input, 857 | >( 858 | input: &'input str, 859 | __lookahead_start: Option<&usize>, 860 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 861 | _: ::std::marker::PhantomData<(&'input ())>, 862 | ) -> (usize, usize) 863 | { 864 | // () = Prefix => ActionFn(40); 865 | let __sym0 = __pop_Variant1(__symbols); 866 | let __start = __sym0.0.clone(); 867 | let __end = __sym0.2.clone(); 868 | let __nt = super::__action40::<>(input, __sym0); 869 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 870 | (1, 3) 871 | } 872 | pub(crate) fn __reduce6< 873 | 'input, 874 | >( 875 | input: &'input str, 876 | __lookahead_start: Option<&usize>, 877 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 878 | _: ::std::marker::PhantomData<(&'input ())>, 879 | ) -> (usize, usize) 880 | { 881 | // ()* = => ActionFn(38); 882 | let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); 883 | let __end = __start.clone(); 884 | let __nt = super::__action38::<>(input, &__start, &__end); 885 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 886 | (0, 4) 887 | } 888 | pub(crate) fn __reduce7< 889 | 'input, 890 | >( 891 | input: &'input str, 892 | __lookahead_start: Option<&usize>, 893 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 894 | _: ::std::marker::PhantomData<(&'input ())>, 895 | ) -> (usize, usize) 896 | { 897 | // ()* = ()+ => ActionFn(39); 898 | let __sym0 = __pop_Variant2(__symbols); 899 | let __start = __sym0.0.clone(); 900 | let __end = __sym0.2.clone(); 901 | let __nt = super::__action39::<>(input, __sym0); 902 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 903 | (1, 4) 904 | } 905 | pub(crate) fn __reduce8< 906 | 'input, 907 | >( 908 | input: &'input str, 909 | __lookahead_start: Option<&usize>, 910 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 911 | _: ::std::marker::PhantomData<(&'input ())>, 912 | ) -> (usize, usize) 913 | { 914 | // ()+ = Prefix => ActionFn(51); 915 | let __sym0 = __pop_Variant1(__symbols); 916 | let __start = __sym0.0.clone(); 917 | let __end = __sym0.2.clone(); 918 | let __nt = super::__action51::<>(input, __sym0); 919 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 920 | (1, 5) 921 | } 922 | pub(crate) fn __reduce9< 923 | 'input, 924 | >( 925 | input: &'input str, 926 | __lookahead_start: Option<&usize>, 927 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 928 | _: ::std::marker::PhantomData<(&'input ())>, 929 | ) -> (usize, usize) 930 | { 931 | // ()+ = ()+, Prefix => ActionFn(52); 932 | assert!(__symbols.len() >= 2); 933 | let __sym1 = __pop_Variant1(__symbols); 934 | let __sym0 = __pop_Variant2(__symbols); 935 | let __start = __sym0.0.clone(); 936 | let __end = __sym1.2.clone(); 937 | let __nt = super::__action52::<>(input, __sym0, __sym1); 938 | __symbols.push((__start, __Symbol::Variant2(__nt), __end)); 939 | (2, 5) 940 | } 941 | pub(crate) fn __reduce10< 942 | 'input, 943 | >( 944 | input: &'input str, 945 | __lookahead_start: Option<&usize>, 946 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 947 | _: ::std::marker::PhantomData<(&'input ())>, 948 | ) -> (usize, usize) 949 | { 950 | // (Expr ",") = Expr, "," => ActionFn(34); 951 | assert!(__symbols.len() >= 2); 952 | let __sym1 = __pop_Variant0(__symbols); 953 | let __sym0 = __pop_Variant5(__symbols); 954 | let __start = __sym0.0.clone(); 955 | let __end = __sym1.2.clone(); 956 | let __nt = super::__action34::<>(input, __sym0, __sym1); 957 | __symbols.push((__start, __Symbol::Variant3(__nt), __end)); 958 | (2, 6) 959 | } 960 | pub(crate) fn __reduce11< 961 | 'input, 962 | >( 963 | input: &'input str, 964 | __lookahead_start: Option<&usize>, 965 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 966 | _: ::std::marker::PhantomData<(&'input ())>, 967 | ) -> (usize, usize) 968 | { 969 | // (Expr ",")* = => ActionFn(32); 970 | let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); 971 | let __end = __start.clone(); 972 | let __nt = super::__action32::<>(input, &__start, &__end); 973 | __symbols.push((__start, __Symbol::Variant4(__nt), __end)); 974 | (0, 7) 975 | } 976 | pub(crate) fn __reduce12< 977 | 'input, 978 | >( 979 | input: &'input str, 980 | __lookahead_start: Option<&usize>, 981 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 982 | _: ::std::marker::PhantomData<(&'input ())>, 983 | ) -> (usize, usize) 984 | { 985 | // (Expr ",")* = (Expr ",")+ => ActionFn(33); 986 | let __sym0 = __pop_Variant4(__symbols); 987 | let __start = __sym0.0.clone(); 988 | let __end = __sym0.2.clone(); 989 | let __nt = super::__action33::<>(input, __sym0); 990 | __symbols.push((__start, __Symbol::Variant4(__nt), __end)); 991 | (1, 7) 992 | } 993 | pub(crate) fn __reduce13< 994 | 'input, 995 | >( 996 | input: &'input str, 997 | __lookahead_start: Option<&usize>, 998 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 999 | _: ::std::marker::PhantomData<(&'input ())>, 1000 | ) -> (usize, usize) 1001 | { 1002 | // (Expr ",")+ = Expr, "," => ActionFn(55); 1003 | assert!(__symbols.len() >= 2); 1004 | let __sym1 = __pop_Variant0(__symbols); 1005 | let __sym0 = __pop_Variant5(__symbols); 1006 | let __start = __sym0.0.clone(); 1007 | let __end = __sym1.2.clone(); 1008 | let __nt = super::__action55::<>(input, __sym0, __sym1); 1009 | __symbols.push((__start, __Symbol::Variant4(__nt), __end)); 1010 | (2, 8) 1011 | } 1012 | pub(crate) fn __reduce14< 1013 | 'input, 1014 | >( 1015 | input: &'input str, 1016 | __lookahead_start: Option<&usize>, 1017 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1018 | _: ::std::marker::PhantomData<(&'input ())>, 1019 | ) -> (usize, usize) 1020 | { 1021 | // (Expr ",")+ = (Expr ",")+, Expr, "," => ActionFn(56); 1022 | assert!(__symbols.len() >= 3); 1023 | let __sym2 = __pop_Variant0(__symbols); 1024 | let __sym1 = __pop_Variant5(__symbols); 1025 | let __sym0 = __pop_Variant4(__symbols); 1026 | let __start = __sym0.0.clone(); 1027 | let __end = __sym2.2.clone(); 1028 | let __nt = super::__action56::<>(input, __sym0, __sym1, __sym2); 1029 | __symbols.push((__start, __Symbol::Variant4(__nt), __end)); 1030 | (3, 8) 1031 | } 1032 | pub(crate) fn __reduce15< 1033 | 'input, 1034 | >( 1035 | input: &'input str, 1036 | __lookahead_start: Option<&usize>, 1037 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1038 | _: ::std::marker::PhantomData<(&'input ())>, 1039 | ) -> (usize, usize) 1040 | { 1041 | // BinOp1 = ":" => ActionFn(15); 1042 | let __sym0 = __pop_Variant0(__symbols); 1043 | let __start = __sym0.0.clone(); 1044 | let __end = __sym0.2.clone(); 1045 | let __nt = super::__action15::<>(input, __sym0); 1046 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1047 | (1, 9) 1048 | } 1049 | pub(crate) fn __reduce16< 1050 | 'input, 1051 | >( 1052 | input: &'input str, 1053 | __lookahead_start: Option<&usize>, 1054 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1055 | _: ::std::marker::PhantomData<(&'input ())>, 1056 | ) -> (usize, usize) 1057 | { 1058 | // BinOp1 = ".." => ActionFn(16); 1059 | let __sym0 = __pop_Variant0(__symbols); 1060 | let __start = __sym0.0.clone(); 1061 | let __end = __sym0.2.clone(); 1062 | let __nt = super::__action16::<>(input, __sym0); 1063 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1064 | (1, 9) 1065 | } 1066 | pub(crate) fn __reduce17< 1067 | 'input, 1068 | >( 1069 | input: &'input str, 1070 | __lookahead_start: Option<&usize>, 1071 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1072 | _: ::std::marker::PhantomData<(&'input ())>, 1073 | ) -> (usize, usize) 1074 | { 1075 | // BinOp2 = "&" => ActionFn(9); 1076 | let __sym0 = __pop_Variant0(__symbols); 1077 | let __start = __sym0.0.clone(); 1078 | let __end = __sym0.2.clone(); 1079 | let __nt = super::__action9::<>(input, __sym0); 1080 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1081 | (1, 10) 1082 | } 1083 | pub(crate) fn __reduce18< 1084 | 'input, 1085 | >( 1086 | input: &'input str, 1087 | __lookahead_start: Option<&usize>, 1088 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1089 | _: ::std::marker::PhantomData<(&'input ())>, 1090 | ) -> (usize, usize) 1091 | { 1092 | // BinOp2 = "and" => ActionFn(10); 1093 | let __sym0 = __pop_Variant0(__symbols); 1094 | let __start = __sym0.0.clone(); 1095 | let __end = __sym0.2.clone(); 1096 | let __nt = super::__action10::<>(input, __sym0); 1097 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1098 | (1, 10) 1099 | } 1100 | pub(crate) fn __reduce19< 1101 | 'input, 1102 | >( 1103 | input: &'input str, 1104 | __lookahead_start: Option<&usize>, 1105 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1106 | _: ::std::marker::PhantomData<(&'input ())>, 1107 | ) -> (usize, usize) 1108 | { 1109 | // BinOp2 = "-" => ActionFn(11); 1110 | let __sym0 = __pop_Variant0(__symbols); 1111 | let __start = __sym0.0.clone(); 1112 | let __end = __sym0.2.clone(); 1113 | let __nt = super::__action11::<>(input, __sym0); 1114 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1115 | (1, 10) 1116 | } 1117 | pub(crate) fn __reduce20< 1118 | 'input, 1119 | >( 1120 | input: &'input str, 1121 | __lookahead_start: Option<&usize>, 1122 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1123 | _: ::std::marker::PhantomData<(&'input ())>, 1124 | ) -> (usize, usize) 1125 | { 1126 | // BinOp2 = "%" => ActionFn(12); 1127 | let __sym0 = __pop_Variant0(__symbols); 1128 | let __start = __sym0.0.clone(); 1129 | let __end = __sym0.2.clone(); 1130 | let __nt = super::__action12::<>(input, __sym0); 1131 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1132 | (1, 10) 1133 | } 1134 | pub(crate) fn __reduce21< 1135 | 'input, 1136 | >( 1137 | input: &'input str, 1138 | __lookahead_start: Option<&usize>, 1139 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1140 | _: ::std::marker::PhantomData<(&'input ())>, 1141 | ) -> (usize, usize) 1142 | { 1143 | // BinOp3 = "|" => ActionFn(4); 1144 | let __sym0 = __pop_Variant0(__symbols); 1145 | let __start = __sym0.0.clone(); 1146 | let __end = __sym0.2.clone(); 1147 | let __nt = super::__action4::<>(input, __sym0); 1148 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1149 | (1, 11) 1150 | } 1151 | pub(crate) fn __reduce22< 1152 | 'input, 1153 | >( 1154 | input: &'input str, 1155 | __lookahead_start: Option<&usize>, 1156 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1157 | _: ::std::marker::PhantomData<(&'input ())>, 1158 | ) -> (usize, usize) 1159 | { 1160 | // BinOp3 = "+" => ActionFn(5); 1161 | let __sym0 = __pop_Variant0(__symbols); 1162 | let __start = __sym0.0.clone(); 1163 | let __end = __sym0.2.clone(); 1164 | let __nt = super::__action5::<>(input, __sym0); 1165 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1166 | (1, 11) 1167 | } 1168 | pub(crate) fn __reduce23< 1169 | 'input, 1170 | >( 1171 | input: &'input str, 1172 | __lookahead_start: Option<&usize>, 1173 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1174 | _: ::std::marker::PhantomData<(&'input ())>, 1175 | ) -> (usize, usize) 1176 | { 1177 | // BinOp3 = "or" => ActionFn(6); 1178 | let __sym0 = __pop_Variant0(__symbols); 1179 | let __start = __sym0.0.clone(); 1180 | let __end = __sym0.2.clone(); 1181 | let __nt = super::__action6::<>(input, __sym0); 1182 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1183 | (1, 11) 1184 | } 1185 | pub(crate) fn __reduce24< 1186 | 'input, 1187 | >( 1188 | input: &'input str, 1189 | __lookahead_start: Option<&usize>, 1190 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1191 | _: ::std::marker::PhantomData<(&'input ())>, 1192 | ) -> (usize, usize) 1193 | { 1194 | // Expr = Expr4 => ActionFn(1); 1195 | let __sym0 = __pop_Variant5(__symbols); 1196 | let __start = __sym0.0.clone(); 1197 | let __end = __sym0.2.clone(); 1198 | let __nt = super::__action1::<>(input, __sym0); 1199 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1200 | (1, 12) 1201 | } 1202 | pub(crate) fn __reduce25< 1203 | 'input, 1204 | >( 1205 | input: &'input str, 1206 | __lookahead_start: Option<&usize>, 1207 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1208 | _: ::std::marker::PhantomData<(&'input ())>, 1209 | ) -> (usize, usize) 1210 | { 1211 | // Expr0 = Symbol2 => ActionFn(19); 1212 | let __sym0 = __pop_Variant7(__symbols); 1213 | let __start = __sym0.0.clone(); 1214 | let __end = __sym0.2.clone(); 1215 | let __nt = super::__action19::<>(input, __sym0); 1216 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1217 | (1, 13) 1218 | } 1219 | pub(crate) fn __reduce26< 1220 | 'input, 1221 | >( 1222 | input: &'input str, 1223 | __lookahead_start: Option<&usize>, 1224 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1225 | _: ::std::marker::PhantomData<(&'input ())>, 1226 | ) -> (usize, usize) 1227 | { 1228 | // Expr0 = Symbol1, "(", Expr, ")" => ActionFn(59); 1229 | assert!(__symbols.len() >= 4); 1230 | let __sym3 = __pop_Variant0(__symbols); 1231 | let __sym2 = __pop_Variant5(__symbols); 1232 | let __sym1 = __pop_Variant0(__symbols); 1233 | let __sym0 = __pop_Variant7(__symbols); 1234 | let __start = __sym0.0.clone(); 1235 | let __end = __sym3.2.clone(); 1236 | let __nt = super::__action59::<>(input, __sym0, __sym1, __sym2, __sym3); 1237 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1238 | (4, 13) 1239 | } 1240 | pub(crate) fn __reduce27< 1241 | 'input, 1242 | >( 1243 | input: &'input str, 1244 | __lookahead_start: Option<&usize>, 1245 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1246 | _: ::std::marker::PhantomData<(&'input ())>, 1247 | ) -> (usize, usize) 1248 | { 1249 | // Expr0 = Symbol1, "(", ")" => ActionFn(60); 1250 | assert!(__symbols.len() >= 3); 1251 | let __sym2 = __pop_Variant0(__symbols); 1252 | let __sym1 = __pop_Variant0(__symbols); 1253 | let __sym0 = __pop_Variant7(__symbols); 1254 | let __start = __sym0.0.clone(); 1255 | let __end = __sym2.2.clone(); 1256 | let __nt = super::__action60::<>(input, __sym0, __sym1, __sym2); 1257 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1258 | (3, 13) 1259 | } 1260 | pub(crate) fn __reduce28< 1261 | 'input, 1262 | >( 1263 | input: &'input str, 1264 | __lookahead_start: Option<&usize>, 1265 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1266 | _: ::std::marker::PhantomData<(&'input ())>, 1267 | ) -> (usize, usize) 1268 | { 1269 | // Expr0 = Symbol1, "(", (Expr ",")+, Expr, ")" => ActionFn(61); 1270 | assert!(__symbols.len() >= 5); 1271 | let __sym4 = __pop_Variant0(__symbols); 1272 | let __sym3 = __pop_Variant5(__symbols); 1273 | let __sym2 = __pop_Variant4(__symbols); 1274 | let __sym1 = __pop_Variant0(__symbols); 1275 | let __sym0 = __pop_Variant7(__symbols); 1276 | let __start = __sym0.0.clone(); 1277 | let __end = __sym4.2.clone(); 1278 | let __nt = super::__action61::<>(input, __sym0, __sym1, __sym2, __sym3, __sym4); 1279 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1280 | (5, 13) 1281 | } 1282 | pub(crate) fn __reduce29< 1283 | 'input, 1284 | >( 1285 | input: &'input str, 1286 | __lookahead_start: Option<&usize>, 1287 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1288 | _: ::std::marker::PhantomData<(&'input ())>, 1289 | ) -> (usize, usize) 1290 | { 1291 | // Expr0 = Symbol1, "(", (Expr ",")+, ")" => ActionFn(62); 1292 | assert!(__symbols.len() >= 4); 1293 | let __sym3 = __pop_Variant0(__symbols); 1294 | let __sym2 = __pop_Variant4(__symbols); 1295 | let __sym1 = __pop_Variant0(__symbols); 1296 | let __sym0 = __pop_Variant7(__symbols); 1297 | let __start = __sym0.0.clone(); 1298 | let __end = __sym3.2.clone(); 1299 | let __nt = super::__action62::<>(input, __sym0, __sym1, __sym2, __sym3); 1300 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1301 | (4, 13) 1302 | } 1303 | pub(crate) fn __reduce30< 1304 | 'input, 1305 | >( 1306 | input: &'input str, 1307 | __lookahead_start: Option<&usize>, 1308 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1309 | _: ::std::marker::PhantomData<(&'input ())>, 1310 | ) -> (usize, usize) 1311 | { 1312 | // Expr0 = "(", Expr, ")" => ActionFn(21); 1313 | assert!(__symbols.len() >= 3); 1314 | let __sym2 = __pop_Variant0(__symbols); 1315 | let __sym1 = __pop_Variant5(__symbols); 1316 | let __sym0 = __pop_Variant0(__symbols); 1317 | let __start = __sym0.0.clone(); 1318 | let __end = __sym2.2.clone(); 1319 | let __nt = super::__action21::<>(input, __sym0, __sym1, __sym2); 1320 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1321 | (3, 13) 1322 | } 1323 | pub(crate) fn __reduce31< 1324 | 'input, 1325 | >( 1326 | input: &'input str, 1327 | __lookahead_start: Option<&usize>, 1328 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1329 | _: ::std::marker::PhantomData<(&'input ())>, 1330 | ) -> (usize, usize) 1331 | { 1332 | // Expr1 = Expr0 => ActionFn(49); 1333 | let __sym0 = __pop_Variant5(__symbols); 1334 | let __start = __sym0.0.clone(); 1335 | let __end = __sym0.2.clone(); 1336 | let __nt = super::__action49::<>(input, __sym0); 1337 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1338 | (1, 14) 1339 | } 1340 | pub(crate) fn __reduce32< 1341 | 'input, 1342 | >( 1343 | input: &'input str, 1344 | __lookahead_start: Option<&usize>, 1345 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1346 | _: ::std::marker::PhantomData<(&'input ())>, 1347 | ) -> (usize, usize) 1348 | { 1349 | // Expr1 = Expr0, ()+ => ActionFn(50); 1350 | assert!(__symbols.len() >= 2); 1351 | let __sym1 = __pop_Variant2(__symbols); 1352 | let __sym0 = __pop_Variant5(__symbols); 1353 | let __start = __sym0.0.clone(); 1354 | let __end = __sym1.2.clone(); 1355 | let __nt = super::__action50::<>(input, __sym0, __sym1); 1356 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1357 | (2, 14) 1358 | } 1359 | pub(crate) fn __reduce33< 1360 | 'input, 1361 | >( 1362 | input: &'input str, 1363 | __lookahead_start: Option<&usize>, 1364 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1365 | _: ::std::marker::PhantomData<(&'input ())>, 1366 | ) -> (usize, usize) 1367 | { 1368 | // Expr15 = Expr1 => ActionFn(53); 1369 | let __sym0 = __pop_Variant5(__symbols); 1370 | let __start = __sym0.0.clone(); 1371 | let __end = __sym0.2.clone(); 1372 | let __nt = super::__action53::<>(input, __sym0); 1373 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1374 | (1, 15) 1375 | } 1376 | pub(crate) fn __reduce34< 1377 | 'input, 1378 | >( 1379 | input: &'input str, 1380 | __lookahead_start: Option<&usize>, 1381 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1382 | _: ::std::marker::PhantomData<(&'input ())>, 1383 | ) -> (usize, usize) 1384 | { 1385 | // Expr15 = ()+, Expr1 => ActionFn(54); 1386 | assert!(__symbols.len() >= 2); 1387 | let __sym1 = __pop_Variant5(__symbols); 1388 | let __sym0 = __pop_Variant2(__symbols); 1389 | let __start = __sym0.0.clone(); 1390 | let __end = __sym1.2.clone(); 1391 | let __nt = super::__action54::<>(input, __sym0, __sym1); 1392 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1393 | (2, 15) 1394 | } 1395 | pub(crate) fn __reduce35< 1396 | 'input, 1397 | >( 1398 | input: &'input str, 1399 | __lookahead_start: Option<&usize>, 1400 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1401 | _: ::std::marker::PhantomData<(&'input ())>, 1402 | ) -> (usize, usize) 1403 | { 1404 | // Expr2 = Expr2, BinOp1, Expr15 => ActionFn(13); 1405 | assert!(__symbols.len() >= 3); 1406 | let __sym2 = __pop_Variant5(__symbols); 1407 | let __sym1 = __pop_Variant1(__symbols); 1408 | let __sym0 = __pop_Variant5(__symbols); 1409 | let __start = __sym0.0.clone(); 1410 | let __end = __sym2.2.clone(); 1411 | let __nt = super::__action13::<>(input, __sym0, __sym1, __sym2); 1412 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1413 | (3, 16) 1414 | } 1415 | pub(crate) fn __reduce36< 1416 | 'input, 1417 | >( 1418 | input: &'input str, 1419 | __lookahead_start: Option<&usize>, 1420 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1421 | _: ::std::marker::PhantomData<(&'input ())>, 1422 | ) -> (usize, usize) 1423 | { 1424 | // Expr2 = Expr15 => ActionFn(14); 1425 | let __sym0 = __pop_Variant5(__symbols); 1426 | let __start = __sym0.0.clone(); 1427 | let __end = __sym0.2.clone(); 1428 | let __nt = super::__action14::<>(input, __sym0); 1429 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1430 | (1, 16) 1431 | } 1432 | pub(crate) fn __reduce37< 1433 | 'input, 1434 | >( 1435 | input: &'input str, 1436 | __lookahead_start: Option<&usize>, 1437 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1438 | _: ::std::marker::PhantomData<(&'input ())>, 1439 | ) -> (usize, usize) 1440 | { 1441 | // Expr3 = Expr3, BinOp2, Expr2 => ActionFn(7); 1442 | assert!(__symbols.len() >= 3); 1443 | let __sym2 = __pop_Variant5(__symbols); 1444 | let __sym1 = __pop_Variant1(__symbols); 1445 | let __sym0 = __pop_Variant5(__symbols); 1446 | let __start = __sym0.0.clone(); 1447 | let __end = __sym2.2.clone(); 1448 | let __nt = super::__action7::<>(input, __sym0, __sym1, __sym2); 1449 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1450 | (3, 17) 1451 | } 1452 | pub(crate) fn __reduce38< 1453 | 'input, 1454 | >( 1455 | input: &'input str, 1456 | __lookahead_start: Option<&usize>, 1457 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1458 | _: ::std::marker::PhantomData<(&'input ())>, 1459 | ) -> (usize, usize) 1460 | { 1461 | // Expr3 = Expr2 => ActionFn(8); 1462 | let __sym0 = __pop_Variant5(__symbols); 1463 | let __start = __sym0.0.clone(); 1464 | let __end = __sym0.2.clone(); 1465 | let __nt = super::__action8::<>(input, __sym0); 1466 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1467 | (1, 17) 1468 | } 1469 | pub(crate) fn __reduce39< 1470 | 'input, 1471 | >( 1472 | input: &'input str, 1473 | __lookahead_start: Option<&usize>, 1474 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1475 | _: ::std::marker::PhantomData<(&'input ())>, 1476 | ) -> (usize, usize) 1477 | { 1478 | // Expr4 = Expr4, BinOp3, Expr3 => ActionFn(2); 1479 | assert!(__symbols.len() >= 3); 1480 | let __sym2 = __pop_Variant5(__symbols); 1481 | let __sym1 = __pop_Variant1(__symbols); 1482 | let __sym0 = __pop_Variant5(__symbols); 1483 | let __start = __sym0.0.clone(); 1484 | let __end = __sym2.2.clone(); 1485 | let __nt = super::__action2::<>(input, __sym0, __sym1, __sym2); 1486 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1487 | (3, 18) 1488 | } 1489 | pub(crate) fn __reduce40< 1490 | 'input, 1491 | >( 1492 | input: &'input str, 1493 | __lookahead_start: Option<&usize>, 1494 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1495 | _: ::std::marker::PhantomData<(&'input ())>, 1496 | ) -> (usize, usize) 1497 | { 1498 | // Expr4 = Expr3 => ActionFn(3); 1499 | let __sym0 = __pop_Variant5(__symbols); 1500 | let __start = __sym0.0.clone(); 1501 | let __end = __sym0.2.clone(); 1502 | let __nt = super::__action3::<>(input, __sym0); 1503 | __symbols.push((__start, __Symbol::Variant5(__nt), __end)); 1504 | (1, 18) 1505 | } 1506 | pub(crate) fn __reduce41< 1507 | 'input, 1508 | >( 1509 | input: &'input str, 1510 | __lookahead_start: Option<&usize>, 1511 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1512 | _: ::std::marker::PhantomData<(&'input ())>, 1513 | ) -> (usize, usize) 1514 | { 1515 | // Expr? = Expr => ActionFn(30); 1516 | let __sym0 = __pop_Variant5(__symbols); 1517 | let __start = __sym0.0.clone(); 1518 | let __end = __sym0.2.clone(); 1519 | let __nt = super::__action30::<>(input, __sym0); 1520 | __symbols.push((__start, __Symbol::Variant6(__nt), __end)); 1521 | (1, 19) 1522 | } 1523 | pub(crate) fn __reduce42< 1524 | 'input, 1525 | >( 1526 | input: &'input str, 1527 | __lookahead_start: Option<&usize>, 1528 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1529 | _: ::std::marker::PhantomData<(&'input ())>, 1530 | ) -> (usize, usize) 1531 | { 1532 | // Expr? = => ActionFn(31); 1533 | let __start = __lookahead_start.cloned().or_else(|| __symbols.last().map(|s| s.2.clone())).unwrap_or_default(); 1534 | let __end = __start.clone(); 1535 | let __nt = super::__action31::<>(input, &__start, &__end); 1536 | __symbols.push((__start, __Symbol::Variant6(__nt), __end)); 1537 | (0, 19) 1538 | } 1539 | pub(crate) fn __reduce43< 1540 | 'input, 1541 | >( 1542 | input: &'input str, 1543 | __lookahead_start: Option<&usize>, 1544 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1545 | _: ::std::marker::PhantomData<(&'input ())>, 1546 | ) -> (usize, usize) 1547 | { 1548 | // Postfix = "::" => ActionFn(25); 1549 | let __sym0 = __pop_Variant0(__symbols); 1550 | let __start = __sym0.0.clone(); 1551 | let __end = __sym0.2.clone(); 1552 | let __nt = super::__action25::<>(input, __sym0); 1553 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1554 | (1, 20) 1555 | } 1556 | pub(crate) fn __reduce44< 1557 | 'input, 1558 | >( 1559 | input: &'input str, 1560 | __lookahead_start: Option<&usize>, 1561 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1562 | _: ::std::marker::PhantomData<(&'input ())>, 1563 | ) -> (usize, usize) 1564 | { 1565 | // Postfix = "^" => ActionFn(26); 1566 | let __sym0 = __pop_Variant0(__symbols); 1567 | let __start = __sym0.0.clone(); 1568 | let __end = __sym0.2.clone(); 1569 | let __nt = super::__action26::<>(input, __sym0); 1570 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1571 | (1, 20) 1572 | } 1573 | pub(crate) fn __reduce45< 1574 | 'input, 1575 | >( 1576 | input: &'input str, 1577 | __lookahead_start: Option<&usize>, 1578 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1579 | _: ::std::marker::PhantomData<(&'input ())>, 1580 | ) -> (usize, usize) 1581 | { 1582 | // Prefix = "!" => ActionFn(22); 1583 | let __sym0 = __pop_Variant0(__symbols); 1584 | let __start = __sym0.0.clone(); 1585 | let __end = __sym0.2.clone(); 1586 | let __nt = super::__action22::<>(input, __sym0); 1587 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1588 | (1, 21) 1589 | } 1590 | pub(crate) fn __reduce46< 1591 | 'input, 1592 | >( 1593 | input: &'input str, 1594 | __lookahead_start: Option<&usize>, 1595 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1596 | _: ::std::marker::PhantomData<(&'input ())>, 1597 | ) -> (usize, usize) 1598 | { 1599 | // Prefix = "not " => ActionFn(23); 1600 | let __sym0 = __pop_Variant0(__symbols); 1601 | let __start = __sym0.0.clone(); 1602 | let __end = __sym0.2.clone(); 1603 | let __nt = super::__action23::<>(input, __sym0); 1604 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1605 | (1, 21) 1606 | } 1607 | pub(crate) fn __reduce47< 1608 | 'input, 1609 | >( 1610 | input: &'input str, 1611 | __lookahead_start: Option<&usize>, 1612 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1613 | _: ::std::marker::PhantomData<(&'input ())>, 1614 | ) -> (usize, usize) 1615 | { 1616 | // Prefix = "::" => ActionFn(24); 1617 | let __sym0 = __pop_Variant0(__symbols); 1618 | let __start = __sym0.0.clone(); 1619 | let __end = __sym0.2.clone(); 1620 | let __nt = super::__action24::<>(input, __sym0); 1621 | __symbols.push((__start, __Symbol::Variant1(__nt), __end)); 1622 | (1, 21) 1623 | } 1624 | pub(crate) fn __reduce48< 1625 | 'input, 1626 | >( 1627 | input: &'input str, 1628 | __lookahead_start: Option<&usize>, 1629 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1630 | _: ::std::marker::PhantomData<(&'input ())>, 1631 | ) -> (usize, usize) 1632 | { 1633 | // Symbol1 = r#"[a-zA-Z0-9/_$@.]+"# => ActionFn(29); 1634 | let __sym0 = __pop_Variant0(__symbols); 1635 | let __start = __sym0.0.clone(); 1636 | let __end = __sym0.2.clone(); 1637 | let __nt = super::__action29::<>(input, __sym0); 1638 | __symbols.push((__start, __Symbol::Variant7(__nt), __end)); 1639 | (1, 22) 1640 | } 1641 | pub(crate) fn __reduce49< 1642 | 'input, 1643 | >( 1644 | input: &'input str, 1645 | __lookahead_start: Option<&usize>, 1646 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1647 | _: ::std::marker::PhantomData<(&'input ())>, 1648 | ) -> (usize, usize) 1649 | { 1650 | // Symbol2 = Symbol1 => ActionFn(27); 1651 | let __sym0 = __pop_Variant7(__symbols); 1652 | let __start = __sym0.0.clone(); 1653 | let __end = __sym0.2.clone(); 1654 | let __nt = super::__action27::<>(input, __sym0); 1655 | __symbols.push((__start, __Symbol::Variant7(__nt), __end)); 1656 | (1, 23) 1657 | } 1658 | pub(crate) fn __reduce50< 1659 | 'input, 1660 | >( 1661 | input: &'input str, 1662 | __lookahead_start: Option<&usize>, 1663 | __symbols: &mut ::std::vec::Vec<(usize,__Symbol<'input>,usize)>, 1664 | _: ::std::marker::PhantomData<(&'input ())>, 1665 | ) -> (usize, usize) 1666 | { 1667 | // Symbol2 = r#"\\x22([^\\x22\\x5c]|\\x5c.)*\\x22"# => ActionFn(28); 1668 | let __sym0 = __pop_Variant0(__symbols); 1669 | let __start = __sym0.0.clone(); 1670 | let __end = __sym0.2.clone(); 1671 | let __nt = super::__action28::<>(input, __sym0); 1672 | __symbols.push((__start, __Symbol::Variant7(__nt), __end)); 1673 | (1, 23) 1674 | } 1675 | } 1676 | pub use self::__parse__Expr::ExprParser; 1677 | #[cfg_attr(rustfmt, rustfmt_skip)] 1678 | mod __intern_token { 1679 | #![allow(unused_imports)] 1680 | use crate::ast::Expr; 1681 | #[allow(unused_extern_crates)] 1682 | extern crate lalrpop_util as __lalrpop_util; 1683 | #[allow(unused_imports)] 1684 | use self::__lalrpop_util::state_machine as __state_machine; 1685 | pub fn new_builder() -> __lalrpop_util::lexer::MatcherBuilder { 1686 | let __strs: &[(&str, bool)] = &[ 1687 | ("^([\\$\\.-9@-Z_a-z]+)", false), 1688 | ("^(\"([\u{0}-!\\#-\\[\\]-\u{10ffff}]|\\\\[\u{0}-\t\u{b}-\u{10ffff}])*\")", false), 1689 | ("^(!)", false), 1690 | ("^(%)", false), 1691 | ("^(\\&)", false), 1692 | ("^(\\()", false), 1693 | ("^(\\))", false), 1694 | ("^(\\+)", false), 1695 | ("^(,)", false), 1696 | ("^(\\-)", false), 1697 | ("^(\\.\\.)", false), 1698 | ("^(:)", false), 1699 | ("^(::)", false), 1700 | ("^(\\^)", false), 1701 | ("^(and)", false), 1702 | ("^(not )", false), 1703 | ("^(or)", false), 1704 | ("^(\\|)", false), 1705 | (r"^(\s*)", true), 1706 | ]; 1707 | __lalrpop_util::lexer::MatcherBuilder::new(__strs.iter().copied()).unwrap() 1708 | } 1709 | } 1710 | pub use self::__lalrpop_util::lexer::Token; 1711 | 1712 | #[allow(unused_variables)] 1713 | fn __action0< 1714 | 'input, 1715 | >( 1716 | input: &'input str, 1717 | (_, __0, _): (usize, Expr, usize), 1718 | ) -> Expr 1719 | { 1720 | __0 1721 | } 1722 | 1723 | #[allow(unused_variables)] 1724 | fn __action1< 1725 | 'input, 1726 | >( 1727 | input: &'input str, 1728 | (_, __0, _): (usize, Expr, usize), 1729 | ) -> Expr 1730 | { 1731 | __0 1732 | } 1733 | 1734 | #[allow(unused_variables)] 1735 | fn __action2< 1736 | 'input, 1737 | >( 1738 | input: &'input str, 1739 | (_, a, _): (usize, Expr, usize), 1740 | (_, op, _): (usize, &'static str, usize), 1741 | (_, b, _): (usize, Expr, usize), 1742 | ) -> Expr 1743 | { 1744 | Expr::Fn(op.into(), vec![a, b]) 1745 | } 1746 | 1747 | #[allow(unused_variables)] 1748 | fn __action3< 1749 | 'input, 1750 | >( 1751 | input: &'input str, 1752 | (_, __0, _): (usize, Expr, usize), 1753 | ) -> Expr 1754 | { 1755 | __0 1756 | } 1757 | 1758 | #[allow(unused_variables)] 1759 | fn __action4< 1760 | 'input, 1761 | >( 1762 | input: &'input str, 1763 | (_, __0, _): (usize, &'input str, usize), 1764 | ) -> &'static str 1765 | { 1766 | "union" 1767 | } 1768 | 1769 | #[allow(unused_variables)] 1770 | fn __action5< 1771 | 'input, 1772 | >( 1773 | input: &'input str, 1774 | (_, __0, _): (usize, &'input str, usize), 1775 | ) -> &'static str 1776 | { 1777 | "union" 1778 | } 1779 | 1780 | #[allow(unused_variables)] 1781 | fn __action6< 1782 | 'input, 1783 | >( 1784 | input: &'input str, 1785 | (_, __0, _): (usize, &'input str, usize), 1786 | ) -> &'static str 1787 | { 1788 | "union" 1789 | } 1790 | 1791 | #[allow(unused_variables)] 1792 | fn __action7< 1793 | 'input, 1794 | >( 1795 | input: &'input str, 1796 | (_, a, _): (usize, Expr, usize), 1797 | (_, op, _): (usize, &'static str, usize), 1798 | (_, b, _): (usize, Expr, usize), 1799 | ) -> Expr 1800 | { 1801 | Expr::Fn(op.into(), vec![a, b]) 1802 | } 1803 | 1804 | #[allow(unused_variables)] 1805 | fn __action8< 1806 | 'input, 1807 | >( 1808 | input: &'input str, 1809 | (_, __0, _): (usize, Expr, usize), 1810 | ) -> Expr 1811 | { 1812 | __0 1813 | } 1814 | 1815 | #[allow(unused_variables)] 1816 | fn __action9< 1817 | 'input, 1818 | >( 1819 | input: &'input str, 1820 | (_, __0, _): (usize, &'input str, usize), 1821 | ) -> &'static str 1822 | { 1823 | "intersection" 1824 | } 1825 | 1826 | #[allow(unused_variables)] 1827 | fn __action10< 1828 | 'input, 1829 | >( 1830 | input: &'input str, 1831 | (_, __0, _): (usize, &'input str, usize), 1832 | ) -> &'static str 1833 | { 1834 | "intersection" 1835 | } 1836 | 1837 | #[allow(unused_variables)] 1838 | fn __action11< 1839 | 'input, 1840 | >( 1841 | input: &'input str, 1842 | (_, __0, _): (usize, &'input str, usize), 1843 | ) -> &'static str 1844 | { 1845 | "difference" 1846 | } 1847 | 1848 | #[allow(unused_variables)] 1849 | fn __action12< 1850 | 'input, 1851 | >( 1852 | input: &'input str, 1853 | (_, __0, _): (usize, &'input str, usize), 1854 | ) -> &'static str 1855 | { 1856 | "only" 1857 | } 1858 | 1859 | #[allow(unused_variables)] 1860 | fn __action13< 1861 | 'input, 1862 | >( 1863 | input: &'input str, 1864 | (_, a, _): (usize, Expr, usize), 1865 | (_, op, _): (usize, &'static str, usize), 1866 | (_, b, _): (usize, Expr, usize), 1867 | ) -> Expr 1868 | { 1869 | Expr::Fn(op.into(), vec![a, b]) 1870 | } 1871 | 1872 | #[allow(unused_variables)] 1873 | fn __action14< 1874 | 'input, 1875 | >( 1876 | input: &'input str, 1877 | (_, __0, _): (usize, Expr, usize), 1878 | ) -> Expr 1879 | { 1880 | __0 1881 | } 1882 | 1883 | #[allow(unused_variables)] 1884 | fn __action15< 1885 | 'input, 1886 | >( 1887 | input: &'input str, 1888 | (_, __0, _): (usize, &'input str, usize), 1889 | ) -> &'static str 1890 | { 1891 | "range" 1892 | } 1893 | 1894 | #[allow(unused_variables)] 1895 | fn __action16< 1896 | 'input, 1897 | >( 1898 | input: &'input str, 1899 | (_, __0, _): (usize, &'input str, usize), 1900 | ) -> &'static str 1901 | { 1902 | "range" 1903 | } 1904 | 1905 | #[allow(unused_variables)] 1906 | fn __action17< 1907 | 'input, 1908 | >( 1909 | input: &'input str, 1910 | (_, pre, _): (usize, ::std::vec::Vec<&'static str>, usize), 1911 | (_, e, _): (usize, Expr, usize), 1912 | ) -> Expr 1913 | { 1914 | { 1915 | let mut e = e; 1916 | for v in pre { e = Expr::Fn(v.into(), vec![e]); } 1917 | e 1918 | } 1919 | } 1920 | 1921 | #[allow(unused_variables)] 1922 | fn __action18< 1923 | 'input, 1924 | >( 1925 | input: &'input str, 1926 | (_, e, _): (usize, Expr, usize), 1927 | (_, post, _): (usize, ::std::vec::Vec<&'static str>, usize), 1928 | ) -> Expr 1929 | { 1930 | { 1931 | let mut e = e; 1932 | for v in post { e = Expr::Fn(v.into(), vec![e]); } 1933 | e 1934 | } 1935 | } 1936 | 1937 | #[allow(unused_variables)] 1938 | fn __action19< 1939 | 'input, 1940 | >( 1941 | input: &'input str, 1942 | (_, __0, _): (usize, String, usize), 1943 | ) -> Expr 1944 | { 1945 | Expr::Name(__0) 1946 | } 1947 | 1948 | #[allow(unused_variables)] 1949 | fn __action20< 1950 | 'input, 1951 | >( 1952 | input: &'input str, 1953 | (_, f, _): (usize, String, usize), 1954 | (_, _, _): (usize, &'input str, usize), 1955 | (_, args, _): (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 1956 | (_, last, _): (usize, ::std::option::Option, usize), 1957 | (_, _, _): (usize, &'input str, usize), 1958 | ) -> Expr 1959 | { 1960 | { 1961 | // Function call. 1962 | let mut arg_list: Vec = args.into_iter().map(|(e, _)| e).collect(); 1963 | if let Some(last_arg) = last { arg_list.push(last_arg); } 1964 | Expr::Fn(f.into(), arg_list) 1965 | } 1966 | } 1967 | 1968 | #[allow(unused_variables)] 1969 | fn __action21< 1970 | 'input, 1971 | >( 1972 | input: &'input str, 1973 | (_, _, _): (usize, &'input str, usize), 1974 | (_, __0, _): (usize, Expr, usize), 1975 | (_, _, _): (usize, &'input str, usize), 1976 | ) -> Expr 1977 | { 1978 | __0 1979 | } 1980 | 1981 | #[allow(unused_variables)] 1982 | fn __action22< 1983 | 'input, 1984 | >( 1985 | input: &'input str, 1986 | (_, __0, _): (usize, &'input str, usize), 1987 | ) -> &'static str 1988 | { 1989 | "negate" 1990 | } 1991 | 1992 | #[allow(unused_variables)] 1993 | fn __action23< 1994 | 'input, 1995 | >( 1996 | input: &'input str, 1997 | (_, __0, _): (usize, &'input str, usize), 1998 | ) -> &'static str 1999 | { 2000 | "negate" 2001 | } 2002 | 2003 | #[allow(unused_variables)] 2004 | fn __action24< 2005 | 'input, 2006 | >( 2007 | input: &'input str, 2008 | (_, __0, _): (usize, &'input str, usize), 2009 | ) -> &'static str 2010 | { 2011 | "ancestors" 2012 | } 2013 | 2014 | #[allow(unused_variables)] 2015 | fn __action25< 2016 | 'input, 2017 | >( 2018 | input: &'input str, 2019 | (_, __0, _): (usize, &'input str, usize), 2020 | ) -> &'static str 2021 | { 2022 | "descendants" 2023 | } 2024 | 2025 | #[allow(unused_variables)] 2026 | fn __action26< 2027 | 'input, 2028 | >( 2029 | input: &'input str, 2030 | (_, __0, _): (usize, &'input str, usize), 2031 | ) -> &'static str 2032 | { 2033 | "parents" 2034 | } 2035 | 2036 | #[allow(unused_variables)] 2037 | fn __action27< 2038 | 'input, 2039 | >( 2040 | input: &'input str, 2041 | (_, __0, _): (usize, String, usize), 2042 | ) -> String 2043 | { 2044 | __0 2045 | } 2046 | 2047 | #[allow(unused_variables)] 2048 | fn __action28< 2049 | 'input, 2050 | >( 2051 | input: &'input str, 2052 | (_, escaped, _): (usize, &'input str, usize), 2053 | ) -> String 2054 | { 2055 | { 2056 | // Escaped string. 2057 | let mut result = String::with_capacity(escaped.len()); 2058 | let mut prev = '_'; 2059 | for ch in escaped[1..escaped.len()-1].chars() { 2060 | match (prev, ch) { 2061 | ('\\', 'n') => result.push('\n'), 2062 | ('\\', _) => result.push(ch), 2063 | (_, '\\') => (), 2064 | (_, _) => result.push(ch), 2065 | } 2066 | prev = ch; 2067 | } 2068 | result 2069 | } 2070 | } 2071 | 2072 | #[allow(unused_variables)] 2073 | fn __action29< 2074 | 'input, 2075 | >( 2076 | input: &'input str, 2077 | (_, __0, _): (usize, &'input str, usize), 2078 | ) -> String 2079 | { 2080 | __0.to_string() 2081 | } 2082 | 2083 | #[allow(unused_variables)] 2084 | fn __action30< 2085 | 'input, 2086 | >( 2087 | input: &'input str, 2088 | (_, __0, _): (usize, Expr, usize), 2089 | ) -> ::std::option::Option 2090 | { 2091 | Some(__0) 2092 | } 2093 | 2094 | #[allow(unused_variables)] 2095 | fn __action31< 2096 | 'input, 2097 | >( 2098 | input: &'input str, 2099 | __lookbehind: &usize, 2100 | __lookahead: &usize, 2101 | ) -> ::std::option::Option 2102 | { 2103 | None 2104 | } 2105 | 2106 | #[allow(unused_variables)] 2107 | fn __action32< 2108 | 'input, 2109 | >( 2110 | input: &'input str, 2111 | __lookbehind: &usize, 2112 | __lookahead: &usize, 2113 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2114 | { 2115 | vec![] 2116 | } 2117 | 2118 | #[allow(unused_variables)] 2119 | fn __action33< 2120 | 'input, 2121 | >( 2122 | input: &'input str, 2123 | (_, v, _): (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2124 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2125 | { 2126 | v 2127 | } 2128 | 2129 | #[allow(unused_variables)] 2130 | fn __action34< 2131 | 'input, 2132 | >( 2133 | input: &'input str, 2134 | (_, __0, _): (usize, Expr, usize), 2135 | (_, __1, _): (usize, &'input str, usize), 2136 | ) -> (Expr, &'input str) 2137 | { 2138 | (__0, __1) 2139 | } 2140 | 2141 | #[allow(unused_variables)] 2142 | fn __action35< 2143 | 'input, 2144 | >( 2145 | input: &'input str, 2146 | __lookbehind: &usize, 2147 | __lookahead: &usize, 2148 | ) -> ::std::vec::Vec<&'static str> 2149 | { 2150 | vec![] 2151 | } 2152 | 2153 | #[allow(unused_variables)] 2154 | fn __action36< 2155 | 'input, 2156 | >( 2157 | input: &'input str, 2158 | (_, v, _): (usize, ::std::vec::Vec<&'static str>, usize), 2159 | ) -> ::std::vec::Vec<&'static str> 2160 | { 2161 | v 2162 | } 2163 | 2164 | #[allow(unused_variables)] 2165 | fn __action37< 2166 | 'input, 2167 | >( 2168 | input: &'input str, 2169 | (_, __0, _): (usize, &'static str, usize), 2170 | ) -> &'static str 2171 | { 2172 | __0 2173 | } 2174 | 2175 | #[allow(unused_variables)] 2176 | fn __action38< 2177 | 'input, 2178 | >( 2179 | input: &'input str, 2180 | __lookbehind: &usize, 2181 | __lookahead: &usize, 2182 | ) -> ::std::vec::Vec<&'static str> 2183 | { 2184 | vec![] 2185 | } 2186 | 2187 | #[allow(unused_variables)] 2188 | fn __action39< 2189 | 'input, 2190 | >( 2191 | input: &'input str, 2192 | (_, v, _): (usize, ::std::vec::Vec<&'static str>, usize), 2193 | ) -> ::std::vec::Vec<&'static str> 2194 | { 2195 | v 2196 | } 2197 | 2198 | #[allow(unused_variables)] 2199 | fn __action40< 2200 | 'input, 2201 | >( 2202 | input: &'input str, 2203 | (_, __0, _): (usize, &'static str, usize), 2204 | ) -> &'static str 2205 | { 2206 | __0 2207 | } 2208 | 2209 | #[allow(unused_variables)] 2210 | fn __action41< 2211 | 'input, 2212 | >( 2213 | input: &'input str, 2214 | (_, __0, _): (usize, &'static str, usize), 2215 | ) -> ::std::vec::Vec<&'static str> 2216 | { 2217 | vec![__0] 2218 | } 2219 | 2220 | #[allow(unused_variables)] 2221 | fn __action42< 2222 | 'input, 2223 | >( 2224 | input: &'input str, 2225 | (_, v, _): (usize, ::std::vec::Vec<&'static str>, usize), 2226 | (_, e, _): (usize, &'static str, usize), 2227 | ) -> ::std::vec::Vec<&'static str> 2228 | { 2229 | { let mut v = v; v.push(e); v } 2230 | } 2231 | 2232 | #[allow(unused_variables)] 2233 | fn __action43< 2234 | 'input, 2235 | >( 2236 | input: &'input str, 2237 | (_, __0, _): (usize, &'static str, usize), 2238 | ) -> ::std::vec::Vec<&'static str> 2239 | { 2240 | vec![__0] 2241 | } 2242 | 2243 | #[allow(unused_variables)] 2244 | fn __action44< 2245 | 'input, 2246 | >( 2247 | input: &'input str, 2248 | (_, v, _): (usize, ::std::vec::Vec<&'static str>, usize), 2249 | (_, e, _): (usize, &'static str, usize), 2250 | ) -> ::std::vec::Vec<&'static str> 2251 | { 2252 | { let mut v = v; v.push(e); v } 2253 | } 2254 | 2255 | #[allow(unused_variables)] 2256 | fn __action45< 2257 | 'input, 2258 | >( 2259 | input: &'input str, 2260 | (_, __0, _): (usize, (Expr, &'input str), usize), 2261 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2262 | { 2263 | vec![__0] 2264 | } 2265 | 2266 | #[allow(unused_variables)] 2267 | fn __action46< 2268 | 'input, 2269 | >( 2270 | input: &'input str, 2271 | (_, v, _): (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2272 | (_, e, _): (usize, (Expr, &'input str), usize), 2273 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2274 | { 2275 | { let mut v = v; v.push(e); v } 2276 | } 2277 | 2278 | #[allow(unused_variables)] 2279 | fn __action47< 2280 | 'input, 2281 | >( 2282 | input: &'input str, 2283 | __0: (usize, &'static str, usize), 2284 | ) -> ::std::vec::Vec<&'static str> 2285 | { 2286 | let __start0 = __0.0.clone(); 2287 | let __end0 = __0.2.clone(); 2288 | let __temp0 = __action37( 2289 | input, 2290 | __0, 2291 | ); 2292 | let __temp0 = (__start0, __temp0, __end0); 2293 | __action43( 2294 | input, 2295 | __temp0, 2296 | ) 2297 | } 2298 | 2299 | #[allow(unused_variables)] 2300 | fn __action48< 2301 | 'input, 2302 | >( 2303 | input: &'input str, 2304 | __0: (usize, ::std::vec::Vec<&'static str>, usize), 2305 | __1: (usize, &'static str, usize), 2306 | ) -> ::std::vec::Vec<&'static str> 2307 | { 2308 | let __start0 = __1.0.clone(); 2309 | let __end0 = __1.2.clone(); 2310 | let __temp0 = __action37( 2311 | input, 2312 | __1, 2313 | ); 2314 | let __temp0 = (__start0, __temp0, __end0); 2315 | __action44( 2316 | input, 2317 | __0, 2318 | __temp0, 2319 | ) 2320 | } 2321 | 2322 | #[allow(unused_variables)] 2323 | fn __action49< 2324 | 'input, 2325 | >( 2326 | input: &'input str, 2327 | __0: (usize, Expr, usize), 2328 | ) -> Expr 2329 | { 2330 | let __start0 = __0.2.clone(); 2331 | let __end0 = __0.2.clone(); 2332 | let __temp0 = __action35( 2333 | input, 2334 | &__start0, 2335 | &__end0, 2336 | ); 2337 | let __temp0 = (__start0, __temp0, __end0); 2338 | __action18( 2339 | input, 2340 | __0, 2341 | __temp0, 2342 | ) 2343 | } 2344 | 2345 | #[allow(unused_variables)] 2346 | fn __action50< 2347 | 'input, 2348 | >( 2349 | input: &'input str, 2350 | __0: (usize, Expr, usize), 2351 | __1: (usize, ::std::vec::Vec<&'static str>, usize), 2352 | ) -> Expr 2353 | { 2354 | let __start0 = __1.0.clone(); 2355 | let __end0 = __1.2.clone(); 2356 | let __temp0 = __action36( 2357 | input, 2358 | __1, 2359 | ); 2360 | let __temp0 = (__start0, __temp0, __end0); 2361 | __action18( 2362 | input, 2363 | __0, 2364 | __temp0, 2365 | ) 2366 | } 2367 | 2368 | #[allow(unused_variables)] 2369 | fn __action51< 2370 | 'input, 2371 | >( 2372 | input: &'input str, 2373 | __0: (usize, &'static str, usize), 2374 | ) -> ::std::vec::Vec<&'static str> 2375 | { 2376 | let __start0 = __0.0.clone(); 2377 | let __end0 = __0.2.clone(); 2378 | let __temp0 = __action40( 2379 | input, 2380 | __0, 2381 | ); 2382 | let __temp0 = (__start0, __temp0, __end0); 2383 | __action41( 2384 | input, 2385 | __temp0, 2386 | ) 2387 | } 2388 | 2389 | #[allow(unused_variables)] 2390 | fn __action52< 2391 | 'input, 2392 | >( 2393 | input: &'input str, 2394 | __0: (usize, ::std::vec::Vec<&'static str>, usize), 2395 | __1: (usize, &'static str, usize), 2396 | ) -> ::std::vec::Vec<&'static str> 2397 | { 2398 | let __start0 = __1.0.clone(); 2399 | let __end0 = __1.2.clone(); 2400 | let __temp0 = __action40( 2401 | input, 2402 | __1, 2403 | ); 2404 | let __temp0 = (__start0, __temp0, __end0); 2405 | __action42( 2406 | input, 2407 | __0, 2408 | __temp0, 2409 | ) 2410 | } 2411 | 2412 | #[allow(unused_variables)] 2413 | fn __action53< 2414 | 'input, 2415 | >( 2416 | input: &'input str, 2417 | __0: (usize, Expr, usize), 2418 | ) -> Expr 2419 | { 2420 | let __start0 = __0.0.clone(); 2421 | let __end0 = __0.0.clone(); 2422 | let __temp0 = __action38( 2423 | input, 2424 | &__start0, 2425 | &__end0, 2426 | ); 2427 | let __temp0 = (__start0, __temp0, __end0); 2428 | __action17( 2429 | input, 2430 | __temp0, 2431 | __0, 2432 | ) 2433 | } 2434 | 2435 | #[allow(unused_variables)] 2436 | fn __action54< 2437 | 'input, 2438 | >( 2439 | input: &'input str, 2440 | __0: (usize, ::std::vec::Vec<&'static str>, usize), 2441 | __1: (usize, Expr, usize), 2442 | ) -> Expr 2443 | { 2444 | let __start0 = __0.0.clone(); 2445 | let __end0 = __0.2.clone(); 2446 | let __temp0 = __action39( 2447 | input, 2448 | __0, 2449 | ); 2450 | let __temp0 = (__start0, __temp0, __end0); 2451 | __action17( 2452 | input, 2453 | __temp0, 2454 | __1, 2455 | ) 2456 | } 2457 | 2458 | #[allow(unused_variables)] 2459 | fn __action55< 2460 | 'input, 2461 | >( 2462 | input: &'input str, 2463 | __0: (usize, Expr, usize), 2464 | __1: (usize, &'input str, usize), 2465 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2466 | { 2467 | let __start0 = __0.0.clone(); 2468 | let __end0 = __1.2.clone(); 2469 | let __temp0 = __action34( 2470 | input, 2471 | __0, 2472 | __1, 2473 | ); 2474 | let __temp0 = (__start0, __temp0, __end0); 2475 | __action45( 2476 | input, 2477 | __temp0, 2478 | ) 2479 | } 2480 | 2481 | #[allow(unused_variables)] 2482 | fn __action56< 2483 | 'input, 2484 | >( 2485 | input: &'input str, 2486 | __0: (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2487 | __1: (usize, Expr, usize), 2488 | __2: (usize, &'input str, usize), 2489 | ) -> ::std::vec::Vec<(Expr, &'input str)> 2490 | { 2491 | let __start0 = __1.0.clone(); 2492 | let __end0 = __2.2.clone(); 2493 | let __temp0 = __action34( 2494 | input, 2495 | __1, 2496 | __2, 2497 | ); 2498 | let __temp0 = (__start0, __temp0, __end0); 2499 | __action46( 2500 | input, 2501 | __0, 2502 | __temp0, 2503 | ) 2504 | } 2505 | 2506 | #[allow(unused_variables)] 2507 | fn __action57< 2508 | 'input, 2509 | >( 2510 | input: &'input str, 2511 | __0: (usize, String, usize), 2512 | __1: (usize, &'input str, usize), 2513 | __2: (usize, ::std::option::Option, usize), 2514 | __3: (usize, &'input str, usize), 2515 | ) -> Expr 2516 | { 2517 | let __start0 = __1.2.clone(); 2518 | let __end0 = __2.0.clone(); 2519 | let __temp0 = __action32( 2520 | input, 2521 | &__start0, 2522 | &__end0, 2523 | ); 2524 | let __temp0 = (__start0, __temp0, __end0); 2525 | __action20( 2526 | input, 2527 | __0, 2528 | __1, 2529 | __temp0, 2530 | __2, 2531 | __3, 2532 | ) 2533 | } 2534 | 2535 | #[allow(unused_variables)] 2536 | fn __action58< 2537 | 'input, 2538 | >( 2539 | input: &'input str, 2540 | __0: (usize, String, usize), 2541 | __1: (usize, &'input str, usize), 2542 | __2: (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2543 | __3: (usize, ::std::option::Option, usize), 2544 | __4: (usize, &'input str, usize), 2545 | ) -> Expr 2546 | { 2547 | let __start0 = __2.0.clone(); 2548 | let __end0 = __2.2.clone(); 2549 | let __temp0 = __action33( 2550 | input, 2551 | __2, 2552 | ); 2553 | let __temp0 = (__start0, __temp0, __end0); 2554 | __action20( 2555 | input, 2556 | __0, 2557 | __1, 2558 | __temp0, 2559 | __3, 2560 | __4, 2561 | ) 2562 | } 2563 | 2564 | #[allow(unused_variables)] 2565 | fn __action59< 2566 | 'input, 2567 | >( 2568 | input: &'input str, 2569 | __0: (usize, String, usize), 2570 | __1: (usize, &'input str, usize), 2571 | __2: (usize, Expr, usize), 2572 | __3: (usize, &'input str, usize), 2573 | ) -> Expr 2574 | { 2575 | let __start0 = __2.0.clone(); 2576 | let __end0 = __2.2.clone(); 2577 | let __temp0 = __action30( 2578 | input, 2579 | __2, 2580 | ); 2581 | let __temp0 = (__start0, __temp0, __end0); 2582 | __action57( 2583 | input, 2584 | __0, 2585 | __1, 2586 | __temp0, 2587 | __3, 2588 | ) 2589 | } 2590 | 2591 | #[allow(unused_variables)] 2592 | fn __action60< 2593 | 'input, 2594 | >( 2595 | input: &'input str, 2596 | __0: (usize, String, usize), 2597 | __1: (usize, &'input str, usize), 2598 | __2: (usize, &'input str, usize), 2599 | ) -> Expr 2600 | { 2601 | let __start0 = __1.2.clone(); 2602 | let __end0 = __2.0.clone(); 2603 | let __temp0 = __action31( 2604 | input, 2605 | &__start0, 2606 | &__end0, 2607 | ); 2608 | let __temp0 = (__start0, __temp0, __end0); 2609 | __action57( 2610 | input, 2611 | __0, 2612 | __1, 2613 | __temp0, 2614 | __2, 2615 | ) 2616 | } 2617 | 2618 | #[allow(unused_variables)] 2619 | fn __action61< 2620 | 'input, 2621 | >( 2622 | input: &'input str, 2623 | __0: (usize, String, usize), 2624 | __1: (usize, &'input str, usize), 2625 | __2: (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2626 | __3: (usize, Expr, usize), 2627 | __4: (usize, &'input str, usize), 2628 | ) -> Expr 2629 | { 2630 | let __start0 = __3.0.clone(); 2631 | let __end0 = __3.2.clone(); 2632 | let __temp0 = __action30( 2633 | input, 2634 | __3, 2635 | ); 2636 | let __temp0 = (__start0, __temp0, __end0); 2637 | __action58( 2638 | input, 2639 | __0, 2640 | __1, 2641 | __2, 2642 | __temp0, 2643 | __4, 2644 | ) 2645 | } 2646 | 2647 | #[allow(unused_variables)] 2648 | fn __action62< 2649 | 'input, 2650 | >( 2651 | input: &'input str, 2652 | __0: (usize, String, usize), 2653 | __1: (usize, &'input str, usize), 2654 | __2: (usize, ::std::vec::Vec<(Expr, &'input str)>, usize), 2655 | __3: (usize, &'input str, usize), 2656 | ) -> Expr 2657 | { 2658 | let __start0 = __2.2.clone(); 2659 | let __end0 = __3.0.clone(); 2660 | let __temp0 = __action31( 2661 | input, 2662 | &__start0, 2663 | &__end0, 2664 | ); 2665 | let __temp0 = (__start0, __temp0, __end0); 2666 | __action58( 2667 | input, 2668 | __0, 2669 | __1, 2670 | __2, 2671 | __temp0, 2672 | __3, 2673 | ) 2674 | } 2675 | 2676 | pub trait __ToTriple<'input, > { 2677 | fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>>; 2678 | } 2679 | 2680 | impl<'input, > __ToTriple<'input, > for (usize, Token<'input>, usize) { 2681 | fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { 2682 | Ok(value) 2683 | } 2684 | } 2685 | impl<'input, > __ToTriple<'input, > for Result<(usize, Token<'input>, usize), &'static str> { 2686 | fn to_triple(value: Self) -> Result<(usize,Token<'input>,usize), __lalrpop_util::ParseError, &'static str>> { 2687 | match value { 2688 | Ok(v) => Ok(v), 2689 | Err(error) => Err(__lalrpop_util::ParseError::User { error }), 2690 | } 2691 | } 2692 | } 2693 | -------------------------------------------------------------------------------- /src/repo.rs: -------------------------------------------------------------------------------- 1 | use crate::ast::Expr; 2 | use crate::ast::ParseToExpr; 3 | use crate::EvalContext; 4 | use crate::Result; 5 | use dag::namedag::MemNameDag; 6 | use dag::DagAlgorithm; 7 | use dag::Set; 8 | use dag::Vertex; 9 | use gitdag::dag; 10 | use gitdag::git2; 11 | use gitdag::GitDag; 12 | use once_cell::sync::OnceCell; 13 | use std::collections::HashMap; 14 | use std::sync::Mutex; 15 | 16 | /// Repo with extra states to support revset queries. 17 | pub struct Repo { 18 | git_repo: Box>, 19 | dag: GitDag, 20 | cached_sets: Mutex>, 21 | cached_mutation_dag: OnceCell, 22 | cached_eval_context: OnceCell, 23 | } 24 | 25 | impl Repo { 26 | /// Open an existing repo. Build indexes on demand. 27 | pub fn open_from_env() -> Result { 28 | let repo = git2::Repository::open_from_env()?; 29 | Self::open_from_repo(Box::new(repo)) 30 | } 31 | 32 | /// Open an existing repo previously opened by libgit2. 33 | /// Build commit graph indexes on demand. 34 | pub fn open_from_repo(git_repo: impl AsRef + 'static) -> Result { 35 | let git_repo_ref = git_repo.as_ref(); 36 | let dag_path = git_repo_ref.path().join("dag"); 37 | let main_branch_name = guess_main_branch_name(git_repo_ref); 38 | let dag = GitDag::open_git_repo(git_repo_ref, &dag_path, &main_branch_name)?; 39 | let cached_sets = Default::default(); 40 | let cached_mutation_dag = Default::default(); 41 | let cached_eval_context = Default::default(); 42 | let result = Repo { 43 | git_repo: Box::new(git_repo), 44 | dag, 45 | cached_sets, 46 | cached_mutation_dag, 47 | cached_eval_context, 48 | }; 49 | 50 | Ok(result) 51 | } 52 | 53 | /// Evaluate the expression. Return the resulting set. 54 | /// User-defined aliases are ignored. 55 | pub fn revs(&self, ast: impl ParseToExpr) -> Result { 56 | self.revs_with_context(ast, &Default::default()) 57 | } 58 | 59 | /// Evaluate the expression. Return the resulting set. 60 | /// User-defined aliases are respected. 61 | /// 62 | /// To define aliases, add a `[revsetalias]` section like: 63 | /// 64 | /// ```plain,ignore 65 | /// [revsetalias] 66 | /// # f(x) can be used, and will be expended to ancestor(x) + x. 67 | /// f = ancestor($1) + $1 68 | /// ``` 69 | pub fn anyrevs(&self, ast: impl ParseToExpr) -> Result { 70 | self.revs_with_context(ast, self.eval_context_from_config()?) 71 | } 72 | 73 | /// Evaluate the expression with the given context. 74 | /// Return the resulting set. 75 | pub fn revs_with_context(&self, ast: impl ParseToExpr, ctx: &EvalContext) -> Result { 76 | let ast = ast.parse_to_expr()?; 77 | crate::eval::eval(self, &ast, ctx) 78 | } 79 | 80 | /// Obtains read-only `dag` reference. 81 | pub fn dag(&self) -> &GitDag { 82 | &self.dag 83 | } 84 | 85 | /// Obtains read-only `git2::Repository` reference. 86 | pub fn git_repo(&self) -> &git2::Repository { 87 | self.git_repo.as_ref().as_ref() 88 | } 89 | 90 | /// Returns a `EvalContext` that contains user-defined alias 91 | /// in the `[revsetalias]` config section. 92 | pub fn eval_context_from_config(&self) -> Result<&EvalContext> { 93 | self.cached_eval_context 94 | .get_or_try_init(|| parse_eval_context(self.git_repo())) 95 | } 96 | 97 | pub(crate) fn cached_set( 98 | &self, 99 | name: &'static str, 100 | func: impl Fn(&Repo) -> Result, 101 | ) -> Result { 102 | if let Some(set) = self.cached_sets.lock().unwrap().get(name) { 103 | return Ok(set.clone()); 104 | } 105 | match func(self) { 106 | Err(e) => Err(e), 107 | Ok(set) => { 108 | self.cached_sets.lock().unwrap().insert(name, set.clone()); 109 | Ok(set) 110 | } 111 | } 112 | } 113 | 114 | pub(crate) fn to_set(&self, iter: impl IntoIterator) -> Result { 115 | Ok(self.dag.sort(&Set::from_static_names(iter.into_iter()))?) 116 | } 117 | 118 | pub(crate) fn mutation_dag(&self) -> Result<&MemNameDag> { 119 | self.cached_mutation_dag 120 | .get_or_try_init(|| crate::mutation::infer_mutation_from_reflog(self)) 121 | } 122 | } 123 | 124 | fn guess_main_branch_name(repo: &git2::Repository) -> String { 125 | if let Ok(config) = repo.config() { 126 | if let Ok(s) = config.get_str("revs.main-branch") { 127 | return s.to_string(); 128 | } 129 | } 130 | let candidates = [ 131 | "refs/remotes/origin/master", 132 | "refs/remotes/origin/main", 133 | "refs/remotes/upstream/master", 134 | "refs/remotes/upstream/main", 135 | ]; 136 | candidates 137 | .iter() 138 | .cloned() 139 | .find(|name| repo.refname_to_id(name).is_ok()) 140 | .unwrap_or(candidates[0]) 141 | .to_string() 142 | } 143 | 144 | fn parse_eval_context(repo: &git2::Repository) -> Result { 145 | let mut result = EvalContext::default(); 146 | let config = repo.config()?; 147 | for entry in &config.entries(Some("revsetalias.*"))? { 148 | let entry = entry?; 149 | if let (Some(name), Some(value)) = (entry.name(), entry.value()) { 150 | if let Some(name) = name.get("revsetalias.".len()..) { 151 | if let Ok(ast) = value.parse_to_expr() { 152 | let func = move |_name: &str, 153 | repo: &Repo, 154 | args: &[Expr], 155 | ctx: &EvalContext| 156 | -> Result { 157 | // Replace arguments in ast, ex. $1 -> args[0], ... 158 | let mut ast = ast.clone(); 159 | for (i, arg) in args.iter().enumerate() { 160 | ast.replace(&format!("${}", i + 1), arg); 161 | } 162 | repo.revs_with_context(ast, ctx) 163 | }; 164 | result.fns.insert(name.to_string(), Box::new(func)); 165 | } 166 | } 167 | } 168 | } 169 | Ok(result) 170 | } 171 | -------------------------------------------------------------------------------- /src/testrepo.rs: -------------------------------------------------------------------------------- 1 | use crate::git2; 2 | use crate::Repo; 3 | use crate::SetExt; 4 | use git2::Oid; 5 | use gitdag::dag::Set; 6 | use std::ops::Deref; 7 | 8 | /// Repo for testing purpose. 9 | pub struct TestRepo { 10 | dir: tempfile::TempDir, 11 | repo: Repo, 12 | } 13 | 14 | impl TestRepo { 15 | /// Create a test repo. 16 | pub fn new() -> Self { 17 | let dir = tempfile::tempdir().unwrap(); 18 | let git_repo = git2::Repository::init(&dir.path()).unwrap(); 19 | let repo = Repo::open_from_repo(Box::new(git_repo)).unwrap(); 20 | Self { dir, repo } 21 | } 22 | 23 | /// Add commits using the ASCII graph. 24 | pub fn drawdag(&mut self, ascii: &str) { 25 | let repo = self.repo.git_repo(); 26 | let mut epoch = 0; 27 | drawdag::drawdag( 28 | ascii, 29 | |name: String, parents: Vec>| -> Box<[u8]> { 30 | let parents: Vec<_> = parents 31 | .into_iter() 32 | .map(|s| { 33 | let oid = git2::Oid::from_bytes(&s).unwrap(); 34 | repo.find_commit(oid).unwrap() 35 | }) 36 | .collect(); 37 | let parent_refs: Vec<_> = parents.iter().collect(); 38 | let time = git2::Time::new(epoch, 0); 39 | epoch += 1; 40 | let sig = git2::Signature::new(&name, "test@example.com", &time).unwrap(); 41 | let mut tree_builder = repo.treebuilder(None).unwrap(); 42 | let blob_oid = repo.blob(name.as_bytes()).unwrap(); 43 | tree_builder.insert(&name, blob_oid, 0o100644).unwrap(); 44 | let tree_id = tree_builder.write().unwrap(); 45 | let tree = repo.find_tree(tree_id).unwrap(); 46 | let commit_id = repo 47 | .commit(None, &sig, &sig, &name, &tree, &parent_refs) 48 | .unwrap(); 49 | repo.reference(&format!("refs/heads/{}", &name), commit_id, true, "commit") 50 | .unwrap(); 51 | commit_id.as_bytes().to_vec().into_boxed_slice() 52 | }, 53 | ); 54 | self.reload(); 55 | } 56 | 57 | /// Run revset query. Return commit messages. 58 | pub fn query(&self, code: &str) -> Vec { 59 | self.desc_set(&self.revs(code).unwrap()) 60 | } 61 | 62 | /// Run revset query with aliases. Return commit messages. 63 | pub fn query_with_alias_config(&self, code: &str) -> Vec { 64 | self.desc_set(&self.anyrevs(code).unwrap()) 65 | } 66 | 67 | /// Set -> Commit messages. 68 | pub fn desc_set(&self, set: &Set) -> Vec { 69 | let mut result = Vec::new(); 70 | for oid in set.to_oids().unwrap() { 71 | let oid = oid.unwrap(); 72 | let commit = self.git_repo().find_commit(oid).unwrap(); 73 | let message = commit.message().unwrap(); 74 | result.push(message.to_string()); 75 | } 76 | result 77 | } 78 | 79 | /// Run revset query. Resolve to a single commit `Oid`. 80 | pub fn query_single_oid(&self, code: &str) -> Oid { 81 | self.revs(code) 82 | .unwrap() 83 | .to_oids() 84 | .unwrap() 85 | .next() 86 | .unwrap() 87 | .unwrap() 88 | } 89 | 90 | /// Add a reference. 91 | pub fn add_ref(&mut self, name: &str, oid: Oid) { 92 | let dir = self.repo.git_repo().path(); 93 | let git_repo = git2::Repository::init(dir).unwrap(); 94 | git_repo.reference(name, oid, true, "add_ref").unwrap(); 95 | self.reload(); 96 | } 97 | 98 | /// Make "commit (amend)" change to a reference. 99 | pub fn amend(&mut self, ref_name: &str) { 100 | let dir = self.repo.git_repo().path(); 101 | let git_repo = git2::Repository::init(dir).unwrap(); 102 | let oid = git_repo.refname_to_id(ref_name).unwrap(); 103 | let commit = git_repo.find_commit(oid).unwrap(); 104 | let msg = commit.message().unwrap(); 105 | let new_msg = format!("{}_new", msg); 106 | let new_oid = commit 107 | .amend(None, None, None, None, Some(&new_msg), None) 108 | .unwrap(); 109 | git_repo 110 | .reference(ref_name, new_oid, true, "commit (amend): amend") 111 | .unwrap(); 112 | let old_ref_name = format!("{}_old", ref_name); 113 | git_repo 114 | .reference(&old_ref_name, oid, true, "before amend") 115 | .unwrap(); 116 | self.reload(); 117 | } 118 | 119 | /// Set git configs. 120 | pub fn set_config(&mut self, name: &str, value: &str) { 121 | let git_repo = self.git_repo(); 122 | let mut config = git_repo.config().unwrap(); 123 | config.set_str(name, value).unwrap(); 124 | } 125 | 126 | /// Update environment variable so open_from_env will open this repo. 127 | pub fn set_env(&self) { 128 | std::env::set_var("GIT_DIR", self.repo.git_repo().path()); 129 | } 130 | 131 | /// Reload the test repo. Pick up changes made via the git2 repo. 132 | pub fn reload(&mut self) { 133 | let dir = self.repo.git_repo().path(); 134 | let git_repo = git2::Repository::init(dir).unwrap(); 135 | let repo = Repo::open_from_repo(Box::new(git_repo)).unwrap(); 136 | self.repo = repo; 137 | } 138 | } 139 | 140 | impl Deref for TestRepo { 141 | type Target = Repo; 142 | fn deref(&self) -> &Repo { 143 | &self.repo 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::ext::OidExt; 2 | use crate::testrepo::TestRepo; 3 | use gitdag::dag::Set; 4 | 5 | #[test] 6 | fn test_revset_functions() { 7 | let mut repo = TestRepo::new(); 8 | repo.drawdag( 9 | r#" 10 | A---B---C---D---E 11 | \ / 12 | F---G---H---I 13 | "#, 14 | ); 15 | 16 | // Basic set operations. 17 | assert_eq!( 18 | repo.query("all()"), 19 | ["I", "H", "E", "D", "G", "F", "C", "B", "A"] 20 | ); 21 | assert_eq!(repo.query("heads(all())"), ["I", "E"]); 22 | assert_eq!(repo.query("heads(A:C + G:H)"), ["H", "C"]); 23 | assert_eq!(repo.query("roots(all())"), ["A"]); 24 | assert_eq!(repo.query("roots(A:C + G:H)"), ["G", "A"]); 25 | assert_eq!(repo.query("B:D"), ["D", "G", "F", "C", "B"]); 26 | assert_eq!(repo.query("A:E - G - (C^ + C)^"), ["E", "D", "F", "C"]); 27 | assert_eq!(repo.query("!!!(B:D)"), ["I", "H", "E", "A"]); 28 | assert_eq!(repo.query("::B + G::"), ["I", "H", "E", "D", "G", "B", "A"]); 29 | assert_eq!(repo.query("H % E"), ["H"]); 30 | assert_eq!(repo.query("H % C"), ["H", "G", "F"]); 31 | assert_eq!(repo.query("gca(E+H)"), ["G"]); 32 | assert_eq!(repo.query("gca(E,H)"), ["G"]); 33 | assert_eq!(repo.query("first(A:D)"), ["D"]); 34 | assert_eq!(repo.query("first(B-B,C,D)"), ["C"]); 35 | assert_eq!(repo.query("first(B-B,C+D)"), ["D"]); 36 | assert_eq!(repo.query("last(A:D)"), ["A"]); 37 | assert_eq!( 38 | repo.query("children(G) | children(A:B)"), 39 | ["H", "D", "F", "C", "B"] 40 | ); 41 | assert_eq!(repo.query("head()"), ["I", "E"]); 42 | assert_eq!(repo.query("desc(C)"), ["C"]); 43 | assert_eq!(repo.query("author(D)"), ["D"]); 44 | assert_eq!(repo.query("heads(author(test))"), ["I", "E"]); 45 | assert_eq!(repo.query("committer(E)"), ["E"]); 46 | assert_eq!(repo.query("heads(committer(test))"), ["I", "E"]); 47 | assert_eq!(repo.query("modifies(B)"), ["B"]); 48 | 49 | // date(), committerdate() 50 | assert_eq!(repo.query(r#"date("0 0")"#), ["B", "A"]); 51 | assert_eq!(repo.query(r#"date("0 0 to 1 0")"#), ["C", "B", "A"]); 52 | assert_eq!( 53 | repo.query(r#"committerdate("before 2 0")"#), 54 | ["C", "B", "A"] 55 | ); 56 | assert_eq!(repo.query(r#"committerdate("since 6 0")"#), ["I", "E", "D"]); 57 | 58 | // public(), draft() 59 | repo.add_ref("refs/heads/master", repo.query_single_oid("E")); 60 | repo.add_ref("refs/remotes/origin/master", repo.query_single_oid("D")); 61 | repo.add_ref("refs/remotes/origin/stable", repo.query_single_oid("B")); 62 | repo.add_ref("refs/tags/v1", repo.query_single_oid("A")); 63 | repo.add_ref("refs/tags/v2", repo.query_single_oid("B")); 64 | 65 | assert_eq!(repo.query("origin/master"), ["D"]); 66 | assert_eq!(repo.query("draft()"), ["E", "I", "H"]); 67 | assert_eq!(repo.query("public()"), ["D", "G", "F", "C", "B", "A"]); 68 | assert_eq!(repo.query("drafthead()"), ["E", "I"]); 69 | assert_eq!(repo.query("publichead()"), ["D", "B"]); 70 | 71 | // id(), ref(), tag(), "." 72 | for name in repo.query("all()") { 73 | let rev_code = format!("id({})", repo.query_single_oid(&name).to_vertex().to_hex()); 74 | assert_eq!(repo.query(&rev_code), [name.clone()]); 75 | } 76 | assert_eq!( 77 | repo.query("ref()"), 78 | ["E", "I", "H", "D", "G", "F", "C", "B", "A"] 79 | ); 80 | assert_eq!(repo.query("ref(origin/master)"), ["D"]); 81 | assert_eq!(repo.query(r#"ref("remotes/origin/*")"#), ["D", "B"]); 82 | assert_eq!(repo.query("."), ["E"]); 83 | assert_eq!(repo.query("tag()"), ["B", "A"]); 84 | assert_eq!(repo.query("tag(v2)"), ["B"]); 85 | assert_eq!(repo.query(r#"tag("v*")"#), ["B", "A"]); 86 | 87 | // empty(), present() 88 | assert!(repo.query("none()").is_empty()); 89 | assert!(repo.query("present(foobar)").is_empty()); 90 | assert_eq!(repo.query("present(master)"), ["E"]); 91 | 92 | // predecessors(), successors() 93 | repo.amend("refs/heads/H"); 94 | assert_eq!(repo.query("H"), ["H_new"]); 95 | assert_eq!(repo.query("H_old"), ["H"]); 96 | assert_eq!(repo.query("predecessors(H)"), ["H_new", "H"]); 97 | assert_eq!(repo.query("successors(H_old)"), ["H_new", "H"]); 98 | assert_eq!(repo.query("obsolete()"), ["H"]); 99 | 100 | // apply 101 | assert_eq!(repo.query("apply($1, .)"), ["E"]); 102 | assert_eq!(repo.query("apply($1 + $2^, ., B)"), ["E", "A"]); 103 | assert_eq!(repo.query("apply(apply($1, C) + $1, A)"), ["C", "A"]); 104 | } 105 | 106 | #[test] 107 | fn test_revset_alias_config() { 108 | let mut repo = TestRepo::new(); 109 | repo.drawdag("A--B--C"); 110 | repo.set_config("revsetalias.t", "B"); 111 | repo.set_config("revsetalias.f", "$1^^ + $1"); 112 | repo.set_config("revsetalias.g", "apply(f($1), children($1))"); 113 | 114 | assert_eq!(repo.query_with_alias_config("t"), ["B"]); 115 | assert_eq!(repo.query_with_alias_config("t()"), ["B"]); 116 | assert_eq!(repo.query_with_alias_config("f(C)"), ["C", "A"]); 117 | assert_eq!(repo.query_with_alias_config("f(t())"), ["B"]); 118 | assert_eq!(repo.query_with_alias_config("g(t)"), ["C", "A"]); 119 | } 120 | 121 | #[test] 122 | fn test_ext() { 123 | use crate::ext::OidExt; 124 | use crate::ext::OidIterExt; 125 | use crate::ext::SetExt; 126 | use crate::ext::VertexExt; 127 | use gitdag::git2::Oid; 128 | 129 | let oid = Oid::zero(); 130 | let v = oid.to_vertex(); 131 | assert_eq!(oid, v.to_oid().unwrap()); 132 | 133 | let oid2 = { 134 | let mut bytes = oid.as_bytes().to_vec(); 135 | bytes[0] ^= 1; 136 | Oid::from_bytes(&bytes).unwrap() 137 | }; 138 | let oid_list = [oid, oid2]; 139 | let set = oid_list.to_vec().to_set(); 140 | assert_eq!( 141 | oid_list.to_vec(), 142 | set.to_oids() 143 | .unwrap() 144 | .collect::>>() 145 | .unwrap() 146 | ); 147 | } 148 | 149 | #[test] 150 | fn test_ast_macro() { 151 | use crate::ast; 152 | let f = |e| format!("{:?}", e); 153 | assert_eq!(f(ast!("foo")), "foo"); 154 | assert_eq!(f(ast!(parents("foo"))), "parents(foo)"); 155 | assert_eq!(f(ast!(draft())), "draft()"); 156 | assert_eq!( 157 | f(ast!(union(draft(), public()))), 158 | "union(draft(), public())" 159 | ); 160 | 161 | let name = "foo"; 162 | assert_eq!( 163 | f(ast!(union(desc({ name }), author({ "bar" })))), 164 | "union(desc(foo), author(bar))" 165 | ); 166 | 167 | let set = Set::from_static_names(vec!["A".into(), "B".into()]); 168 | assert_eq!(f(ast!(parents({ set }))), "parents()") 169 | } 170 | 171 | #[test] 172 | fn test_ast_repo() -> crate::Result<()> { 173 | use crate::ast; 174 | let mut repo = TestRepo::new(); 175 | repo.drawdag("A-B-C-D"); 176 | repo.add_ref("refs/heads/master", repo.query_single_oid("D")); 177 | repo.add_ref("refs/remotes/origin/master", repo.query_single_oid("B")); 178 | 179 | let master = "origin/master"; 180 | let stack = repo.revs(ast!(only(".", ref({ master })))).unwrap(); 181 | assert_eq!(repo.desc_set(&stack), ["D", "C"]); 182 | let head = repo.revs(ast!(heads({ stack }))).unwrap(); 183 | assert_eq!(repo.desc_set(&head), ["D"]); 184 | Ok(()) 185 | } 186 | --------------------------------------------------------------------------------