diff options
Diffstat (limited to 'third_party/rust/glsl')
20 files changed, 10640 insertions, 0 deletions
diff --git a/third_party/rust/glsl/.cargo-checksum.json b/third_party/rust/glsl/.cargo-checksum.json new file mode 100644 index 0000000000..b9768e63f7 --- /dev/null +++ b/third_party/rust/glsl/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"CHANGELOG.md":"668a1459ea4feb4099a2d357ea3ba4f79883f17263434ff6b62f7252e85eb48a","Cargo.toml":"b5827f1e73a80ebe684853fca3e4573d4fd0267d0b4b4c7abee5015bc628b1a9","LICENSE":"09719a44ba10012b20e7a36e7f2e7806a89476813668b66067e0e5dbcc9ce34c","README.md":"56863e436aadfd5e66bacb46f0d78da148dc47a4362bb233b433befe84898793","data/tests/buffer_block_0.glsl":"335b2b5360635eb0a1c994e44a33787d43ea6ce84da4c0a795e7526da64b37bd","data/tests/layout_buffer_block_0.glsl":"d64c27188cd27b7fc81413e8afe621d6f44ddcdeddb115d62661358b2131e8a3","src/lib.rs":"6dbcee8beb9a09a2b49bf648ab949be24066ba27f54e3d01fad57c7cee3edc28","src/parse_tests.rs":"1ddcffb3eeb5cbf473194633a042b7833b40629745a55d35e2ff0a7578d11870","src/parser.rs":"1fd9a6d2110fb25e5ccf038354b1215b1fd129bbf68c37c1b52e8ac6c154704b","src/parsers.rs":"0ba6f11633bde917347655cd4410ed15c59f78d257f29bdfde5c0baac5f18c93","src/parsers/nom_helpers.rs":"1773e52b4f2625bb669c935bcb36dbacf73d992e2954d85f0eda12cb5c2e0e35","src/syntax.rs":"56f4cc3f93657fdb64b9fb400ef38a9beef793d0d2fe71337d998393f816a439","src/transpiler/glsl.rs":"e3b44be9b10c19ac2a4ad50e1ec81d60d19551e626e8c443d033f229c24ac97a","src/transpiler/mod.rs":"244cc5d9ab4c6e6ac4a30d42df81f4718b79fe653cc04e138071ae5905aa9e28","src/transpiler/spirv.rs":"8c777f0f5717a3e269f9062b187da3e66706705a81a392f6aeea0630ed498deb","src/visitor.rs":"ba02f44cb840bc8aa5c4a384da708805df21de602f091d0702f9738f6527b2e4","tests/incorrect_statement.rs":"6b182e705421450d016d6a078cf8fcc0b748bf6208dfbf2fd3474467261241fd","tests/left_associativity.rs":"29790670aa699e0e1a98bcb4464e642c35bcf92ecfa5da712550cdc9ac9b8ead","tests/missing_zero_float.rs":"1bb2fa7c55354ecec8fb5e82b1cb821b052c2047c32edc030ed0e529d77af3d2"},"package":"65c80dbf169ac31dbe6e0a69a7cef0b09ec9805f955da206ff1ee2e47895f836"}
\ No newline at end of file diff --git a/third_party/rust/glsl/CHANGELOG.md b/third_party/rust/glsl/CHANGELOG.md new file mode 100644 index 0000000000..6b087e06b0 --- /dev/null +++ b/third_party/rust/glsl/CHANGELOG.md @@ -0,0 +1,465 @@ +# Changelog + +# 6.0.2 + +> Mar 23, 2023 + +- Switch to `nom-7`. + +# 6.0.1 + +> Jul 11, 2021 + +- Better scheme for parenthesis generation in the GLSL transpiler. +- Fix matrices types in the GLSL transpiler. +- Fix end-of-line parser, which now accepts CR LF too. + +# 6.0 + +> Dec 7th, 2020 + +- Change the meaning of `Visitor` and `Host`. They now take the AST nodes by simple references (`&`) and not via + mutable references (`&mut`) anymore. This will allow people to use visitors in much more contexts. +- Add `VisitorMut` and `HostMut` to visit AST nodes mutably. These correspond to the previous (version `<6.0`) `Visitor` + and `Host` traits. If you were using them and require mutability, feel free to simply switch to `VisitorMut` and + `HostMut`. + +# 5.0.2 + +> Nov 1st, 2020 + +- Bump `nom-6.0`. + +# 5.0.1 + +> Aug 12th, 2020 + +- Fix float / double literal parsing priority. Floats are parsed first now in case of a polymorphic + constant. + +# 5.0.0 + +> Jul 27th, 2020 + +- Fix array specifiers by splitting the `ArraySpecifier` type into two types: + - `ArraySpecifier`, which holds a non-empty list of `ArraySpecifierDimension`. + - `ArraySpecifierDimension`, which is the “old” `ArraySpecifier`. + This change allows for multidimensional array specifiers. + +## Migration guide + +- If you were using array specifiers, you need to wrap them inside an `ArraySpecifierDimension` + and wrap it in a `ArraySpecifier { dimensions: NonEmpty(vec![here]) }` expression, where `here` + is your specifier. + +# 4.1.1 + +> Wed Jul 1st 2020 + +- Fix the _identifier_ parser, that previously failed to parse identifiers starting with an + underscore (`_`) while it is permitted (chapter 3.7 of the GLSLang spec). +- Fix associativity for logical binary operators (`||`, `&&`). + +# 4.1 + +> Fri Jun 19th 2020 + +- Implement `std::error::Error` for error types. + +# 4.0.3 + +> Fri Mar 6th 2020 + +- Move all the unit tests code into their ow module to prevent rustc from parsing them if we + don’t build with tests. + +# 4.0.2 + +> Mon 10th Feb 2020 + +- Remove the `lexical` feature from `nom`. It’s useful only when using functions such as the + `float` combinator, which we don’t use. + +# 4.0.1 + +> Tue 21st Jan 2020 + +- Fix a bug occurring in the function-like `#define` parser (i.e. `#define foo(x) (y)`) that + would prevent the use of whitespaces in the argument list. + +# 4.0 + +> Mon 6th Jan 2020 + +## Major changes + +- Add support for `attribute` and `varying` keyword as a backward-compatible parser. +- Fix binary operator associativity. They were (_erroneously_) right-associative. They’re now + parsed as left-associative. + +# 3.0 + +> Wed 14th Nov 2019 + +## Major changes + +- `JumpStatement::Return` now takes an optional `Expr` instead of an `Expr` directly. That allows + for parsing statements such as `return;`, which should have + been supported from the beginning. +- Support for missing preprocessor directives: + - `#if`. + - `#ifdef`. + - `#ifndef`. + - `#elseif`. + - `#else`. + - `#endif`. + - `#error`. + - `#include`. + - `#line`. + - `#pragma`. + - `#undef`. + +## Patch changes + +- Add a `rustfmt.toml` to reformat everything and stay consistent. If you want contribute, ensure + you are correctly using `rustfmt` (either by running `cargo fmt` before submitting a PR or by + configuring your editor to format the code for you). This is important, as not respecting the + formatting would make your contribution impossible to merge due to a CI check. +- Support for _multiline_ annotations (`\`). Multiline annotations are currently supported as + part of _spaces_ — i.e. you cannot use them to break an identifier on two lines for instance. + This choice makes it faster to parse without too much compromises (after all, `\` is mostly used + in CPP directives in GLSL). +- Fix a bug with _recoverable parsers_ that would produce parent parsers to ignore GLSL grammar + errors. That fix also implies a boost in performance due to short-circuiting optimizations. + +# 2.0.1 + +> Fri 8th Nov 2019 + +- Improve performance of expression parsers. + +# 2.0 + +> Thu 24th Oct 2019 + +## Major changes + +- Add `ShaderKind::Compute`. +- Remove `NonEmpty::from_iter` and `TranslationUnit::from_iter` as they were deprecated. Use + `*::from_non_empty_iter` instead. + +## Patch changes + +- Fix tokenizer of `Block`. +- Fix a bug while parsing floating-point numbers. +- Reformat with `rustfmt`. + +# 1.2 + +> Wed 18th Sep 2019 + +## Deprecations + +- `NonEmpty::from_iter` and `TranslationUnit::from_iter` are deprecated in favor of + `*::from_non_empty_iter`. + +## Minor changes + +- Add binary SPIR-V transpilation. That enables to transpile GLSL directly into a SPIR-V buffer. +- Add `NonEmpty::from_non_empty_iter` and `TranslationUnit::from_non_empty_iter`. + +# 1.1.1 + +> Tue 17th Sep 2019 + +- Update internal code for Rust edition 2018. + +# 1.1 + +> Tue 30th of July 2019 + +- Add the `ShaderStage` type alias to `TranslationUnit`. +- Enhance the front documentation to showcase how to use how to use the crate. + +# 1.0.2 + +> Tue 23rd of July 2019 + +- Change the description of the project and update documentation. + +# 1.0.1 + +> Tue 23rd of July 2019 + +- Change the `external_declaration` parser so that it can also accept _GLSL460_. That should be a + breaking change because now, _GLSL450_ formatted input accepts feature from _GLSL460_, which + shouldn’t be allowed. Nevertheless, the added feature (being able to use semicolons (`;`) on + empty lines at top-level) is not really an interesting property and no breakage should happen. + +# 1.0 + +> Thu 18th of July 2019 + +- Migrate all parsers to [nom-5](https://crates.io/crates/nom/5.0.0). +- Improve and add unit and integration tests. +- Improve overall documentation. +- Enhance some allocation scheme (removed them by using more adapted parsers). +- Completely remove the byte (`&[u8]`) parsing. That was a bad idea, for both its impractical + aspect and error removing. Parsing is done on string slices now (`&str`). + +# 0.13.5 + +> Sun 9th of December 2018 + +- Add the SPIR-V transpiler. Currently, it’s feature-gated and *very* experimental. Feel free to + try and provide feedback about it. +- Add simple accessors for `ParseResult`. +- Fix a typo in the documentation of syntax. +- Add some unit and integration tests. +- Add `Identifier::as_str` and `TypeName::as_str` + +# 0.13.4 + +> Wed 25nd of November 2018 + +- Add `NonEmpty::push` and `NonEmpty::pop`. +- Implement `Deref` and `DerefMut` for `TranslationUnit`. + +# 0.13.3 + +> Wed 24nd of November 2018 + +- Add `NonEmpty::from_iter` and `TranslationUnit::from_iter`. +- Implement `IntoIterator` for `NonEmpty` and `TranslationUnit`. + +# 0.13.2 + +> Wed 22nd of November 2018 + +- Fix a typo in documentation. + +# 0.13.1 + +> Wed 22nd of November 2018 + +- Fix a link in documentation. + +# 0.13 + +> Wed 21st of November 2018 + +- Update/reset hyperlinks in all the documentation for types, traits, methods, etc. +- General enhancement of the documentation. +- `ExternalDeclaration::new_struct` can now fail. Check the doc for further details. +- `NonEmpty`, `Identifier` and `TypeName` and `TranslationUnit` are now plain types and not + aliases anymore. +- Add AST visitors. Visitors allow for traversing through an AST and on-the-fly mutation, + filtering, etc. +- The `#define` preprocessor pragma is now supported in a limited form (it can only be used in + the global scope). + +# 0.12 + +> Sun 11th of November 2018 + +- Fix the type of identifier stored in `Block`: it now uses the new encoding for arrayed + identifiers. +- `Block` parsers update for the arrayd identifier change. + +# 0.11 + +> Sat 10th of November 2018 + +- Add helper functions to build objects form the `syntax` module. Those are intended to be a + simple way to build ASTs out of Rust code instead of parsing. +- Enhance the documentation of the `Preprocessor` type. + +# 0.10.1 + +> Fri 2nd of November 2018 + +- Add some missing implementors of `Parse` for types from `glsl::syntax`. + +# 0.10 + +> Fri 2nd of November 2018 + +- Hide the `parsers` module. It’s not exposed anymore as it was mostly + [nom](https://crates.io/crates/nom) parsers and we don’t like leaking internals. +- Introduce the `Parser` trait, acting as an abstraction over the internal parsers. Implementors + provide a type-driven parsing experience that is very similar to the one as + [FromStr](https://doc.rust-lang.org/std/str/trait.FromStr.html). This change is actually + mandatory for the [glsl-quasiquote](https://crates.io/crates/glsl-quasiquote) crate’s `glsl!` + proc-macro to allow people use it for any GLSL item (and not only `TranslationUnit`). +- Enhance the overall documentation. + +# 0.9.2 + +> Wed 3rd of October 2018 + + - Fix GLSL transpiled representation of `IVec*`. It was plain wrong. + +# 0.9.1 + +> Sat 7th of July 2018 + +- Fix unit testing in transpilers. + +# 0.9 + +> Sat 7th of July 2018 + +- Big cleanup of the module hierarchy. +- Enhanced the documentation. + +# 0.8.1 + +> Sun, 17th of June 2018 + +- Add the `README.md` path to the `Cargo.toml` manifest. + +# 0.8 + +> Sun 17th of June 2018 + +This version introduces breaking changes because public types used in return positions have changed. +These concern only intermediate `nom` functions, so if you do not make a fancy use of this crate, +you souldn’t have to worry too much when migrating. + +- Fix the roundtrip issue with the GLSL writer (precedence wasn’t correctly respected). +- Simplify internal code. +- Error instead of panicking when parsing overflowing integer literals. +- Fix panic trying to parse literals starting with whitespace. +- Add fuzzing to find out panics. + +# 0.7.2 + +> Wed 13th of December 2017 + +- Fix the `show_expr` when the `Expr` is a `Expr::UIntConst`. + +# 0.7.1 + +> Mon 20th of November 2017 + +- `std::error::Error` is now implemented for `ParseError`. + +# 0.7 + +> Wed 27th of September 2017 + +- Add support for postfix expressions as function identifiers. + +# 0.6.5 + +> Mon 4th of September 2017 + +- Fix the formatting of floating values when the fractional part is `0`. + +# 0.6.4 + +> Mon 4th of September 2017 + +- Fix the output for `show_struct_specifier`. + +# 0.6.3 + +> Mon 4th of September 2017 + +- Fix the output for `show_struct_specifier`. + +# 0.6.2 + +> Mon 4th of September 2017 + +- Remove a warning. + +# 0.6.1 + +> Mon 4th of September 2017 + +- Fix `show_struct_specifier`. + +# 0.6 + +> Fri 1st of September 2017 + +- The `TypeSpecifier` type was wrong as it didn’t carry any `ArraySpecifier` information while the + GLSL specification’s grammar about type specifiers states they should. Fixed. + +# 0.5 + +> Mon 7th of August 2017 + +- The `parse` and `parse_str` functions now take as second argument the parser to run. This enables + using those functions and all the neat logic the wrap in dependent projects. + +# 0.4.2 + +> Fri 4th of August 2017 + +- A GLSL writer is now available. +- Some parsers yield non-empty list of syntax trees. Those had the incorrect `Vec` type. They were + replaced by `NonEmpty`, which is an alias to `Vec`, but carry the semantic that it has at least + one element in it. + +# 0.4.1 + +> Thu 3rd of August 2017 + +- Uni/multi-line comments are now supported. + +# 0.4 + +> Wed 2nd of August 2017 + +- The `Declaration::Block` variant was refactored for a better usage. +- Dot field selections and, in a mory general way, postfix expressions completely fixed. The + `syntax` module was altered to make it easier to work with dot field selection. Also related, + the function identifier syntax is now isomorphic to an identifier. + +# 0.3.1 + +> Tue 1st of August 2017 + +- Fix the `preprocessor` parser so that it eats surrounding blanks. + +# 0.3 + +> Mon 31st of July 2017 + +- Add a very minimalistic yet working preprocessor. It parses `#version` and `#extension` + commands. Those have to be declared at the top of your file, even though this implementation + accepts them at any place an external declaration could be defined. Feel free to submit a PR + if you want to change that behavior, I don’t really mind. +- Enhance the runtime error reporting. It’s not perfect, but it’s way better than before! +- `void` is now recognized as `TypeSpecifier::Void` instead of the erroneous + `TypeSpecifier::TypeName("void")`. + +# 0.2.2 + +> Mon 31st of July 2017 + +- The `layout` parser had a nasty bug that would treat a list of key-value pairs as an expression + assignment. This was fixed and it now treats it as a list of pairs of identifier associated with a + possible constant expression. +- The `StructFieldSpecifier` type and its associated parser were wrong. Was missing: + + the type qualifier + + for each identifier defined in the field specifier, its optional array specifier, as in + `float foo[3];` or `vec3 bar[];` for unsized ones. + +# 0.2.1 + +> Sun 30th of July 2017 + +- More documentation to help people to get their feet wet. + +# 0.2 + +> Sat 29th of July 2017 + +- The whole parsing API is public. + +# 0.1 + +- Initial revision. diff --git a/third_party/rust/glsl/Cargo.toml b/third_party/rust/glsl/Cargo.toml new file mode 100644 index 0000000000..8d31a8599b --- /dev/null +++ b/third_party/rust/glsl/Cargo.toml @@ -0,0 +1,44 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "glsl" +version = "6.0.2" +authors = ["Dimitri Sabadie <dimitri.sabadie@gmail.com>"] +description = "A GLSL450/GLSL460 parser." +homepage = "https://github.com/phaazon/glsl" +documentation = "https://docs.rs/glsl" +readme = "README.md" +keywords = [ + "GLSL", + "OpenGL", + "SPIR-V", + "parser", +] +categories = [ + "parsing", + "rendering", +] +license = "BSD-3-Clause" +repository = "https://github.com/phaazon/glsl" + +[dependencies.nom] +version = "7" +features = ["std"] +default-features = false + +[dependencies.shaderc] +version = "0.6" +optional = true + +[features] +spirv = ["shaderc"] diff --git a/third_party/rust/glsl/LICENSE b/third_party/rust/glsl/LICENSE new file mode 100644 index 0000000000..8e86cfc9d6 --- /dev/null +++ b/third_party/rust/glsl/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2018, Dimitri Sabadie <dimitri.sabadie@gmail.com> + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Dimitri Sabadie <dimitri.sabadie@gmail.com> nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/rust/glsl/README.md b/third_party/rust/glsl/README.md new file mode 100644 index 0000000000..25813e0a3d --- /dev/null +++ b/third_party/rust/glsl/README.md @@ -0,0 +1,126 @@ +[![Build Status](https://travis-ci.org/phaazon/glsl.svg?branch=master)](https://travis-ci.org/phaazon/glsl) +[![crates.io](https://img.shields.io/crates/v/glsl.svg)](https://crates.io/crates/glsl) +[![docs.rs](https://docs.rs/glsl/badge.svg)](https://docs.rs/glsl) +![License](https://img.shields.io/badge/license-BSD3-blue.svg?style=flat) + +<!-- cargo-sync-readme start --> + +This crate is a GLSL450/GLSL460 compiler. It’s able to parse valid GLSL formatted source into +an abstract syntax tree (AST). That AST can then be transformed into SPIR-V, your own format or +even folded back to a raw GLSL [`String`] (think of a minifier, for instance). + +You’ll find several modules: + + - [`parser`], which exports the parsing interface. This is the place you will get most + interesting types and traits, such as [`Parse`] and [`ParseError`]. + - [`syntax`], which exports the AST and language definitions. If you look into destructuring, + transpiling or getting information on the GLSL code that got parsed, you will likely + manipulate objects which types are defined in this module. + - [`transpiler`], which provides you with GLSL transpilers. For instance, you will find _GLSL + to GLSL_ transpiler, _GLSL to SPIR-V_ transpiler, etc. + - [`visitor`](visitor), which gives you a way to visit AST nodes and mutate them, both with + inner and outer mutation. + +Feel free to inspect those modules for further information. + +# GLSL parsing and transpiling + +Parsing is the most common operation you will do. It is not required per-se (you can still +create your AST by hand or use [glsl-quasiquote] to create it at compile-time by using the GLSL +syntax directly in Rust). However, in this section, we are going to see how we can parse from a +string to several GLSL types. + +## Parsing architecture + +Basically, the [`Parse`] trait gives you all you need to start parsing. This crate is designed +around the concept of type-driven parsing: parsers are hidden and you just have to state what +result type you expect. + +The most common type you want to parse to is [`TranslationUnit`], which represents a set of +[`ExternalDeclaration`]s. An [`ExternalDeclaration`] is just a declaration at the top-most level +of a shader. It can be a global, uniform declarations, vertex attributes, a function, a +structure, etc. In that sense, a [`TranslationUnit`] is akin to a shader stage (vertex shader, +fragment shader, etc.). + +You can parse any type that implements [`Parse`]. Parsers are mostly sensible to external +blanks, which means that parsing an [`Expr`] starting with a blank will not work (this is not +true for a [`TranslationUnit`] as it’s exceptionnally more permissive). + +## Parsing an expression + +Let’s try to parse an expression. + +```rust +use glsl::parser::Parse as _; +use glsl::syntax::Expr; + +let glsl = "(vec3(r, g, b) * cos(t * PI * .5)).xxz"; +let expr = Expr::parse(glsl); +assert!(expr.is_ok()); +``` + +Here, `expr` is an AST which type is `Result<Expr, ParseError>` that represents the GLSL +expression `(vec3(r, g, b) * cos(t * PI * .5)).xxz`, which is an outer (scalar) multiplication +of an RGB color by a cosine of a time, the whole thing being +[swizzled](https://en.wikipedia.org/wiki/Swizzling_\(computer_graphics\)) with XXZ. It is your +responsibility to check if the parsing process has succeeded. + +In the previous example, the GLSL string is a constant and hardcoded. It could come from a file, +network or built on the fly, but in the case of constant GLSL code, it would be preferable not +to parse the string at runtime, right? Well, [glsl-quasiquote] is there exactly for that. You +can ask **rustc** to parse that string and, if the parsing has succeeded, inject the AST +directly into your code. No [`Result`], just the pure AST. Have a look at [glsl-quasiquote] for +further details. + +## Parsing a whole shader + +Vertex shaders, geometry shaders, fragment shaders and control and evaluation tessellation +shaders can be parsed the same way by using one of the `TranslationUnit` or `ShaderStage` types. + +Here, a simple vertex shader being parsed. + +```rust +use glsl::parser::Parse as _; +use glsl::syntax::ShaderStage; + +let glsl = " + layout (location = 0) in vec3 pos; + layout (location = 1) in vec4 col; + + out vec4 v_col; + + uniform mat4 projview; + + void main() { + v_col = col; // pass color to the next stage + gl_Position = projview * vec4(pos, 1.); + } +"; +let stage = ShaderStage::parse(glsl); +assert!(stage.is_ok()); +``` + +## Visiting AST nodes + +The crate is also getting more and more combinators and functions to transform the AST or create +nodes with regular Rust. The [`Visitor`] trait will be a great friend of yours when you will +want to cope with deep mutation, filtering and validation. Have a look at the +[`visitor`](visitor) module for a tutorial on how to use visitors. + +# About the GLSL versions… + +This crate can parse both GLSL450 and GLSL460 formatted input sources. At the language level, +the difference between GLSL450 and GLSL460 is pretty much nothing, so both cases are covered. + +> If you’re wondering, the only difference between both versions is that in GLSL460, it’s +> authorized to have semicolons (`;`) on empty lines at top-level in a shader. + +[glsl-quasiquote]: https://crates.io/crates/glsl-quasiquote +[`Parse`]: crate::parser::Parse +[`ParseError`]: crate::parser::ParseError +[`ExternalDeclaration`]: crate::syntax::ExternalDeclaration +[`TranslationUnit`]: crate::syntax::TranslationUnit +[`Expr`]: crate::syntax::Expr +[`Visitor`]: crate::visitor::Visitor + +<!-- cargo-sync-readme end --> diff --git a/third_party/rust/glsl/data/tests/buffer_block_0.glsl b/third_party/rust/glsl/data/tests/buffer_block_0.glsl new file mode 100644 index 0000000000..de0b43928e --- /dev/null +++ b/third_party/rust/glsl/data/tests/buffer_block_0.glsl @@ -0,0 +1,6 @@ +buffer Foo { + char tiles[]; +} main_tiles; + +void main() { +} diff --git a/third_party/rust/glsl/data/tests/layout_buffer_block_0.glsl b/third_party/rust/glsl/data/tests/layout_buffer_block_0.glsl new file mode 100644 index 0000000000..00be0a172c --- /dev/null +++ b/third_party/rust/glsl/data/tests/layout_buffer_block_0.glsl @@ -0,0 +1,3 @@ +layout(set = 0, binding = 0) buffer Foo { + char a; +} foo; diff --git a/third_party/rust/glsl/src/lib.rs b/third_party/rust/glsl/src/lib.rs new file mode 100644 index 0000000000..2461c78bdc --- /dev/null +++ b/third_party/rust/glsl/src/lib.rs @@ -0,0 +1,125 @@ +//! This crate is a GLSL450/GLSL460 compiler. It’s able to parse valid GLSL formatted source into +//! an abstract syntax tree (AST). That AST can then be transformed into SPIR-V, your own format or +//! even folded back to a raw GLSL [`String`] (think of a minifier, for instance). +//! +//! You’ll find several modules: +//! +//! - [`parser`], which exports the parsing interface. This is the place you will get most +//! interesting types and traits, such as [`Parse`] and [`ParseError`]. +//! - [`syntax`], which exports the AST and language definitions. If you look into destructuring, +//! transpiling or getting information on the GLSL code that got parsed, you will likely +//! manipulate objects which types are defined in this module. +//! - [`transpiler`], which provides you with GLSL transpilers. For instance, you will find _GLSL +//! to GLSL_ transpiler, _GLSL to SPIR-V_ transpiler, etc. +//! - [`visitor`](visitor), which gives you a way to visit AST nodes and mutate them, both with +//! inner and outer mutation. +//! +//! Feel free to inspect those modules for further information. +//! +//! # GLSL parsing and transpiling +//! +//! Parsing is the most common operation you will do. It is not required per-se (you can still +//! create your AST by hand or use [glsl-quasiquote] to create it at compile-time by using the GLSL +//! syntax directly in Rust). However, in this section, we are going to see how we can parse from a +//! string to several GLSL types. +//! +//! ## Parsing architecture +//! +//! Basically, the [`Parse`] trait gives you all you need to start parsing. This crate is designed +//! around the concept of type-driven parsing: parsers are hidden and you just have to state what +//! result type you expect. +//! +//! The most common type you want to parse to is [`TranslationUnit`], which represents a set of +//! [`ExternalDeclaration`]s. An [`ExternalDeclaration`] is just a declaration at the top-most level +//! of a shader. It can be a global, uniform declarations, vertex attributes, a function, a +//! structure, etc. In that sense, a [`TranslationUnit`] is akin to a shader stage (vertex shader, +//! fragment shader, etc.). +//! +//! You can parse any type that implements [`Parse`]. Parsers are mostly sensible to external +//! blanks, which means that parsing an [`Expr`] starting with a blank will not work (this is not +//! true for a [`TranslationUnit`] as it’s exceptionnally more permissive). +//! +//! ## Parsing an expression +//! +//! Let’s try to parse an expression. +//! +//! ```rust +//! use glsl::parser::Parse as _; +//! use glsl::syntax::Expr; +//! +//! let glsl = "(vec3(r, g, b) * cos(t * PI * .5)).xxz"; +//! let expr = Expr::parse(glsl); +//! assert!(expr.is_ok()); +//! ``` +//! +//! Here, `expr` is an AST which type is `Result<Expr, ParseError>` that represents the GLSL +//! expression `(vec3(r, g, b) * cos(t * PI * .5)).xxz`, which is an outer (scalar) multiplication +//! of an RGB color by a cosine of a time, the whole thing being +//! [swizzled](https://en.wikipedia.org/wiki/Swizzling_(computer_graphics)) with XXZ. It is your +//! responsibility to check if the parsing process has succeeded. +//! +//! In the previous example, the GLSL string is a constant and hardcoded. It could come from a file, +//! network or built on the fly, but in the case of constant GLSL code, it would be preferable not +//! to parse the string at runtime, right? Well, [glsl-quasiquote] is there exactly for that. You +//! can ask **rustc** to parse that string and, if the parsing has succeeded, inject the AST +//! directly into your code. No [`Result`], just the pure AST. Have a look at [glsl-quasiquote] for +//! further details. +//! +//! ## Parsing a whole shader +//! +//! Vertex shaders, geometry shaders, fragment shaders and control and evaluation tessellation +//! shaders can be parsed the same way by using one of the `TranslationUnit` or `ShaderStage` types. +//! +//! Here, a simple vertex shader being parsed. +//! +//! ```rust +//! use glsl::parser::Parse as _; +//! use glsl::syntax::ShaderStage; +//! +//! let glsl = " +//! layout (location = 0) in vec3 pos; +//! layout (location = 1) in vec4 col; +//! +//! out vec4 v_col; +//! +//! uniform mat4 projview; +//! +//! void main() { +//! v_col = col; // pass color to the next stage +//! gl_Position = projview * vec4(pos, 1.); +//! } +//! "; +//! let stage = ShaderStage::parse(glsl); +//! assert!(stage.is_ok()); +//! ``` +//! +//! ## Visiting AST nodes +//! +//! The crate is also getting more and more combinators and functions to transform the AST or create +//! nodes with regular Rust. The [`Visitor`] trait will be a great friend of yours when you will +//! want to cope with deep mutation, filtering and validation. Have a look at the +//! [`visitor`](visitor) module for a tutorial on how to use visitors. +//! +//! # About the GLSL versions… +//! +//! This crate can parse both GLSL450 and GLSL460 formatted input sources. At the language level, +//! the difference between GLSL450 and GLSL460 is pretty much nothing, so both cases are covered. +//! +//! > If you’re wondering, the only difference between both versions is that in GLSL460, it’s +//! > authorized to have semicolons (`;`) on empty lines at top-level in a shader. +//! +//! [glsl-quasiquote]: https://crates.io/crates/glsl-quasiquote +//! [`Parse`]: crate::parser::Parse +//! [`ParseError`]: crate::parser::ParseError +//! [`ExternalDeclaration`]: crate::syntax::ExternalDeclaration +//! [`TranslationUnit`]: crate::syntax::TranslationUnit +//! [`Expr`]: crate::syntax::Expr +//! [`Visitor`]: crate::visitor::Visitor + +#[cfg(test)] +mod parse_tests; +pub mod parser; +mod parsers; +pub mod syntax; +pub mod transpiler; +pub mod visitor; diff --git a/third_party/rust/glsl/src/parse_tests.rs b/third_party/rust/glsl/src/parse_tests.rs new file mode 100644 index 0000000000..3a2c7a6880 --- /dev/null +++ b/third_party/rust/glsl/src/parse_tests.rs @@ -0,0 +1,2847 @@ +use crate::parsers::*; +use crate::syntax; + +#[test] +fn parse_uniline_comment() { + assert_eq!(comment("// lol"), Ok(("", " lol"))); + assert_eq!(comment("// lol\nfoo"), Ok(("foo", " lol"))); + assert_eq!(comment("// lol\\\nfoo"), Ok(("", " lol\\\nfoo"))); + assert_eq!( + comment("// lol \\\n foo\n"), + Ok(("", " lol \\\n foo")) + ); +} + +#[test] +fn parse_multiline_comment() { + assert_eq!(comment("/* lol\nfoo\n*/bar"), Ok(("bar", " lol\nfoo\n"))); +} + +#[test] +fn parse_unsigned_suffix() { + assert_eq!(unsigned_suffix("u"), Ok(("", 'u'))); + assert_eq!(unsigned_suffix("U"), Ok(("", 'U'))); +} + +#[test] +fn parse_nonzero_digits() { + assert_eq!(nonzero_digits("3"), Ok(("", "3"))); + assert_eq!(nonzero_digits("12345953"), Ok(("", "12345953"))); +} + +#[test] +fn parse_decimal_lit() { + assert_eq!(decimal_lit("3"), Ok(("", Ok(3)))); + assert_eq!(decimal_lit("3"), Ok(("", Ok(3)))); + assert_eq!(decimal_lit("13"), Ok(("", Ok(13)))); + assert_eq!(decimal_lit("42"), Ok(("", Ok(42)))); + assert_eq!(decimal_lit("123456"), Ok(("", Ok(123456)))); +} + +#[test] +fn parse_octal_lit() { + assert_eq!(octal_lit("0"), Ok(("", Ok(0o0)))); + assert_eq!(octal_lit("03 "), Ok((" ", Ok(0o3)))); + assert_eq!(octal_lit("012 "), Ok((" ", Ok(0o12)))); + assert_eq!(octal_lit("07654321 "), Ok((" ", Ok(0o7654321)))); +} + +#[test] +fn parse_hexadecimal_lit() { + assert_eq!(hexadecimal_lit("0x3 "), Ok((" ", Ok(0x3)))); + assert_eq!(hexadecimal_lit("0x0123789"), Ok(("", Ok(0x0123789)))); + assert_eq!(hexadecimal_lit("0xABCDEF"), Ok(("", Ok(0xabcdef)))); + assert_eq!(hexadecimal_lit("0xabcdef"), Ok(("", Ok(0xabcdef)))); +} + +#[test] +fn parse_integral_lit() { + assert_eq!(integral_lit("0"), Ok(("", 0))); + assert_eq!(integral_lit("3"), Ok(("", 3))); + assert_eq!(integral_lit("3 "), Ok((" ", 3))); + assert_eq!(integral_lit("03 "), Ok((" ", 3))); + assert_eq!(integral_lit("076556 "), Ok((" ", 0o76556))); + assert_eq!(integral_lit("012 "), Ok((" ", 0o12))); + assert_eq!(integral_lit("0x3 "), Ok((" ", 0x3))); + assert_eq!(integral_lit("0x9ABCDEF"), Ok(("", 0x9ABCDEF))); + assert_eq!(integral_lit("0x9ABCDEF"), Ok(("", 0x9ABCDEF))); + assert_eq!(integral_lit("0x9abcdef"), Ok(("", 0x9abcdef))); + assert_eq!(integral_lit("0x9abcdef"), Ok(("", 0x9abcdef))); + assert_eq!(integral_lit("0xffffffff"), Ok(("", 0xffffffffu32 as i32))); +} + +#[test] +fn parse_integral_neg_lit() { + assert_eq!(integral_lit("-3"), Ok(("", -3))); + assert_eq!(integral_lit("-3 "), Ok((" ", -3))); + assert_eq!(integral_lit("-03 "), Ok((" ", -3))); + assert_eq!(integral_lit("-076556 "), Ok((" ", -0o76556))); + assert_eq!(integral_lit("-012 "), Ok((" ", -0o12))); + assert_eq!(integral_lit("-0x3 "), Ok((" ", -0x3))); + assert_eq!(integral_lit("-0x9ABCDEF"), Ok(("", -0x9ABCDEF))); + assert_eq!(integral_lit("-0x9ABCDEF"), Ok(("", -0x9ABCDEF))); + assert_eq!(integral_lit("-0x9abcdef"), Ok(("", -0x9abcdef))); + assert_eq!(integral_lit("-0x9abcdef"), Ok(("", -0x9abcdef))); +} + +#[test] +fn parse_unsigned_lit() { + assert_eq!(unsigned_lit("0xffffffffU"), Ok(("", 0xffffffff as u32))); + assert_eq!(unsigned_lit("-1u"), Ok(("", 0xffffffff as u32))); + assert!(unsigned_lit("0xfffffffffU").is_err()); +} + +#[test] +fn parse_float_lit() { + assert_eq!(float_lit("0.;"), Ok((";", 0.))); + assert_eq!(float_lit(".0;"), Ok((";", 0.))); + assert_eq!(float_lit(".035 "), Ok((" ", 0.035))); + assert_eq!(float_lit("0. "), Ok((" ", 0.))); + assert_eq!(float_lit("0.035 "), Ok((" ", 0.035))); + assert_eq!(float_lit(".035f"), Ok(("", 0.035))); + assert_eq!(float_lit("0.f"), Ok(("", 0.))); + assert_eq!(float_lit("314.f"), Ok(("", 314.))); + assert_eq!(float_lit("0.035f"), Ok(("", 0.035))); + assert_eq!(float_lit(".035F"), Ok(("", 0.035))); + assert_eq!(float_lit("0.F"), Ok(("", 0.))); + assert_eq!(float_lit("0.035F"), Ok(("", 0.035))); + assert_eq!(float_lit("1.03e+34 "), Ok((" ", 1.03e+34))); + assert_eq!(float_lit("1.03E+34 "), Ok((" ", 1.03E+34))); + assert_eq!(float_lit("1.03e-34 "), Ok((" ", 1.03e-34))); + assert_eq!(float_lit("1.03E-34 "), Ok((" ", 1.03E-34))); + assert_eq!(float_lit("1.03e+34f"), Ok(("", 1.03e+34))); + assert_eq!(float_lit("1.03E+34f"), Ok(("", 1.03E+34))); + assert_eq!(float_lit("1.03e-34f"), Ok(("", 1.03e-34))); + assert_eq!(float_lit("1.03E-34f"), Ok(("", 1.03E-34))); + assert_eq!(float_lit("1.03e+34F"), Ok(("", 1.03e+34))); + assert_eq!(float_lit("1.03E+34F"), Ok(("", 1.03E+34))); + assert_eq!(float_lit("1.03e-34F"), Ok(("", 1.03e-34))); + assert_eq!(float_lit("1.03E-34F"), Ok(("", 1.03E-34))); +} + +#[test] +fn parse_float_neg_lit() { + assert_eq!(float_lit("-.035 "), Ok((" ", -0.035))); + assert_eq!(float_lit("-0. "), Ok((" ", -0.))); + assert_eq!(float_lit("-0.035 "), Ok((" ", -0.035))); + assert_eq!(float_lit("-.035f"), Ok(("", -0.035))); + assert_eq!(float_lit("-0.f"), Ok(("", -0.))); + assert_eq!(float_lit("-0.035f"), Ok(("", -0.035))); + assert_eq!(float_lit("-.035F"), Ok(("", -0.035))); + assert_eq!(float_lit("-0.F"), Ok(("", -0.))); + assert_eq!(float_lit("-0.035F"), Ok(("", -0.035))); + assert_eq!(float_lit("-1.03e+34 "), Ok((" ", -1.03e+34))); + assert_eq!(float_lit("-1.03E+34 "), Ok((" ", -1.03E+34))); + assert_eq!(float_lit("-1.03e-34 "), Ok((" ", -1.03e-34))); + assert_eq!(float_lit("-1.03E-34 "), Ok((" ", -1.03E-34))); + assert_eq!(float_lit("-1.03e+34f"), Ok(("", -1.03e+34))); + assert_eq!(float_lit("-1.03E+34f"), Ok(("", -1.03E+34))); + assert_eq!(float_lit("-1.03e-34f"), Ok(("", -1.03e-34))); + assert_eq!(float_lit("-1.03E-34f"), Ok(("", -1.03E-34))); + assert_eq!(float_lit("-1.03e+34F"), Ok(("", -1.03e+34))); + assert_eq!(float_lit("-1.03E+34F"), Ok(("", -1.03E+34))); + assert_eq!(float_lit("-1.03e-34F"), Ok(("", -1.03e-34))); + assert_eq!(float_lit("-1.03E-34F"), Ok(("", -1.03E-34))); +} + +#[test] +fn parse_double_lit() { + assert_eq!(double_lit("0.;"), Ok((";", 0.))); + assert_eq!(double_lit(".0;"), Ok((";", 0.))); + assert_eq!(double_lit(".035 "), Ok((" ", 0.035))); + assert_eq!(double_lit("0. "), Ok((" ", 0.))); + assert_eq!(double_lit("0.035 "), Ok((" ", 0.035))); + assert_eq!(double_lit("0.lf"), Ok(("", 0.))); + assert_eq!(double_lit("0.035lf"), Ok(("", 0.035))); + assert_eq!(double_lit(".035lf"), Ok(("", 0.035))); + assert_eq!(double_lit(".035LF"), Ok(("", 0.035))); + assert_eq!(double_lit("0.LF"), Ok(("", 0.))); + assert_eq!(double_lit("0.035LF"), Ok(("", 0.035))); + assert_eq!(double_lit("1.03e+34lf"), Ok(("", 1.03e+34))); + assert_eq!(double_lit("1.03E+34lf"), Ok(("", 1.03E+34))); + assert_eq!(double_lit("1.03e-34lf"), Ok(("", 1.03e-34))); + assert_eq!(double_lit("1.03E-34lf"), Ok(("", 1.03E-34))); + assert_eq!(double_lit("1.03e+34LF"), Ok(("", 1.03e+34))); + assert_eq!(double_lit("1.03E+34LF"), Ok(("", 1.03E+34))); + assert_eq!(double_lit("1.03e-34LF"), Ok(("", 1.03e-34))); + assert_eq!(double_lit("1.03E-34LF"), Ok(("", 1.03E-34))); +} + +#[test] +fn parse_double_neg_lit() { + assert_eq!(double_lit("-0.;"), Ok((";", -0.))); + assert_eq!(double_lit("-.0;"), Ok((";", -0.))); + assert_eq!(double_lit("-.035 "), Ok((" ", -0.035))); + assert_eq!(double_lit("-0. "), Ok((" ", -0.))); + assert_eq!(double_lit("-0.035 "), Ok((" ", -0.035))); + assert_eq!(double_lit("-0.lf"), Ok(("", -0.))); + assert_eq!(double_lit("-0.035lf"), Ok(("", -0.035))); + assert_eq!(double_lit("-.035lf"), Ok(("", -0.035))); + assert_eq!(double_lit("-.035LF"), Ok(("", -0.035))); + assert_eq!(double_lit("-0.LF"), Ok(("", -0.))); + assert_eq!(double_lit("-0.035LF"), Ok(("", -0.035))); + assert_eq!(double_lit("-1.03e+34lf"), Ok(("", -1.03e+34))); + assert_eq!(double_lit("-1.03E+34lf"), Ok(("", -1.03E+34))); + assert_eq!(double_lit("-1.03e-34lf"), Ok(("", -1.03e-34))); + assert_eq!(double_lit("-1.03E-34lf"), Ok(("", -1.03E-34))); + assert_eq!(double_lit("-1.03e+34LF"), Ok(("", -1.03e+34))); + assert_eq!(double_lit("-1.03E+34LF"), Ok(("", -1.03E+34))); + assert_eq!(double_lit("-1.03e-34LF"), Ok(("", -1.03e-34))); + assert_eq!(double_lit("-1.03E-34LF"), Ok(("", -1.03E-34))); +} + +#[test] +fn parse_bool_lit() { + assert_eq!(bool_lit("false"), Ok(("", false))); + assert_eq!(bool_lit("true"), Ok(("", true))); +} + +#[test] +fn parse_identifier() { + assert_eq!(identifier("a"), Ok(("", "a".into()))); + assert_eq!(identifier("ab_cd"), Ok(("", "ab_cd".into()))); + assert_eq!(identifier("Ab_cd"), Ok(("", "Ab_cd".into()))); + assert_eq!(identifier("Ab_c8d"), Ok(("", "Ab_c8d".into()))); + assert_eq!(identifier("Ab_c8d9"), Ok(("", "Ab_c8d9".into()))); +} + +#[test] +fn parse_unary_op_add() { + assert_eq!(unary_op("+ "), Ok((" ", syntax::UnaryOp::Add))); +} + +#[test] +fn parse_unary_op_minus() { + assert_eq!(unary_op("- "), Ok((" ", syntax::UnaryOp::Minus))); +} + +#[test] +fn parse_unary_op_not() { + assert_eq!(unary_op("!"), Ok(("", syntax::UnaryOp::Not))); +} + +#[test] +fn parse_unary_op_complement() { + assert_eq!(unary_op("~"), Ok(("", syntax::UnaryOp::Complement))); +} + +#[test] +fn parse_unary_op_inc() { + assert_eq!(unary_op("++"), Ok(("", syntax::UnaryOp::Inc))); +} + +#[test] +fn parse_unary_op_dec() { + assert_eq!(unary_op("--"), Ok(("", syntax::UnaryOp::Dec))); +} + +#[test] +fn parse_array_specifier_dimension_unsized() { + assert_eq!( + array_specifier_dimension("[]"), + Ok(("", syntax::ArraySpecifierDimension::Unsized)) + ); + assert_eq!( + array_specifier_dimension("[ ]"), + Ok(("", syntax::ArraySpecifierDimension::Unsized)) + ); + assert_eq!( + array_specifier_dimension("[\n]"), + Ok(("", syntax::ArraySpecifierDimension::Unsized)) + ); +} + +#[test] +fn parse_array_specifier_dimension_sized() { + let ix = syntax::Expr::IntConst(0); + + assert_eq!( + array_specifier_dimension("[0]"), + Ok(( + "", + syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(ix.clone())) + )) + ); + assert_eq!( + array_specifier_dimension("[\n0 \t]"), + Ok(( + "", + syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(ix)) + )) + ); +} + +#[test] +fn parse_array_specifier_unsized() { + assert_eq!( + array_specifier("[]"), + Ok(( + "", + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]) + } + )) + ) +} + +#[test] +fn parse_array_specifier_sized() { + let ix = syntax::Expr::IntConst(123); + + assert_eq!( + array_specifier("[123]"), + Ok(( + "", + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( + Box::new(ix) + )]) + } + )) + ) +} + +#[test] +fn parse_array_specifier_sized_multiple() { + let a = syntax::Expr::IntConst(2); + let b = syntax::Expr::IntConst(100); + let d = syntax::Expr::IntConst(5); + + assert_eq!( + array_specifier("[2][100][][5]"), + Ok(( + "", + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![ + syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(a)), + syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(b)), + syntax::ArraySpecifierDimension::Unsized, + syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(d)), + ]) + } + )) + ) +} + +#[test] +fn parse_precise_qualifier() { + assert_eq!(precise_qualifier("precise "), Ok((" ", ()))); +} + +#[test] +fn parse_invariant_qualifier() { + assert_eq!(invariant_qualifier("invariant "), Ok((" ", ()))); +} + +#[test] +fn parse_interpolation_qualifier() { + assert_eq!( + interpolation_qualifier("smooth "), + Ok((" ", syntax::InterpolationQualifier::Smooth)) + ); + assert_eq!( + interpolation_qualifier("flat "), + Ok((" ", syntax::InterpolationQualifier::Flat)) + ); + assert_eq!( + interpolation_qualifier("noperspective "), + Ok((" ", syntax::InterpolationQualifier::NoPerspective)) + ); +} + +#[test] +fn parse_precision_qualifier() { + assert_eq!( + precision_qualifier("highp "), + Ok((" ", syntax::PrecisionQualifier::High)) + ); + assert_eq!( + precision_qualifier("mediump "), + Ok((" ", syntax::PrecisionQualifier::Medium)) + ); + assert_eq!( + precision_qualifier("lowp "), + Ok((" ", syntax::PrecisionQualifier::Low)) + ); +} + +#[test] +fn parse_storage_qualifier() { + assert_eq!( + storage_qualifier("const "), + Ok((" ", syntax::StorageQualifier::Const)) + ); + assert_eq!( + storage_qualifier("inout "), + Ok((" ", syntax::StorageQualifier::InOut)) + ); + assert_eq!( + storage_qualifier("in "), + Ok((" ", syntax::StorageQualifier::In)) + ); + assert_eq!( + storage_qualifier("out "), + Ok((" ", syntax::StorageQualifier::Out)) + ); + assert_eq!( + storage_qualifier("centroid "), + Ok((" ", syntax::StorageQualifier::Centroid)) + ); + assert_eq!( + storage_qualifier("patch "), + Ok((" ", syntax::StorageQualifier::Patch)) + ); + assert_eq!( + storage_qualifier("sample "), + Ok((" ", syntax::StorageQualifier::Sample)) + ); + assert_eq!( + storage_qualifier("uniform "), + Ok((" ", syntax::StorageQualifier::Uniform)) + ); + assert_eq!( + storage_qualifier("attribute "), + Ok((" ", syntax::StorageQualifier::Attribute)) + ); + assert_eq!( + storage_qualifier("varying "), + Ok((" ", syntax::StorageQualifier::Varying)) + ); + assert_eq!( + storage_qualifier("buffer "), + Ok((" ", syntax::StorageQualifier::Buffer)) + ); + assert_eq!( + storage_qualifier("shared "), + Ok((" ", syntax::StorageQualifier::Shared)) + ); + assert_eq!( + storage_qualifier("coherent "), + Ok((" ", syntax::StorageQualifier::Coherent)) + ); + assert_eq!( + storage_qualifier("volatile "), + Ok((" ", syntax::StorageQualifier::Volatile)) + ); + assert_eq!( + storage_qualifier("restrict "), + Ok((" ", syntax::StorageQualifier::Restrict)) + ); + assert_eq!( + storage_qualifier("readonly "), + Ok((" ", syntax::StorageQualifier::ReadOnly)) + ); + assert_eq!( + storage_qualifier("writeonly "), + Ok((" ", syntax::StorageQualifier::WriteOnly)) + ); + assert_eq!( + storage_qualifier("subroutine a"), + Ok((" a", syntax::StorageQualifier::Subroutine(vec![]))) + ); + + let a = syntax::TypeName("vec3".to_owned()); + let b = syntax::TypeName("float".to_owned()); + let c = syntax::TypeName("dmat43".to_owned()); + let types = vec![a, b, c]; + assert_eq!( + storage_qualifier("subroutine ( vec3 , float \\\n, dmat43)"), + Ok(("", syntax::StorageQualifier::Subroutine(types))) + ); +} + +#[test] +fn parse_layout_qualifier_std430() { + let expected = syntax::LayoutQualifier { + ids: syntax::NonEmpty(vec![syntax::LayoutQualifierSpec::Identifier( + "std430".into(), + None, + )]), + }; + + assert_eq!( + layout_qualifier("layout (std430)"), + Ok(("", expected.clone())) + ); + assert_eq!( + layout_qualifier("layout (std430 )"), + Ok(("", expected.clone())) + ); + assert_eq!( + layout_qualifier("layout \n\t ( std430 )"), + Ok(("", expected.clone())) + ); + assert_eq!(layout_qualifier("layout(std430)"), Ok(("", expected))); +} + +#[test] +fn parse_layout_qualifier_shared() { + let expected = syntax::LayoutQualifier { + ids: syntax::NonEmpty(vec![syntax::LayoutQualifierSpec::Shared]), + }; + + assert_eq!( + layout_qualifier("layout (shared)"), + Ok(("", expected.clone())) + ); + assert_eq!( + layout_qualifier("layout ( shared )"), + Ok(("", expected.clone())) + ); + assert_eq!(layout_qualifier("layout(shared)"), Ok(("", expected))); +} + +#[test] +fn parse_layout_qualifier_list() { + let id_0 = syntax::LayoutQualifierSpec::Shared; + let id_1 = syntax::LayoutQualifierSpec::Identifier("std140".into(), None); + let id_2 = syntax::LayoutQualifierSpec::Identifier( + "max_vertices".into(), + Some(Box::new(syntax::Expr::IntConst(3))), + ); + let expected = syntax::LayoutQualifier { + ids: syntax::NonEmpty(vec![id_0, id_1, id_2]), + }; + + assert_eq!( + layout_qualifier("layout (shared, std140, max_vertices = 3)"), + Ok(("", expected.clone())) + ); + assert_eq!( + layout_qualifier("layout(shared,std140,max_vertices=3)"), + Ok(("", expected.clone())) + ); + assert_eq!( + layout_qualifier("layout\n\n\t ( shared , std140, max_vertices= 3)"), + Ok(("", expected.clone())) + ); +} + +#[test] +fn parse_type_qualifier() { + let storage_qual = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Const); + let id_0 = syntax::LayoutQualifierSpec::Shared; + let id_1 = syntax::LayoutQualifierSpec::Identifier("std140".into(), None); + let id_2 = syntax::LayoutQualifierSpec::Identifier( + "max_vertices".into(), + Some(Box::new(syntax::Expr::IntConst(3))), + ); + let layout_qual = syntax::TypeQualifierSpec::Layout(syntax::LayoutQualifier { + ids: syntax::NonEmpty(vec![id_0, id_1, id_2]), + }); + let expected = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![storage_qual, layout_qual]), + }; + + assert_eq!( + type_qualifier("const layout (shared, std140, max_vertices = 3)"), + Ok(("", expected.clone())) + ); + assert_eq!( + type_qualifier("const layout(shared,std140,max_vertices=3)"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_struct_field_specifier() { + let expected = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec4, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["foo".into()]), + }; + + assert_eq!( + struct_field_specifier("vec4 foo;"), + Ok(("", expected.clone())) + ); + assert_eq!( + struct_field_specifier("vec4 foo ; "), + Ok((" ", expected.clone())) + ); +} + +#[test] +fn parse_struct_field_specifier_type_name() { + let expected = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("S0238_3".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["x".into()]), + }; + + assert_eq!( + struct_field_specifier("S0238_3 x;"), + Ok(("", expected.clone())) + ); + assert_eq!( + struct_field_specifier("S0238_3 x ;"), + Ok(("", expected.clone())) + ); +} + +#[test] +fn parse_struct_field_specifier_several() { + let expected = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec4, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["foo".into(), "bar".into(), "zoo".into()]), + }; + + assert_eq!( + struct_field_specifier("vec4 foo, bar, zoo;"), + Ok(("", expected.clone())) + ); + assert_eq!( + struct_field_specifier("vec4 foo , bar , zoo ;"), + Ok(("", expected.clone())) + ); +} + +#[test] +fn parse_struct_specifier_one_field() { + let field = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec4, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["foo".into()]), + }; + let expected = syntax::StructSpecifier { + name: Some("TestStruct".into()), + fields: syntax::NonEmpty(vec![field]), + }; + + assert_eq!( + struct_specifier("struct TestStruct { vec4 foo; }"), + Ok(("", expected.clone())) + ); + assert_eq!( + struct_specifier("struct TestStruct \n \n\n {\n vec4 foo ;}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_struct_specifier_multi_fields() { + let a = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec4, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["foo".into()]), + }; + let b = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["bar".into()]), + }; + let c = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::UInt, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["zoo".into()]), + }; + let d = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::BVec3, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["foo_BAR_zoo3497_34".into()]), + }; + let e = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("S0238_3".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["x".into()]), + }; + let expected = syntax::StructSpecifier { + name: Some("_TestStruct_934i".into()), + fields: syntax::NonEmpty(vec![a, b, c, d, e]), + }; + + assert_eq!( + struct_specifier( + "struct _TestStruct_934i { vec4 foo; float bar; uint zoo; bvec3 foo_BAR_zoo3497_34; S0238_3 x; }" + ), + Ok(("", expected.clone())) + ); + assert_eq!( + struct_specifier( + "struct _TestStruct_934i{vec4 foo;float bar;uint zoo;bvec3 foo_BAR_zoo3497_34;S0238_3 x;}" + ), + Ok(("", expected.clone())) + ); + assert_eq!(struct_specifier("struct _TestStruct_934i\n { vec4\nfoo ; \n\t float\n\t\t bar ; \nuint zoo; \n bvec3 foo_BAR_zoo3497_34\n\n\t\n\t\n ; S0238_3 x;}"), Ok(("", expected))); +} + +#[test] +fn parse_type_specifier_non_array() { + assert_eq!( + type_specifier_non_array("bool"), + Ok(("", syntax::TypeSpecifierNonArray::Bool)) + ); + assert_eq!( + type_specifier_non_array("int"), + Ok(("", syntax::TypeSpecifierNonArray::Int)) + ); + assert_eq!( + type_specifier_non_array("uint"), + Ok(("", syntax::TypeSpecifierNonArray::UInt)) + ); + assert_eq!( + type_specifier_non_array("float"), + Ok(("", syntax::TypeSpecifierNonArray::Float)) + ); + assert_eq!( + type_specifier_non_array("double"), + Ok(("", syntax::TypeSpecifierNonArray::Double)) + ); + assert_eq!( + type_specifier_non_array("vec2"), + Ok(("", syntax::TypeSpecifierNonArray::Vec2)) + ); + assert_eq!( + type_specifier_non_array("vec3"), + Ok(("", syntax::TypeSpecifierNonArray::Vec3)) + ); + assert_eq!( + type_specifier_non_array("vec4"), + Ok(("", syntax::TypeSpecifierNonArray::Vec4)) + ); + assert_eq!( + type_specifier_non_array("dvec2"), + Ok(("", syntax::TypeSpecifierNonArray::DVec2)) + ); + assert_eq!( + type_specifier_non_array("dvec3"), + Ok(("", syntax::TypeSpecifierNonArray::DVec3)) + ); + assert_eq!( + type_specifier_non_array("dvec4"), + Ok(("", syntax::TypeSpecifierNonArray::DVec4)) + ); + assert_eq!( + type_specifier_non_array("bvec2"), + Ok(("", syntax::TypeSpecifierNonArray::BVec2)) + ); + assert_eq!( + type_specifier_non_array("bvec3"), + Ok(("", syntax::TypeSpecifierNonArray::BVec3)) + ); + assert_eq!( + type_specifier_non_array("bvec4"), + Ok(("", syntax::TypeSpecifierNonArray::BVec4)) + ); + assert_eq!( + type_specifier_non_array("ivec2"), + Ok(("", syntax::TypeSpecifierNonArray::IVec2)) + ); + assert_eq!( + type_specifier_non_array("ivec3"), + Ok(("", syntax::TypeSpecifierNonArray::IVec3)) + ); + assert_eq!( + type_specifier_non_array("ivec4"), + Ok(("", syntax::TypeSpecifierNonArray::IVec4)) + ); + assert_eq!( + type_specifier_non_array("uvec2"), + Ok(("", syntax::TypeSpecifierNonArray::UVec2)) + ); + assert_eq!( + type_specifier_non_array("uvec3"), + Ok(("", syntax::TypeSpecifierNonArray::UVec3)) + ); + assert_eq!( + type_specifier_non_array("uvec4"), + Ok(("", syntax::TypeSpecifierNonArray::UVec4)) + ); + assert_eq!( + type_specifier_non_array("mat2"), + Ok(("", syntax::TypeSpecifierNonArray::Mat2)) + ); + assert_eq!( + type_specifier_non_array("mat3"), + Ok(("", syntax::TypeSpecifierNonArray::Mat3)) + ); + assert_eq!( + type_specifier_non_array("mat4"), + Ok(("", syntax::TypeSpecifierNonArray::Mat4)) + ); + assert_eq!( + type_specifier_non_array("mat2x2"), + Ok(("", syntax::TypeSpecifierNonArray::Mat2)) + ); + assert_eq!( + type_specifier_non_array("mat2x3"), + Ok(("", syntax::TypeSpecifierNonArray::Mat23)) + ); + assert_eq!( + type_specifier_non_array("mat2x4"), + Ok(("", syntax::TypeSpecifierNonArray::Mat24)) + ); + assert_eq!( + type_specifier_non_array("mat3x2"), + Ok(("", syntax::TypeSpecifierNonArray::Mat32)) + ); + assert_eq!( + type_specifier_non_array("mat3x3"), + Ok(("", syntax::TypeSpecifierNonArray::Mat3)) + ); + assert_eq!( + type_specifier_non_array("mat3x4"), + Ok(("", syntax::TypeSpecifierNonArray::Mat34)) + ); + assert_eq!( + type_specifier_non_array("mat4x2"), + Ok(("", syntax::TypeSpecifierNonArray::Mat42)) + ); + assert_eq!( + type_specifier_non_array("mat4x3"), + Ok(("", syntax::TypeSpecifierNonArray::Mat43)) + ); + assert_eq!( + type_specifier_non_array("mat4x4"), + Ok(("", syntax::TypeSpecifierNonArray::Mat4)) + ); + assert_eq!( + type_specifier_non_array("dmat2"), + Ok(("", syntax::TypeSpecifierNonArray::DMat2)) + ); + assert_eq!( + type_specifier_non_array("dmat3"), + Ok(("", syntax::TypeSpecifierNonArray::DMat3)) + ); + assert_eq!( + type_specifier_non_array("dmat4"), + Ok(("", syntax::TypeSpecifierNonArray::DMat4)) + ); + assert_eq!( + type_specifier_non_array("dmat2x2"), + Ok(("", syntax::TypeSpecifierNonArray::DMat2)) + ); + assert_eq!( + type_specifier_non_array("dmat2x3"), + Ok(("", syntax::TypeSpecifierNonArray::DMat23)) + ); + assert_eq!( + type_specifier_non_array("dmat2x4"), + Ok(("", syntax::TypeSpecifierNonArray::DMat24)) + ); + assert_eq!( + type_specifier_non_array("dmat3x2"), + Ok(("", syntax::TypeSpecifierNonArray::DMat32)) + ); + assert_eq!( + type_specifier_non_array("dmat3x3"), + Ok(("", syntax::TypeSpecifierNonArray::DMat3)) + ); + assert_eq!( + type_specifier_non_array("dmat3x4"), + Ok(("", syntax::TypeSpecifierNonArray::DMat34)) + ); + assert_eq!( + type_specifier_non_array("dmat4x2"), + Ok(("", syntax::TypeSpecifierNonArray::DMat42)) + ); + assert_eq!( + type_specifier_non_array("dmat4x3"), + Ok(("", syntax::TypeSpecifierNonArray::DMat43)) + ); + assert_eq!( + type_specifier_non_array("dmat4x4"), + Ok(("", syntax::TypeSpecifierNonArray::DMat4)) + ); + assert_eq!( + type_specifier_non_array("sampler1D"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler1D)) + ); + assert_eq!( + type_specifier_non_array("image1D"), + Ok(("", syntax::TypeSpecifierNonArray::Image1D)) + ); + assert_eq!( + type_specifier_non_array("sampler2D"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2D)) + ); + assert_eq!( + type_specifier_non_array("image2D"), + Ok(("", syntax::TypeSpecifierNonArray::Image2D)) + ); + assert_eq!( + type_specifier_non_array("sampler3D"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler3D)) + ); + assert_eq!( + type_specifier_non_array("image3D"), + Ok(("", syntax::TypeSpecifierNonArray::Image3D)) + ); + assert_eq!( + type_specifier_non_array("samplerCube"), + Ok(("", syntax::TypeSpecifierNonArray::SamplerCube)) + ); + assert_eq!( + type_specifier_non_array("imageCube"), + Ok(("", syntax::TypeSpecifierNonArray::ImageCube)) + ); + assert_eq!( + type_specifier_non_array("sampler2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DRect)) + ); + assert_eq!( + type_specifier_non_array("image2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::Image2DRect)) + ); + assert_eq!( + type_specifier_non_array("sampler1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler1DArray)) + ); + assert_eq!( + type_specifier_non_array("image1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::Image1DArray)) + ); + assert_eq!( + type_specifier_non_array("sampler2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DArray)) + ); + assert_eq!( + type_specifier_non_array("image2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::Image2DArray)) + ); + assert_eq!( + type_specifier_non_array("samplerBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::SamplerBuffer)) + ); + assert_eq!( + type_specifier_non_array("imageBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::ImageBuffer)) + ); + assert_eq!( + type_specifier_non_array("sampler2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DMS)) + ); + assert_eq!( + type_specifier_non_array("image2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::Image2DMS)) + ); + assert_eq!( + type_specifier_non_array("sampler2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("image2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::Image2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("samplerCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeArray)) + ); + assert_eq!( + type_specifier_non_array("imageCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::ImageCubeArray)) + ); + assert_eq!( + type_specifier_non_array("sampler1DShadow"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler1DShadow)) + ); + assert_eq!( + type_specifier_non_array("sampler2DShadow"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DShadow)) + ); + assert_eq!( + type_specifier_non_array("sampler2DRectShadow"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DRectShadow)) + ); + assert_eq!( + type_specifier_non_array("sampler1DArrayShadow"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler1DArrayShadow)) + ); + assert_eq!( + type_specifier_non_array("sampler2DArrayShadow"), + Ok(("", syntax::TypeSpecifierNonArray::Sampler2DArrayShadow)) + ); + assert_eq!( + type_specifier_non_array("samplerCubeShadow"), + Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeShadow)) + ); + assert_eq!( + type_specifier_non_array("samplerCubeArrayShadow"), + Ok(("", syntax::TypeSpecifierNonArray::SamplerCubeArrayShadow)) + ); + assert_eq!( + type_specifier_non_array("isampler1D"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler1D)) + ); + assert_eq!( + type_specifier_non_array("iimage1D"), + Ok(("", syntax::TypeSpecifierNonArray::IImage1D)) + ); + assert_eq!( + type_specifier_non_array("isampler2D"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler2D)) + ); + assert_eq!( + type_specifier_non_array("iimage2D"), + Ok(("", syntax::TypeSpecifierNonArray::IImage2D)) + ); + assert_eq!( + type_specifier_non_array("isampler3D"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler3D)) + ); + assert_eq!( + type_specifier_non_array("iimage3D"), + Ok(("", syntax::TypeSpecifierNonArray::IImage3D)) + ); + assert_eq!( + type_specifier_non_array("isamplerCube"), + Ok(("", syntax::TypeSpecifierNonArray::ISamplerCube)) + ); + assert_eq!( + type_specifier_non_array("iimageCube"), + Ok(("", syntax::TypeSpecifierNonArray::IImageCube)) + ); + assert_eq!( + type_specifier_non_array("isampler2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler2DRect)) + ); + assert_eq!( + type_specifier_non_array("iimage2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::IImage2DRect)) + ); + assert_eq!( + type_specifier_non_array("isampler1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler1DArray)) + ); + assert_eq!( + type_specifier_non_array("iimage1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::IImage1DArray)) + ); + assert_eq!( + type_specifier_non_array("isampler2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler2DArray)) + ); + assert_eq!( + type_specifier_non_array("iimage2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::IImage2DArray)) + ); + assert_eq!( + type_specifier_non_array("isamplerBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::ISamplerBuffer)) + ); + assert_eq!( + type_specifier_non_array("iimageBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::IImageBuffer)) + ); + assert_eq!( + type_specifier_non_array("isampler2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler2DMS)) + ); + assert_eq!( + type_specifier_non_array("iimage2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::IImage2DMS)) + ); + assert_eq!( + type_specifier_non_array("isampler2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::ISampler2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("iimage2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::IImage2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("isamplerCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::ISamplerCubeArray)) + ); + assert_eq!( + type_specifier_non_array("iimageCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::IImageCubeArray)) + ); + assert_eq!( + type_specifier_non_array("atomic_uint"), + Ok(("", syntax::TypeSpecifierNonArray::AtomicUInt)) + ); + assert_eq!( + type_specifier_non_array("usampler1D"), + Ok(("", syntax::TypeSpecifierNonArray::USampler1D)) + ); + assert_eq!( + type_specifier_non_array("uimage1D"), + Ok(("", syntax::TypeSpecifierNonArray::UImage1D)) + ); + assert_eq!( + type_specifier_non_array("usampler2D"), + Ok(("", syntax::TypeSpecifierNonArray::USampler2D)) + ); + assert_eq!( + type_specifier_non_array("uimage2D"), + Ok(("", syntax::TypeSpecifierNonArray::UImage2D)) + ); + assert_eq!( + type_specifier_non_array("usampler3D"), + Ok(("", syntax::TypeSpecifierNonArray::USampler3D)) + ); + assert_eq!( + type_specifier_non_array("uimage3D"), + Ok(("", syntax::TypeSpecifierNonArray::UImage3D)) + ); + assert_eq!( + type_specifier_non_array("usamplerCube"), + Ok(("", syntax::TypeSpecifierNonArray::USamplerCube)) + ); + assert_eq!( + type_specifier_non_array("uimageCube"), + Ok(("", syntax::TypeSpecifierNonArray::UImageCube)) + ); + assert_eq!( + type_specifier_non_array("usampler2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::USampler2DRect)) + ); + assert_eq!( + type_specifier_non_array("uimage2DRect"), + Ok(("", syntax::TypeSpecifierNonArray::UImage2DRect)) + ); + assert_eq!( + type_specifier_non_array("usampler1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::USampler1DArray)) + ); + assert_eq!( + type_specifier_non_array("uimage1DArray"), + Ok(("", syntax::TypeSpecifierNonArray::UImage1DArray)) + ); + assert_eq!( + type_specifier_non_array("usampler2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::USampler2DArray)) + ); + assert_eq!( + type_specifier_non_array("uimage2DArray"), + Ok(("", syntax::TypeSpecifierNonArray::UImage2DArray)) + ); + assert_eq!( + type_specifier_non_array("usamplerBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::USamplerBuffer)) + ); + assert_eq!( + type_specifier_non_array("uimageBuffer"), + Ok(("", syntax::TypeSpecifierNonArray::UImageBuffer)) + ); + assert_eq!( + type_specifier_non_array("usampler2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::USampler2DMS)) + ); + assert_eq!( + type_specifier_non_array("uimage2DMS"), + Ok(("", syntax::TypeSpecifierNonArray::UImage2DMS)) + ); + assert_eq!( + type_specifier_non_array("usampler2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::USampler2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("uimage2DMSArray"), + Ok(("", syntax::TypeSpecifierNonArray::UImage2DMSArray)) + ); + assert_eq!( + type_specifier_non_array("usamplerCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::USamplerCubeArray)) + ); + assert_eq!( + type_specifier_non_array("uimageCubeArray"), + Ok(("", syntax::TypeSpecifierNonArray::UImageCubeArray)) + ); + assert_eq!( + type_specifier_non_array("ReturnType"), + Ok(( + "", + syntax::TypeSpecifierNonArray::TypeName(syntax::TypeName::new("ReturnType").unwrap()) + )) + ); +} + +#[test] +fn parse_type_specifier() { + assert_eq!( + type_specifier("uint;"), + Ok(( + ";", + syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::UInt, + array_specifier: None + } + )) + ); + assert_eq!( + type_specifier("iimage2DMSArray[35];"), + Ok(( + ";", + syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, + array_specifier: Some(syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( + Box::new(syntax::Expr::IntConst(35)) + )]) + }) + } + )) + ); +} + +#[test] +fn parse_fully_specified_type() { + let ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, + array_specifier: None, + }; + let expected = syntax::FullySpecifiedType { + qualifier: None, + ty, + }; + + assert_eq!( + fully_specified_type("iimage2DMSArray;"), + Ok((";", expected.clone())) + ); +} + +#[test] +fn parse_fully_specified_type_with_qualifier() { + let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Subroutine(vec![ + "vec2".into(), + "S032_29k".into(), + ])); + let qual = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![qual_spec]), + }; + let ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::IImage2DMSArray, + array_specifier: None, + }; + let expected = syntax::FullySpecifiedType { + qualifier: Some(qual), + ty, + }; + + assert_eq!( + fully_specified_type("subroutine (vec2, S032_29k) iimage2DMSArray;"), + Ok((";", expected.clone())) + ); + assert_eq!( + fully_specified_type("subroutine ( vec2\t\n \t , \n S032_29k )\n iimage2DMSArray ;"), + Ok((" ;", expected.clone())) + ); + assert_eq!( + fully_specified_type("subroutine(vec2,S032_29k)iimage2DMSArray;"), + Ok((";", expected)) + ); +} + +#[test] +fn parse_primary_expr_intconst() { + assert_eq!(primary_expr("0 "), Ok((" ", syntax::Expr::IntConst(0)))); + assert_eq!(primary_expr("1 "), Ok((" ", syntax::Expr::IntConst(1)))); +} + +#[test] +fn parse_primary_expr_uintconst() { + assert_eq!(primary_expr("0u "), Ok((" ", syntax::Expr::UIntConst(0)))); + assert_eq!(primary_expr("1u "), Ok((" ", syntax::Expr::UIntConst(1)))); +} + +#[test] +fn parse_primary_expr_floatconst() { + assert_eq!( + primary_expr("0.f "), + Ok((" ", syntax::Expr::FloatConst(0.))) + ); + assert_eq!( + primary_expr("1.f "), + Ok((" ", syntax::Expr::FloatConst(1.))) + ); + assert_eq!( + primary_expr("0.F "), + Ok((" ", syntax::Expr::FloatConst(0.))) + ); + assert_eq!( + primary_expr("1.F "), + Ok((" ", syntax::Expr::FloatConst(1.))) + ); +} + +#[test] +fn parse_primary_expr_doubleconst() { + assert_eq!(primary_expr("0. "), Ok((" ", syntax::Expr::FloatConst(0.)))); + assert_eq!(primary_expr("1. "), Ok((" ", syntax::Expr::FloatConst(1.)))); + assert_eq!( + primary_expr("0.lf "), + Ok((" ", syntax::Expr::DoubleConst(0.))) + ); + assert_eq!( + primary_expr("1.lf "), + Ok((" ", syntax::Expr::DoubleConst(1.))) + ); + assert_eq!( + primary_expr("0.LF "), + Ok((" ", syntax::Expr::DoubleConst(0.))) + ); + assert_eq!( + primary_expr("1.LF "), + Ok((" ", syntax::Expr::DoubleConst(1.))) + ); +} + +#[test] +fn parse_primary_expr_boolconst() { + assert_eq!( + primary_expr("false"), + Ok(("", syntax::Expr::BoolConst(false.to_owned()))) + ); + assert_eq!( + primary_expr("true"), + Ok(("", syntax::Expr::BoolConst(true.to_owned()))) + ); +} + +#[test] +fn parse_primary_expr_parens() { + assert_eq!(primary_expr("(0)"), Ok(("", syntax::Expr::IntConst(0)))); + assert_eq!(primary_expr("( 0 )"), Ok(("", syntax::Expr::IntConst(0)))); + assert_eq!( + primary_expr("( .0 )"), + Ok(("", syntax::Expr::FloatConst(0.))) + ); + assert_eq!( + primary_expr("( (.0) )"), + Ok(("", syntax::Expr::FloatConst(0.))) + ); + assert_eq!( + primary_expr("(true) "), + Ok((" ", syntax::Expr::BoolConst(true))) + ); +} + +#[test] +fn parse_postfix_function_call_no_args() { + let fun = syntax::FunIdentifier::Identifier("vec3".into()); + let args = Vec::new(); + let expected = syntax::Expr::FunCall(fun, args); + + assert_eq!(postfix_expr("vec3();"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("vec3 ( ) ;"), Ok((" ;", expected.clone()))); + assert_eq!(postfix_expr("vec3 (\nvoid\n) ;"), Ok((" ;", expected))); +} + +#[test] +fn parse_postfix_function_call_one_arg() { + let fun = syntax::FunIdentifier::Identifier("foo".into()); + let args = vec![syntax::Expr::IntConst(0)]; + let expected = syntax::Expr::FunCall(fun, args); + + assert_eq!(postfix_expr("foo(0);"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("foo ( 0 ) ;"), Ok((" ;", expected.clone()))); + assert_eq!(postfix_expr("foo (\n0\t\n) ;"), Ok((" ;", expected))); +} + +#[test] +fn parse_postfix_function_call_multi_arg() { + let fun = syntax::FunIdentifier::Identifier("foo".into()); + let args = vec![ + syntax::Expr::IntConst(0), + syntax::Expr::BoolConst(false), + syntax::Expr::Variable("bar".into()), + ]; + let expected = syntax::Expr::FunCall(fun, args); + + assert_eq!( + postfix_expr("foo(0, false, bar);"), + Ok((";", expected.clone())) + ); + assert_eq!( + postfix_expr("foo ( 0\t, false ,\t\tbar) ;"), + Ok((" ;", expected)) + ); +} + +#[test] +fn parse_postfix_expr_bracket() { + let id = syntax::Expr::Variable("foo".into()); + let array_spec = syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( + Box::new(syntax::Expr::IntConst(7354)), + )]), + }; + let expected = syntax::Expr::Bracket(Box::new(id), array_spec); + + assert_eq!(postfix_expr("foo[7354];"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("foo[\n 7354 ] ;"), Ok((";", expected))); +} + +#[test] +fn parse_postfix_expr_dot() { + let foo = Box::new(syntax::Expr::Variable("foo".into())); + let expected = syntax::Expr::Dot(foo, "bar".into()); + + assert_eq!(postfix_expr("foo.bar;"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("(foo).bar;"), Ok((";", expected))); +} + +#[test] +fn parse_postfix_expr_dot_several() { + let foo = Box::new(syntax::Expr::Variable("foo".into())); + let expected = syntax::Expr::Dot(Box::new(syntax::Expr::Dot(foo, "bar".into())), "zoo".into()); + + assert_eq!(postfix_expr("foo.bar.zoo;"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("(foo).bar.zoo;"), Ok((";", expected.clone()))); + assert_eq!(postfix_expr("(foo.bar).zoo;"), Ok((";", expected))); +} + +#[test] +fn parse_postfix_postinc() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::PostInc(Box::new(foo)); + + assert_eq!(postfix_expr("foo++;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_postfix_postdec() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::PostDec(Box::new(foo)); + + assert_eq!(postfix_expr("foo--;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_unary_add() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Add, Box::new(foo)); + + assert_eq!(unary_expr("+foo;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_unary_minus() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Minus, Box::new(foo)); + + assert_eq!(unary_expr("-foo;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_unary_not() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Not, Box::new(foo)); + + assert_eq!(unary_expr("!foo;"), Ok((";", expected))); +} + +#[test] +fn parse_unary_complement() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Complement, Box::new(foo)); + + assert_eq!(unary_expr("~foo;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_unary_inc() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Inc, Box::new(foo)); + + assert_eq!(unary_expr("++foo;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_unary_dec() { + let foo = syntax::Expr::Variable("foo".into()); + let expected = syntax::Expr::Unary(syntax::UnaryOp::Dec, Box::new(foo)); + + assert_eq!(unary_expr("--foo;"), Ok((";", expected.clone()))); +} + +#[test] +fn parse_expr_float() { + assert_eq!(expr("314.;"), Ok((";", syntax::Expr::FloatConst(314.)))); + assert_eq!(expr("314.f;"), Ok((";", syntax::Expr::FloatConst(314.)))); + assert_eq!(expr("314.LF;"), Ok((";", syntax::Expr::DoubleConst(314.)))); +} + +#[test] +fn parse_expr_add_2() { + let one = Box::new(syntax::Expr::IntConst(1)); + let expected = syntax::Expr::Binary(syntax::BinaryOp::Add, one.clone(), one); + + assert_eq!(expr("1 + 1;"), Ok((";", expected.clone()))); + assert_eq!(expr("1+1;"), Ok((";", expected.clone()))); + assert_eq!(expr("(1 + 1);"), Ok((";", expected))); +} + +#[test] +fn parse_expr_add_3() { + let one = Box::new(syntax::Expr::UIntConst(1)); + let two = Box::new(syntax::Expr::UIntConst(2)); + let three = Box::new(syntax::Expr::UIntConst(3)); + let expected = syntax::Expr::Binary( + syntax::BinaryOp::Add, + Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, one, two)), + three, + ); + + assert_eq!(expr("1u + 2u + 3u"), Ok(("", expected.clone()))); + assert_eq!(expr("1u + 2u + 3u "), Ok((" ", expected.clone()))); + assert_eq!(expr("1u+2u+3u"), Ok(("", expected.clone()))); + assert_eq!(expr("((1u + 2u) + 3u)"), Ok(("", expected))); +} + +#[test] +fn parse_expr_add_mult_3() { + let one = Box::new(syntax::Expr::UIntConst(1)); + let two = Box::new(syntax::Expr::UIntConst(2)); + let three = Box::new(syntax::Expr::UIntConst(3)); + let expected = syntax::Expr::Binary( + syntax::BinaryOp::Add, + Box::new(syntax::Expr::Binary(syntax::BinaryOp::Mult, one, two)), + three, + ); + + assert_eq!(expr("1u * 2u + 3u ;"), Ok((" ;", expected.clone()))); + assert_eq!(expr("1u*2u+3u;"), Ok((";", expected.clone()))); + assert_eq!(expr("(1u * 2u) + 3u;"), Ok((";", expected))); +} + +#[test] +fn parse_expr_add_sub_mult_div() { + let one = Box::new(syntax::Expr::IntConst(1)); + let two = Box::new(syntax::Expr::IntConst(2)); + let three = Box::new(syntax::Expr::IntConst(3)); + let four = Box::new(syntax::Expr::IntConst(4)); + let five = Box::new(syntax::Expr::IntConst(5)); + let six = Box::new(syntax::Expr::IntConst(6)); + let expected = syntax::Expr::Binary( + syntax::BinaryOp::Add, + Box::new(syntax::Expr::Binary( + syntax::BinaryOp::Mult, + one, + Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, two, three)), + )), + Box::new(syntax::Expr::Binary( + syntax::BinaryOp::Div, + four, + Box::new(syntax::Expr::Binary(syntax::BinaryOp::Add, five, six)), + )), + ); + + assert_eq!( + expr("1 * (2 + 3) + 4 / (5 + 6);"), + Ok((";", expected.clone())) + ); +} + +#[test] +fn parse_complex_expr() { + let input = "normalize((inverse(view) * vec4(ray.dir, 0.)).xyz);"; + let zero = syntax::Expr::FloatConst(0.); + let ray = syntax::Expr::Variable("ray".into()); + let raydir = syntax::Expr::Dot(Box::new(ray), "dir".into()); + let vec4 = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("vec4".into()), + vec![raydir, zero], + ); + let view = syntax::Expr::Variable("view".into()); + let iview = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("inverse".into()), + vec![view], + ); + let mul = syntax::Expr::Binary(syntax::BinaryOp::Mult, Box::new(iview), Box::new(vec4)); + let xyz = syntax::Expr::Dot(Box::new(mul), "xyz".into()); + let normalize = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("normalize".into()), + vec![xyz], + ); + let expected = normalize; + + assert_eq!(expr(&input[..]), Ok((";", expected))); +} + +#[test] +fn parse_function_identifier_typename() { + let expected = syntax::FunIdentifier::Identifier("foo".into()); + assert_eq!(function_identifier("foo("), Ok(("(", expected.clone()))); + assert_eq!(function_identifier("foo\n\t("), Ok(("(", expected.clone()))); + assert_eq!(function_identifier("foo\n ("), Ok(("(", expected))); +} + +#[test] +fn parse_function_identifier_cast() { + let expected = syntax::FunIdentifier::Identifier("vec3".into()); + assert_eq!(function_identifier("vec3("), Ok(("(", expected.clone()))); + assert_eq!(function_identifier("vec3 ("), Ok(("(", expected.clone()))); + assert_eq!(function_identifier("vec3\t\n\n \t ("), Ok(("(", expected))); +} + +#[test] +fn parse_function_identifier_cast_array_unsized() { + let expected = syntax::FunIdentifier::Expr(Box::new(syntax::Expr::Bracket( + Box::new(syntax::Expr::Variable("vec3".into())), + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), + }, + ))); + + assert_eq!(function_identifier("vec3[]("), Ok(("(", expected.clone()))); + assert_eq!(function_identifier("vec3 [\t\n]("), Ok(("(", expected))); +} + +#[test] +fn parse_function_identifier_cast_array_sized() { + let expected = syntax::FunIdentifier::Expr(Box::new(syntax::Expr::Bracket( + Box::new(syntax::Expr::Variable("vec3".into())), + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( + Box::new(syntax::Expr::IntConst(12)), + )]), + }, + ))); + + assert_eq!( + function_identifier("vec3[12]("), + Ok(("(", expected.clone())) + ); + assert_eq!(function_identifier("vec3 [\t 12\n]("), Ok(("(", expected))); +} + +#[test] +fn parse_void() { + assert_eq!(void("void "), Ok((" ", ()))); +} + +#[test] +fn parse_assignment_op_equal() { + assert_eq!(assignment_op("= "), Ok((" ", syntax::AssignmentOp::Equal))); +} + +#[test] +fn parse_assignment_op_mult() { + assert_eq!(assignment_op("*= "), Ok((" ", syntax::AssignmentOp::Mult))); +} + +#[test] +fn parse_assignment_op_div() { + assert_eq!(assignment_op("/= "), Ok((" ", syntax::AssignmentOp::Div))); +} + +#[test] +fn parse_assignment_op_mod() { + assert_eq!(assignment_op("%= "), Ok((" ", syntax::AssignmentOp::Mod))); +} + +#[test] +fn parse_assignment_op_add() { + assert_eq!(assignment_op("+= "), Ok((" ", syntax::AssignmentOp::Add))); +} + +#[test] +fn parse_assignment_op_sub() { + assert_eq!(assignment_op("-= "), Ok((" ", syntax::AssignmentOp::Sub))); +} + +#[test] +fn parse_assignment_op_lshift() { + assert_eq!( + assignment_op("<<= "), + Ok((" ", syntax::AssignmentOp::LShift)) + ); +} + +#[test] +fn parse_assignment_op_rshift() { + assert_eq!( + assignment_op(">>= "), + Ok((" ", syntax::AssignmentOp::RShift)) + ); +} + +#[test] +fn parse_assignment_op_and() { + assert_eq!(assignment_op("&= "), Ok((" ", syntax::AssignmentOp::And))); +} + +#[test] +fn parse_assignment_op_xor() { + assert_eq!(assignment_op("^= "), Ok((" ", syntax::AssignmentOp::Xor))); +} + +#[test] +fn parse_assignment_op_or() { + assert_eq!(assignment_op("|= "), Ok((" ", syntax::AssignmentOp::Or))); +} + +#[test] +fn parse_expr_statement() { + let expected = Some(syntax::Expr::Assignment( + Box::new(syntax::Expr::Variable("foo".into())), + syntax::AssignmentOp::Equal, + Box::new(syntax::Expr::FloatConst(314.)), + )); + + assert_eq!(expr_statement("foo = 314.f;"), Ok(("", expected.clone()))); + assert_eq!(expr_statement("foo=314.f;"), Ok(("", expected.clone()))); + assert_eq!(expr_statement("foo\n\t= \n314.f;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_function_prototype() { + let rt = syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec3, + array_specifier: None, + }, + }; + let arg0_ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec2, + array_specifier: None, + }; + let arg0 = syntax::FunctionParameterDeclaration::Unnamed(None, arg0_ty); + let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Out); + let qual = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![qual_spec]), + }; + let arg1 = syntax::FunctionParameterDeclaration::Named( + Some(qual), + syntax::FunctionParameterDeclarator { + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }, + ident: "the_arg".into(), + }, + ); + let fp = syntax::FunctionPrototype { + ty: rt, + name: "foo".into(), + parameters: vec![arg0, arg1], + }; + let expected = syntax::Declaration::FunctionPrototype(fp); + + assert_eq!( + declaration("vec3 foo(vec2, out float the_arg);"), + Ok(("", expected.clone())) + ); + assert_eq!( + declaration("vec3 \nfoo ( vec2\n, out float \n\tthe_arg )\n;"), + Ok(("", expected.clone())) + ); + assert_eq!( + declaration("vec3 foo(vec2,out float the_arg);"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_declaration_init_declarator_list_single() { + let ty = syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Int, + array_specifier: None, + }, + }; + let sd = syntax::SingleDeclaration { + ty, + name: Some("foo".into()), + array_specifier: None, + initializer: Some(syntax::Initializer::Simple(Box::new( + syntax::Expr::IntConst(34), + ))), + }; + let idl = syntax::InitDeclaratorList { + head: sd, + tail: Vec::new(), + }; + let expected = syntax::Declaration::InitDeclaratorList(idl); + + assert_eq!(declaration("int foo = 34;"), Ok(("", expected.clone()))); + assert_eq!(declaration("int foo=34;"), Ok(("", expected.clone()))); + assert_eq!(declaration("int \t \nfoo =\t34 ;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_init_declarator_list_complex() { + let ty = syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Int, + array_specifier: None, + }, + }; + let sd = syntax::SingleDeclaration { + ty, + name: Some("foo".into()), + array_specifier: None, + initializer: Some(syntax::Initializer::Simple(Box::new( + syntax::Expr::IntConst(34), + ))), + }; + let sdnt = syntax::SingleDeclarationNoType { + ident: "bar".into(), + initializer: Some(syntax::Initializer::Simple(Box::new( + syntax::Expr::IntConst(12), + ))), + }; + let expected = syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { + head: sd, + tail: vec![sdnt], + }); + + assert_eq!( + declaration("int foo = 34, bar = 12;"), + Ok(("", expected.clone())) + ); + assert_eq!( + declaration("int foo=34,bar=12;"), + Ok(("", expected.clone())) + ); + assert_eq!( + declaration("int \t \nfoo =\t34 \n,\tbar= 12\n ;"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_declaration_precision_low() { + let qual = syntax::PrecisionQualifier::Low; + let ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }; + let expected = syntax::Declaration::Precision(qual, ty); + + assert_eq!(declaration("precision lowp float;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_precision_medium() { + let qual = syntax::PrecisionQualifier::Medium; + let ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }; + let expected = syntax::Declaration::Precision(qual, ty); + + assert_eq!(declaration("precision mediump float;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_precision_high() { + let qual = syntax::PrecisionQualifier::High; + let ty = syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }; + let expected = syntax::Declaration::Precision(qual, ty); + + assert_eq!(declaration("precision highp float;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_uniform_block() { + let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Uniform); + let qual = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![qual_spec]), + }; + let f0 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["a".into()]), + }; + let f1 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec3, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["b".into()]), + }; + let f2 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("foo".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["c".into(), "d".into()]), + }; + let expected = syntax::Declaration::Block(syntax::Block { + qualifier: qual, + name: "UniformBlockTest".into(), + fields: vec![f0, f1, f2], + identifier: None, + }); + + assert_eq!( + declaration("uniform UniformBlockTest { float a; vec3 b; foo c, d; };"), + Ok(("", expected.clone())) + ); + assert_eq!(declaration("uniform \nUniformBlockTest\n {\n \t float a \n; \nvec3 b\n; foo \nc\n, \nd\n;\n }\n\t\n\t\t \t;"), Ok(("", expected))); +} + +#[test] +fn parse_declaration_buffer_block() { + let qual_spec = syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Buffer); + let qual = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![qual_spec]), + }; + let f0 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["a".into()]), + }; + let f1 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec3, + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec![syntax::ArrayedIdentifier::new( + "b", + Some(syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), + }), + )]), + }; + let f2 = syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("foo".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["c".into(), "d".into()]), + }; + let expected = syntax::Declaration::Block(syntax::Block { + qualifier: qual, + name: "UniformBlockTest".into(), + fields: vec![f0, f1, f2], + identifier: None, + }); + + assert_eq!( + declaration("buffer UniformBlockTest { float a; vec3 b[]; foo c, d; };"), + Ok(("", expected.clone())) + ); + assert_eq!(declaration("buffer \nUniformBlockTest\n {\n \t float a \n; \nvec3 b [ ]\n; foo \nc\n, \nd\n;\n }\n\t\n\t\t \t;"), Ok(("", expected))); +} + +#[test] +fn parse_selection_statement_if() { + let cond = syntax::Expr::Binary( + syntax::BinaryOp::LT, + Box::new(syntax::Expr::Variable("foo".into())), + Box::new(syntax::Expr::IntConst(10)), + ); + let ret = Box::new(syntax::Expr::BoolConst(false)); + let st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(ret)), + ))); + let body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: vec![st], + })); + let rest = syntax::SelectionRestStatement::Statement(Box::new(body)); + let expected = syntax::SelectionStatement { + cond: Box::new(cond), + rest, + }; + + assert_eq!( + selection_statement("if (foo < 10) { return false; }K"), + Ok(("K", expected.clone())) + ); + assert_eq!( + selection_statement("if \n(foo<10\n) \t{return false;}K"), + Ok(("K", expected)) + ); +} + +#[test] +fn parse_selection_statement_if_else() { + let cond = syntax::Expr::Binary( + syntax::BinaryOp::LT, + Box::new(syntax::Expr::Variable("foo".into())), + Box::new(syntax::Expr::IntConst(10)), + ); + let if_ret = Box::new(syntax::Expr::FloatConst(0.)); + let if_st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(if_ret)), + ))); + let if_body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: vec![if_st], + })); + let else_ret = Box::new(syntax::Expr::Variable("foo".into())); + let else_st = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(else_ret)), + ))); + let else_body = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: vec![else_st], + })); + let rest = syntax::SelectionRestStatement::Else(Box::new(if_body), Box::new(else_body)); + let expected = syntax::SelectionStatement { + cond: Box::new(cond), + rest, + }; + + assert_eq!( + selection_statement("if (foo < 10) { return 0.f; } else { return foo; }"), + Ok(("", expected.clone())) + ); + assert_eq!( + selection_statement("if \n(foo<10\n) \t{return 0.f\t;\n\n}\n else{\n\t return foo ;}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_switch_statement_empty() { + let head = Box::new(syntax::Expr::Variable("foo".into())); + let expected = syntax::SwitchStatement { + head, + body: Vec::new(), + }; + + assert_eq!( + switch_statement("switch (foo) {}"), + Ok(("", expected.clone())) + ); + assert_eq!( + switch_statement("switch(foo){}"), + Ok(("", expected.clone())) + ); + assert_eq!( + switch_statement("switch\n\n ( foo \t \n) { \n\n }"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_switch_statement_cases() { + let head = Box::new(syntax::Expr::Variable("foo".into())); + let case0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::CaseLabel( + syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(0))), + ))); + let case1 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::CaseLabel( + syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(1))), + ))); + let ret = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::UIntConst(12)))), + ))); + let expected = syntax::SwitchStatement { + head, + body: vec![case0, case1, ret], + }; + + assert_eq!( + switch_statement("switch (foo) { case 0: case 1: return 12u; }"), + Ok(("", expected.clone())) + ); +} + +#[test] +fn parse_case_label_def() { + assert_eq!(case_label("default:"), Ok(("", syntax::CaseLabel::Def))); + assert_eq!(case_label("default :"), Ok(("", syntax::CaseLabel::Def))); +} + +#[test] +fn parse_case_label() { + let expected = syntax::CaseLabel::Case(Box::new(syntax::Expr::IntConst(3))); + + assert_eq!(case_label("case 3:"), Ok(("", expected.clone()))); + assert_eq!(case_label("case\n\t 3 :"), Ok(("", expected))); +} + +#[test] +fn parse_iteration_statement_while_empty() { + let cond = syntax::Condition::Expr(Box::new(syntax::Expr::Binary( + syntax::BinaryOp::GTE, + Box::new(syntax::Expr::Variable("a".into())), + Box::new(syntax::Expr::Variable("b".into())), + ))); + let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: Vec::new(), + })); + let expected = syntax::IterationStatement::While(cond, Box::new(st)); + + assert_eq!( + iteration_statement("while (a >= b) {}"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("while(a>=b){}"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("while ( a >=\n\tb )\t { \n}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_iteration_statement_do_while_empty() { + let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: Vec::new(), + })); + let cond = Box::new(syntax::Expr::Binary( + syntax::BinaryOp::GTE, + Box::new(syntax::Expr::Variable("a".into())), + Box::new(syntax::Expr::Variable("b".into())), + )); + let expected = syntax::IterationStatement::DoWhile(Box::new(st), cond); + + assert_eq!( + iteration_statement("do {} while (a >= b);"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("do{}while(a>=b);"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("do \n {\n} while ( a >=\n\tb )\t \n;"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_iteration_statement_for_empty() { + let init = syntax::ForInitStatement::Declaration(Box::new( + syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { + head: syntax::SingleDeclaration { + ty: syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Float, + array_specifier: None, + }, + }, + name: Some("i".into()), + array_specifier: None, + initializer: Some(syntax::Initializer::Simple(Box::new( + syntax::Expr::FloatConst(0.), + ))), + }, + tail: Vec::new(), + }), + )); + let rest = syntax::ForRestStatement { + condition: Some(syntax::Condition::Expr(Box::new(syntax::Expr::Binary( + syntax::BinaryOp::LTE, + Box::new(syntax::Expr::Variable("i".into())), + Box::new(syntax::Expr::FloatConst(10.)), + )))), + post_expr: Some(Box::new(syntax::Expr::Unary( + syntax::UnaryOp::Inc, + Box::new(syntax::Expr::Variable("i".into())), + ))), + }; + let st = syntax::Statement::Compound(Box::new(syntax::CompoundStatement { + statement_list: Vec::new(), + })); + let expected = syntax::IterationStatement::For(init, rest, Box::new(st)); + + assert_eq!( + iteration_statement("for (float i = 0.f; i <= 10.f; ++i) {}"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("for(float i=0.f;i<=10.f;++i){}"), + Ok(("", expected.clone())) + ); + assert_eq!( + iteration_statement("for\n\t ( \t\n\nfloat \ni \t=\n0.f\n;\ni\t<= 10.f; \n++i\n)\n{\n}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_jump_continue() { + assert_eq!( + jump_statement("continue;"), + Ok(("", syntax::JumpStatement::Continue)) + ); +} + +#[test] +fn parse_jump_break() { + assert_eq!( + jump_statement("break;"), + Ok(("", syntax::JumpStatement::Break)) + ); +} + +#[test] +fn parse_jump_return() { + let expected = syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::IntConst(3)))); + assert_eq!(jump_statement("return 3;"), Ok(("", expected))); +} + +#[test] +fn parse_jump_empty_return() { + let expected = syntax::SimpleStatement::Jump(syntax::JumpStatement::Return(None)); + assert_eq!(simple_statement("return;"), Ok(("", expected))); +} + +#[test] +fn parse_jump_discard() { + assert_eq!( + jump_statement("discard;"), + Ok(("", syntax::JumpStatement::Discard)) + ); +} + +#[test] +fn parse_simple_statement_return() { + let e = syntax::Expr::BoolConst(false); + let expected = syntax::SimpleStatement::Jump(syntax::JumpStatement::Return(Some(Box::new(e)))); + + assert_eq!(simple_statement("return false;"), Ok(("", expected))); +} + +#[test] +fn parse_compound_statement_empty() { + let expected = syntax::CompoundStatement { + statement_list: Vec::new(), + }; + + assert_eq!(compound_statement("{}"), Ok(("", expected))); +} + +#[test] +fn parse_compound_statement() { + let st0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Selection( + syntax::SelectionStatement { + cond: Box::new(syntax::Expr::BoolConst(true)), + rest: syntax::SelectionRestStatement::Statement(Box::new(syntax::Statement::Compound( + Box::new(syntax::CompoundStatement { + statement_list: Vec::new(), + }), + ))), + }, + ))); + let st1 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Declaration( + syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { + head: syntax::SingleDeclaration { + ty: syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::ISampler3D, + array_specifier: None, + }, + }, + name: Some("x".into()), + array_specifier: None, + initializer: None, + }, + tail: Vec::new(), + }), + ))); + let st2 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::IntConst(42)))), + ))); + let expected = syntax::CompoundStatement { + statement_list: vec![st0, st1, st2], + }; + + assert_eq!( + compound_statement("{ if (true) {} isampler3D x; return 42 ; }"), + Ok(("", expected.clone())) + ); + assert_eq!( + compound_statement("{if(true){}isampler3D x;return 42;}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_function_definition() { + let rt = syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::IImage2DArray, + array_specifier: None, + }, + }; + let fp = syntax::FunctionPrototype { + ty: rt, + name: "foo".into(), + parameters: Vec::new(), + }; + let st0 = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Jump( + syntax::JumpStatement::Return(Some(Box::new(syntax::Expr::Variable("bar".into())))), + ))); + let expected = syntax::FunctionDefinition { + prototype: fp, + statement: syntax::CompoundStatement { + statement_list: vec![st0], + }, + }; + + assert_eq!( + function_definition("iimage2DArray foo() { return bar; }"), + Ok(("", expected.clone())) + ); + assert_eq!( + function_definition("iimage2DArray \tfoo\n()\n \n{\n return \nbar\n;}"), + Ok(("", expected.clone())) + ); + assert_eq!( + function_definition("iimage2DArray foo(){return bar;}"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_buffer_block_0() { + let src = include_str!("../data/tests/buffer_block_0.glsl"); + let main_fn = syntax::ExternalDeclaration::FunctionDefinition(syntax::FunctionDefinition { + prototype: syntax::FunctionPrototype { + ty: syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Void, + array_specifier: None, + }, + }, + name: "main".into(), + parameters: Vec::new(), + }, + statement: syntax::CompoundStatement { + statement_list: Vec::new(), + }, + }); + let buffer_block = + syntax::ExternalDeclaration::Declaration(syntax::Declaration::Block(syntax::Block { + qualifier: syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![syntax::TypeQualifierSpec::Storage( + syntax::StorageQualifier::Buffer, + )]), + }, + name: "Foo".into(), + fields: vec![syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("char".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec![syntax::ArrayedIdentifier::new( + "tiles", + Some(syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), + }), + )]), + }], + identifier: Some("main_tiles".into()), + })); + let expected = syntax::TranslationUnit(syntax::NonEmpty(vec![buffer_block, main_fn])); + + assert_eq!(translation_unit(src), Ok(("", expected))); +} + +#[test] +fn parse_layout_buffer_block_0() { + let src = include_str!("../data/tests/layout_buffer_block_0.glsl"); + let layout = syntax::LayoutQualifier { + ids: syntax::NonEmpty(vec![ + syntax::LayoutQualifierSpec::Identifier( + "set".into(), + Some(Box::new(syntax::Expr::IntConst(0))), + ), + syntax::LayoutQualifierSpec::Identifier( + "binding".into(), + Some(Box::new(syntax::Expr::IntConst(0))), + ), + ]), + }; + let type_qual = syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(vec![ + syntax::TypeQualifierSpec::Layout(layout), + syntax::TypeQualifierSpec::Storage(syntax::StorageQualifier::Buffer), + ]), + }; + let block = syntax::ExternalDeclaration::Declaration(syntax::Declaration::Block(syntax::Block { + qualifier: type_qual, + name: "Foo".into(), + fields: vec![syntax::StructFieldSpecifier { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::TypeName("char".into()), + array_specifier: None, + }, + identifiers: syntax::NonEmpty(vec!["a".into()]), + }], + identifier: Some("foo".into()), + })); + + let expected = syntax::TranslationUnit(syntax::NonEmpty(vec![block])); + + assert_eq!(translation_unit(src), Ok(("", expected))); +} + +#[test] +fn parse_pp_space0() { + assert_eq!(pp_space0(" \\\n "), Ok(("", " \\\n "))); + assert_eq!(pp_space0(""), Ok(("", ""))); +} + +#[test] +fn parse_pp_version_number() { + assert_eq!(pp_version_number("450"), Ok(("", 450))); +} + +#[test] +fn parse_pp_version_profile() { + assert_eq!( + pp_version_profile("core"), + Ok(("", syntax::PreprocessorVersionProfile::Core)) + ); + assert_eq!( + pp_version_profile("compatibility"), + Ok(("", syntax::PreprocessorVersionProfile::Compatibility)) + ); + assert_eq!( + pp_version_profile("es"), + Ok(("", syntax::PreprocessorVersionProfile::ES)) + ); +} + +#[test] +fn parse_pp_version() { + assert_eq!( + preprocessor("#version 450\n"), + Ok(( + "", + syntax::Preprocessor::Version(syntax::PreprocessorVersion { + version: 450, + profile: None, + }) + )) + ); + + assert_eq!( + preprocessor("#version 450 core\n"), + Ok(( + "", + syntax::Preprocessor::Version(syntax::PreprocessorVersion { + version: 450, + profile: Some(syntax::PreprocessorVersionProfile::Core) + }) + )) + ); +} + +#[test] +fn parse_pp_version_newline() { + assert_eq!( + preprocessor("#version 450\n"), + Ok(( + "", + syntax::Preprocessor::Version(syntax::PreprocessorVersion { + version: 450, + profile: None, + }) + )) + ); + + assert_eq!( + preprocessor("#version 450 core\n"), + Ok(( + "", + syntax::Preprocessor::Version(syntax::PreprocessorVersion { + version: 450, + profile: Some(syntax::PreprocessorVersionProfile::Core) + }) + )) + ); +} + +#[test] +fn parse_pp_define() { + let expect = |v: &str| { + Ok(( + "", + syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { + ident: "test".into(), + value: v.to_owned(), + }), + )) + }; + + assert_eq!(preprocessor("#define test 1.0"), expect("1.0")); + assert_eq!(preprocessor("#define test \\\n 1.0"), expect("1.0")); + assert_eq!(preprocessor("#define test 1.0\n"), expect("1.0")); + + assert_eq!( + preprocessor("#define test123 .0f\n"), + Ok(( + "", + syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { + ident: "test123".into(), + value: ".0f".to_owned() + }) + )) + ); + + assert_eq!( + preprocessor("#define test 1\n"), + Ok(( + "", + syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { + ident: "test".into(), + value: "1".to_owned() + }) + )) + ); +} + +#[test] +fn parse_pp_define_with_args() { + let expected = syntax::Preprocessor::Define(syntax::PreprocessorDefine::FunctionLike { + ident: "add".into(), + args: vec![ + syntax::Identifier::new("x").unwrap(), + syntax::Identifier::new("y").unwrap(), + ], + value: "(x + y)".to_owned(), + }); + + assert_eq!( + preprocessor("#define \\\n add(x, y) \\\n (x + y)"), + Ok(("", expected.clone())) + ); + + assert_eq!( + preprocessor("#define \\\n add( x, y ) \\\n (x + y)"), + Ok(("", expected)) + ); +} + +#[test] +fn parse_pp_define_multiline() { + assert_eq!( + preprocessor( + r#"#define foo \ + 32"# + ), + Ok(( + "", + syntax::Preprocessor::Define(syntax::PreprocessorDefine::ObjectLike { + ident: "foo".into(), + value: "32".to_owned(), + }) + )) + ); +} + +#[test] +fn parse_pp_else() { + assert_eq!( + preprocessor("# else\n"), + Ok(("", syntax::Preprocessor::Else)) + ); +} + +#[test] +fn parse_pp_elseif() { + assert_eq!( + preprocessor("# elseif \\\n42\n"), + Ok(( + "", + syntax::Preprocessor::ElseIf(syntax::PreprocessorElseIf { + condition: "42".to_owned() + }) + )) + ); +} + +#[test] +fn parse_pp_endif() { + assert_eq!( + preprocessor("#\\\nendif"), + Ok(("", syntax::Preprocessor::EndIf)) + ); +} + +#[test] +fn parse_pp_error() { + assert_eq!( + preprocessor("#error \\\n some message"), + Ok(( + "", + syntax::Preprocessor::Error(syntax::PreprocessorError { + message: "some message".to_owned() + }) + )) + ); +} + +#[test] +fn parse_pp_if() { + assert_eq!( + preprocessor("# \\\nif 42"), + Ok(( + "", + syntax::Preprocessor::If(syntax::PreprocessorIf { + condition: "42".to_owned() + }) + )) + ); +} + +#[test] +fn parse_pp_ifdef() { + assert_eq!( + preprocessor("#ifdef FOO\n"), + Ok(( + "", + syntax::Preprocessor::IfDef(syntax::PreprocessorIfDef { + ident: syntax::Identifier("FOO".to_owned()) + }) + )) + ); +} + +#[test] +fn parse_pp_ifndef() { + assert_eq!( + preprocessor("#\\\nifndef \\\n FOO\n"), + Ok(( + "", + syntax::Preprocessor::IfNDef(syntax::PreprocessorIfNDef { + ident: syntax::Identifier("FOO".to_owned()) + }) + )) + ); +} + +#[test] +fn parse_pp_include() { + assert_eq!( + preprocessor("#include <filename>\n"), + Ok(( + "", + syntax::Preprocessor::Include(syntax::PreprocessorInclude { + path: syntax::Path::Absolute("filename".to_owned()) + }) + )) + ); + + assert_eq!( + preprocessor("#include \\\n\"filename\"\n"), + Ok(( + "", + syntax::Preprocessor::Include(syntax::PreprocessorInclude { + path: syntax::Path::Relative("filename".to_owned()) + }) + )) + ); +} + +#[test] +fn parse_pp_line() { + assert_eq!( + preprocessor("# line \\\n2\n"), + Ok(( + "", + syntax::Preprocessor::Line(syntax::PreprocessorLine { + line: 2, + source_string_number: None, + }) + )) + ); + + assert_eq!( + preprocessor("#line 2 \\\n 4\n"), + Ok(( + "", + syntax::Preprocessor::Line(syntax::PreprocessorLine { + line: 2, + source_string_number: Some(4), + }) + )) + ); +} + +#[test] +fn parse_pp_pragma() { + assert_eq!( + preprocessor("#\\\npragma some flag"), + Ok(( + "", + syntax::Preprocessor::Pragma(syntax::PreprocessorPragma { + command: "some flag".to_owned() + }) + )) + ); +} + +#[test] +fn parse_pp_undef() { + assert_eq!( + preprocessor("# undef \\\n FOO"), + Ok(( + "", + syntax::Preprocessor::Undef(syntax::PreprocessorUndef { + name: syntax::Identifier("FOO".to_owned()) + }) + )) + ); +} + +#[test] +fn parse_pp_extension_name() { + assert_eq!( + pp_extension_name("all"), + Ok(("", syntax::PreprocessorExtensionName::All)) + ); + assert_eq!( + pp_extension_name("GL_foobar_extension "), + Ok(( + " ", + syntax::PreprocessorExtensionName::Specific("GL_foobar_extension".to_owned()) + )) + ); +} + +#[test] +fn parse_pp_extension_behavior() { + assert_eq!( + pp_extension_behavior("require"), + Ok(("", syntax::PreprocessorExtensionBehavior::Require)) + ); + assert_eq!( + pp_extension_behavior("enable"), + Ok(("", syntax::PreprocessorExtensionBehavior::Enable)) + ); + assert_eq!( + pp_extension_behavior("warn"), + Ok(("", syntax::PreprocessorExtensionBehavior::Warn)) + ); + assert_eq!( + pp_extension_behavior("disable"), + Ok(("", syntax::PreprocessorExtensionBehavior::Disable)) + ); +} + +#[test] +fn parse_pp_extension() { + assert_eq!( + preprocessor("#extension all: require\n"), + Ok(( + "", + syntax::Preprocessor::Extension(syntax::PreprocessorExtension { + name: syntax::PreprocessorExtensionName::All, + behavior: Some(syntax::PreprocessorExtensionBehavior::Require) + }) + )) + ); +} + +#[test] +fn parse_dot_field_expr_array() { + let src = "a[0].xyz;"; + let expected = syntax::Expr::Dot( + Box::new(syntax::Expr::Bracket( + Box::new(syntax::Expr::Variable("a".into())), + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::ExplicitlySized( + Box::new(syntax::Expr::IntConst(0)), + )]), + }, + )), + "xyz".into(), + ); + + assert_eq!(expr(src), Ok((";", expected))); +} + +#[test] +fn parse_dot_field_expr_statement() { + let src = "vec3 v = smoothstep(vec3(border_width), vec3(0.0), v_barycenter).zyx;"; + let fun = syntax::FunIdentifier::Identifier("smoothstep".into()); + let args = vec![ + syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("vec3".into()), + vec![syntax::Expr::Variable("border_width".into())], + ), + syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("vec3".into()), + vec![syntax::Expr::FloatConst(0.)], + ), + syntax::Expr::Variable("v_barycenter".into()), + ]; + let ini = syntax::Initializer::Simple(Box::new(syntax::Expr::Dot( + Box::new(syntax::Expr::FunCall(fun, args)), + "zyx".into(), + ))); + let sd = syntax::SingleDeclaration { + ty: syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Vec3, + array_specifier: None, + }, + }, + name: Some("v".into()), + array_specifier: None, + initializer: Some(ini), + }; + let expected = syntax::Statement::Simple(Box::new(syntax::SimpleStatement::Declaration( + syntax::Declaration::InitDeclaratorList(syntax::InitDeclaratorList { + head: sd, + tail: Vec::new(), + }), + ))); + + assert_eq!(statement(src), Ok(("", expected))); +} + +#[test] +fn parse_arrayed_identifier() { + let expected = syntax::ArrayedIdentifier::new( + "foo", + syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(vec![syntax::ArraySpecifierDimension::Unsized]), + }, + ); + + assert_eq!(arrayed_identifier("foo[]"), Ok(("", expected.clone()))); + assert_eq!(arrayed_identifier("foo \t\n [\n\t ]"), Ok(("", expected))); +} + +#[test] +fn parse_nested_parens() { + let start = std::time::Instant::now(); + parens_expr("((((((((1.0f))))))))").unwrap(); + let elapsed = start.elapsed(); + assert!(elapsed.as_millis() < 100, "{} ms", elapsed.as_millis()); +} diff --git a/third_party/rust/glsl/src/parser.rs b/third_party/rust/glsl/src/parser.rs new file mode 100644 index 0000000000..d8a33f8594 --- /dev/null +++ b/third_party/rust/glsl/src/parser.rs @@ -0,0 +1,121 @@ +//! GLSL parsing. +//! +//! This module gives you several functions and types to deal with GLSL parsing, transforming an +//! input source into an AST. The AST is defined in the [`syntax`] module. +//! +//! You want to use the [`Parse`]’s methods to get starting with parsing and pattern match on +//! the resulting [`Result`]. In case of an error, you can inspect the content of the [`ParseError`] +//! object in the `Err` variant. +//! +//! [`Parse`]: crate::parser::Parse +//! [`ParseError`]: crate::parser::ParseError + +use nom::error::convert_error; +use nom::Err as NomErr; +use std::fmt; + +use crate::parsers::ParserResult; +use crate::syntax; + +/// A parse error. It contains a [`String`] giving information on the reason why the parser failed. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ParseError { + pub info: String, +} + +impl std::error::Error for ParseError {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "error: {}", self.info) + } +} + +/// Run a parser `P` on a given `[&str`] input. +pub(crate) fn run_parser<P, T>(source: &str, parser: P) -> Result<T, ParseError> +where + P: FnOnce(&str) -> ParserResult<T>, +{ + match parser(source) { + Ok((_, x)) => Ok(x), + + Err(e) => match e { + NomErr::Incomplete(_) => Err(ParseError { + info: "incomplete parser".to_owned(), + }), + + NomErr::Error(err) | NomErr::Failure(err) => { + let info = convert_error(source, err); + Err(ParseError { info }) + } + }, + } +} + +/// Class of types that can be parsed. +/// +/// This trait exposes the [`Parse::parse`] function that can be used to parse GLSL types. +/// +/// The methods from this trait are the standard way to parse data into GLSL ASTs. +pub trait Parse: Sized { + /// Parse from a string slice. + fn parse<B>(source: B) -> Result<Self, ParseError> + where + B: AsRef<str>; +} + +/// Macro to implement Parse for a given type. +macro_rules! impl_parse { + ($type_name:ty, $parser_name:ident) => { + impl Parse for $type_name { + fn parse<B>(source: B) -> Result<Self, ParseError> + where + B: AsRef<str>, + { + run_parser(source.as_ref(), $crate::parsers::$parser_name) + } + } + }; +} + +impl_parse!(syntax::Identifier, identifier); +impl_parse!(syntax::TypeSpecifierNonArray, type_specifier_non_array); +impl_parse!(syntax::TypeSpecifier, type_specifier); +impl_parse!(syntax::UnaryOp, unary_op); +impl_parse!(syntax::StructFieldSpecifier, struct_field_specifier); +impl_parse!(syntax::StructSpecifier, struct_specifier); +impl_parse!(syntax::StorageQualifier, storage_qualifier); +impl_parse!(syntax::LayoutQualifier, layout_qualifier); +impl_parse!(syntax::PrecisionQualifier, precision_qualifier); +impl_parse!(syntax::InterpolationQualifier, interpolation_qualifier); +impl_parse!(syntax::TypeQualifier, type_qualifier); +impl_parse!(syntax::TypeQualifierSpec, type_qualifier_spec); +impl_parse!(syntax::FullySpecifiedType, fully_specified_type); +impl_parse!(syntax::ArraySpecifier, array_specifier); +impl_parse!(syntax::Expr, expr); +impl_parse!(syntax::Declaration, declaration); +impl_parse!(syntax::FunctionPrototype, function_prototype); +impl_parse!(syntax::InitDeclaratorList, init_declarator_list); +impl_parse!(syntax::SingleDeclaration, single_declaration); +impl_parse!(syntax::Initializer, initializer); +impl_parse!(syntax::FunIdentifier, function_identifier); +impl_parse!(syntax::AssignmentOp, assignment_op); +impl_parse!(syntax::SimpleStatement, simple_statement); +impl_parse!(syntax::ExprStatement, expr_statement); +impl_parse!(syntax::SelectionStatement, selection_statement); +impl_parse!(syntax::SwitchStatement, switch_statement); +impl_parse!(syntax::CaseLabel, case_label); +impl_parse!(syntax::IterationStatement, iteration_statement); +impl_parse!(syntax::JumpStatement, jump_statement); +impl_parse!(syntax::Condition, condition); +impl_parse!(syntax::Statement, statement); +impl_parse!(syntax::CompoundStatement, compound_statement); +impl_parse!(syntax::FunctionDefinition, function_definition); +impl_parse!(syntax::ExternalDeclaration, external_declaration); +impl_parse!(syntax::TranslationUnit, translation_unit); +impl_parse!(syntax::Preprocessor, preprocessor); +impl_parse!(syntax::PreprocessorVersion, pp_version); +impl_parse!(syntax::PreprocessorVersionProfile, pp_version_profile); +impl_parse!(syntax::PreprocessorExtensionName, pp_extension_name); +impl_parse!(syntax::PreprocessorExtensionBehavior, pp_extension_behavior); +impl_parse!(syntax::PreprocessorExtension, pp_extension); diff --git a/third_party/rust/glsl/src/parsers.rs b/third_party/rust/glsl/src/parsers.rs new file mode 100644 index 0000000000..824f7bbba3 --- /dev/null +++ b/third_party/rust/glsl/src/parsers.rs @@ -0,0 +1,1866 @@ +//! GLSL parsers. +//! +//! The more general parser is `translation_unit`, that recognizes the most external form of a GLSL +//! source (a shader, basically). +//! +//! Other parsers are exported if you want more control on how you want to parse your source. + +mod nom_helpers; + +use nom::branch::alt; +use nom::bytes::complete::{tag, take_until, take_while1}; +use nom::character::complete::{anychar, char, digit1, space0, space1}; +use nom::character::{is_hex_digit, is_oct_digit}; +use nom::combinator::{cut, map, not, opt, peek, recognize, value, verify}; +use nom::error::{ErrorKind, ParseError as _, VerboseError, VerboseErrorKind}; +use nom::multi::{fold_many0, many0, many1, separated_list0}; +use nom::sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}; +use nom::{Err as NomErr, ParseTo}; +use std::num::ParseIntError; + +pub use self::nom_helpers::ParserResult; +use self::nom_helpers::{blank_space, cnst, eol, many0_, str_till_eol}; +use crate::syntax; + +// Parse a keyword. A keyword is just a regular string that must be followed by punctuation. +fn keyword<'a>(kwd: &'a str) -> impl FnMut(&'a str) -> ParserResult<'a, &'a str> { + terminated( + tag(kwd), + not(verify(peek(anychar), |&c| identifier_pred(c))), + ) +} + +/// Parse a single comment. +pub fn comment(i: &str) -> ParserResult<&str> { + preceded( + char('/'), + alt(( + preceded(char('/'), cut(str_till_eol)), + preceded(char('*'), cut(terminated(take_until("*/"), tag("*/")))), + )), + )(i) +} + +/// Parse several comments. +pub fn comments(i: &str) -> ParserResult<&str> { + recognize(many0_(terminated(comment, blank_space)))(i) +} + +/// In-between token parser (spaces and comments). +/// +/// This parser also allows to break a line into two by finishing the line with a backslack ('\'). +fn blank(i: &str) -> ParserResult<()> { + value((), preceded(blank_space, comments))(i) +} + +#[inline] +fn identifier_pred(ch: char) -> bool { + ch.is_alphanumeric() || ch == '_' +} + +#[inline] +fn verify_identifier(s: &str) -> bool { + !char::from(s.as_bytes()[0]).is_digit(10) +} + +/// Parse an identifier (raw version). +fn identifier_str(i: &str) -> ParserResult<&str> { + verify(take_while1(identifier_pred), verify_identifier)(i) +} + +/// Parse a string that could be used as an identifier. +pub fn string(i: &str) -> ParserResult<String> { + map(identifier_str, String::from)(i) +} + +/// Parse an identifier. +pub fn identifier(i: &str) -> ParserResult<syntax::Identifier> { + map(string, syntax::Identifier)(i) +} + +/// Parse a type name. +pub fn type_name(i: &str) -> ParserResult<syntax::TypeName> { + map(string, syntax::TypeName)(i) +} + +/// Parse a non-empty list of type names, delimited by comma (,). +fn nonempty_type_names(i: &str) -> ParserResult<Vec<syntax::TypeName>> { + separated_list0(terminated(char(','), blank), terminated(type_name, blank))(i) +} + +/// Parse a type specifier non struct. +pub fn type_specifier_non_struct(i: &str) -> ParserResult<syntax::TypeSpecifierNonArray> { + let (i1, t) = identifier_str(i)?; + + match t { + "void" => Ok((i1, syntax::TypeSpecifierNonArray::Void)), + "bool" => Ok((i1, syntax::TypeSpecifierNonArray::Bool)), + "int" => Ok((i1, syntax::TypeSpecifierNonArray::Int)), + "uint" => Ok((i1, syntax::TypeSpecifierNonArray::UInt)), + "float" => Ok((i1, syntax::TypeSpecifierNonArray::Float)), + "double" => Ok((i1, syntax::TypeSpecifierNonArray::Double)), + "vec2" => Ok((i1, syntax::TypeSpecifierNonArray::Vec2)), + "vec3" => Ok((i1, syntax::TypeSpecifierNonArray::Vec3)), + "vec4" => Ok((i1, syntax::TypeSpecifierNonArray::Vec4)), + "dvec2" => Ok((i1, syntax::TypeSpecifierNonArray::DVec2)), + "dvec3" => Ok((i1, syntax::TypeSpecifierNonArray::DVec3)), + "dvec4" => Ok((i1, syntax::TypeSpecifierNonArray::DVec4)), + "bvec2" => Ok((i1, syntax::TypeSpecifierNonArray::BVec2)), + "bvec3" => Ok((i1, syntax::TypeSpecifierNonArray::BVec3)), + "bvec4" => Ok((i1, syntax::TypeSpecifierNonArray::BVec4)), + "ivec2" => Ok((i1, syntax::TypeSpecifierNonArray::IVec2)), + "ivec3" => Ok((i1, syntax::TypeSpecifierNonArray::IVec3)), + "ivec4" => Ok((i1, syntax::TypeSpecifierNonArray::IVec4)), + "uvec2" => Ok((i1, syntax::TypeSpecifierNonArray::UVec2)), + "uvec3" => Ok((i1, syntax::TypeSpecifierNonArray::UVec3)), + "uvec4" => Ok((i1, syntax::TypeSpecifierNonArray::UVec4)), + "mat2" => Ok((i1, syntax::TypeSpecifierNonArray::Mat2)), + "mat3" => Ok((i1, syntax::TypeSpecifierNonArray::Mat3)), + "mat4" => Ok((i1, syntax::TypeSpecifierNonArray::Mat4)), + "mat2x2" => Ok((i1, syntax::TypeSpecifierNonArray::Mat2)), + "mat2x3" => Ok((i1, syntax::TypeSpecifierNonArray::Mat23)), + "mat2x4" => Ok((i1, syntax::TypeSpecifierNonArray::Mat24)), + "mat3x2" => Ok((i1, syntax::TypeSpecifierNonArray::Mat32)), + "mat3x3" => Ok((i1, syntax::TypeSpecifierNonArray::Mat3)), + "mat3x4" => Ok((i1, syntax::TypeSpecifierNonArray::Mat34)), + "mat4x2" => Ok((i1, syntax::TypeSpecifierNonArray::Mat42)), + "mat4x3" => Ok((i1, syntax::TypeSpecifierNonArray::Mat43)), + "mat4x4" => Ok((i1, syntax::TypeSpecifierNonArray::Mat4)), + "dmat2" => Ok((i1, syntax::TypeSpecifierNonArray::DMat2)), + "dmat3" => Ok((i1, syntax::TypeSpecifierNonArray::DMat3)), + "dmat4" => Ok((i1, syntax::TypeSpecifierNonArray::DMat4)), + "dmat2x2" => Ok((i1, syntax::TypeSpecifierNonArray::DMat2)), + "dmat2x3" => Ok((i1, syntax::TypeSpecifierNonArray::DMat23)), + "dmat2x4" => Ok((i1, syntax::TypeSpecifierNonArray::DMat24)), + "dmat3x2" => Ok((i1, syntax::TypeSpecifierNonArray::DMat32)), + "dmat3x3" => Ok((i1, syntax::TypeSpecifierNonArray::DMat3)), + "dmat3x4" => Ok((i1, syntax::TypeSpecifierNonArray::DMat34)), + "dmat4x2" => Ok((i1, syntax::TypeSpecifierNonArray::DMat42)), + "dmat4x3" => Ok((i1, syntax::TypeSpecifierNonArray::DMat43)), + "dmat4x4" => Ok((i1, syntax::TypeSpecifierNonArray::DMat4)), + "sampler1D" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler1D)), + "image1D" => Ok((i1, syntax::TypeSpecifierNonArray::Image1D)), + "sampler2D" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2D)), + "image2D" => Ok((i1, syntax::TypeSpecifierNonArray::Image2D)), + "sampler3D" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler3D)), + "image3D" => Ok((i1, syntax::TypeSpecifierNonArray::Image3D)), + "samplerCube" => Ok((i1, syntax::TypeSpecifierNonArray::SamplerCube)), + "imageCube" => Ok((i1, syntax::TypeSpecifierNonArray::ImageCube)), + "sampler2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DRect)), + "image2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::Image2DRect)), + "sampler1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler1DArray)), + "image1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::Image1DArray)), + "sampler2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DArray)), + "image2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::Image2DArray)), + "samplerBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::SamplerBuffer)), + "imageBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::ImageBuffer)), + "sampler2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DMS)), + "image2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::Image2DMS)), + "sampler2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DMSArray)), + "image2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::Image2DMSArray)), + "samplerCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::SamplerCubeArray)), + "imageCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::ImageCubeArray)), + "sampler1DShadow" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler1DShadow)), + "sampler2DShadow" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DShadow)), + "sampler2DRectShadow" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DRectShadow)), + "sampler1DArrayShadow" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler1DArrayShadow)), + "sampler2DArrayShadow" => Ok((i1, syntax::TypeSpecifierNonArray::Sampler2DArrayShadow)), + "samplerCubeShadow" => Ok((i1, syntax::TypeSpecifierNonArray::SamplerCubeShadow)), + "samplerCubeArrayShadow" => Ok((i1, syntax::TypeSpecifierNonArray::SamplerCubeArrayShadow)), + "isampler1D" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler1D)), + "iimage1D" => Ok((i1, syntax::TypeSpecifierNonArray::IImage1D)), + "isampler2D" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler2D)), + "iimage2D" => Ok((i1, syntax::TypeSpecifierNonArray::IImage2D)), + "isampler3D" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler3D)), + "iimage3D" => Ok((i1, syntax::TypeSpecifierNonArray::IImage3D)), + "isamplerCube" => Ok((i1, syntax::TypeSpecifierNonArray::ISamplerCube)), + "iimageCube" => Ok((i1, syntax::TypeSpecifierNonArray::IImageCube)), + "isampler2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler2DRect)), + "iimage2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::IImage2DRect)), + "isampler1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler1DArray)), + "iimage1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::IImage1DArray)), + "isampler2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler2DArray)), + "iimage2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::IImage2DArray)), + "isamplerBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::ISamplerBuffer)), + "iimageBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::IImageBuffer)), + "isampler2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler2DMS)), + "iimage2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::IImage2DMS)), + "isampler2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::ISampler2DMSArray)), + "iimage2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::IImage2DMSArray)), + "isamplerCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::ISamplerCubeArray)), + "iimageCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::IImageCubeArray)), + "atomic_uint" => Ok((i1, syntax::TypeSpecifierNonArray::AtomicUInt)), + "usampler1D" => Ok((i1, syntax::TypeSpecifierNonArray::USampler1D)), + "uimage1D" => Ok((i1, syntax::TypeSpecifierNonArray::UImage1D)), + "usampler2D" => Ok((i1, syntax::TypeSpecifierNonArray::USampler2D)), + "uimage2D" => Ok((i1, syntax::TypeSpecifierNonArray::UImage2D)), + "usampler3D" => Ok((i1, syntax::TypeSpecifierNonArray::USampler3D)), + "uimage3D" => Ok((i1, syntax::TypeSpecifierNonArray::UImage3D)), + "usamplerCube" => Ok((i1, syntax::TypeSpecifierNonArray::USamplerCube)), + "uimageCube" => Ok((i1, syntax::TypeSpecifierNonArray::UImageCube)), + "usampler2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::USampler2DRect)), + "uimage2DRect" => Ok((i1, syntax::TypeSpecifierNonArray::UImage2DRect)), + "usampler1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::USampler1DArray)), + "uimage1DArray" => Ok((i1, syntax::TypeSpecifierNonArray::UImage1DArray)), + "usampler2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::USampler2DArray)), + "uimage2DArray" => Ok((i1, syntax::TypeSpecifierNonArray::UImage2DArray)), + "usamplerBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::USamplerBuffer)), + "uimageBuffer" => Ok((i1, syntax::TypeSpecifierNonArray::UImageBuffer)), + "usampler2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::USampler2DMS)), + "uimage2DMS" => Ok((i1, syntax::TypeSpecifierNonArray::UImage2DMS)), + "usampler2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::USampler2DMSArray)), + "uimage2DMSArray" => Ok((i1, syntax::TypeSpecifierNonArray::UImage2DMSArray)), + "usamplerCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::USamplerCubeArray)), + "uimageCubeArray" => Ok((i1, syntax::TypeSpecifierNonArray::UImageCubeArray)), + _ => { + let vek = VerboseErrorKind::Context("unknown type specifier non array"); + let ve = VerboseError { + errors: vec![(i1, vek)], + }; + Err(NomErr::Error(ve)) + } + } +} + +/// Parse a type specifier (non-array version). +pub fn type_specifier_non_array(i: &str) -> ParserResult<syntax::TypeSpecifierNonArray> { + alt(( + type_specifier_non_struct, + map(struct_specifier, syntax::TypeSpecifierNonArray::Struct), + map(type_name, syntax::TypeSpecifierNonArray::TypeName), + ))(i) +} + +/// Parse a type specifier. +pub fn type_specifier(i: &str) -> ParserResult<syntax::TypeSpecifier> { + map( + pair( + type_specifier_non_array, + opt(preceded(blank, array_specifier)), + ), + |(ty, array_specifier)| syntax::TypeSpecifier { + ty, + array_specifier, + }, + )(i) +} + +/// Parse the void type. +pub fn void(i: &str) -> ParserResult<()> { + value((), keyword("void"))(i) +} + +/// Parse a digit that precludes a leading 0. +pub(crate) fn nonzero_digits(i: &str) -> ParserResult<&str> { + verify(digit1, |s: &str| s.as_bytes()[0] != b'0')(i) +} + +#[inline] +fn is_octal(s: &str) -> bool { + s.as_bytes()[0] == b'0' && s.bytes().all(is_oct_digit) +} + +#[inline] +fn all_hexa(s: &str) -> bool { + s.bytes().all(is_hex_digit) +} + +#[inline] +fn alphanumeric_no_u(c: char) -> bool { + c.is_alphanumeric() && c != 'u' && c != 'U' +} + +/// Parse an hexadecimal literal. +pub(crate) fn hexadecimal_lit(i: &str) -> ParserResult<Result<u32, ParseIntError>> { + preceded( + preceded(char('0'), cut(alt((char('x'), char('X'))))), // 0x | 0X + cut(map(verify(take_while1(alphanumeric_no_u), all_hexa), |i| { + u32::from_str_radix(i, 16) + })), + )(i) +} + +/// Parse an octal literal. +pub(crate) fn octal_lit(i: &str) -> ParserResult<Result<u32, ParseIntError>> { + map(verify(take_while1(alphanumeric_no_u), is_octal), |i| { + u32::from_str_radix(i, 8) + })(i) +} + +/// Parse a decimal literal. +pub(crate) fn decimal_lit(i: &str) -> ParserResult<Result<u32, ParseIntError>> { + map(nonzero_digits, |i| i.parse())(i) +} + +/// Parse a literal integral string. +/// +/// From the GLSL 4.30 spec: +/// +/// > No white space is allowed between the digits of an integer +/// > constant, including after the leading 0 or after the leading +/// > 0x or 0X of a constant, or before the suffix u or U. When +/// > tokenizing, the maximal token matching the above will be +/// > recognized before a new token is started. When the suffix u or +/// > U is present, the literal has type uint, otherwise the type is +/// > int. A leading unary minus sign (-) is interpreted as an +/// > arithmetic unary negation, not as part of the constant. Hence, +/// > literals themselves are always expressed with non-negative +/// > syntax, though they could result in a negative value. +/// +/// > It is a compile-time error to provide a literal integer whose +/// > bit pattern cannot fit in 32 bits. The bit pattern of the +/// > literal is always used unmodified. So a signed literal whose +/// > bit pattern includes a set sign bit creates a negative value. +pub fn integral_lit_try(i: &str) -> ParserResult<Result<i32, ParseIntError>> { + let (i, sign) = opt(char('-'))(i)?; + + map(alt((octal_lit, hexadecimal_lit, decimal_lit)), move |lit| { + lit.map(|v| { + let v = v as i32; + + if sign.is_some() { + -v + } else { + v + } + }) + })(i) +} + +pub fn integral_lit(i: &str) -> ParserResult<i32> { + match integral_lit_try(i) { + Ok((i, v)) => match v { + Ok(v) => Ok((i, v)), + _ => Err(NomErr::Failure(VerboseError::from_error_kind( + i, + ErrorKind::AlphaNumeric, + ))), + }, + + Err(NomErr::Failure(x)) | Err(NomErr::Error(x)) => Err(NomErr::Error(x)), + + Err(NomErr::Incomplete(n)) => Err(NomErr::Incomplete(n)), + } +} + +/// Parse the unsigned suffix. +pub(crate) fn unsigned_suffix(i: &str) -> ParserResult<char> { + alt((char('u'), char('U')))(i) +} + +/// Parse a literal unsigned string. +pub fn unsigned_lit(i: &str) -> ParserResult<u32> { + map(terminated(integral_lit, unsigned_suffix), |lit| lit as u32)(i) +} + +/// Parse a floating point suffix. +fn float_suffix(i: &str) -> ParserResult<&str> { + alt((keyword("f"), keyword("F")))(i) +} + +/// Parse a double point suffix. +fn double_suffix(i: &str) -> ParserResult<&str> { + alt((keyword("lf"), keyword("LF")))(i) +} + +/// Parse the exponent part of a floating point literal. +fn floating_exponent(i: &str) -> ParserResult<()> { + value( + (), + preceded( + alt((char('e'), char('E'))), + preceded(opt(alt((char('+'), char('-')))), digit1), + ), + )(i) +} + +/// Parse the fractional constant part of a floating point literal. +fn floating_frac(i: &str) -> ParserResult<()> { + alt(( + value((), preceded(char('.'), digit1)), + value((), delimited(digit1, char('.'), opt(digit1))), + ))(i) +} + +/// Parse the « middle » part of a floating value – i.e. fractional and exponential parts. +fn floating_middle(i: &str) -> ParserResult<&str> { + recognize(alt(( + value((), preceded(floating_frac, opt(floating_exponent))), + value((), preceded(nonzero_digits, floating_exponent)), + )))(i) +} + +/// Parse a float literal string. +pub fn float_lit(i: &str) -> ParserResult<f32> { + let (i, (sign, f)) = tuple(( + opt(char('-')), + terminated(floating_middle, pair(opt(float_suffix), not(double_suffix))), + ))(i)?; + + // if the parsed data is in the accepted form ".394634…", we parse it as if it was < 0 + let n: f32 = if f.as_bytes()[0] == b'.' { + let mut f_ = f.to_owned(); + f_.insert(0, '0'); + + f_.parse().unwrap() + } else { + f.parse().unwrap() + }; + + // handle the sign and return + let r = if sign.is_some() { -n } else { n }; + Ok((i, r)) +} + +/// Parse a double literal string. +pub fn double_lit(i: &str) -> ParserResult<f64> { + let (i, (sign, f)) = tuple(( + opt(char('-')), + terminated(floating_middle, pair(not(float_suffix), opt(double_suffix))), + ))(i)?; + + // if the parsed data is in the accepted form ".394634…", we parse it as if it was < 0 + let n: f64 = if f.as_bytes()[0] == b'.' { + let mut f_ = f.to_owned(); + f_.insert(0, '0'); + f_.parse().unwrap() + } else { + f.parse().unwrap() + }; + + // handle the sign and return + let r = if sign.is_some() { -n } else { n }; + Ok((i, r)) +} + +/// Parse a constant boolean. +pub fn bool_lit(i: &str) -> ParserResult<bool> { + alt((value(true, keyword("true")), value(false, keyword("false"))))(i) +} + +/// Parse a path literal. +pub fn path_lit(i: &str) -> ParserResult<syntax::Path> { + alt(( + map(path_lit_absolute, syntax::Path::Absolute), + map(path_lit_relative, syntax::Path::Relative), + ))(i) +} + +/// Parse a path literal with angle brackets. +pub fn path_lit_absolute(i: &str) -> ParserResult<String> { + map( + delimited(char('<'), cut(take_until(">")), cut(char('>'))), + |s: &str| s.to_owned(), + )(i) +} + +/// Parse a path literal with double quotes. +pub fn path_lit_relative(i: &str) -> ParserResult<String> { + map( + delimited(char('"'), cut(take_until("\"")), cut(char('"'))), + |s: &str| s.to_owned(), + )(i) +} + +/// Parse a unary operator. +pub fn unary_op(i: &str) -> ParserResult<syntax::UnaryOp> { + alt(( + value(syntax::UnaryOp::Inc, tag("++")), + value(syntax::UnaryOp::Dec, tag("--")), + value(syntax::UnaryOp::Add, char('+')), + value(syntax::UnaryOp::Minus, char('-')), + value(syntax::UnaryOp::Not, char('!')), + value(syntax::UnaryOp::Complement, char('~')), + ))(i) +} + +/// Parse an identifier with an optional array specifier. +pub fn arrayed_identifier(i: &str) -> ParserResult<syntax::ArrayedIdentifier> { + map( + pair(identifier, opt(preceded(blank, array_specifier))), + |(i, a)| syntax::ArrayedIdentifier::new(i, a), + )(i) +} + +/// Parse a struct field declaration. +pub fn struct_field_specifier(i: &str) -> ParserResult<syntax::StructFieldSpecifier> { + let (i, (qualifier, ty, identifiers, _)) = tuple(( + opt(terminated(type_qualifier, blank)), + terminated(type_specifier, blank), + cut(separated_list0( + terminated(char(','), blank), + terminated(arrayed_identifier, blank), + )), + cut(char(';')), + ))(i)?; + + let r = syntax::StructFieldSpecifier { + qualifier, + ty, + identifiers: syntax::NonEmpty(identifiers), + }; + + Ok((i, r)) +} + +/// Parse a struct. +pub fn struct_specifier(i: &str) -> ParserResult<syntax::StructSpecifier> { + preceded( + terminated(keyword("struct"), blank), + map( + pair( + opt(terminated(type_name, blank)), + cut(delimited( + terminated(char('{'), blank), + many1(terminated(struct_field_specifier, blank)), + char('}'), + )), + ), + |(name, fields)| syntax::StructSpecifier { + name, + fields: syntax::NonEmpty(fields), + }, + ), + )(i) +} + +/// Parse a storage qualifier subroutine rule with a list of type names. +pub fn storage_qualifier_subroutine_list(i: &str) -> ParserResult<syntax::StorageQualifier> { + map( + preceded( + terminated(keyword("subroutine"), blank), + delimited( + terminated(char('('), blank), + cut(terminated(nonempty_type_names, blank)), + cut(char(')')), + ), + ), + syntax::StorageQualifier::Subroutine, + )(i) +} + +/// Parse a storage qualifier subroutine rule. +pub fn storage_qualifier_subroutine(i: &str) -> ParserResult<syntax::StorageQualifier> { + alt(( + storage_qualifier_subroutine_list, + value( + syntax::StorageQualifier::Subroutine(Vec::new()), + keyword("subroutine"), + ), + ))(i) +} + +/// Parse a storage qualifier. +pub fn storage_qualifier(i: &str) -> ParserResult<syntax::StorageQualifier> { + alt(( + value(syntax::StorageQualifier::Const, keyword("const")), + value(syntax::StorageQualifier::InOut, keyword("inout")), + value(syntax::StorageQualifier::In, keyword("in")), + value(syntax::StorageQualifier::Out, keyword("out")), + value(syntax::StorageQualifier::Centroid, keyword("centroid")), + value(syntax::StorageQualifier::Patch, keyword("patch")), + value(syntax::StorageQualifier::Sample, keyword("sample")), + value(syntax::StorageQualifier::Uniform, keyword("uniform")), + value(syntax::StorageQualifier::Attribute, keyword("attribute")), + value(syntax::StorageQualifier::Varying, keyword("varying")), + value(syntax::StorageQualifier::Buffer, keyword("buffer")), + value(syntax::StorageQualifier::Shared, keyword("shared")), + value(syntax::StorageQualifier::Coherent, keyword("coherent")), + value(syntax::StorageQualifier::Volatile, keyword("volatile")), + value(syntax::StorageQualifier::Restrict, keyword("restrict")), + value(syntax::StorageQualifier::ReadOnly, keyword("readonly")), + value(syntax::StorageQualifier::WriteOnly, keyword("writeonly")), + storage_qualifier_subroutine, + ))(i) +} + +/// Parse a layout qualifier. +pub fn layout_qualifier(i: &str) -> ParserResult<syntax::LayoutQualifier> { + preceded( + terminated(keyword("layout"), blank), + delimited( + terminated(char('('), blank), + cut(layout_qualifier_inner), + cut(char(')')), + ), + )(i) +} + +fn layout_qualifier_inner(i: &str) -> ParserResult<syntax::LayoutQualifier> { + map( + separated_list0( + terminated(char(','), blank), + terminated(layout_qualifier_spec, blank), + ), + |ids| syntax::LayoutQualifier { + ids: syntax::NonEmpty(ids), + }, + )(i) +} + +fn layout_qualifier_spec(i: &str) -> ParserResult<syntax::LayoutQualifierSpec> { + alt(( + value(syntax::LayoutQualifierSpec::Shared, keyword("shared")), + map( + separated_pair( + terminated(identifier, blank), + terminated(char('='), blank), + cond_expr, + ), + |(i, e)| syntax::LayoutQualifierSpec::Identifier(i, Some(Box::new(e))), + ), + map(identifier, |i| { + syntax::LayoutQualifierSpec::Identifier(i, None) + }), + ))(i) +} + +/// Parse a precision qualifier. +pub fn precision_qualifier(i: &str) -> ParserResult<syntax::PrecisionQualifier> { + alt(( + value(syntax::PrecisionQualifier::High, keyword("highp")), + value(syntax::PrecisionQualifier::Medium, keyword("mediump")), + value(syntax::PrecisionQualifier::Low, keyword("lowp")), + ))(i) +} + +/// Parse an interpolation qualifier. +pub fn interpolation_qualifier(i: &str) -> ParserResult<syntax::InterpolationQualifier> { + alt(( + value(syntax::InterpolationQualifier::Smooth, keyword("smooth")), + value(syntax::InterpolationQualifier::Flat, keyword("flat")), + value( + syntax::InterpolationQualifier::NoPerspective, + keyword("noperspective"), + ), + ))(i) +} + +/// Parse an invariant qualifier. +pub fn invariant_qualifier(i: &str) -> ParserResult<()> { + value((), keyword("invariant"))(i) +} + +/// Parse a precise qualifier. +pub fn precise_qualifier(i: &str) -> ParserResult<()> { + value((), keyword("precise"))(i) +} + +/// Parse a type qualifier. +pub fn type_qualifier(i: &str) -> ParserResult<syntax::TypeQualifier> { + map(many1(terminated(type_qualifier_spec, blank)), |qlfs| { + syntax::TypeQualifier { + qualifiers: syntax::NonEmpty(qlfs), + } + })(i) +} + +/// Parse a type qualifier spec. +pub fn type_qualifier_spec(i: &str) -> ParserResult<syntax::TypeQualifierSpec> { + alt(( + map(storage_qualifier, syntax::TypeQualifierSpec::Storage), + map(layout_qualifier, syntax::TypeQualifierSpec::Layout), + map(precision_qualifier, syntax::TypeQualifierSpec::Precision), + map( + interpolation_qualifier, + syntax::TypeQualifierSpec::Interpolation, + ), + value(syntax::TypeQualifierSpec::Invariant, invariant_qualifier), + value(syntax::TypeQualifierSpec::Precise, precise_qualifier), + ))(i) +} + +/// Parse a fully specified type. +pub fn fully_specified_type(i: &str) -> ParserResult<syntax::FullySpecifiedType> { + map( + pair(opt(type_qualifier), type_specifier), + |(qualifier, ty)| syntax::FullySpecifiedType { qualifier, ty }, + )(i) +} + +/// Parse an array specifier +pub fn array_specifier(i: &str) -> ParserResult<syntax::ArraySpecifier> { + map( + many1(delimited(blank, array_specifier_dimension, blank)), + |dimensions| syntax::ArraySpecifier { + dimensions: syntax::NonEmpty(dimensions), + }, + )(i) +} + +/// Parse an array specifier dimension. +pub fn array_specifier_dimension(i: &str) -> ParserResult<syntax::ArraySpecifierDimension> { + alt(( + value( + syntax::ArraySpecifierDimension::Unsized, + delimited(char('['), blank, char(']')), + ), + map( + delimited( + terminated(char('['), blank), + cut(cond_expr), + preceded(blank, cut(char(']'))), + ), + |e| syntax::ArraySpecifierDimension::ExplicitlySized(Box::new(e)), + ), + ))(i) +} + +/// Parse a primary expression. +pub fn primary_expr(i: &str) -> ParserResult<syntax::Expr> { + alt(( + parens_expr, + map(float_lit, syntax::Expr::FloatConst), + map(double_lit, syntax::Expr::DoubleConst), + map(unsigned_lit, syntax::Expr::UIntConst), + map(integral_lit, syntax::Expr::IntConst), + map(bool_lit, syntax::Expr::BoolConst), + map(identifier, syntax::Expr::Variable), + ))(i) +} + +/// Parse a postfix expression. +pub fn postfix_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, e) = alt(( + function_call_with_identifier, + function_call_with_expr_ident_or_expr, + ))(i)?; + + postfix_part(i, e) +} + +// Parse the postfix part of a primary expression. This function will just parse until it cannot +// find any more postfix construct. +fn postfix_part(i: &str, e: syntax::Expr) -> ParserResult<syntax::Expr> { + let r = alt(( + map(preceded(blank, array_specifier), |a| { + syntax::Expr::Bracket(Box::new(e.clone()), a) + }), + map(preceded(blank, dot_field_selection), |i| { + syntax::Expr::Dot(Box::new(e.clone()), i) + }), + value( + syntax::Expr::PostInc(Box::new(e.clone())), + preceded(blank, tag("++")), + ), + value( + syntax::Expr::PostDec(Box::new(e.clone())), + preceded(blank, tag("--")), + ), + ))(i); + + match r { + Ok((i, e)) => postfix_part(i, e), + Err(NomErr::Error(_)) => Ok((i, e)), + _ => r, + } +} + +/// Parse a unary expression. +pub fn unary_expr(i: &str) -> ParserResult<syntax::Expr> { + alt(( + map(separated_pair(unary_op, blank, unary_expr), |(op, e)| { + syntax::Expr::Unary(op, Box::new(e)) + }), + postfix_expr, + ))(i) +} + +/// Parse an expression between parens. +pub fn parens_expr(i: &str) -> ParserResult<syntax::Expr> { + delimited( + terminated(char('('), blank), + expr, + preceded(blank, cut(char(')'))), + )(i) +} + +/// Parse a dot field selection identifier. +pub fn dot_field_selection(i: &str) -> ParserResult<syntax::Identifier> { + preceded(terminated(char('.'), blank), cut(identifier))(i) +} + +/// Parse a declaration. +pub fn declaration(i: &str) -> ParserResult<syntax::Declaration> { + alt(( + map( + terminated(function_prototype, terminated(blank, char(';'))), + syntax::Declaration::FunctionPrototype, + ), + map( + terminated(init_declarator_list, terminated(blank, char(';'))), + syntax::Declaration::InitDeclaratorList, + ), + precision_declaration, + block_declaration, + global_declaration, + ))(i) +} + +/// Parse a precision declaration. +pub fn precision_declaration(i: &str) -> ParserResult<syntax::Declaration> { + delimited( + terminated(keyword("precision"), blank), + map( + cut(pair( + terminated(precision_qualifier, blank), + terminated(type_specifier, blank), + )), + |(qual, ty)| syntax::Declaration::Precision(qual, ty), + ), + char(';'), + )(i) +} + +/// Parse a block declaration. +pub fn block_declaration(i: &str) -> ParserResult<syntax::Declaration> { + map( + tuple(( + terminated(type_qualifier, blank), + terminated(identifier, blank), + delimited( + terminated(char('{'), blank), + many1(terminated(struct_field_specifier, blank)), + cut(terminated(char('}'), blank)), + ), + alt(( + value(None, preceded(blank, char(';'))), + terminated( + opt(preceded(blank, arrayed_identifier)), + preceded(blank, cut(char(';'))), + ), + )), + )), + |(qualifier, name, fields, identifier)| { + syntax::Declaration::Block(syntax::Block { + qualifier, + name, + fields, + identifier, + }) + }, + )(i) +} + +/// Parse a global declaration. +pub fn global_declaration(i: &str) -> ParserResult<syntax::Declaration> { + map( + pair( + terminated(type_qualifier, blank), + many0(delimited(terminated(char(','), blank), identifier, blank)), + ), + |(qual, idents)| syntax::Declaration::Global(qual, idents), + )(i) +} + +/// Parse a function prototype. +pub fn function_prototype(i: &str) -> ParserResult<syntax::FunctionPrototype> { + terminated(function_declarator, terminated(blank, cut(char(')'))))(i) +} + +/// Parse an init declarator list. +pub fn init_declarator_list(i: &str) -> ParserResult<syntax::InitDeclaratorList> { + map( + pair( + single_declaration, + many0(map( + tuple(( + preceded(delimited(blank, char(','), blank), cut(identifier)), + opt(preceded(blank, array_specifier)), + opt(preceded(delimited(blank, char('='), blank), initializer)), + )), + |(name, arr_spec, init)| syntax::SingleDeclarationNoType { + ident: syntax::ArrayedIdentifier::new(name, arr_spec), + initializer: init, + }, + )), + ), + |(head, tail)| syntax::InitDeclaratorList { head, tail }, + )(i) +} + +/// Parse a single declaration. +pub fn single_declaration(i: &str) -> ParserResult<syntax::SingleDeclaration> { + let (i, ty) = fully_specified_type(i)?; + let ty_ = ty.clone(); + + alt(( + map( + tuple(( + preceded(blank, identifier), + opt(preceded(blank, array_specifier)), + opt(preceded( + delimited(blank, char('='), blank), + cut(initializer), + )), + )), + move |(name, array_specifier, initializer)| syntax::SingleDeclaration { + ty: ty_.clone(), + name: Some(name), + array_specifier, + initializer, + }, + ), + cnst(syntax::SingleDeclaration { + ty, + name: None, + array_specifier: None, + initializer: None, + }), + ))(i) +} + +/// Parse an initializer. +pub fn initializer(i: &str) -> ParserResult<syntax::Initializer> { + alt(( + map(assignment_expr, |e| { + syntax::Initializer::Simple(Box::new(e)) + }), + map( + delimited( + terminated(char('{'), blank), + terminated( + cut(initializer_list), + terminated(blank, opt(terminated(char(','), blank))), + ), + cut(char('}')), + ), + |il| syntax::Initializer::List(syntax::NonEmpty(il)), + ), + ))(i) +} + +/// Parse an initializer list. +pub fn initializer_list(i: &str) -> ParserResult<Vec<syntax::Initializer>> { + separated_list0(delimited(blank, char(','), blank), initializer)(i) +} + +fn function_declarator(i: &str) -> ParserResult<syntax::FunctionPrototype> { + alt(( + function_header_with_parameters, + map(function_header, |(ty, name)| syntax::FunctionPrototype { + ty, + name, + parameters: Vec::new(), + }), + ))(i) +} + +fn function_header(i: &str) -> ParserResult<(syntax::FullySpecifiedType, syntax::Identifier)> { + pair( + terminated(fully_specified_type, blank), + terminated(identifier, terminated(blank, char('('))), + )(i) +} + +fn function_header_with_parameters(i: &str) -> ParserResult<syntax::FunctionPrototype> { + map( + pair( + function_header, + separated_list0( + preceded(blank, char(',')), + preceded(blank, function_parameter_declaration), + ), + ), + |(header, parameters)| syntax::FunctionPrototype { + ty: header.0, + name: header.1, + parameters, + }, + )(i) +} + +fn function_parameter_declaration(i: &str) -> ParserResult<syntax::FunctionParameterDeclaration> { + alt(( + function_parameter_declaration_named, + function_parameter_declaration_unnamed, + ))(i) +} + +fn function_parameter_declaration_named( + i: &str, +) -> ParserResult<syntax::FunctionParameterDeclaration> { + map( + pair( + opt(terminated(type_qualifier, blank)), + function_parameter_declarator, + ), + |(ty_qual, fpd)| syntax::FunctionParameterDeclaration::Named(ty_qual, fpd), + )(i) +} + +fn function_parameter_declaration_unnamed( + i: &str, +) -> ParserResult<syntax::FunctionParameterDeclaration> { + map( + pair(opt(terminated(type_qualifier, blank)), type_specifier), + |(ty_qual, ty_spec)| syntax::FunctionParameterDeclaration::Unnamed(ty_qual, ty_spec), + )(i) +} + +fn function_parameter_declarator(i: &str) -> ParserResult<syntax::FunctionParameterDeclarator> { + map( + tuple(( + terminated(type_specifier, blank), + terminated(identifier, blank), + opt(array_specifier), + )), + |(ty, name, a)| syntax::FunctionParameterDeclarator { + ty, + ident: syntax::ArrayedIdentifier::new(name, a), + }, + )(i) +} + +fn function_call_with_identifier(i: &str) -> ParserResult<syntax::Expr> { + map( + tuple((function_identifier_identifier, function_call_args)), + |(fi, args)| syntax::Expr::FunCall(fi, args), + )(i) +} + +fn function_call_with_expr_ident_or_expr(i: &str) -> ParserResult<syntax::Expr> { + map( + tuple((function_identifier_expr, opt(function_call_args))), + |(expr, args)| match args { + Some(args) => syntax::Expr::FunCall(expr, args), + None => expr.into_expr().unwrap(), + }, + )(i) +} + +fn function_call_args(i: &str) -> ParserResult<Vec<syntax::Expr>> { + preceded( + terminated(terminated(blank, char('(')), blank), + alt(( + map( + terminated(blank, terminated(opt(void), terminated(blank, char(')')))), + |_| vec![], + ), + terminated( + separated_list0( + terminated(char(','), blank), + cut(terminated(assignment_expr, blank)), + ), + cut(char(')')), + ), + )), + )(i) +} + +fn function_identifier_identifier(i: &str) -> ParserResult<syntax::FunIdentifier> { + map( + terminated(identifier, terminated(blank, peek(char('(')))), + syntax::FunIdentifier::Identifier, + )(i) +} + +fn function_identifier_expr(i: &str) -> ParserResult<syntax::FunIdentifier> { + (|i| { + let (i, e) = primary_expr(i)?; + postfix_part(i, e).map(|(i, pfe)| (i, syntax::FunIdentifier::Expr(Box::new(pfe)))) + })(i) +} + +/// Parse a function identifier just behind a function list argument. +pub fn function_identifier(i: &str) -> ParserResult<syntax::FunIdentifier> { + alt((function_identifier_identifier, function_identifier_expr))(i) +} + +/// Parse the most general expression. +pub fn expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, first) = assignment_expr(i)?; + let first_ = first.clone(); + + alt(( + map(preceded(terminated(char(','), blank), expr), move |next| { + syntax::Expr::Comma(Box::new(first_.clone()), Box::new(next)) + }), + cnst(first), + ))(i) +} + +/// Parse an assignment expression. +pub fn assignment_expr(i: &str) -> ParserResult<syntax::Expr> { + alt(( + map( + tuple(( + terminated(unary_expr, blank), + terminated(assignment_op, blank), + assignment_expr, + )), + |(e, o, v)| syntax::Expr::Assignment(Box::new(e), o, Box::new(v)), + ), + cond_expr, + ))(i) +} + +/// Parse an assignment operator. +pub fn assignment_op(i: &str) -> ParserResult<syntax::AssignmentOp> { + alt(( + value(syntax::AssignmentOp::Equal, char('=')), + value(syntax::AssignmentOp::Mult, tag("*=")), + value(syntax::AssignmentOp::Div, tag("/=")), + value(syntax::AssignmentOp::Mod, tag("%=")), + value(syntax::AssignmentOp::Add, tag("+=")), + value(syntax::AssignmentOp::Sub, tag("-=")), + value(syntax::AssignmentOp::LShift, tag("<<=")), + value(syntax::AssignmentOp::RShift, tag(">>=")), + value(syntax::AssignmentOp::And, tag("&=")), + value(syntax::AssignmentOp::Xor, tag("^=")), + value(syntax::AssignmentOp::Or, tag("|=")), + ))(i) +} + +/// Parse a conditional expression. +pub fn cond_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = logical_or_expr(i)?; + + fold_many0( + tuple(( + delimited(blank, char('?'), blank), + cut(terminated(expr, blank)), + cut(terminated(char(':'), blank)), + cut(assignment_expr), + )), + move || a.clone(), + move |acc, (_, b, _, c)| syntax::Expr::Ternary(Box::new(acc), Box::new(b), Box::new(c)), + )(i) +} + +/// Parse a logical OR expression. +pub fn logical_or_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = logical_xor_expr(i)?; + + fold_many0( + preceded(delimited(blank, tag("||"), blank), logical_xor_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::Or, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a logical XOR expression. +pub fn logical_xor_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = logical_and_expr(i)?; + + fold_many0( + preceded(delimited(blank, tag("^^"), blank), logical_and_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::Xor, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a logical AND expression. +pub fn logical_and_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = inclusive_or_expr(i)?; + + fold_many0( + preceded(delimited(blank, tag("&&"), blank), inclusive_or_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::And, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a bitwise OR expression. +pub fn inclusive_or_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = exclusive_or_expr(i)?; + + fold_many0( + preceded(delimited(blank, char('|'), blank), inclusive_or_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::BitOr, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a bitwise XOR expression. +pub fn exclusive_or_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = and_expr(i)?; + + fold_many0( + preceded(delimited(blank, char('^'), blank), exclusive_or_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::BitXor, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a bitwise AND expression. +pub fn and_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = equality_expr(i)?; + + fold_many0( + preceded(delimited(blank, char('&'), blank), and_expr), + move || a.clone(), + move |acc, b| syntax::Expr::Binary(syntax::BinaryOp::BitAnd, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse an equality expression. +pub fn equality_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = rel_expr(i)?; + + fold_many0( + pair( + delimited( + blank, + alt(( + value(syntax::BinaryOp::Equal, tag("==")), + value(syntax::BinaryOp::NonEqual, tag("!=")), + )), + blank, + ), + rel_expr, + ), + move || a.clone(), + move |acc, (op, b)| syntax::Expr::Binary(op, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a relational expression. +pub fn rel_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = shift_expr(i)?; + + fold_many0( + pair( + delimited( + blank, + alt(( + value(syntax::BinaryOp::LTE, tag("<=")), + value(syntax::BinaryOp::GTE, tag(">=")), + value(syntax::BinaryOp::LT, char('<')), + value(syntax::BinaryOp::GT, char('>')), + )), + blank, + ), + shift_expr, + ), + move || a.clone(), + move |acc, (op, b)| syntax::Expr::Binary(op, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a shift expression. +pub fn shift_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = additive_expr(i)?; + + fold_many0( + pair( + delimited( + blank, + alt(( + value(syntax::BinaryOp::LShift, tag("<<")), + value(syntax::BinaryOp::RShift, tag(">>")), + )), + blank, + ), + additive_expr, + ), + move || a.clone(), + move |acc, (op, b)| syntax::Expr::Binary(op, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse an additive expression. +pub fn additive_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = multiplicative_expr(i)?; + + fold_many0( + pair( + delimited( + blank, + alt(( + value(syntax::BinaryOp::Add, char('+')), + value(syntax::BinaryOp::Sub, char('-')), + )), + blank, + ), + multiplicative_expr, + ), + move || a.clone(), + move |acc, (op, b)| syntax::Expr::Binary(op, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a multiplicative expression. +pub fn multiplicative_expr(i: &str) -> ParserResult<syntax::Expr> { + let (i, a) = unary_expr(i)?; + + fold_many0( + pair( + delimited( + blank, + alt(( + value(syntax::BinaryOp::Mult, char('*')), + value(syntax::BinaryOp::Div, char('/')), + value(syntax::BinaryOp::Mod, char('%')), + )), + blank, + ), + unary_expr, + ), + move || a.clone(), + move |acc, (op, b)| syntax::Expr::Binary(op, Box::new(acc), Box::new(b)), + )(i) +} + +/// Parse a simple statement. +pub fn simple_statement(i: &str) -> ParserResult<syntax::SimpleStatement> { + alt(( + map(jump_statement, syntax::SimpleStatement::Jump), + map(iteration_statement, syntax::SimpleStatement::Iteration), + map(case_label, syntax::SimpleStatement::CaseLabel), + map(switch_statement, syntax::SimpleStatement::Switch), + map(selection_statement, syntax::SimpleStatement::Selection), + map(declaration, syntax::SimpleStatement::Declaration), + map(expr_statement, syntax::SimpleStatement::Expression), + ))(i) +} + +/// Parse an expression statement. +pub fn expr_statement(i: &str) -> ParserResult<syntax::ExprStatement> { + terminated(terminated(opt(expr), blank), char(';'))(i) +} + +/// Parse a selection statement. +pub fn selection_statement(i: &str) -> ParserResult<syntax::SelectionStatement> { + map( + tuple(( + terminated(keyword("if"), blank), + cut(terminated(char('('), blank)), + cut(terminated(expr, blank)), + cut(terminated(char(')'), blank)), + cut(selection_rest_statement), + )), + |(_, _, cond_expr, _, rest)| syntax::SelectionStatement { + cond: Box::new(cond_expr), + rest, + }, + )(i) +} + +fn selection_rest_statement(i: &str) -> ParserResult<syntax::SelectionRestStatement> { + let (i, st) = statement(i)?; + let st_ = st.clone(); + + alt(( + map( + preceded(delimited(blank, keyword("else"), blank), cut(statement)), + move |rest| syntax::SelectionRestStatement::Else(Box::new(st_.clone()), Box::new(rest)), + ), + cnst(syntax::SelectionRestStatement::Statement(Box::new(st))), + ))(i) +} + +/// Parse a switch statement. +pub fn switch_statement(i: &str) -> ParserResult<syntax::SwitchStatement> { + map( + tuple(( + terminated(keyword("switch"), blank), + cut(terminated(char('('), blank)), + cut(terminated(expr, blank)), + cut(terminated(char(')'), blank)), + cut(terminated(char('{'), blank)), + cut(many0(terminated(statement, blank))), + cut(char('}')), + )), + |(_, _, head, _, _, body, _)| syntax::SwitchStatement { + head: Box::new(head), + body, + }, + )(i) +} + +/// Parse a case label. +pub fn case_label(i: &str) -> ParserResult<syntax::CaseLabel> { + alt(( + map( + delimited( + terminated(keyword("case"), blank), + cut(terminated(expr, blank)), + cut(char(':')), + ), + |e| syntax::CaseLabel::Case(Box::new(e)), + ), + value( + syntax::CaseLabel::Def, + preceded(terminated(keyword("default"), blank), char(':')), + ), + ))(i) +} + +/// Parse an iteration statement. +pub fn iteration_statement(i: &str) -> ParserResult<syntax::IterationStatement> { + alt(( + iteration_statement_while, + iteration_statement_do_while, + iteration_statement_for, + ))(i) +} + +/// Parse a while statement. +pub fn iteration_statement_while(i: &str) -> ParserResult<syntax::IterationStatement> { + map( + tuple(( + terminated(keyword("while"), blank), + cut(terminated(char('('), blank)), + cut(terminated(condition, blank)), + cut(terminated(char(')'), blank)), + cut(statement), + )), + |(_, _, cond, _, st)| syntax::IterationStatement::While(cond, Box::new(st)), + )(i) +} + +/// Parse a while statement. +pub fn iteration_statement_do_while(i: &str) -> ParserResult<syntax::IterationStatement> { + map( + tuple(( + terminated(keyword("do"), blank), + cut(terminated(statement, blank)), + cut(terminated(keyword("while"), blank)), + cut(terminated(char('('), blank)), + cut(terminated(expr, blank)), + cut(terminated(char(')'), blank)), + cut(char(';')), + )), + |(_, st, _, _, e, _, _)| syntax::IterationStatement::DoWhile(Box::new(st), Box::new(e)), + )(i) +} + +// Parse a for statement. +pub fn iteration_statement_for(i: &str) -> ParserResult<syntax::IterationStatement> { + map( + tuple(( + terminated(keyword("for"), blank), + cut(terminated(char('('), blank)), + cut(terminated(iteration_statement_for_init_statement, blank)), + cut(terminated(iteration_statement_for_rest_statement, blank)), + cut(terminated(char(')'), blank)), + cut(statement), + )), + |(_, _, head, rest, _, body)| syntax::IterationStatement::For(head, rest, Box::new(body)), + )(i) +} + +fn iteration_statement_for_init_statement(i: &str) -> ParserResult<syntax::ForInitStatement> { + alt(( + map(expr_statement, syntax::ForInitStatement::Expression), + map(declaration, |d| { + syntax::ForInitStatement::Declaration(Box::new(d)) + }), + ))(i) +} + +fn iteration_statement_for_rest_statement(i: &str) -> ParserResult<syntax::ForRestStatement> { + map( + separated_pair( + opt(terminated(condition, blank)), + terminated(char(';'), blank), + opt(expr), + ), + |(condition, e)| syntax::ForRestStatement { + condition, + post_expr: e.map(Box::new), + }, + )(i) +} + +/// Parse a jump statement. +pub fn jump_statement(i: &str) -> ParserResult<syntax::JumpStatement> { + alt(( + jump_statement_continue, + jump_statement_break, + jump_statement_return, + jump_statement_discard, + ))(i) +} + +// Parse a continue statement. +pub fn jump_statement_continue(i: &str) -> ParserResult<syntax::JumpStatement> { + value( + syntax::JumpStatement::Continue, + terminated(keyword("continue"), cut(terminated(blank, char(';')))), + )(i) +} + +// Parse a break statement. +pub fn jump_statement_break(i: &str) -> ParserResult<syntax::JumpStatement> { + value( + syntax::JumpStatement::Break, + terminated(keyword("break"), cut(terminated(blank, char(';')))), + )(i) +} + +// Parse a discard statement. +pub fn jump_statement_discard(i: &str) -> ParserResult<syntax::JumpStatement> { + value( + syntax::JumpStatement::Discard, + terminated(keyword("discard"), cut(terminated(blank, char(';')))), + )(i) +} + +// Parse a return statement. +pub fn jump_statement_return(i: &str) -> ParserResult<syntax::JumpStatement> { + map( + delimited( + terminated(keyword("return"), blank), + opt(terminated(expr, blank)), + cut(char(';')), + ), + |e| syntax::JumpStatement::Return(e.map(|e| Box::new(e))), + )(i) +} + +/// Parse a condition. +pub fn condition(i: &str) -> ParserResult<syntax::Condition> { + alt(( + map(expr, |e| syntax::Condition::Expr(Box::new(e))), + condition_assignment, + ))(i) +} + +fn condition_assignment(i: &str) -> ParserResult<syntax::Condition> { + map( + tuple(( + terminated(fully_specified_type, blank), + terminated(identifier, blank), + terminated(char('='), blank), + cut(initializer), + )), + |(ty, id, _, ini)| syntax::Condition::Assignment(ty, id, ini), + )(i) +} + +/// Parse a statement. +pub fn statement(i: &str) -> ParserResult<syntax::Statement> { + alt(( + map(compound_statement, |c| { + syntax::Statement::Compound(Box::new(c)) + }), + map(simple_statement, |s| syntax::Statement::Simple(Box::new(s))), + ))(i) +} + +/// Parse a compound statement. +pub fn compound_statement(i: &str) -> ParserResult<syntax::CompoundStatement> { + map( + delimited( + terminated(char('{'), blank), + many0(terminated(statement, blank)), + cut(char('}')), + ), + |statement_list| syntax::CompoundStatement { statement_list }, + )(i) +} + +/// Parse a function definition. +pub fn function_definition(i: &str) -> ParserResult<syntax::FunctionDefinition> { + map( + pair(terminated(function_prototype, blank), compound_statement), + |(prototype, statement)| syntax::FunctionDefinition { + prototype, + statement, + }, + )(i) +} + +/// Parse an external declaration. +pub fn external_declaration(i: &str) -> ParserResult<syntax::ExternalDeclaration> { + alt(( + map(preprocessor, syntax::ExternalDeclaration::Preprocessor), + map( + function_definition, + syntax::ExternalDeclaration::FunctionDefinition, + ), + map(declaration, syntax::ExternalDeclaration::Declaration), + preceded( + delimited(blank, char(';'), blank), + cut(external_declaration), + ), + ))(i) +} + +/// Parse a translation unit (entry point). +pub fn translation_unit(i: &str) -> ParserResult<syntax::TranslationUnit> { + map( + many1(delimited(blank, external_declaration, blank)), + |eds| syntax::TranslationUnit(syntax::NonEmpty(eds)), + )(i) +} + +/// Parse a preprocessor directive. +pub fn preprocessor(i: &str) -> ParserResult<syntax::Preprocessor> { + preceded( + terminated(char('#'), pp_space0), + cut(alt(( + map(pp_define, syntax::Preprocessor::Define), + value(syntax::Preprocessor::Else, pp_else), + map(pp_elseif, syntax::Preprocessor::ElseIf), + value(syntax::Preprocessor::EndIf, pp_endif), + map(pp_error, syntax::Preprocessor::Error), + map(pp_if, syntax::Preprocessor::If), + map(pp_ifdef, syntax::Preprocessor::IfDef), + map(pp_ifndef, syntax::Preprocessor::IfNDef), + map(pp_include, syntax::Preprocessor::Include), + map(pp_line, syntax::Preprocessor::Line), + map(pp_pragma, syntax::Preprocessor::Pragma), + map(pp_undef, syntax::Preprocessor::Undef), + map(pp_version, syntax::Preprocessor::Version), + map(pp_extension, syntax::Preprocessor::Extension), + ))), + )(i) +} + +/// Parse a preprocessor version number. +pub(crate) fn pp_version_number(i: &str) -> ParserResult<u16> { + map(digit1, |x: &str| x.parse_to().unwrap())(i) +} + +/// Parse a preprocessor version profile. +pub(crate) fn pp_version_profile(i: &str) -> ParserResult<syntax::PreprocessorVersionProfile> { + alt(( + value(syntax::PreprocessorVersionProfile::Core, keyword("core")), + value( + syntax::PreprocessorVersionProfile::Compatibility, + keyword("compatibility"), + ), + value(syntax::PreprocessorVersionProfile::ES, keyword("es")), + ))(i) +} + +/// The space parser in preprocessor directives. +/// +/// This parser is needed to authorize breaking a line with the multiline annotation (\). +pub(crate) fn pp_space0(i: &str) -> ParserResult<&str> { + recognize(many0_(alt((space1, tag("\\\n")))))(i) +} + +/// Parse a preprocessor define. +pub(crate) fn pp_define(i: &str) -> ParserResult<syntax::PreprocessorDefine> { + let (i, ident) = map( + tuple((terminated(keyword("define"), pp_space0), cut(identifier))), + |(_, ident)| ident, + )(i)?; + + alt(( + pp_define_function_like(ident.clone()), + pp_define_object_like(ident), + ))(i) +} + +// Parse an object-like #define content. +pub(crate) fn pp_define_object_like<'a>( + ident: syntax::Identifier, +) -> impl Fn(&'a str) -> ParserResult<'a, syntax::PreprocessorDefine> { + move |i| { + map(preceded(pp_space0, cut(str_till_eol)), |value| { + syntax::PreprocessorDefine::ObjectLike { + ident: ident.clone(), + value: value.to_owned(), + } + })(i) + } +} + +// Parse a function-like #define content. +pub(crate) fn pp_define_function_like<'a>( + ident: syntax::Identifier, +) -> impl Fn(&'a str) -> ParserResult<'a, syntax::PreprocessorDefine> { + move |i| { + map( + tuple(( + terminated(char('('), pp_space0), + separated_list0( + terminated(char(','), pp_space0), + cut(terminated(identifier, pp_space0)), + ), + cut(terminated(char(')'), pp_space0)), + cut(map(str_till_eol, String::from)), + )), + |(_, args, _, value)| syntax::PreprocessorDefine::FunctionLike { + ident: ident.clone(), + args, + value, + }, + )(i) + } +} + +/// Parse a preprocessor else. +pub(crate) fn pp_else(i: &str) -> ParserResult<syntax::Preprocessor> { + value( + syntax::Preprocessor::Else, + tuple((terminated(keyword("else"), pp_space0), cut(eol))), + )(i) +} + +/// Parse a preprocessor elseif. +pub(crate) fn pp_elseif(i: &str) -> ParserResult<syntax::PreprocessorElseIf> { + map( + tuple(( + terminated(keyword("elseif"), pp_space0), + cut(map(str_till_eol, String::from)), + )), + |(_, condition)| syntax::PreprocessorElseIf { condition }, + )(i) +} + +/// Parse a preprocessor endif. +pub(crate) fn pp_endif(i: &str) -> ParserResult<syntax::Preprocessor> { + map( + tuple((terminated(keyword("endif"), space0), cut(eol))), + |(_, _)| syntax::Preprocessor::EndIf, + )(i) +} + +/// Parse a preprocessor error. +pub(crate) fn pp_error(i: &str) -> ParserResult<syntax::PreprocessorError> { + map( + tuple((terminated(keyword("error"), pp_space0), cut(str_till_eol))), + |(_, message)| syntax::PreprocessorError { + message: message.to_owned(), + }, + )(i) +} + +/// Parse a preprocessor if. +pub(crate) fn pp_if(i: &str) -> ParserResult<syntax::PreprocessorIf> { + map( + tuple(( + terminated(keyword("if"), pp_space0), + cut(map(str_till_eol, String::from)), + )), + |(_, condition)| syntax::PreprocessorIf { condition }, + )(i) +} + +/// Parse a preprocessor ifdef. +pub(crate) fn pp_ifdef(i: &str) -> ParserResult<syntax::PreprocessorIfDef> { + map( + tuple(( + terminated(keyword("ifdef"), pp_space0), + cut(terminated(identifier, pp_space0)), + eol, + )), + |(_, ident, _)| syntax::PreprocessorIfDef { ident }, + )(i) +} + +/// Parse a preprocessor ifndef. +pub(crate) fn pp_ifndef(i: &str) -> ParserResult<syntax::PreprocessorIfNDef> { + map( + tuple(( + terminated(keyword("ifndef"), pp_space0), + cut(terminated(identifier, pp_space0)), + eol, + )), + |(_, ident, _)| syntax::PreprocessorIfNDef { ident }, + )(i) +} + +/// Parse a preprocessor include. +pub(crate) fn pp_include(i: &str) -> ParserResult<syntax::PreprocessorInclude> { + map( + tuple(( + terminated(keyword("include"), pp_space0), + cut(terminated(path_lit, pp_space0)), + cut(eol), + )), + |(_, path, _)| syntax::PreprocessorInclude { path }, + )(i) +} + +/// Parse a preprocessor line. +pub(crate) fn pp_line(i: &str) -> ParserResult<syntax::PreprocessorLine> { + map( + tuple(( + terminated(keyword("line"), pp_space0), + cut(terminated(integral_lit, pp_space0)), + opt(terminated(integral_lit, pp_space0)), + cut(eol), + )), + |(_, line, source_string_number, _)| syntax::PreprocessorLine { + line: line as u32, + source_string_number: source_string_number.map(|n| n as u32), + }, + )(i) +} + +/// Parse a preprocessor pragma. +pub(crate) fn pp_pragma(i: &str) -> ParserResult<syntax::PreprocessorPragma> { + map( + tuple((terminated(keyword("pragma"), pp_space0), cut(str_till_eol))), + |(_, command)| syntax::PreprocessorPragma { + command: command.to_owned(), + }, + )(i) +} + +/// Parse a preprocessor undef. +pub(crate) fn pp_undef(i: &str) -> ParserResult<syntax::PreprocessorUndef> { + map( + tuple(( + terminated(keyword("undef"), pp_space0), + cut(terminated(identifier, pp_space0)), + eol, + )), + |(_, name, _)| syntax::PreprocessorUndef { name }, + )(i) +} + +/// Parse a preprocessor version. +pub(crate) fn pp_version(i: &str) -> ParserResult<syntax::PreprocessorVersion> { + map( + tuple(( + terminated(keyword("version"), pp_space0), + cut(terminated(pp_version_number, pp_space0)), + opt(terminated(pp_version_profile, pp_space0)), + cut(eol), + )), + |(_, version, profile, _)| syntax::PreprocessorVersion { version, profile }, + )(i) +} + +/// Parse a preprocessor extension name. +pub(crate) fn pp_extension_name(i: &str) -> ParserResult<syntax::PreprocessorExtensionName> { + alt(( + value(syntax::PreprocessorExtensionName::All, keyword("all")), + map(string, syntax::PreprocessorExtensionName::Specific), + ))(i) +} + +/// Parse a preprocessor extension behavior. +pub(crate) fn pp_extension_behavior( + i: &str, +) -> ParserResult<syntax::PreprocessorExtensionBehavior> { + alt(( + value( + syntax::PreprocessorExtensionBehavior::Require, + keyword("require"), + ), + value( + syntax::PreprocessorExtensionBehavior::Enable, + keyword("enable"), + ), + value(syntax::PreprocessorExtensionBehavior::Warn, keyword("warn")), + value( + syntax::PreprocessorExtensionBehavior::Disable, + keyword("disable"), + ), + ))(i) +} + +/// Parse a preprocessor extension. +pub(crate) fn pp_extension(i: &str) -> ParserResult<syntax::PreprocessorExtension> { + map( + tuple(( + terminated(keyword("extension"), pp_space0), + cut(terminated(pp_extension_name, pp_space0)), + opt(preceded( + terminated(char(':'), pp_space0), + cut(terminated(pp_extension_behavior, pp_space0)), + )), + cut(eol), + )), + |(_, name, behavior, _)| syntax::PreprocessorExtension { name, behavior }, + )(i) +} diff --git a/third_party/rust/glsl/src/parsers/nom_helpers.rs b/third_party/rust/glsl/src/parsers/nom_helpers.rs new file mode 100644 index 0000000000..657468e9e3 --- /dev/null +++ b/third_party/rust/glsl/src/parsers/nom_helpers.rs @@ -0,0 +1,95 @@ +//! Various nom parser helpers. + +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::complete::{anychar, line_ending, multispace1}; +use nom::combinator::{map, recognize, value}; +use nom::error::{ErrorKind, VerboseError, VerboseErrorKind}; +use nom::multi::fold_many0; +use nom::{Err as NomErr, IResult}; + +pub type ParserResult<'a, O> = IResult<&'a str, O, VerboseError<&'a str>>; + +// A constant parser that just forwards the value it’s parametered with without reading anything +// from the input. Especially useful as “fallback” in an alternative parser. +pub fn cnst<'a, T, E>(t: T) -> impl FnMut(&'a str) -> Result<(&'a str, T), E> +where + T: 'a + Clone, +{ + move |i| Ok((i, t.clone())) +} + +// End-of-input parser. +// +// Yields `()` if the parser is at the end of the input; an error otherwise. +pub fn eoi(i: &str) -> ParserResult<()> { + if i.is_empty() { + Ok((i, ())) + } else { + Err(NomErr::Error(VerboseError { + errors: vec![(i, VerboseErrorKind::Nom(ErrorKind::Eof))], + })) + } +} + +// A newline parser that accepts: +// +// - A newline. +// - The end of input. +pub fn eol(i: &str) -> ParserResult<()> { + alt(( + eoi, // this one goes first because it’s very cheap + value((), line_ending), + ))(i) +} + +// Apply the `f` parser until `g` succeeds. Both parsers consume the input. +pub fn till<'a, A, B, F, G>(mut f: F, mut g: G) -> impl FnMut(&'a str) -> ParserResult<'a, ()> +where + F: FnMut(&'a str) -> ParserResult<'a, A>, + G: FnMut(&'a str) -> ParserResult<'a, B>, +{ + move |mut i| loop { + if let Ok((i2, _)) = g(i) { + break Ok((i2, ())); + } + + let (i2, _) = f(i)?; + i = i2; + } +} + +// A version of many0 that discards the result of the parser, preventing allocating. +pub fn many0_<'a, A, F>(mut f: F) -> impl FnMut(&'a str) -> ParserResult<'a, ()> +where + F: FnMut(&'a str) -> ParserResult<'a, A>, +{ + move |i| fold_many0(&mut f, || (), |_, _| ())(i) +} + +/// Parse a string until the end of line. +/// +/// This parser accepts the multiline annotation (\) to break the string on several lines. +/// +/// Discard any leading newline. +pub fn str_till_eol(i: &str) -> ParserResult<&str> { + map( + recognize(till(alt((value((), tag("\\\n")), value((), anychar))), eol)), + |i| { + if i.as_bytes().last() == Some(&b'\n') { + &i[0..i.len() - 1] + } else { + i + } + }, + )(i) +} + +// Blank base parser. +// +// This parser succeeds with multispaces and multiline annotation. +// +// Taylor Swift loves it. +pub fn blank_space(i: &str) -> ParserResult<&str> { + recognize(many0_(alt((multispace1, tag("\\\n")))))(i) +} diff --git a/third_party/rust/glsl/src/syntax.rs b/third_party/rust/glsl/src/syntax.rs new file mode 100644 index 0000000000..36c0ea501f --- /dev/null +++ b/third_party/rust/glsl/src/syntax.rs @@ -0,0 +1,1368 @@ +//! GLSL abstract syntax tree and grammar. +//! +//! This module exports all the grammar syntax that defines GLSL. You’ll be handling ASTs +//! representing your GLSL source. +//! +//! The most external form of a GLSL parsed AST is [`TranslationUnit`] (a shader). Some parts of the +//! tree are *boxed*. This is due to two facts: +//! +//! - Recursion is used, hence we need a way to give our types a static size. +//! - Because of some very deep variants, runtime size would explode if no indirection weren’t +//! in place. +//! +//! The types are commented so feel free to inspect each of theme. As a starter, you should read +//! the documentation of [`Expr`], [`FunctionDefinition`], [`Statement`] and [`TranslationUnit`]. +//! +//! [`Statement`]: crate::syntax::Statement +//! [`TranslationUnit`]: crate::syntax::TranslationUnit +//! [`Expr`]: crate::syntax::Expr +//! [`FunctionDefinition`]: crate::syntax::FunctionDefinition + +use std::fmt; +use std::iter::{once, FromIterator}; +use std::ops::{Deref, DerefMut}; + +/// A non-empty [`Vec`]. It has at least one element. +#[derive(Clone, Debug, PartialEq)] +pub struct NonEmpty<T>(pub Vec<T>); + +impl<T> NonEmpty<T> { + /// Construct a non-empty from an iterator. + /// + /// # Errors + /// + /// `None` if the iterator yields no value. + pub fn from_non_empty_iter<I>(iter: I) -> Option<Self> + where + I: IntoIterator<Item = T>, + { + let vec: Vec<_> = iter.into_iter().collect(); + + if vec.is_empty() { + None + } else { + Some(NonEmpty(vec)) + } + } + + /// Move a new item at the end of the non-empty. + pub fn push(&mut self, item: T) { + self.0.push(item); + } + + /// Move out the last element of the non-empty. + /// + /// # Errors + /// + /// This function returns `None` if called on a non-empty that contains a single element. + pub fn pop(&mut self) -> Option<T> { + if self.0.len() == 1 { + None + } else { + self.0.pop() + } + } +} + +impl<T> IntoIterator for NonEmpty<T> { + type IntoIter = <Vec<T> as IntoIterator>::IntoIter; + type Item = T; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, T> IntoIterator for &'a NonEmpty<T> { + type IntoIter = <&'a Vec<T> as IntoIterator>::IntoIter; + type Item = &'a T; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter() + } +} + +impl<'a, T> IntoIterator for &'a mut NonEmpty<T> { + type IntoIter = <&'a mut Vec<T> as IntoIterator>::IntoIter; + type Item = &'a mut T; + + fn into_iter(self) -> Self::IntoIter { + self.0.iter_mut() + } +} + +impl<T> Extend<T> for NonEmpty<T> { + fn extend<I>(&mut self, iter: I) + where + I: IntoIterator<Item = T>, + { + self.0.extend(iter); + } +} + +/// A path literal. +#[derive(Clone, Debug, PartialEq)] +pub enum Path { + /// Specified with angle brackets. + Absolute(String), + /// Specified with double quotes. + Relative(String), +} + +/// Error that might occur when creating a new [`Identifier`]. +#[derive(Debug)] +pub enum IdentifierError { + StartsWithDigit, + ContainsNonASCIIAlphaNum, +} + +impl std::error::Error for IdentifierError {} + +impl fmt::Display for IdentifierError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + IdentifierError::StartsWithDigit => f.write_str("starts starts with a digit"), + + IdentifierError::ContainsNonASCIIAlphaNum => { + f.write_str("contains at least one non-alphanumeric ASCII character") + } + } + } +} + +/// A generic identifier. +#[derive(Clone, Debug, PartialEq)] +pub struct Identifier(pub String); + +impl Identifier { + /// Create a new [`Identifier`]. + /// + /// # Errors + /// + /// This function will fail if the identifier starts with a digit or contains non-alphanumeric + /// ASCII characters. + pub fn new<N>(name: N) -> Result<Self, IdentifierError> + where + N: Into<String>, + { + let name = name.into(); + + if name + .chars() + .next() + .map(|c| c.is_ascii_alphabetic() || c == '_') + == Some(false) + { + // check the first letter is not a digit + Err(IdentifierError::StartsWithDigit) + } else if name.contains(|c: char| !(c.is_ascii_alphanumeric() || c == '_')) { + // check we only have ASCII alphanumeric characters + Err(IdentifierError::ContainsNonASCIIAlphaNum) + } else { + Ok(Identifier(name)) + } + } + + /// Get the string representation of the identifier. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'a> From<&'a str> for Identifier { + fn from(s: &str) -> Self { + Identifier(s.to_owned()) + } +} + +impl From<String> for Identifier { + fn from(s: String) -> Self { + Identifier(s) + } +} + +impl fmt::Display for Identifier { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// Any type name. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeName(pub String); + +impl TypeName { + /// Create a new [`TypeName`]. + /// + /// # Errors + /// + /// This function will fail if the type name starts with a digit or contains non-alphanumeric + /// ASCII characters. + pub fn new<N>(name: N) -> Result<Self, IdentifierError> + where + N: Into<String>, + { + // build as identifier and unwrap into type name + let Identifier(tn) = Identifier::new(name)?; + Ok(TypeName(tn)) + } + + /// Get the string representation of the type name. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl<'a> From<&'a str> for TypeName { + fn from(s: &str) -> Self { + TypeName(s.to_owned()) + } +} + +impl From<String> for TypeName { + fn from(s: String) -> Self { + TypeName(s) + } +} + +impl fmt::Display for TypeName { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + self.0.fmt(f) + } +} + +/// Type specifier (non-array). +#[derive(Clone, Debug, PartialEq)] +pub enum TypeSpecifierNonArray { + // transparent types + Void, + Bool, + Int, + UInt, + Float, + Double, + Vec2, + Vec3, + Vec4, + DVec2, + DVec3, + DVec4, + BVec2, + BVec3, + BVec4, + IVec2, + IVec3, + IVec4, + UVec2, + UVec3, + UVec4, + Mat2, + Mat3, + Mat4, + Mat23, + Mat24, + Mat32, + Mat34, + Mat42, + Mat43, + DMat2, + DMat3, + DMat4, + DMat23, + DMat24, + DMat32, + DMat34, + DMat42, + DMat43, + // floating point opaque types + Sampler1D, + Image1D, + Sampler2D, + Image2D, + Sampler3D, + Image3D, + SamplerCube, + ImageCube, + Sampler2DRect, + Image2DRect, + Sampler1DArray, + Image1DArray, + Sampler2DArray, + Image2DArray, + SamplerBuffer, + ImageBuffer, + Sampler2DMS, + Image2DMS, + Sampler2DMSArray, + Image2DMSArray, + SamplerCubeArray, + ImageCubeArray, + Sampler1DShadow, + Sampler2DShadow, + Sampler2DRectShadow, + Sampler1DArrayShadow, + Sampler2DArrayShadow, + SamplerCubeShadow, + SamplerCubeArrayShadow, + // signed integer opaque types + ISampler1D, + IImage1D, + ISampler2D, + IImage2D, + ISampler3D, + IImage3D, + ISamplerCube, + IImageCube, + ISampler2DRect, + IImage2DRect, + ISampler1DArray, + IImage1DArray, + ISampler2DArray, + IImage2DArray, + ISamplerBuffer, + IImageBuffer, + ISampler2DMS, + IImage2DMS, + ISampler2DMSArray, + IImage2DMSArray, + ISamplerCubeArray, + IImageCubeArray, + // unsigned integer opaque types + AtomicUInt, + USampler1D, + UImage1D, + USampler2D, + UImage2D, + USampler3D, + UImage3D, + USamplerCube, + UImageCube, + USampler2DRect, + UImage2DRect, + USampler1DArray, + UImage1DArray, + USampler2DArray, + UImage2DArray, + USamplerBuffer, + UImageBuffer, + USampler2DMS, + UImage2DMS, + USampler2DMSArray, + UImage2DMSArray, + USamplerCubeArray, + UImageCubeArray, + Struct(StructSpecifier), + TypeName(TypeName), +} + +/// Type specifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeSpecifier { + pub ty: TypeSpecifierNonArray, + pub array_specifier: Option<ArraySpecifier>, +} + +impl TypeSpecifier { + pub fn new(ty: TypeSpecifierNonArray) -> Self { + TypeSpecifier { + ty, + array_specifier: None, + } + } +} + +impl From<TypeSpecifierNonArray> for TypeSpecifier { + fn from(ty: TypeSpecifierNonArray) -> Self { + TypeSpecifier::new(ty) + } +} + +/// Struct specifier. Used to create new, user-defined types. +#[derive(Clone, Debug, PartialEq)] +pub struct StructSpecifier { + pub name: Option<TypeName>, + pub fields: NonEmpty<StructFieldSpecifier>, +} + +/// Struct field specifier. Used to add fields to struct specifiers. +#[derive(Clone, Debug, PartialEq)] +pub struct StructFieldSpecifier { + pub qualifier: Option<TypeQualifier>, + pub ty: TypeSpecifier, + pub identifiers: NonEmpty<ArrayedIdentifier>, // several identifiers of the same type +} + +impl StructFieldSpecifier { + /// Create a struct field. + pub fn new<A, T>(identifier: A, ty: T) -> Self + where + A: Into<ArrayedIdentifier>, + T: Into<TypeSpecifier>, + { + StructFieldSpecifier { + qualifier: None, + ty: ty.into(), + identifiers: NonEmpty(vec![identifier.into()]), + } + } + + /// Create a list of struct fields that all have the same type. + pub fn new_many<I>(identifiers: I, ty: TypeSpecifier) -> Self + where + I: IntoIterator<Item = ArrayedIdentifier>, + { + StructFieldSpecifier { + qualifier: None, + ty, + identifiers: NonEmpty(identifiers.into_iter().collect()), + } + } +} + +/// An identifier with an optional array specifier. +#[derive(Clone, Debug, PartialEq)] +pub struct ArrayedIdentifier { + pub ident: Identifier, + pub array_spec: Option<ArraySpecifier>, +} + +impl ArrayedIdentifier { + pub fn new<I, AS>(ident: I, array_spec: AS) -> Self + where + I: Into<Identifier>, + AS: Into<Option<ArraySpecifier>>, + { + ArrayedIdentifier { + ident: ident.into(), + array_spec: array_spec.into(), + } + } +} + +impl<'a> From<&'a str> for ArrayedIdentifier { + fn from(ident: &str) -> Self { + ArrayedIdentifier { + ident: Identifier(ident.to_owned()), + array_spec: None, + } + } +} + +impl From<Identifier> for ArrayedIdentifier { + fn from(ident: Identifier) -> Self { + ArrayedIdentifier { + ident, + array_spec: None, + } + } +} + +/// Type qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct TypeQualifier { + pub qualifiers: NonEmpty<TypeQualifierSpec>, +} + +/// Type qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum TypeQualifierSpec { + Storage(StorageQualifier), + Layout(LayoutQualifier), + Precision(PrecisionQualifier), + Interpolation(InterpolationQualifier), + Invariant, + Precise, +} + +/// Storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum StorageQualifier { + Const, + InOut, + In, + Out, + Centroid, + Patch, + Sample, + Uniform, + Attribute, + Varying, + Buffer, + Shared, + Coherent, + Volatile, + Restrict, + ReadOnly, + WriteOnly, + Subroutine(Vec<TypeName>), +} + +/// Layout qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct LayoutQualifier { + pub ids: NonEmpty<LayoutQualifierSpec>, +} + +/// Layout qualifier spec. +#[derive(Clone, Debug, PartialEq)] +pub enum LayoutQualifierSpec { + Identifier(Identifier, Option<Box<Expr>>), + Shared, +} + +/// Precision qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum PrecisionQualifier { + High, + Medium, + Low, +} + +/// Interpolation qualifier. +#[derive(Clone, Debug, PartialEq)] +pub enum InterpolationQualifier { + Smooth, + Flat, + NoPerspective, +} + +/// Fully specified type. +#[derive(Clone, Debug, PartialEq)] +pub struct FullySpecifiedType { + pub qualifier: Option<TypeQualifier>, + pub ty: TypeSpecifier, +} + +impl FullySpecifiedType { + pub fn new(ty: TypeSpecifierNonArray) -> Self { + FullySpecifiedType { + qualifier: None, + ty: TypeSpecifier { + ty, + array_specifier: None, + }, + } + } +} + +impl From<TypeSpecifierNonArray> for FullySpecifiedType { + fn from(ty: TypeSpecifierNonArray) -> Self { + FullySpecifiedType::new(ty) + } +} + +/// Dimensionality of an array. +#[derive(Clone, Debug, PartialEq)] +pub struct ArraySpecifier { + /// List of all the dimensions – possibly unsized or explicitly-sized. + pub dimensions: NonEmpty<ArraySpecifierDimension>, +} + +/// One array specifier dimension. +#[derive(Clone, Debug, PartialEq)] +pub enum ArraySpecifierDimension { + Unsized, + ExplicitlySized(Box<Expr>), +} + +/// A declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum Declaration { + FunctionPrototype(FunctionPrototype), + InitDeclaratorList(InitDeclaratorList), + Precision(PrecisionQualifier, TypeSpecifier), + Block(Block), + Global(TypeQualifier, Vec<Identifier>), +} + +/// A general purpose block, containing fields and possibly a list of declared identifiers. Semantic +/// is given with the storage qualifier. +#[derive(Clone, Debug, PartialEq)] +pub struct Block { + pub qualifier: TypeQualifier, + pub name: Identifier, + pub fields: Vec<StructFieldSpecifier>, + pub identifier: Option<ArrayedIdentifier>, +} + +/// Function identifier. +#[derive(Clone, Debug, PartialEq)] +pub enum FunIdentifier { + Identifier(Identifier), + Expr(Box<Expr>), +} + +impl FunIdentifier { + pub(crate) fn into_expr(self) -> Option<Expr> { + match self { + FunIdentifier::Identifier(..) => None, + FunIdentifier::Expr(expr) => Some(*expr), + } + } +} + +/// Function prototype. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionPrototype { + pub ty: FullySpecifiedType, + pub name: Identifier, + pub parameters: Vec<FunctionParameterDeclaration>, +} + +/// Function parameter declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum FunctionParameterDeclaration { + Named(Option<TypeQualifier>, FunctionParameterDeclarator), + Unnamed(Option<TypeQualifier>, TypeSpecifier), +} + +impl FunctionParameterDeclaration { + /// Create a named function argument. + pub fn new_named<I, T>(ident: I, ty: T) -> Self + where + I: Into<ArrayedIdentifier>, + T: Into<TypeSpecifier>, + { + let declator = FunctionParameterDeclarator { + ty: ty.into(), + ident: ident.into(), + }; + + FunctionParameterDeclaration::Named(None, declator) + } + + /// Create an unnamed function argument (mostly useful for interfaces / function prototypes). + pub fn new_unnamed<T>(ty: T) -> Self + where + T: Into<TypeSpecifier>, + { + FunctionParameterDeclaration::Unnamed(None, ty.into()) + } +} + +/// Function parameter declarator. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionParameterDeclarator { + pub ty: TypeSpecifier, + pub ident: ArrayedIdentifier, +} + +/// Init declarator list. +#[derive(Clone, Debug, PartialEq)] +pub struct InitDeclaratorList { + pub head: SingleDeclaration, + pub tail: Vec<SingleDeclarationNoType>, +} + +/// Single declaration. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclaration { + pub ty: FullySpecifiedType, + pub name: Option<Identifier>, + pub array_specifier: Option<ArraySpecifier>, + pub initializer: Option<Initializer>, +} + +/// A single declaration with implicit, already-defined type. +#[derive(Clone, Debug, PartialEq)] +pub struct SingleDeclarationNoType { + pub ident: ArrayedIdentifier, + pub initializer: Option<Initializer>, +} + +/// Initializer. +#[derive(Clone, Debug, PartialEq)] +pub enum Initializer { + Simple(Box<Expr>), + List(NonEmpty<Initializer>), +} + +impl From<Expr> for Initializer { + fn from(e: Expr) -> Self { + Initializer::Simple(Box::new(e)) + } +} + +/// The most general form of an expression. As you can see if you read the variant list, in GLSL, an +/// assignment is an expression. This is a bit silly but think of an assignment as a statement first +/// then an expression which evaluates to what the statement “returns”. +/// +/// An expression is either an assignment or a list (comma) of assignments. +#[derive(Clone, Debug, PartialEq)] +pub enum Expr { + /// A variable expression, using an identifier. + Variable(Identifier), + /// Integral constant expression. + IntConst(i32), + /// Unsigned integral constant expression. + UIntConst(u32), + /// Boolean constant expression. + BoolConst(bool), + /// Single precision floating expression. + FloatConst(f32), + /// Double precision floating expression. + DoubleConst(f64), + /// A unary expression, gathering a single expression and a unary operator. + Unary(UnaryOp, Box<Expr>), + /// A binary expression, gathering two expressions and a binary operator. + Binary(BinaryOp, Box<Expr>, Box<Expr>), + /// A ternary conditional expression, gathering three expressions. + Ternary(Box<Expr>, Box<Expr>, Box<Expr>), + /// An assignment is also an expression. Gathers an expression that defines what to assign to, an + /// assignment operator and the value to associate with. + Assignment(Box<Expr>, AssignmentOp, Box<Expr>), + /// Add an array specifier to an expression. + Bracket(Box<Expr>, ArraySpecifier), + /// A functional call. It has a function identifier and a list of expressions (arguments). + FunCall(FunIdentifier, Vec<Expr>), + /// An expression associated with a field selection (struct). + Dot(Box<Expr>, Identifier), + /// Post-incrementation of an expression. + PostInc(Box<Expr>), + /// Post-decrementation of an expression. + PostDec(Box<Expr>), + /// An expression that contains several, separated with comma. + Comma(Box<Expr>, Box<Expr>), +} + +impl From<i32> for Expr { + fn from(x: i32) -> Expr { + Expr::IntConst(x) + } +} + +impl From<u32> for Expr { + fn from(x: u32) -> Expr { + Expr::UIntConst(x) + } +} + +impl From<bool> for Expr { + fn from(x: bool) -> Expr { + Expr::BoolConst(x) + } +} + +impl From<f32> for Expr { + fn from(x: f32) -> Expr { + Expr::FloatConst(x) + } +} + +impl From<f64> for Expr { + fn from(x: f64) -> Expr { + Expr::DoubleConst(x) + } +} + +/// All unary operators that exist in GLSL. +#[derive(Clone, Debug, PartialEq)] +pub enum UnaryOp { + Inc, + Dec, + Add, + Minus, + Not, + Complement, +} + +/// All binary operators that exist in GLSL. +#[derive(Clone, Debug, PartialEq)] +pub enum BinaryOp { + Or, + Xor, + And, + BitOr, + BitXor, + BitAnd, + Equal, + NonEqual, + LT, + GT, + LTE, + GTE, + LShift, + RShift, + Add, + Sub, + Mult, + Div, + Mod, +} + +/// All possible operators for assigning expressions. +#[derive(Clone, Debug, PartialEq)] +pub enum AssignmentOp { + Equal, + Mult, + Div, + Mod, + Add, + Sub, + LShift, + RShift, + And, + Xor, + Or, +} + +/// Starting rule. +#[derive(Clone, Debug, PartialEq)] +pub struct TranslationUnit(pub NonEmpty<ExternalDeclaration>); + +/// A shader stage. +pub type ShaderStage = TranslationUnit; + +impl TranslationUnit { + /// Construct a translation unit from an iterator representing a _non-empty_ sequence of + /// [`ExternalDeclaration`]. + /// + /// # Errors + /// + /// `None` if the iterator yields no value. + pub fn from_non_empty_iter<I>(iter: I) -> Option<Self> + where + I: IntoIterator<Item = ExternalDeclaration>, + { + NonEmpty::from_non_empty_iter(iter).map(TranslationUnit) + } +} + +impl Deref for TranslationUnit { + type Target = NonEmpty<ExternalDeclaration>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for TranslationUnit { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntoIterator for TranslationUnit { + type IntoIter = <NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a> IntoIterator for &'a TranslationUnit { + type IntoIter = <&'a NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&self.0).into_iter() + } +} + +impl<'a> IntoIterator for &'a mut TranslationUnit { + type IntoIter = <&'a mut NonEmpty<ExternalDeclaration> as IntoIterator>::IntoIter; + type Item = &'a mut ExternalDeclaration; + + fn into_iter(self) -> Self::IntoIter { + (&mut self.0).into_iter() + } +} + +/// External declaration. +#[derive(Clone, Debug, PartialEq)] +pub enum ExternalDeclaration { + Preprocessor(Preprocessor), + FunctionDefinition(FunctionDefinition), + Declaration(Declaration), +} + +impl ExternalDeclaration { + /// Create a new function. + pub fn new_fn<T, N, A, S>(ret_ty: T, name: N, args: A, body: S) -> Self + where + T: Into<FullySpecifiedType>, + N: Into<Identifier>, + A: IntoIterator<Item = FunctionParameterDeclaration>, + S: IntoIterator<Item = Statement>, + { + ExternalDeclaration::FunctionDefinition(FunctionDefinition { + prototype: FunctionPrototype { + ty: ret_ty.into(), + name: name.into(), + parameters: args.into_iter().collect(), + }, + statement: CompoundStatement { + statement_list: body.into_iter().collect(), + }, + }) + } + + /// Create a new structure. + /// + /// # Errors + /// + /// - [`None`] if no fields are provided. GLSL forbids having empty structs. + pub fn new_struct<N, F>(name: N, fields: F) -> Option<Self> + where + N: Into<TypeName>, + F: IntoIterator<Item = StructFieldSpecifier>, + { + let fields: Vec<_> = fields.into_iter().collect(); + + if fields.is_empty() { + None + } else { + Some(ExternalDeclaration::Declaration( + Declaration::InitDeclaratorList(InitDeclaratorList { + head: SingleDeclaration { + ty: FullySpecifiedType { + qualifier: None, + ty: TypeSpecifier { + ty: TypeSpecifierNonArray::Struct(StructSpecifier { + name: Some(name.into()), + fields: NonEmpty(fields.into_iter().collect()), + }), + array_specifier: None, + }, + }, + name: None, + array_specifier: None, + initializer: None, + }, + tail: vec![], + }), + )) + } + } +} + +/// Function definition. +#[derive(Clone, Debug, PartialEq)] +pub struct FunctionDefinition { + pub prototype: FunctionPrototype, + pub statement: CompoundStatement, +} + +/// Compound statement (with no new scope). +#[derive(Clone, Debug, PartialEq)] +pub struct CompoundStatement { + pub statement_list: Vec<Statement>, +} + +impl FromIterator<Statement> for CompoundStatement { + fn from_iter<T>(iter: T) -> Self + where + T: IntoIterator<Item = Statement>, + { + CompoundStatement { + statement_list: iter.into_iter().collect(), + } + } +} + +/// Statement. +#[derive(Clone, Debug, PartialEq)] +pub enum Statement { + Compound(Box<CompoundStatement>), + Simple(Box<SimpleStatement>), +} + +impl Statement { + /// Create a case-label sequence of nested statements. + pub fn new_case<C, S>(case: C, statements: S) -> Self + where + C: Into<CaseLabel>, + S: IntoIterator<Item = Statement>, + { + let case_stmt = Statement::Simple(Box::new(SimpleStatement::CaseLabel(case.into()))); + + Statement::Compound(Box::new(CompoundStatement { + statement_list: once(case_stmt).chain(statements.into_iter()).collect(), + })) + } + + /// Declare a new variable. + /// + /// `ty` is the type of the variable, `name` the name of the binding to create, + /// `array_specifier` an optional argument to make your binding an array and + /// `initializer` + pub fn declare_var<T, N, A, I>(ty: T, name: N, array_specifier: A, initializer: I) -> Self + where + T: Into<FullySpecifiedType>, + N: Into<Identifier>, + A: Into<Option<ArraySpecifier>>, + I: Into<Option<Initializer>>, + { + Statement::Simple(Box::new(SimpleStatement::Declaration( + Declaration::InitDeclaratorList(InitDeclaratorList { + head: SingleDeclaration { + ty: ty.into(), + name: Some(name.into()), + array_specifier: array_specifier.into(), + initializer: initializer.into(), + }, + tail: Vec::new(), + }), + ))) + } +} + +/// Simple statement. +#[derive(Clone, Debug, PartialEq)] +pub enum SimpleStatement { + Declaration(Declaration), + Expression(ExprStatement), + Selection(SelectionStatement), + Switch(SwitchStatement), + CaseLabel(CaseLabel), + Iteration(IterationStatement), + Jump(JumpStatement), +} + +impl SimpleStatement { + /// Create a new expression statement. + pub fn new_expr<E>(expr: E) -> Self + where + E: Into<Expr>, + { + SimpleStatement::Expression(Some(expr.into())) + } + + /// Create a new selection statement (if / else). + pub fn new_if_else<If, True, False>(ife: If, truee: True, falsee: False) -> Self + where + If: Into<Expr>, + True: Into<Statement>, + False: Into<Statement>, + { + SimpleStatement::Selection(SelectionStatement { + cond: Box::new(ife.into()), + rest: SelectionRestStatement::Else(Box::new(truee.into()), Box::new(falsee.into())), + }) + } + + /// Create a new switch statement. + /// + /// A switch statement is always composed of a [`SimpleStatement::Switch`] block, that contains it + /// all, and has as body a compound list of case statements. + pub fn new_switch<H, B>(head: H, body: B) -> Self + where + H: Into<Expr>, + B: IntoIterator<Item = Statement>, + { + SimpleStatement::Switch(SwitchStatement { + head: Box::new(head.into()), + body: body.into_iter().collect(), + }) + } + + /// Create a new while statement. + pub fn new_while<C, S>(cond: C, body: S) -> Self + where + C: Into<Condition>, + S: Into<Statement>, + { + SimpleStatement::Iteration(IterationStatement::While( + cond.into(), + Box::new(body.into()), + )) + } + + /// Create a new do-while statement. + pub fn new_do_while<C, S>(body: S, cond: C) -> Self + where + S: Into<Statement>, + C: Into<Expr>, + { + SimpleStatement::Iteration(IterationStatement::DoWhile( + Box::new(body.into()), + Box::new(cond.into()), + )) + } +} + +/// Expression statement. +pub type ExprStatement = Option<Expr>; + +/// Selection statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SelectionStatement { + pub cond: Box<Expr>, + pub rest: SelectionRestStatement, +} + +/// Condition. +#[derive(Clone, Debug, PartialEq)] +pub enum Condition { + Expr(Box<Expr>), + Assignment(FullySpecifiedType, Identifier, Initializer), +} + +impl From<Expr> for Condition { + fn from(expr: Expr) -> Self { + Condition::Expr(Box::new(expr)) + } +} + +/// Selection rest statement. +#[derive(Clone, Debug, PartialEq)] +pub enum SelectionRestStatement { + /// Body of the if. + Statement(Box<Statement>), + /// The first argument is the body of the if, the rest is the next statement. + Else(Box<Statement>, Box<Statement>), +} + +/// Switch statement. +#[derive(Clone, Debug, PartialEq)] +pub struct SwitchStatement { + pub head: Box<Expr>, + pub body: Vec<Statement>, +} + +/// Case label statement. +#[derive(Clone, Debug, PartialEq)] +pub enum CaseLabel { + Case(Box<Expr>), + Def, +} + +/// Iteration statement. +#[derive(Clone, Debug, PartialEq)] +pub enum IterationStatement { + While(Condition, Box<Statement>), + DoWhile(Box<Statement>, Box<Expr>), + For(ForInitStatement, ForRestStatement, Box<Statement>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub enum ForInitStatement { + Expression(Option<Expr>), + Declaration(Box<Declaration>), +} + +/// For init statement. +#[derive(Clone, Debug, PartialEq)] +pub struct ForRestStatement { + pub condition: Option<Condition>, + pub post_expr: Option<Box<Expr>>, +} + +/// Jump statement. +#[derive(Clone, Debug, PartialEq)] +pub enum JumpStatement { + Continue, + Break, + Return(Option<Box<Expr>>), + Discard, +} + +/// Some basic preprocessor directives. +/// +/// As it’s important to carry them around the AST because they cannot be substituted in a normal +/// preprocessor (they’re used by GPU’s compilers), those preprocessor directives are available for +/// inspection. +#[derive(Clone, Debug, PartialEq)] +pub enum Preprocessor { + Define(PreprocessorDefine), + Else, + ElseIf(PreprocessorElseIf), + EndIf, + Error(PreprocessorError), + If(PreprocessorIf), + IfDef(PreprocessorIfDef), + IfNDef(PreprocessorIfNDef), + Include(PreprocessorInclude), + Line(PreprocessorLine), + Pragma(PreprocessorPragma), + Undef(PreprocessorUndef), + Version(PreprocessorVersion), + Extension(PreprocessorExtension), +} + +/// A #define preprocessor directive. +/// +/// Allows any expression but only Integer and Float literals make sense +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorDefine { + ObjectLike { + ident: Identifier, + value: String, + }, + + FunctionLike { + ident: Identifier, + args: Vec<Identifier>, + value: String, + }, +} + +/// An #else preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorElseIf { + pub condition: String, +} + +/// An #error preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorError { + pub message: String, +} + +/// An #if preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIf { + pub condition: String, +} + +/// An #ifdef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIfDef { + pub ident: Identifier, +} + +/// A #ifndef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorIfNDef { + pub ident: Identifier, +} + +/// An #include name annotation. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorInclude { + pub path: Path, +} + +/// A #line preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorLine { + pub line: u32, + pub source_string_number: Option<u32>, +} + +/// A #pragma preprocessor directive. +/// Holds compiler-specific command. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorPragma { + pub command: String, +} + +/// A #undef preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorUndef { + pub name: Identifier, +} + +/// A #version preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorVersion { + pub version: u16, + pub profile: Option<PreprocessorVersionProfile>, +} + +/// A #version profile annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorVersionProfile { + Core, + Compatibility, + ES, +} + +/// An #extension preprocessor directive. +#[derive(Clone, Debug, PartialEq)] +pub struct PreprocessorExtension { + pub name: PreprocessorExtensionName, + pub behavior: Option<PreprocessorExtensionBehavior>, +} + +/// An #extension name annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorExtensionName { + /// All extensions you could ever imagine in your whole lifetime (how crazy is that!). + All, + /// A specific extension. + Specific(String), +} + +/// An #extension behavior annotation. +#[derive(Clone, Debug, PartialEq)] +pub enum PreprocessorExtensionBehavior { + Require, + Enable, + Warn, + Disable, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn create_new_identifier() { + assert!(Identifier::new("foo_bar").is_ok()); + assert!(Identifier::new("3foo_bar").is_err()); + assert!(Identifier::new("FooBar").is_ok()); + assert!(Identifier::new("_FooBar").is_ok()); + assert!(Identifier::new("foo3").is_ok()); + assert!(Identifier::new("foo3_").is_ok()); + assert!(Identifier::new("fδo3_").is_err()); + } + + #[test] + fn create_new_type_name() { + assert!(TypeName::new("foo_bar").is_ok()); + assert!(TypeName::new("FooBar").is_ok()); + assert!(TypeName::new("foo3").is_ok()); + assert!(TypeName::new("foo3_").is_ok()); + + assert!(TypeName::new("_FooBar").is_ok()); + assert!(TypeName::new("3foo_bar").is_err()); + assert!(TypeName::new("fδo3_").is_err()); + } + + // bool predicate(float x) { + // } + #[test] + fn declare_new_fn() { + let _ = ExternalDeclaration::new_fn( + TypeSpecifierNonArray::Bool, + "predicate", + vec![FunctionParameterDeclaration::new_named( + "x", + TypeSpecifierNonArray::Float, + )], + vec![], + ); + } + + // struct Point2D { + // float x; + // float y; + // }; + #[test] + fn declare_struct() { + let point = ExternalDeclaration::new_struct( + "Point2D", + vec![ + StructFieldSpecifier::new("x", TypeSpecifierNonArray::Double), + StructFieldSpecifier::new("y", TypeSpecifierNonArray::Double), + ], + ); + + assert!(point.is_some()); + } + + // struct Point2D {}; + #[test] + fn declare_bad_struct() { + let point = ExternalDeclaration::new_struct("Point2D", vec![]); + assert!(point.is_none()); + } + + #[test] + fn initializer_from_expr() { + let _: Initializer = Expr::from(false).into(); + } +} diff --git a/third_party/rust/glsl/src/transpiler/glsl.rs b/third_party/rust/glsl/src/transpiler/glsl.rs new file mode 100644 index 0000000000..da9395d333 --- /dev/null +++ b/third_party/rust/glsl/src/transpiler/glsl.rs @@ -0,0 +1,1870 @@ +//! A GLSL450/GLSL460 transpiler that takes a syntax tree and writes it as a plain raw GLSL +//! [`String`]. +//! +//! # Foreword +//! +//! This module exports several functions that just transform a part of a syntax tree into its raw +//! GLSL [`String`] representation. +//! +//! > Important note: this module – and actually, any [`transpiler`] module – is not responsible in +//! > optimizing the syntax tree nor semantically check its validity. This is done in other stages +//! > of the compilation process. +//! +//! In order to achieve that purpose, you could: +//! +//! - For each elements in the AST, return a [`String`] or [`Cow<str>`]. +//! - Insert the string representation via a formatter. +//! +//! The second solution is better because it lets the user handle the memory the way they want: +//! they might just use a dynamic buffer that implements [`Write`] or simply pass a `&mut` +//! [`String`]. It’s up to you. +//! +//! # How to use this module +//! +//! First, head over to the [`syntax`] module. That module defines the AST items defined by GLSL. This +//! very module provides you with functions like `show_*` taking the AST item and writing it to a +//! [`Write`] object. You’re likely to be interested in [`show_translation_unit`] to start with. +//! +//! [`Cow<str>`]: std::borrow::Cow +//! [`Write`]: std::fmt::Write +//! [`show_translation_unit`]: crate::transpiler::glsl::show_translation_unit +//! [`syntax`]: crate::syntax +//! [`transpiler`]: crate::transpiler + +use std::fmt::Write; + +use crate::syntax; + +// Precedence information for transpiling parentheses properly +trait HasPrecedence { + fn precedence(&self) -> u32; +} + +impl HasPrecedence for syntax::Expr { + fn precedence(&self) -> u32 { + match self { + // 0 isn't a valid precedence, but we use this to represent atomic expressions + Self::Variable(_) + | Self::IntConst(_) + | Self::UIntConst(_) + | Self::BoolConst(_) + | Self::FloatConst(_) + | Self::DoubleConst(_) => 0, + // Precedence operator expression is precedence of operator + Self::Unary(op, _) => op.precedence(), + Self::Binary(op, _, _) => op.precedence(), + Self::Ternary(_, _, _) => 15, + Self::Assignment(_, op, _) => op.precedence(), + Self::Bracket(_, _) + | Self::FunCall(_, _) + | Self::Dot(_, _) + | Self::PostInc(_) + | Self::PostDec(_) => 2, + Self::Comma(_, _) => 17, + } + } +} + +impl HasPrecedence for syntax::UnaryOp { + fn precedence(&self) -> u32 { + 3 + } +} + +impl HasPrecedence for syntax::BinaryOp { + fn precedence(&self) -> u32 { + match self { + Self::Mult | Self::Div | Self::Mod => 4, + Self::Add | Self::Sub => 5, + Self::LShift | Self::RShift => 6, + Self::LT | Self::GT | Self::LTE | Self::GTE => 7, + Self::Equal | Self::NonEqual => 8, + Self::BitAnd => 9, + Self::BitXor => 10, + Self::BitOr => 11, + Self::And => 12, + Self::Xor => 13, + Self::Or => 14, + } + } +} + +impl HasPrecedence for syntax::AssignmentOp { + fn precedence(&self) -> u32 { + 16 + } +} + +pub fn show_identifier<F>(f: &mut F, i: &syntax::Identifier) +where + F: Write, +{ + let _ = f.write_str(&i.0); +} + +pub fn show_type_name<F>(f: &mut F, t: &syntax::TypeName) +where + F: Write, +{ + let _ = f.write_str(&t.0); +} + +pub fn show_type_specifier_non_array<F>(f: &mut F, t: &syntax::TypeSpecifierNonArray) +where + F: Write, +{ + match *t { + syntax::TypeSpecifierNonArray::Void => { + let _ = f.write_str("void"); + } + syntax::TypeSpecifierNonArray::Bool => { + let _ = f.write_str("bool"); + } + syntax::TypeSpecifierNonArray::Int => { + let _ = f.write_str("int"); + } + syntax::TypeSpecifierNonArray::UInt => { + let _ = f.write_str("uint"); + } + syntax::TypeSpecifierNonArray::Float => { + let _ = f.write_str("float"); + } + syntax::TypeSpecifierNonArray::Double => { + let _ = f.write_str("double"); + } + syntax::TypeSpecifierNonArray::Vec2 => { + let _ = f.write_str("vec2"); + } + syntax::TypeSpecifierNonArray::Vec3 => { + let _ = f.write_str("vec3"); + } + syntax::TypeSpecifierNonArray::Vec4 => { + let _ = f.write_str("vec4"); + } + syntax::TypeSpecifierNonArray::DVec2 => { + let _ = f.write_str("dvec2"); + } + syntax::TypeSpecifierNonArray::DVec3 => { + let _ = f.write_str("dvec3"); + } + syntax::TypeSpecifierNonArray::DVec4 => { + let _ = f.write_str("dvec4"); + } + syntax::TypeSpecifierNonArray::BVec2 => { + let _ = f.write_str("bvec2"); + } + syntax::TypeSpecifierNonArray::BVec3 => { + let _ = f.write_str("bvec3"); + } + syntax::TypeSpecifierNonArray::BVec4 => { + let _ = f.write_str("bvec4"); + } + syntax::TypeSpecifierNonArray::IVec2 => { + let _ = f.write_str("ivec2"); + } + syntax::TypeSpecifierNonArray::IVec3 => { + let _ = f.write_str("ivec3"); + } + syntax::TypeSpecifierNonArray::IVec4 => { + let _ = f.write_str("ivec4"); + } + syntax::TypeSpecifierNonArray::UVec2 => { + let _ = f.write_str("uvec2"); + } + syntax::TypeSpecifierNonArray::UVec3 => { + let _ = f.write_str("uvec3"); + } + syntax::TypeSpecifierNonArray::UVec4 => { + let _ = f.write_str("uvec4"); + } + syntax::TypeSpecifierNonArray::Mat2 => { + let _ = f.write_str("mat2"); + } + syntax::TypeSpecifierNonArray::Mat3 => { + let _ = f.write_str("mat3"); + } + syntax::TypeSpecifierNonArray::Mat4 => { + let _ = f.write_str("mat4"); + } + syntax::TypeSpecifierNonArray::Mat23 => { + let _ = f.write_str("mat2x3"); + } + syntax::TypeSpecifierNonArray::Mat24 => { + let _ = f.write_str("mat2x4"); + } + syntax::TypeSpecifierNonArray::Mat32 => { + let _ = f.write_str("mat3x2"); + } + syntax::TypeSpecifierNonArray::Mat34 => { + let _ = f.write_str("mat3x4"); + } + syntax::TypeSpecifierNonArray::Mat42 => { + let _ = f.write_str("mat4x2"); + } + syntax::TypeSpecifierNonArray::Mat43 => { + let _ = f.write_str("mat4x3"); + } + syntax::TypeSpecifierNonArray::DMat2 => { + let _ = f.write_str("dmat2"); + } + syntax::TypeSpecifierNonArray::DMat3 => { + let _ = f.write_str("dmat3"); + } + syntax::TypeSpecifierNonArray::DMat4 => { + let _ = f.write_str("dmat4"); + } + syntax::TypeSpecifierNonArray::DMat23 => { + let _ = f.write_str("dmat2x3"); + } + syntax::TypeSpecifierNonArray::DMat24 => { + let _ = f.write_str("dmat2x4"); + } + syntax::TypeSpecifierNonArray::DMat32 => { + let _ = f.write_str("dmat3x2"); + } + syntax::TypeSpecifierNonArray::DMat34 => { + let _ = f.write_str("dmat3x4"); + } + syntax::TypeSpecifierNonArray::DMat42 => { + let _ = f.write_str("dmat4x2"); + } + syntax::TypeSpecifierNonArray::DMat43 => { + let _ = f.write_str("dmat4x3"); + } + syntax::TypeSpecifierNonArray::Sampler1D => { + let _ = f.write_str("sampler1D"); + } + syntax::TypeSpecifierNonArray::Image1D => { + let _ = f.write_str("image1D"); + } + syntax::TypeSpecifierNonArray::Sampler2D => { + let _ = f.write_str("sampler2D"); + } + syntax::TypeSpecifierNonArray::Image2D => { + let _ = f.write_str("image2D"); + } + syntax::TypeSpecifierNonArray::Sampler3D => { + let _ = f.write_str("sampler3D"); + } + syntax::TypeSpecifierNonArray::Image3D => { + let _ = f.write_str("image3D"); + } + syntax::TypeSpecifierNonArray::SamplerCube => { + let _ = f.write_str("samplerCube"); + } + syntax::TypeSpecifierNonArray::ImageCube => { + let _ = f.write_str("imageCube"); + } + syntax::TypeSpecifierNonArray::Sampler2DRect => { + let _ = f.write_str("sampler2DRect"); + } + syntax::TypeSpecifierNonArray::Image2DRect => { + let _ = f.write_str("image2DRect"); + } + syntax::TypeSpecifierNonArray::Sampler1DArray => { + let _ = f.write_str("sampler1DArray"); + } + syntax::TypeSpecifierNonArray::Image1DArray => { + let _ = f.write_str("image1DArray"); + } + syntax::TypeSpecifierNonArray::Sampler2DArray => { + let _ = f.write_str("sampler2DArray"); + } + syntax::TypeSpecifierNonArray::Image2DArray => { + let _ = f.write_str("image2DArray"); + } + syntax::TypeSpecifierNonArray::SamplerBuffer => { + let _ = f.write_str("samplerBuffer"); + } + syntax::TypeSpecifierNonArray::ImageBuffer => { + let _ = f.write_str("imageBuffer"); + } + syntax::TypeSpecifierNonArray::Sampler2DMS => { + let _ = f.write_str("sampler2DMS"); + } + syntax::TypeSpecifierNonArray::Image2DMS => { + let _ = f.write_str("image2DMS"); + } + syntax::TypeSpecifierNonArray::Sampler2DMSArray => { + let _ = f.write_str("sampler2DMSArray"); + } + syntax::TypeSpecifierNonArray::Image2DMSArray => { + let _ = f.write_str("image2DMSArray"); + } + syntax::TypeSpecifierNonArray::SamplerCubeArray => { + let _ = f.write_str("samplerCubeArray"); + } + syntax::TypeSpecifierNonArray::ImageCubeArray => { + let _ = f.write_str("imageCubeArray"); + } + syntax::TypeSpecifierNonArray::Sampler1DShadow => { + let _ = f.write_str("sampler1DShadow"); + } + syntax::TypeSpecifierNonArray::Sampler2DShadow => { + let _ = f.write_str("sampler2DShadow"); + } + syntax::TypeSpecifierNonArray::Sampler2DRectShadow => { + let _ = f.write_str("sampler2DRectShadow"); + } + syntax::TypeSpecifierNonArray::Sampler1DArrayShadow => { + let _ = f.write_str("sampler1DArrayShadow"); + } + syntax::TypeSpecifierNonArray::Sampler2DArrayShadow => { + let _ = f.write_str("sampler2DArrayShadow"); + } + syntax::TypeSpecifierNonArray::SamplerCubeShadow => { + let _ = f.write_str("samplerCubeShadow"); + } + syntax::TypeSpecifierNonArray::SamplerCubeArrayShadow => { + let _ = f.write_str("samplerCubeArrayShadow"); + } + syntax::TypeSpecifierNonArray::ISampler1D => { + let _ = f.write_str("isampler1D"); + } + syntax::TypeSpecifierNonArray::IImage1D => { + let _ = f.write_str("iimage1D"); + } + syntax::TypeSpecifierNonArray::ISampler2D => { + let _ = f.write_str("isampler2D"); + } + syntax::TypeSpecifierNonArray::IImage2D => { + let _ = f.write_str("iimage2D"); + } + syntax::TypeSpecifierNonArray::ISampler3D => { + let _ = f.write_str("isampler3D"); + } + syntax::TypeSpecifierNonArray::IImage3D => { + let _ = f.write_str("iimage3D"); + } + syntax::TypeSpecifierNonArray::ISamplerCube => { + let _ = f.write_str("isamplerCube"); + } + syntax::TypeSpecifierNonArray::IImageCube => { + let _ = f.write_str("iimageCube"); + } + syntax::TypeSpecifierNonArray::ISampler2DRect => { + let _ = f.write_str("isampler2DRect"); + } + syntax::TypeSpecifierNonArray::IImage2DRect => { + let _ = f.write_str("iimage2DRect"); + } + syntax::TypeSpecifierNonArray::ISampler1DArray => { + let _ = f.write_str("isampler1DArray"); + } + syntax::TypeSpecifierNonArray::IImage1DArray => { + let _ = f.write_str("iimage1DArray"); + } + syntax::TypeSpecifierNonArray::ISampler2DArray => { + let _ = f.write_str("isampler2DArray"); + } + syntax::TypeSpecifierNonArray::IImage2DArray => { + let _ = f.write_str("iimage2DArray"); + } + syntax::TypeSpecifierNonArray::ISamplerBuffer => { + let _ = f.write_str("isamplerBuffer"); + } + syntax::TypeSpecifierNonArray::IImageBuffer => { + let _ = f.write_str("iimageBuffer"); + } + syntax::TypeSpecifierNonArray::ISampler2DMS => { + let _ = f.write_str("isampler2MS"); + } + syntax::TypeSpecifierNonArray::IImage2DMS => { + let _ = f.write_str("iimage2DMS"); + } + syntax::TypeSpecifierNonArray::ISampler2DMSArray => { + let _ = f.write_str("isampler2DMSArray"); + } + syntax::TypeSpecifierNonArray::IImage2DMSArray => { + let _ = f.write_str("iimage2DMSArray"); + } + syntax::TypeSpecifierNonArray::ISamplerCubeArray => { + let _ = f.write_str("isamplerCubeArray"); + } + syntax::TypeSpecifierNonArray::IImageCubeArray => { + let _ = f.write_str("iimageCubeArray"); + } + syntax::TypeSpecifierNonArray::AtomicUInt => { + let _ = f.write_str("atomic_uint"); + } + syntax::TypeSpecifierNonArray::USampler1D => { + let _ = f.write_str("usampler1D"); + } + syntax::TypeSpecifierNonArray::UImage1D => { + let _ = f.write_str("uimage1D"); + } + syntax::TypeSpecifierNonArray::USampler2D => { + let _ = f.write_str("usampler2D"); + } + syntax::TypeSpecifierNonArray::UImage2D => { + let _ = f.write_str("uimage2D"); + } + syntax::TypeSpecifierNonArray::USampler3D => { + let _ = f.write_str("usampler3D"); + } + syntax::TypeSpecifierNonArray::UImage3D => { + let _ = f.write_str("uimage3D"); + } + syntax::TypeSpecifierNonArray::USamplerCube => { + let _ = f.write_str("usamplerCube"); + } + syntax::TypeSpecifierNonArray::UImageCube => { + let _ = f.write_str("uimageCube"); + } + syntax::TypeSpecifierNonArray::USampler2DRect => { + let _ = f.write_str("usampler2DRect"); + } + syntax::TypeSpecifierNonArray::UImage2DRect => { + let _ = f.write_str("uimage2DRect"); + } + syntax::TypeSpecifierNonArray::USampler1DArray => { + let _ = f.write_str("usampler1DArray"); + } + syntax::TypeSpecifierNonArray::UImage1DArray => { + let _ = f.write_str("uimage1DArray"); + } + syntax::TypeSpecifierNonArray::USampler2DArray => { + let _ = f.write_str("usampler2DArray"); + } + syntax::TypeSpecifierNonArray::UImage2DArray => { + let _ = f.write_str("uimage2DArray"); + } + syntax::TypeSpecifierNonArray::USamplerBuffer => { + let _ = f.write_str("usamplerBuffer"); + } + syntax::TypeSpecifierNonArray::UImageBuffer => { + let _ = f.write_str("uimageBuffer"); + } + syntax::TypeSpecifierNonArray::USampler2DMS => { + let _ = f.write_str("usampler2DMS"); + } + syntax::TypeSpecifierNonArray::UImage2DMS => { + let _ = f.write_str("uimage2DMS"); + } + syntax::TypeSpecifierNonArray::USampler2DMSArray => { + let _ = f.write_str("usamplerDMSArray"); + } + syntax::TypeSpecifierNonArray::UImage2DMSArray => { + let _ = f.write_str("uimage2DMSArray"); + } + syntax::TypeSpecifierNonArray::USamplerCubeArray => { + let _ = f.write_str("usamplerCubeArray"); + } + syntax::TypeSpecifierNonArray::UImageCubeArray => { + let _ = f.write_str("uimageCubeArray"); + } + syntax::TypeSpecifierNonArray::Struct(ref s) => show_struct_non_declaration(f, s), + syntax::TypeSpecifierNonArray::TypeName(ref tn) => show_type_name(f, tn), + } +} + +pub fn show_type_specifier<F>(f: &mut F, t: &syntax::TypeSpecifier) +where + F: Write, +{ + show_type_specifier_non_array(f, &t.ty); + + if let Some(ref arr_spec) = t.array_specifier { + show_array_spec(f, arr_spec); + } +} + +pub fn show_fully_specified_type<F>(f: &mut F, t: &syntax::FullySpecifiedType) +where + F: Write, +{ + if let Some(ref qual) = t.qualifier { + show_type_qualifier(f, &qual); + let _ = f.write_str(" "); + } + + show_type_specifier(f, &t.ty); +} + +pub fn show_struct_non_declaration<F>(f: &mut F, s: &syntax::StructSpecifier) +where + F: Write, +{ + let _ = f.write_str("struct "); + + if let Some(ref name) = s.name { + let _ = write!(f, "{} ", name); + } + + let _ = f.write_str("{\n"); + + for field in &s.fields.0 { + show_struct_field(f, field); + } + + let _ = f.write_str("}"); +} + +pub fn show_struct<F>(f: &mut F, s: &syntax::StructSpecifier) +where + F: Write, +{ + show_struct_non_declaration(f, s); + let _ = f.write_str(";\n"); +} + +pub fn show_struct_field<F>(f: &mut F, field: &syntax::StructFieldSpecifier) +where + F: Write, +{ + if let Some(ref qual) = field.qualifier { + show_type_qualifier(f, &qual); + let _ = f.write_str(" "); + } + + show_type_specifier(f, &field.ty); + let _ = f.write_str(" "); + + // there’s at least one identifier + let mut identifiers = field.identifiers.0.iter(); + let identifier = identifiers.next().unwrap(); + + show_arrayed_identifier(f, identifier); + + // write the rest of the identifiers + for identifier in identifiers { + let _ = f.write_str(", "); + show_arrayed_identifier(f, identifier); + } + + let _ = f.write_str(";\n"); +} + +pub fn show_array_spec<F>(f: &mut F, a: &syntax::ArraySpecifier) +where + F: Write, +{ + for dimension in &a.dimensions { + match *dimension { + syntax::ArraySpecifierDimension::Unsized => { + let _ = f.write_str("[]"); + } + syntax::ArraySpecifierDimension::ExplicitlySized(ref e) => { + let _ = f.write_str("["); + show_expr(f, &e); + let _ = f.write_str("]"); + } + } + } +} + +pub fn show_arrayed_identifier<F>(f: &mut F, a: &syntax::ArrayedIdentifier) +where + F: Write, +{ + let _ = write!(f, "{}", a.ident); + + if let Some(ref arr_spec) = a.array_spec { + show_array_spec(f, arr_spec); + } +} + +pub fn show_type_qualifier<F>(f: &mut F, q: &syntax::TypeQualifier) +where + F: Write, +{ + let mut qualifiers = q.qualifiers.0.iter(); + let first = qualifiers.next().unwrap(); + + show_type_qualifier_spec(f, first); + + for qual_spec in qualifiers { + let _ = f.write_str(" "); + show_type_qualifier_spec(f, qual_spec) + } +} + +pub fn show_type_qualifier_spec<F>(f: &mut F, q: &syntax::TypeQualifierSpec) +where + F: Write, +{ + match *q { + syntax::TypeQualifierSpec::Storage(ref s) => show_storage_qualifier(f, &s), + syntax::TypeQualifierSpec::Layout(ref l) => show_layout_qualifier(f, &l), + syntax::TypeQualifierSpec::Precision(ref p) => show_precision_qualifier(f, &p), + syntax::TypeQualifierSpec::Interpolation(ref i) => show_interpolation_qualifier(f, &i), + syntax::TypeQualifierSpec::Invariant => { + let _ = f.write_str("invariant"); + } + syntax::TypeQualifierSpec::Precise => { + let _ = f.write_str("precise"); + } + } +} + +pub fn show_storage_qualifier<F>(f: &mut F, q: &syntax::StorageQualifier) +where + F: Write, +{ + match *q { + syntax::StorageQualifier::Const => { + let _ = f.write_str("const"); + } + syntax::StorageQualifier::InOut => { + let _ = f.write_str("inout"); + } + syntax::StorageQualifier::In => { + let _ = f.write_str("in"); + } + syntax::StorageQualifier::Out => { + let _ = f.write_str("out"); + } + syntax::StorageQualifier::Centroid => { + let _ = f.write_str("centroid"); + } + syntax::StorageQualifier::Patch => { + let _ = f.write_str("patch"); + } + syntax::StorageQualifier::Sample => { + let _ = f.write_str("sample"); + } + syntax::StorageQualifier::Uniform => { + let _ = f.write_str("uniform"); + } + syntax::StorageQualifier::Attribute => { + let _ = f.write_str("attribute"); + } + syntax::StorageQualifier::Varying => { + let _ = f.write_str("varying"); + } + syntax::StorageQualifier::Buffer => { + let _ = f.write_str("buffer"); + } + syntax::StorageQualifier::Shared => { + let _ = f.write_str("shared"); + } + syntax::StorageQualifier::Coherent => { + let _ = f.write_str("coherent"); + } + syntax::StorageQualifier::Volatile => { + let _ = f.write_str("volatile"); + } + syntax::StorageQualifier::Restrict => { + let _ = f.write_str("restrict"); + } + syntax::StorageQualifier::ReadOnly => { + let _ = f.write_str("readonly"); + } + syntax::StorageQualifier::WriteOnly => { + let _ = f.write_str("writeonly"); + } + syntax::StorageQualifier::Subroutine(ref n) => show_subroutine(f, &n), + } +} + +pub fn show_subroutine<F>(f: &mut F, types: &Vec<syntax::TypeName>) +where + F: Write, +{ + let _ = f.write_str("subroutine"); + + if !types.is_empty() { + let _ = f.write_str("("); + + let mut types_iter = types.iter(); + let first = types_iter.next().unwrap(); + + show_type_name(f, first); + + for type_name in types_iter { + let _ = f.write_str(", "); + show_type_name(f, type_name); + } + + let _ = f.write_str(")"); + } +} + +pub fn show_layout_qualifier<F>(f: &mut F, l: &syntax::LayoutQualifier) +where + F: Write, +{ + let mut qualifiers = l.ids.0.iter(); + let first = qualifiers.next().unwrap(); + + let _ = f.write_str("layout ("); + show_layout_qualifier_spec(f, first); + + for qual_spec in qualifiers { + let _ = f.write_str(", "); + show_layout_qualifier_spec(f, qual_spec); + } + + let _ = f.write_str(")"); +} + +pub fn show_layout_qualifier_spec<F>(f: &mut F, l: &syntax::LayoutQualifierSpec) +where + F: Write, +{ + match *l { + syntax::LayoutQualifierSpec::Identifier(ref i, Some(ref e)) => { + let _ = write!(f, "{} = ", i); + show_expr(f, &e); + } + syntax::LayoutQualifierSpec::Identifier(ref i, None) => show_identifier(f, &i), + syntax::LayoutQualifierSpec::Shared => { + let _ = f.write_str("shared"); + } + } +} + +pub fn show_precision_qualifier<F>(f: &mut F, p: &syntax::PrecisionQualifier) +where + F: Write, +{ + match *p { + syntax::PrecisionQualifier::High => { + let _ = f.write_str("highp"); + } + syntax::PrecisionQualifier::Medium => { + let _ = f.write_str("mediump"); + } + syntax::PrecisionQualifier::Low => { + let _ = f.write_str("low"); + } + } +} + +pub fn show_interpolation_qualifier<F>(f: &mut F, i: &syntax::InterpolationQualifier) +where + F: Write, +{ + match *i { + syntax::InterpolationQualifier::Smooth => { + let _ = f.write_str("smooth"); + } + syntax::InterpolationQualifier::Flat => { + let _ = f.write_str("flat"); + } + syntax::InterpolationQualifier::NoPerspective => { + let _ = f.write_str("noperspective"); + } + } +} + +pub fn show_float<F>(f: &mut F, x: f32) +where + F: Write, +{ + if x.fract() == 0. { + let _ = write!(f, "{}.", x); + } else { + let _ = write!(f, "{}", x); + } +} + +pub fn show_double<F>(f: &mut F, x: f64) +where + F: Write, +{ + if x.fract() == 0. { + let _ = write!(f, "{}.lf", x); + } else { + let _ = write!(f, "{}lf", x); + } +} + +pub fn show_expr<F>(f: &mut F, expr: &syntax::Expr) +where + F: Write, +{ + match *expr { + syntax::Expr::Variable(ref i) => show_identifier(f, &i), + syntax::Expr::IntConst(ref x) => { + let _ = write!(f, "{}", x); + } + syntax::Expr::UIntConst(ref x) => { + let _ = write!(f, "{}u", x); + } + syntax::Expr::BoolConst(ref x) => { + let _ = write!(f, "{}", x); + } + syntax::Expr::FloatConst(ref x) => show_float(f, *x), + syntax::Expr::DoubleConst(ref x) => show_double(f, *x), + syntax::Expr::Unary(ref op, ref e) => { + // Note: all unary ops are right-to-left associative + show_unary_op(f, &op); + + if e.precedence() > op.precedence() { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } else if let syntax::Expr::Unary(eop, _) = &**e { + // Prevent double-unary plus/minus turning into inc/dec + if eop == op && (*eop == syntax::UnaryOp::Add || *eop == syntax::UnaryOp::Minus) { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } else { + show_expr(f, &e); + } + } else { + show_expr(f, &e); + } + } + syntax::Expr::Binary(ref op, ref l, ref r) => { + // Note: all binary ops are left-to-right associative (<= for left part) + + if l.precedence() <= op.precedence() { + show_expr(f, &l); + } else { + let _ = f.write_str("("); + show_expr(f, &l); + let _ = f.write_str(")"); + } + + show_binary_op(f, &op); + + if r.precedence() < op.precedence() { + show_expr(f, &r); + } else { + let _ = f.write_str("("); + show_expr(f, &r); + let _ = f.write_str(")"); + } + } + syntax::Expr::Ternary(ref c, ref s, ref e) => { + // Note: ternary is right-to-left associative (<= for right part) + + if c.precedence() < expr.precedence() { + show_expr(f, &c); + } else { + let _ = f.write_str("("); + show_expr(f, &c); + let _ = f.write_str(")"); + } + let _ = f.write_str(" ? "); + show_expr(f, &s); + let _ = f.write_str(" : "); + if e.precedence() <= expr.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + } + syntax::Expr::Assignment(ref v, ref op, ref e) => { + // Note: all assignment ops are right-to-left associative + + if v.precedence() < op.precedence() { + show_expr(f, &v); + } else { + let _ = f.write_str("("); + show_expr(f, &v); + let _ = f.write_str(")"); + } + + let _ = f.write_str(" "); + show_assignment_op(f, &op); + let _ = f.write_str(" "); + + if e.precedence() <= op.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + } + syntax::Expr::Bracket(ref e, ref a) => { + // Note: bracket is left-to-right associative + + if e.precedence() <= expr.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + + show_array_spec(f, &a); + } + syntax::Expr::FunCall(ref fun, ref args) => { + show_function_identifier(f, &fun); + let _ = f.write_str("("); + + if !args.is_empty() { + let mut args_iter = args.iter(); + let first = args_iter.next().unwrap(); + show_expr(f, first); + + for e in args_iter { + let _ = f.write_str(", "); + show_expr(f, e); + } + } + + let _ = f.write_str(")"); + } + syntax::Expr::Dot(ref e, ref i) => { + // Note: dot is left-to-right associative + + if e.precedence() <= expr.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + let _ = f.write_str("."); + show_identifier(f, &i); + } + syntax::Expr::PostInc(ref e) => { + // Note: post-increment is right-to-left associative + + if e.precedence() < expr.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + + let _ = f.write_str("++"); + } + syntax::Expr::PostDec(ref e) => { + // Note: post-decrement is right-to-left associative + + if e.precedence() < expr.precedence() { + show_expr(f, &e); + } else { + let _ = f.write_str("("); + show_expr(f, &e); + let _ = f.write_str(")"); + } + + let _ = f.write_str("--"); + } + syntax::Expr::Comma(ref a, ref b) => { + // Note: comma is left-to-right associative + + if a.precedence() <= expr.precedence() { + show_expr(f, &a); + } else { + let _ = f.write_str("("); + show_expr(f, &a); + let _ = f.write_str(")"); + } + + let _ = f.write_str(", "); + + if b.precedence() < expr.precedence() { + show_expr(f, &b); + } else { + let _ = f.write_str("("); + show_expr(f, &b); + let _ = f.write_str(")"); + } + } + } +} + +pub fn show_path<F>(f: &mut F, path: &syntax::Path) +where + F: Write, +{ + match path { + syntax::Path::Absolute(s) => { + let _ = write!(f, "<{}>", s); + } + syntax::Path::Relative(s) => { + let _ = write!(f, "\"{}\"", s); + } + } +} + +pub fn show_unary_op<F>(f: &mut F, op: &syntax::UnaryOp) +where + F: Write, +{ + match *op { + syntax::UnaryOp::Inc => { + let _ = f.write_str("++"); + } + syntax::UnaryOp::Dec => { + let _ = f.write_str("--"); + } + syntax::UnaryOp::Add => { + let _ = f.write_str("+"); + } + syntax::UnaryOp::Minus => { + let _ = f.write_str("-"); + } + syntax::UnaryOp::Not => { + let _ = f.write_str("!"); + } + syntax::UnaryOp::Complement => { + let _ = f.write_str("~"); + } + } +} + +pub fn show_binary_op<F>(f: &mut F, op: &syntax::BinaryOp) +where + F: Write, +{ + match *op { + syntax::BinaryOp::Or => { + let _ = f.write_str("||"); + } + syntax::BinaryOp::Xor => { + let _ = f.write_str("^^"); + } + syntax::BinaryOp::And => { + let _ = f.write_str("&&"); + } + syntax::BinaryOp::BitOr => { + let _ = f.write_str("|"); + } + syntax::BinaryOp::BitXor => { + let _ = f.write_str("^"); + } + syntax::BinaryOp::BitAnd => { + let _ = f.write_str("&"); + } + syntax::BinaryOp::Equal => { + let _ = f.write_str("=="); + } + syntax::BinaryOp::NonEqual => { + let _ = f.write_str("!="); + } + syntax::BinaryOp::LT => { + let _ = f.write_str("<"); + } + syntax::BinaryOp::GT => { + let _ = f.write_str(">"); + } + syntax::BinaryOp::LTE => { + let _ = f.write_str("<="); + } + syntax::BinaryOp::GTE => { + let _ = f.write_str(">="); + } + syntax::BinaryOp::LShift => { + let _ = f.write_str("<<"); + } + syntax::BinaryOp::RShift => { + let _ = f.write_str(">>"); + } + syntax::BinaryOp::Add => { + let _ = f.write_str("+"); + } + syntax::BinaryOp::Sub => { + let _ = f.write_str("-"); + } + syntax::BinaryOp::Mult => { + let _ = f.write_str("*"); + } + syntax::BinaryOp::Div => { + let _ = f.write_str("/"); + } + syntax::BinaryOp::Mod => { + let _ = f.write_str("%"); + } + } +} + +pub fn show_assignment_op<F>(f: &mut F, op: &syntax::AssignmentOp) +where + F: Write, +{ + match *op { + syntax::AssignmentOp::Equal => { + let _ = f.write_str("="); + } + syntax::AssignmentOp::Mult => { + let _ = f.write_str("*="); + } + syntax::AssignmentOp::Div => { + let _ = f.write_str("/="); + } + syntax::AssignmentOp::Mod => { + let _ = f.write_str("%="); + } + syntax::AssignmentOp::Add => { + let _ = f.write_str("+="); + } + syntax::AssignmentOp::Sub => { + let _ = f.write_str("-="); + } + syntax::AssignmentOp::LShift => { + let _ = f.write_str("<<="); + } + syntax::AssignmentOp::RShift => { + let _ = f.write_str(">>="); + } + syntax::AssignmentOp::And => { + let _ = f.write_str("&="); + } + syntax::AssignmentOp::Xor => { + let _ = f.write_str("^="); + } + syntax::AssignmentOp::Or => { + let _ = f.write_str("|="); + } + } +} + +pub fn show_function_identifier<F>(f: &mut F, i: &syntax::FunIdentifier) +where + F: Write, +{ + match *i { + syntax::FunIdentifier::Identifier(ref n) => show_identifier(f, &n), + syntax::FunIdentifier::Expr(ref e) => show_expr(f, &*e), + } +} + +pub fn show_declaration<F>(f: &mut F, d: &syntax::Declaration) +where + F: Write, +{ + match *d { + syntax::Declaration::FunctionPrototype(ref proto) => { + show_function_prototype(f, &proto); + let _ = f.write_str(";\n"); + } + syntax::Declaration::InitDeclaratorList(ref list) => { + show_init_declarator_list(f, &list); + let _ = f.write_str(";\n"); + } + syntax::Declaration::Precision(ref qual, ref ty) => { + show_precision_qualifier(f, &qual); + show_type_specifier(f, &ty); + let _ = f.write_str(";\n"); + } + syntax::Declaration::Block(ref block) => { + show_block(f, &block); + let _ = f.write_str(";\n"); + } + syntax::Declaration::Global(ref qual, ref identifiers) => { + show_type_qualifier(f, &qual); + + if !identifiers.is_empty() { + let mut iter = identifiers.iter(); + let first = iter.next().unwrap(); + show_identifier(f, first); + + for identifier in iter { + let _ = write!(f, ", {}", identifier); + } + } + + let _ = f.write_str(";\n"); + } + } +} + +pub fn show_function_prototype<F>(f: &mut F, fp: &syntax::FunctionPrototype) +where + F: Write, +{ + show_fully_specified_type(f, &fp.ty); + let _ = f.write_str(" "); + show_identifier(f, &fp.name); + + let _ = f.write_str("("); + + if !fp.parameters.is_empty() { + let mut iter = fp.parameters.iter(); + let first = iter.next().unwrap(); + show_function_parameter_declaration(f, first); + + for param in iter { + let _ = f.write_str(", "); + show_function_parameter_declaration(f, param); + } + } + + let _ = f.write_str(")"); +} +pub fn show_function_parameter_declaration<F>(f: &mut F, p: &syntax::FunctionParameterDeclaration) +where + F: Write, +{ + match *p { + syntax::FunctionParameterDeclaration::Named(ref qual, ref fpd) => { + if let Some(ref q) = *qual { + show_type_qualifier(f, q); + let _ = f.write_str(" "); + } + + show_function_parameter_declarator(f, fpd); + } + syntax::FunctionParameterDeclaration::Unnamed(ref qual, ref ty) => { + if let Some(ref q) = *qual { + show_type_qualifier(f, q); + let _ = f.write_str(" "); + } + + show_type_specifier(f, ty); + } + } +} + +pub fn show_function_parameter_declarator<F>(f: &mut F, p: &syntax::FunctionParameterDeclarator) +where + F: Write, +{ + show_type_specifier(f, &p.ty); + let _ = f.write_str(" "); + show_arrayed_identifier(f, &p.ident); +} + +pub fn show_init_declarator_list<F>(f: &mut F, i: &syntax::InitDeclaratorList) +where + F: Write, +{ + show_single_declaration(f, &i.head); + + for decl in &i.tail { + let _ = f.write_str(", "); + show_single_declaration_no_type(f, decl); + } +} + +pub fn show_single_declaration<F>(f: &mut F, d: &syntax::SingleDeclaration) +where + F: Write, +{ + show_fully_specified_type(f, &d.ty); + + if let Some(ref name) = d.name { + let _ = f.write_str(" "); + show_identifier(f, name); + } + + if let Some(ref arr_spec) = d.array_specifier { + show_array_spec(f, arr_spec); + } + + if let Some(ref initializer) = d.initializer { + let _ = f.write_str(" = "); + show_initializer(f, initializer); + } +} + +pub fn show_single_declaration_no_type<F>(f: &mut F, d: &syntax::SingleDeclarationNoType) +where + F: Write, +{ + show_arrayed_identifier(f, &d.ident); + + if let Some(ref initializer) = d.initializer { + let _ = f.write_str(" = "); + show_initializer(f, initializer); + } +} + +pub fn show_initializer<F>(f: &mut F, i: &syntax::Initializer) +where + F: Write, +{ + match *i { + syntax::Initializer::Simple(ref e) => show_expr(f, e), + syntax::Initializer::List(ref list) => { + let mut iter = list.0.iter(); + let first = iter.next().unwrap(); + + let _ = f.write_str("{ "); + show_initializer(f, first); + + for ini in iter { + let _ = f.write_str(", "); + show_initializer(f, ini); + } + + let _ = f.write_str(" }"); + } + } +} + +pub fn show_block<F>(f: &mut F, b: &syntax::Block) +where + F: Write, +{ + show_type_qualifier(f, &b.qualifier); + let _ = f.write_str(" "); + show_identifier(f, &b.name); + let _ = f.write_str(" {"); + + for field in &b.fields { + show_struct_field(f, field); + let _ = f.write_str("\n"); + } + let _ = f.write_str("}"); + + if let Some(ref ident) = b.identifier { + show_arrayed_identifier(f, ident); + } +} + +pub fn show_function_definition<F>(f: &mut F, fd: &syntax::FunctionDefinition) +where + F: Write, +{ + show_function_prototype(f, &fd.prototype); + let _ = f.write_str(" "); + show_compound_statement(f, &fd.statement); +} + +pub fn show_compound_statement<F>(f: &mut F, cst: &syntax::CompoundStatement) +where + F: Write, +{ + let _ = f.write_str("{\n"); + + for st in &cst.statement_list { + show_statement(f, st); + } + + let _ = f.write_str("}\n"); +} + +pub fn show_statement<F>(f: &mut F, st: &syntax::Statement) +where + F: Write, +{ + match *st { + syntax::Statement::Compound(ref cst) => show_compound_statement(f, cst), + syntax::Statement::Simple(ref sst) => show_simple_statement(f, sst), + } +} + +pub fn show_simple_statement<F>(f: &mut F, sst: &syntax::SimpleStatement) +where + F: Write, +{ + match *sst { + syntax::SimpleStatement::Declaration(ref d) => show_declaration(f, d), + syntax::SimpleStatement::Expression(ref e) => show_expression_statement(f, e), + syntax::SimpleStatement::Selection(ref s) => show_selection_statement(f, s), + syntax::SimpleStatement::Switch(ref s) => show_switch_statement(f, s), + syntax::SimpleStatement::CaseLabel(ref cl) => show_case_label(f, cl), + syntax::SimpleStatement::Iteration(ref i) => show_iteration_statement(f, i), + syntax::SimpleStatement::Jump(ref j) => show_jump_statement(f, j), + } +} + +pub fn show_expression_statement<F>(f: &mut F, est: &syntax::ExprStatement) +where + F: Write, +{ + if let Some(ref e) = *est { + show_expr(f, e); + } + + let _ = f.write_str(";\n"); +} + +pub fn show_selection_statement<F>(f: &mut F, sst: &syntax::SelectionStatement) +where + F: Write, +{ + let _ = f.write_str("if ("); + show_expr(f, &sst.cond); + let _ = f.write_str(") {\n"); + show_selection_rest_statement(f, &sst.rest); +} + +pub fn show_selection_rest_statement<F>(f: &mut F, sst: &syntax::SelectionRestStatement) +where + F: Write, +{ + match *sst { + syntax::SelectionRestStatement::Statement(ref if_st) => { + show_statement(f, if_st); + let _ = f.write_str("}\n"); + } + syntax::SelectionRestStatement::Else(ref if_st, ref else_st) => { + show_statement(f, if_st); + let _ = f.write_str("} else "); + show_statement(f, else_st); + } + } +} + +pub fn show_switch_statement<F>(f: &mut F, sst: &syntax::SwitchStatement) +where + F: Write, +{ + let _ = f.write_str("switch ("); + show_expr(f, &sst.head); + let _ = f.write_str(") {\n"); + + for st in &sst.body { + show_statement(f, st); + } + + let _ = f.write_str("}\n"); +} + +pub fn show_case_label<F>(f: &mut F, cl: &syntax::CaseLabel) +where + F: Write, +{ + match *cl { + syntax::CaseLabel::Case(ref e) => { + let _ = f.write_str("case "); + show_expr(f, e); + let _ = f.write_str(":\n"); + } + syntax::CaseLabel::Def => { + let _ = f.write_str("default:\n"); + } + } +} + +pub fn show_iteration_statement<F>(f: &mut F, ist: &syntax::IterationStatement) +where + F: Write, +{ + match *ist { + syntax::IterationStatement::While(ref cond, ref body) => { + let _ = f.write_str("while ("); + show_condition(f, cond); + let _ = f.write_str(") "); + show_statement(f, body); + } + syntax::IterationStatement::DoWhile(ref body, ref cond) => { + let _ = f.write_str("do "); + show_statement(f, body); + let _ = f.write_str(" while ("); + show_expr(f, cond); + let _ = f.write_str(")\n"); + } + syntax::IterationStatement::For(ref init, ref rest, ref body) => { + let _ = f.write_str("for ("); + show_for_init_statement(f, init); + show_for_rest_statement(f, rest); + let _ = f.write_str(") "); + show_statement(f, body); + } + } +} + +pub fn show_condition<F>(f: &mut F, c: &syntax::Condition) +where + F: Write, +{ + match *c { + syntax::Condition::Expr(ref e) => show_expr(f, e), + syntax::Condition::Assignment(ref ty, ref name, ref initializer) => { + show_fully_specified_type(f, ty); + let _ = f.write_str(" "); + show_identifier(f, name); + let _ = f.write_str(" = "); + show_initializer(f, initializer); + } + } +} + +pub fn show_for_init_statement<F>(f: &mut F, i: &syntax::ForInitStatement) +where + F: Write, +{ + match *i { + syntax::ForInitStatement::Expression(ref expr) => { + if let Some(ref e) = *expr { + show_expr(f, e); + } + } + syntax::ForInitStatement::Declaration(ref d) => show_declaration(f, d), + } +} + +pub fn show_for_rest_statement<F>(f: &mut F, r: &syntax::ForRestStatement) +where + F: Write, +{ + if let Some(ref cond) = r.condition { + show_condition(f, cond); + } + + let _ = f.write_str("; "); + + if let Some(ref e) = r.post_expr { + show_expr(f, e); + } +} + +pub fn show_jump_statement<F>(f: &mut F, j: &syntax::JumpStatement) +where + F: Write, +{ + match *j { + syntax::JumpStatement::Continue => { + let _ = f.write_str("continue;\n"); + } + syntax::JumpStatement::Break => { + let _ = f.write_str("break;\n"); + } + syntax::JumpStatement::Discard => { + let _ = f.write_str("discard;\n"); + } + syntax::JumpStatement::Return(ref e) => { + let _ = f.write_str("return "); + if let Some(e) = e { + show_expr(f, e); + } + let _ = f.write_str(";\n"); + } + } +} + +pub fn show_preprocessor<F>(f: &mut F, pp: &syntax::Preprocessor) +where + F: Write, +{ + match *pp { + syntax::Preprocessor::Define(ref pd) => show_preprocessor_define(f, pd), + syntax::Preprocessor::Else => show_preprocessor_else(f), + syntax::Preprocessor::ElseIf(ref pei) => show_preprocessor_elseif(f, pei), + syntax::Preprocessor::EndIf => show_preprocessor_endif(f), + syntax::Preprocessor::Error(ref pe) => show_preprocessor_error(f, pe), + syntax::Preprocessor::If(ref pi) => show_preprocessor_if(f, pi), + syntax::Preprocessor::IfDef(ref pid) => show_preprocessor_ifdef(f, pid), + syntax::Preprocessor::IfNDef(ref pind) => show_preprocessor_ifndef(f, pind), + syntax::Preprocessor::Include(ref pi) => show_preprocessor_include(f, pi), + syntax::Preprocessor::Line(ref pl) => show_preprocessor_line(f, pl), + syntax::Preprocessor::Pragma(ref pp) => show_preprocessor_pragma(f, pp), + syntax::Preprocessor::Undef(ref pu) => show_preprocessor_undef(f, pu), + syntax::Preprocessor::Version(ref pv) => show_preprocessor_version(f, pv), + syntax::Preprocessor::Extension(ref pe) => show_preprocessor_extension(f, pe), + } +} + +pub fn show_preprocessor_define<F>(f: &mut F, pd: &syntax::PreprocessorDefine) +where + F: Write, +{ + match *pd { + syntax::PreprocessorDefine::ObjectLike { + ref ident, + ref value, + } => { + let _ = write!(f, "#define {} {}\n", ident, value); + } + + syntax::PreprocessorDefine::FunctionLike { + ref ident, + ref args, + ref value, + } => { + let _ = write!(f, "#define {}(", ident); + + if !args.is_empty() { + let _ = write!(f, "{}", &args[0]); + + for arg in &args[1..args.len()] { + let _ = write!(f, ", {}", arg); + } + } + + let _ = write!(f, ") {}\n", value); + } + } +} + +pub fn show_preprocessor_else<F>(f: &mut F) +where + F: Write, +{ + let _ = f.write_str("#else\n"); +} + +pub fn show_preprocessor_elseif<F>(f: &mut F, pei: &syntax::PreprocessorElseIf) +where + F: Write, +{ + let _ = write!(f, "#elseif {}\n", pei.condition); +} + +pub fn show_preprocessor_error<F>(f: &mut F, pe: &syntax::PreprocessorError) +where + F: Write, +{ + let _ = writeln!(f, "#error {}", pe.message); +} + +pub fn show_preprocessor_endif<F>(f: &mut F) +where + F: Write, +{ + let _ = f.write_str("#endif\n"); +} + +pub fn show_preprocessor_if<F>(f: &mut F, pi: &syntax::PreprocessorIf) +where + F: Write, +{ + let _ = write!(f, "#if {}\n", pi.condition); +} + +pub fn show_preprocessor_ifdef<F>(f: &mut F, pid: &syntax::PreprocessorIfDef) +where + F: Write, +{ + let _ = f.write_str("#ifdef "); + show_identifier(f, &pid.ident); + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_ifndef<F>(f: &mut F, pind: &syntax::PreprocessorIfNDef) +where + F: Write, +{ + let _ = f.write_str("#ifndef "); + show_identifier(f, &pind.ident); + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_include<F>(f: &mut F, pi: &syntax::PreprocessorInclude) +where + F: Write, +{ + let _ = f.write_str("#include "); + show_path(f, &pi.path); + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_line<F>(f: &mut F, pl: &syntax::PreprocessorLine) +where + F: Write, +{ + let _ = write!(f, "#line {}", pl.line); + if let Some(source_string_number) = pl.source_string_number { + let _ = write!(f, " {}", source_string_number); + } + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_pragma<F>(f: &mut F, pp: &syntax::PreprocessorPragma) +where + F: Write, +{ + let _ = writeln!(f, "#pragma {}", pp.command); +} + +pub fn show_preprocessor_undef<F>(f: &mut F, pud: &syntax::PreprocessorUndef) +where + F: Write, +{ + let _ = f.write_str("#undef "); + show_identifier(f, &pud.name); + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_version<F>(f: &mut F, pv: &syntax::PreprocessorVersion) +where + F: Write, +{ + let _ = write!(f, "#version {}", pv.version); + + if let Some(ref profile) = pv.profile { + match *profile { + syntax::PreprocessorVersionProfile::Core => { + let _ = f.write_str(" core"); + } + syntax::PreprocessorVersionProfile::Compatibility => { + let _ = f.write_str(" compatibility"); + } + syntax::PreprocessorVersionProfile::ES => { + let _ = f.write_str(" es"); + } + } + } + + let _ = f.write_str("\n"); +} + +pub fn show_preprocessor_extension<F>(f: &mut F, pe: &syntax::PreprocessorExtension) +where + F: Write, +{ + let _ = f.write_str("#extension "); + + match pe.name { + syntax::PreprocessorExtensionName::All => { + let _ = f.write_str("all"); + } + syntax::PreprocessorExtensionName::Specific(ref n) => { + let _ = f.write_str(n); + } + } + + if let Some(ref behavior) = pe.behavior { + match *behavior { + syntax::PreprocessorExtensionBehavior::Require => { + let _ = f.write_str(" : require"); + } + syntax::PreprocessorExtensionBehavior::Enable => { + let _ = f.write_str(" : enable"); + } + syntax::PreprocessorExtensionBehavior::Warn => { + let _ = f.write_str(" : warn"); + } + syntax::PreprocessorExtensionBehavior::Disable => { + let _ = f.write_str(" : disable"); + } + } + } + + let _ = f.write_str("\n"); +} + +pub fn show_external_declaration<F>(f: &mut F, ed: &syntax::ExternalDeclaration) +where + F: Write, +{ + match *ed { + syntax::ExternalDeclaration::Preprocessor(ref pp) => show_preprocessor(f, pp), + syntax::ExternalDeclaration::FunctionDefinition(ref fd) => show_function_definition(f, fd), + syntax::ExternalDeclaration::Declaration(ref d) => show_declaration(f, d), + } +} + +pub fn show_translation_unit<F>(f: &mut F, tu: &syntax::TranslationUnit) +where + F: Write, +{ + for ed in &(tu.0).0 { + show_external_declaration(f, ed); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parsers::expr; + + fn to_string(e: &syntax::Expr) -> String { + let mut s = String::new(); + show_expr(&mut s, e); + s + } + + #[test] + fn unary_parentheses() { + assert_eq!(to_string(&expr("-a").unwrap().1), "-a"); + assert_eq!(to_string(&expr("-(a + b)").unwrap().1), "-(a+b)"); + assert_eq!(to_string(&expr("-a.x").unwrap().1), "-a.x"); + + assert_eq!(to_string(&expr("-(-a)").unwrap().1), "-(-a)"); + assert_eq!(to_string(&expr("+(+a)").unwrap().1), "+(+a)"); + assert_eq!(to_string(&expr("~~a").unwrap().1), "~~a"); + assert_eq!(to_string(&expr("--a").unwrap().1), "--a"); + assert_eq!(to_string(&expr("++a").unwrap().1), "++a"); + assert_eq!(to_string(&expr("+-a").unwrap().1), "+-a"); + } + + #[test] + fn binary_parentheses() { + assert_eq!(to_string(&expr("a + b").unwrap().1), "a+b"); + assert_eq!(to_string(&expr("a * b + c").unwrap().1), "a*b+c"); + assert_eq!(to_string(&expr("(a + b) * c").unwrap().1), "(a+b)*c"); + assert_eq!(to_string(&expr("a + (b * c)").unwrap().1), "a+b*c"); + assert_eq!(to_string(&expr("a * (b + c)").unwrap().1), "a*(b+c)"); + assert_eq!(to_string(&expr("(a * b) * c").unwrap().1), "a*b*c"); + assert_eq!(to_string(&expr("a * (b * c)").unwrap().1), "a*(b*c)"); + assert_eq!(to_string(&expr("a&&b&&c").unwrap().1), "a&&b&&c"); + assert_eq!( + to_string(&expr("n - p > 0. && u.y < n && u.y > p").unwrap().1), + "n-p>0.&&u.y<n&&u.y>p" + ); + } + + #[test] + fn ternary_parentheses() { + assert_eq!( + to_string(&expr("a ? b : c ? d : e").unwrap().1), + "a ? b : c ? d : e" + ); + assert_eq!( + to_string(&expr("(a ? b : c) ? d : e").unwrap().1), + "(a ? b : c) ? d : e" + ); + } + + #[test] + fn assignment_parentheses() { + assert_eq!(to_string(&expr("a = b = c").unwrap().1), "a = b = c"); + assert_eq!(to_string(&expr("(a = b) = c").unwrap().1), "(a = b) = c"); + } + + #[test] + fn dot_parentheses() { + assert_eq!(to_string(&expr("a.x").unwrap().1), "a.x"); + assert_eq!(to_string(&expr("(a + b).x").unwrap().1), "(a+b).x"); + } + + #[test] + fn test_parentheses() { + use crate::parsers::function_definition; + + const SRC: &'static str = r#"vec2 main() { +float n = 0.; +float p = 0.; +float u = vec2(0., 0.); +if (n-p>0.&&u.y<n&&u.y>p) { +} +return u; +} +"#; + + // Ideally we would use SRC as the expected, but there's a bug in block braces generation + const DST: &'static str = r#"vec2 main() { +float n = 0.; +float p = 0.; +float u = vec2(0., 0.); +if (n-p>0.&&u.y<n&&u.y>p) { +{ +} +} +return u; +} +"#; + + let mut s = String::new(); + show_function_definition(&mut s, &function_definition(SRC).unwrap().1); + + assert_eq!(s, DST); + } + + #[test] + fn roundtrip_glsl_complex_expr() { + let zero = syntax::Expr::DoubleConst(0.); + let ray = syntax::Expr::Variable("ray".into()); + let raydir = syntax::Expr::Dot(Box::new(ray), "dir".into()); + let vec4 = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("vec4".into()), + vec![raydir, zero], + ); + let view = syntax::Expr::Variable("view".into()); + let iview = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("inverse".into()), + vec![view], + ); + let mul = syntax::Expr::Binary(syntax::BinaryOp::Mult, Box::new(iview), Box::new(vec4)); + let xyz = syntax::Expr::Dot(Box::new(mul), "xyz".into()); + let input = syntax::Expr::FunCall( + syntax::FunIdentifier::Identifier("normalize".into()), + vec![xyz], + ); + + let mut output = String::new(); + show_expr(&mut output, &input); + let _ = output.write_str(";"); + + let back = expr(&output); + + assert_eq!(back, Ok((";", input)), "intermediate source '{}'", output); + } +} diff --git a/third_party/rust/glsl/src/transpiler/mod.rs b/third_party/rust/glsl/src/transpiler/mod.rs new file mode 100644 index 0000000000..cbb7d78190 --- /dev/null +++ b/third_party/rust/glsl/src/transpiler/mod.rs @@ -0,0 +1,8 @@ +//! GLSL transpilers – i.e. going from GLSL to anything else. +//! +//! There’s no public interface / trait to define what a transpiler is. It depends on the target +//! representation you aim. + +pub mod glsl; +#[cfg(feature = "spirv")] +pub mod spirv; diff --git a/third_party/rust/glsl/src/transpiler/spirv.rs b/third_party/rust/glsl/src/transpiler/spirv.rs new file mode 100644 index 0000000000..a75f3afab6 --- /dev/null +++ b/third_party/rust/glsl/src/transpiler/spirv.rs @@ -0,0 +1,95 @@ +//! SPIR-V transpiler. +//! +//! The current implementation uses the [shaderc](https://crates.io/crates/shaderc) crate to +//! transpile GLSL to SPIR-V. This is not ideal but will provide a default and starting +//! implementation. + +use shaderc; + +use crate::syntax; +use crate::transpiler::glsl as glsl_transpiler; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ShaderKind { + TessControl, + TessEvaluation, + Vertex, + Geometry, + Fragment, + Compute, +} + +impl From<ShaderKind> for shaderc::ShaderKind { + fn from(kind: ShaderKind) -> Self { + match kind { + ShaderKind::TessControl => shaderc::ShaderKind::TessControl, + ShaderKind::TessEvaluation => shaderc::ShaderKind::TessEvaluation, + ShaderKind::Vertex => shaderc::ShaderKind::Vertex, + ShaderKind::Geometry => shaderc::ShaderKind::Geometry, + ShaderKind::Fragment => shaderc::ShaderKind::Fragment, + ShaderKind::Compute => shaderc::ShaderKind::Compute, + } + } +} + +/// Transpile a GLSL AST into a SPIR-V internal buffer and write it to the given buffer. +/// +/// The current implementation is highly inefficient as it relies on internal allocations and +/// [shaderc](https://crates.io/crates/shaderc). +/// +/// If any error happens while transpiling, they’re returned as an opaque string. +pub fn transpile_translation_unit_to_binary<F>( + f: &mut F, + tu: &syntax::TranslationUnit, + kind: ShaderKind, +) -> Result<(), String> +where + F: std::io::Write, +{ + // write as GLSL in an intermediate buffer + let mut glsl_buffer = String::new(); + glsl_transpiler::show_translation_unit(&mut glsl_buffer, tu); + + // pass the GLSL-formatted string to shaderc + let mut compiler = shaderc::Compiler::new().unwrap(); + let options = shaderc::CompileOptions::new().unwrap(); + let kind = kind.into(); + let output = compiler + .compile_into_spirv(&glsl_buffer, kind, "glsl input", "main", Some(&options)) + .map_err(|e| format!("{}", e))?; + + let _ = f.write_all(output.as_binary_u8()); + + Ok(()) +} + +/// Transpile a GLSL AST into a SPIR-V internal buffer and write it to the given buffer. +/// +/// The current implementation is highly inefficient as it relies on internal allocations and +/// [shaderc](https://crates.io/crates/shaderc). +/// +/// If any error happens while transpiling, they’re returned as an opaque string. +pub fn transpile_translation_unit<F>( + f: &mut F, + tu: &syntax::TranslationUnit, + kind: ShaderKind, +) -> Result<(), String> +where + F: std::fmt::Write, +{ + // write as GLSL in an intermediate buffer + let mut glsl_buffer = String::new(); + glsl_transpiler::show_translation_unit(&mut glsl_buffer, tu); + + // pass the GLSL-formatted string to shaderc + let mut compiler = shaderc::Compiler::new().unwrap(); + let options = shaderc::CompileOptions::new().unwrap(); + let kind = kind.into(); + let output = compiler + .compile_into_spirv_assembly(&glsl_buffer, kind, "glsl input", "main", Some(&options)) + .map_err(|e| format!("{}", e))?; + + let _ = f.write_str(&output.as_text()); + + Ok(()) +} diff --git a/third_party/rust/glsl/src/visitor.rs b/third_party/rust/glsl/src/visitor.rs new file mode 100644 index 0000000000..5867c72ded --- /dev/null +++ b/third_party/rust/glsl/src/visitor.rs @@ -0,0 +1,1465 @@ +//! AST visitors (i.e. on-the-fly mutation at different places in the AST). +//! +//! Visitors are mutable objects that can mutate parts of an AST while traversing it. You can see +//! them as flexible mutations taking place on *patterns* representing your AST – they get called +//! everytime an interesting node gets visited. Because of their mutable nature, you can accumulate +//! a state as you traverse the AST and implement exotic filtering. +//! +//! Visitors must implement the [`Visitor`] trait in order to be usable. +//! +//! In order to visit any part of an AST (from its very top root or from any part of it), you must +//! use the [`Host`] interface, that provides the [`Host::visit`] function. +//! +//! For instance, we can imagine visiting an AST to count how many variables are declared: +//! +//! ``` +//! use glsl::syntax::{CompoundStatement, Expr, SingleDeclaration, Statement, TypeSpecifierNonArray}; +//! use glsl::visitor::{Host, Visit, Visitor}; +//! use std::iter::FromIterator; +//! +//! let decl0 = Statement::declare_var( +//! TypeSpecifierNonArray::Float, +//! "x", +//! None, +//! Some(Expr::from(3.14).into()) +//! ); +//! +//! let decl1 = Statement::declare_var( +//! TypeSpecifierNonArray::Int, +//! "y", +//! None, +//! None +//! ); +//! +//! let decl2 = Statement::declare_var( +//! TypeSpecifierNonArray::Vec4, +//! "z", +//! None, +//! None +//! ); +//! +//! let compound = CompoundStatement::from_iter(vec![decl0, decl1, decl2]); +//! +//! // our visitor that will count the number of variables it saw +//! struct Counter { +//! var_nb: usize +//! } +//! +//! impl Visitor for Counter { +//! // we are only interested in single declaration with a name +//! fn visit_single_declaration(&mut self, declaration: &SingleDeclaration) -> Visit { +//! if declaration.name.is_some() { +//! self.var_nb += 1; +//! } +//! +//! // do not go deeper +//! Visit::Parent +//! } +//! } +//! +//! let mut counter = Counter { var_nb: 0 }; +//! compound.visit(&mut counter); +//! assert_eq!(counter.var_nb, 3); +//! ``` +//! +//! [`Host`]: crate::visitor::Host +//! [`Host::visit`]: crate::visitor::Host::visit +//! [`Visitor`]: crate::visitor::Visitor + +use crate::syntax; + +/// Visit strategy after having visited an AST node. +/// +/// Some AST nodes have *children* – in enum’s variants, in some fields as nested in [`Vec`], etc. +/// Those nodes can be visited depending on the strategy you chose. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum Visit { + /// The visitor will go deeper in the AST by visiting all the children, if any. If no children are + /// present or if having children doesn’t make sense for a specific part of the AST, this + /// strategy will be ignored. + Children, + /// The visitor won’t visit children nor siblings and will go up. + Parent, +} + +macro_rules! make_visitor_trait { + ($t:ident, $($ref:tt)*) => { + /// Visitor object, visiting AST nodes. + /// + /// This trait exists in two flavors, depending on whether you want to mutate the AST or not: [`Visitor`] doesn’t + /// allow for mutation while [`VisitorMut`] does. + pub trait $t { + fn visit_translation_unit(&mut self, _: $($ref)* syntax::TranslationUnit) -> Visit { + Visit::Children + } + + fn visit_external_declaration(&mut self, _: $($ref)* syntax::ExternalDeclaration) -> Visit { + Visit::Children + } + + fn visit_identifier(&mut self, _: $($ref)* syntax::Identifier) -> Visit { + Visit::Children + } + + fn visit_arrayed_identifier(&mut self, _: $($ref)* syntax::ArrayedIdentifier) -> Visit { + Visit::Children + } + + fn visit_type_name(&mut self, _: $($ref)* syntax::TypeName) -> Visit { + Visit::Children + } + + fn visit_block(&mut self, _: $($ref)* syntax::Block) -> Visit { + Visit::Children + } + + fn visit_for_init_statement(&mut self, _: $($ref)* syntax::ForInitStatement) -> Visit { + Visit::Children + } + + fn visit_for_rest_statement(&mut self, _: $($ref)* syntax::ForRestStatement) -> Visit { + Visit::Children + } + + fn visit_function_definition(&mut self, _: $($ref)* syntax::FunctionDefinition) -> Visit { + Visit::Children + } + + fn visit_function_parameter_declarator( + &mut self, + _: $($ref)* syntax::FunctionParameterDeclarator, + ) -> Visit { + Visit::Children + } + + fn visit_function_prototype(&mut self, _: $($ref)* syntax::FunctionPrototype) -> Visit { + Visit::Children + } + + fn visit_init_declarator_list(&mut self, _: $($ref)* syntax::InitDeclaratorList) -> Visit { + Visit::Children + } + + fn visit_layout_qualifier(&mut self, _: $($ref)* syntax::LayoutQualifier) -> Visit { + Visit::Children + } + + fn visit_preprocessor(&mut self, _: $($ref)* syntax::Preprocessor) -> Visit { + Visit::Children + } + + fn visit_preprocessor_define(&mut self, _: $($ref)* syntax::PreprocessorDefine) -> Visit { + Visit::Children + } + + fn visit_preprocessor_elseif(&mut self, _: $($ref)* syntax::PreprocessorElseIf) -> Visit { + Visit::Children + } + + fn visit_preprocessor_error(&mut self, _: $($ref)* syntax::PreprocessorError) -> Visit { + Visit::Children + } + + fn visit_preprocessor_extension(&mut self, _: $($ref)* syntax::PreprocessorExtension) -> Visit { + Visit::Children + } + + fn visit_preprocessor_extension_behavior( + &mut self, + _: $($ref)* syntax::PreprocessorExtensionBehavior, + ) -> Visit { + Visit::Children + } + + fn visit_preprocessor_extension_name( + &mut self, + _: $($ref)* syntax::PreprocessorExtensionName, + ) -> Visit { + Visit::Children + } + + fn visit_preprocessor_if(&mut self, _: $($ref)* syntax::PreprocessorIf) -> Visit { + Visit::Children + } + + fn visit_preprocessor_ifdef(&mut self, _: $($ref)* syntax::PreprocessorIfDef) -> Visit { + Visit::Children + } + + fn visit_preprocessor_ifndef(&mut self, _: $($ref)* syntax::PreprocessorIfNDef) -> Visit { + Visit::Children + } + + fn visit_preprocessor_include(&mut self, _: $($ref)* syntax::PreprocessorInclude) -> Visit { + Visit::Children + } + + fn visit_preprocessor_line(&mut self, _: $($ref)* syntax::PreprocessorLine) -> Visit { + Visit::Children + } + + fn visit_preprocessor_pragma(&mut self, _: $($ref)* syntax::PreprocessorPragma) -> Visit { + Visit::Children + } + + fn visit_preprocessor_undef(&mut self, _: $($ref)* syntax::PreprocessorUndef) -> Visit { + Visit::Children + } + + fn visit_preprocessor_version(&mut self, _: $($ref)* syntax::PreprocessorVersion) -> Visit { + Visit::Children + } + + fn visit_preprocessor_version_profile( + &mut self, + _: $($ref)* syntax::PreprocessorVersionProfile, + ) -> Visit { + Visit::Children + } + + fn visit_selection_statement(&mut self, _: $($ref)* syntax::SelectionStatement) -> Visit { + Visit::Children + } + + fn visit_selection_rest_statement(&mut self, _: $($ref)* syntax::SelectionRestStatement) -> Visit { + Visit::Children + } + + fn visit_single_declaration(&mut self, _: $($ref)* syntax::SingleDeclaration) -> Visit { + Visit::Children + } + + fn visit_single_declaration_no_type(&mut self, _: $($ref)* syntax::SingleDeclarationNoType) -> Visit { + Visit::Children + } + + fn visit_struct_field_specifier(&mut self, _: $($ref)* syntax::StructFieldSpecifier) -> Visit { + Visit::Children + } + + fn visit_struct_specifier(&mut self, _: $($ref)* syntax::StructSpecifier) -> Visit { + Visit::Children + } + + fn visit_switch_statement(&mut self, _: $($ref)* syntax::SwitchStatement) -> Visit { + Visit::Children + } + + fn visit_type_qualifier(&mut self, _: $($ref)* syntax::TypeQualifier) -> Visit { + Visit::Children + } + + fn visit_type_specifier(&mut self, _: $($ref)* syntax::TypeSpecifier) -> Visit { + Visit::Children + } + + fn visit_full_specified_type(&mut self, _: $($ref)* syntax::FullySpecifiedType) -> Visit { + Visit::Children + } + + fn visit_array_specifier(&mut self, _: $($ref)* syntax::ArraySpecifier) -> Visit { + Visit::Children + } + + fn visit_array_specifier_dimension(&mut self, _: $($ref)* syntax::ArraySpecifierDimension) -> Visit { + Visit::Children + } + + fn visit_assignment_op(&mut self, _: $($ref)* syntax::AssignmentOp) -> Visit { + Visit::Children + } + + fn visit_binary_op(&mut self, _: $($ref)* syntax::BinaryOp) -> Visit { + Visit::Children + } + + fn visit_case_label(&mut self, _: $($ref)* syntax::CaseLabel) -> Visit { + Visit::Children + } + + fn visit_condition(&mut self, _: $($ref)* syntax::Condition) -> Visit { + Visit::Children + } + + fn visit_declaration(&mut self, _: $($ref)* syntax::Declaration) -> Visit { + Visit::Children + } + + fn visit_expr(&mut self, _: $($ref)* syntax::Expr) -> Visit { + Visit::Children + } + + fn visit_fun_identifier(&mut self, _: $($ref)* syntax::FunIdentifier) -> Visit { + Visit::Children + } + + fn visit_function_parameter_declaration( + &mut self, + _: $($ref)* syntax::FunctionParameterDeclaration, + ) -> Visit { + Visit::Children + } + + fn visit_initializer(&mut self, _: $($ref)* syntax::Initializer) -> Visit { + Visit::Children + } + + fn visit_interpolation_qualifier(&mut self, _: $($ref)* syntax::InterpolationQualifier) -> Visit { + Visit::Children + } + + fn visit_iteration_statement(&mut self, _: $($ref)* syntax::IterationStatement) -> Visit { + Visit::Children + } + + fn visit_jump_statement(&mut self, _: $($ref)* syntax::JumpStatement) -> Visit { + Visit::Children + } + + fn visit_layout_qualifier_spec(&mut self, _: $($ref)* syntax::LayoutQualifierSpec) -> Visit { + Visit::Children + } + + fn visit_precision_qualifier(&mut self, _: $($ref)* syntax::PrecisionQualifier) -> Visit { + Visit::Children + } + + fn visit_statement(&mut self, _: $($ref)* syntax::Statement) -> Visit { + Visit::Children + } + + fn visit_compound_statement(&mut self, _: $($ref)* syntax::CompoundStatement) -> Visit { + Visit::Children + } + + fn visit_simple_statement(&mut self, _: $($ref)* syntax::SimpleStatement) -> Visit { + Visit::Children + } + + fn visit_storage_qualifier(&mut self, _: $($ref)* syntax::StorageQualifier) -> Visit { + Visit::Children + } + + fn visit_type_qualifier_spec(&mut self, _: $($ref)* syntax::TypeQualifierSpec) -> Visit { + Visit::Children + } + + fn visit_type_specifier_non_array(&mut self, _: $($ref)* syntax::TypeSpecifierNonArray) -> Visit { + Visit::Children + } + + fn visit_unary_op(&mut self, _: $($ref)* syntax::UnaryOp) -> Visit { + Visit::Children + } + + fn visit_expr_statement(&mut self, _: $($ref)* syntax::ExprStatement) -> Visit { + Visit::Children + } + } + } +} + +macro_rules! make_host_trait { + ($host_ty:ident, $visitor_ty:ident, $mthd_name:ident, $($ref:tt)*) => { + /// Part of the AST that can be visited. + /// + /// You shouldn’t have to worry about this type nor how to implement it – it’s completely + /// implemented for you. However, it works in a pretty simple way: any implementor of the host trait can + /// be used with a visitor. + /// + /// The idea is that visiting an AST node is a two-step process: + /// + /// - First, you *can* get your visitor called once as soon as an interesting node gets visited. + /// For instance, if your visitor has an implementation for visiting expressions, everytime an + /// expression gets visited, your visitor will run. + /// - If your implementation of visiting an AST node returns [`Visit::Children`] and if the given + /// node has children, the visitor will go deeper, invoking other calls if you have defined any. + /// A typical pattern you might want to do is to implement your visitor to gets run on all + /// typenames. Since expressions contains variables, you will get your visitor called once again + /// there. + /// - Notice that since visitors are mutable, you can accumulate a state as you go deeper in the + /// AST to implement various checks and validations. + /// + /// Note that this trait exists in two versions: an immutable one, [`Host`], which doesn’t allow you to mutate the + /// AST (but takes a `&`), and a mutable one, [`HostMut`], which allows for AST mutation. + pub trait $host_ty { + /// Visit an AST node. + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty; + } + + impl<T> $host_ty for Option<T> + where + T: $host_ty, + { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + if let Some(x) = self { + x.$mthd_name(visitor); + } + } + } + + impl<T> $host_ty for Box<T> + where + T: $host_ty, + { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + (**self).$mthd_name(visitor); + } + } + + impl $host_ty for syntax::TranslationUnit { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_translation_unit(self); + + if visit == Visit::Children { + for ed in $($ref)* (self.0).0 { + ed.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::ExternalDeclaration { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_external_declaration(self); + + if visit == Visit::Children { + match self { + syntax::ExternalDeclaration::Preprocessor(p) => p.$mthd_name(visitor), + syntax::ExternalDeclaration::FunctionDefinition(fd) => fd.$mthd_name(visitor), + syntax::ExternalDeclaration::Declaration(d) => d.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::Preprocessor { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor(self); + + if visit == Visit::Children { + match self { + syntax::Preprocessor::Define(pd) => pd.$mthd_name(visitor), + syntax::Preprocessor::Else => (), + syntax::Preprocessor::ElseIf(pei) => pei.$mthd_name(visitor), + syntax::Preprocessor::EndIf => (), + syntax::Preprocessor::Error(pe) => pe.$mthd_name(visitor), + syntax::Preprocessor::If(pi) => pi.$mthd_name(visitor), + syntax::Preprocessor::IfDef(pid) => pid.$mthd_name(visitor), + syntax::Preprocessor::IfNDef(pind) => pind.$mthd_name(visitor), + syntax::Preprocessor::Include(pi) => pi.$mthd_name(visitor), + syntax::Preprocessor::Line(pl) => pl.$mthd_name(visitor), + syntax::Preprocessor::Pragma(pp) => pp.$mthd_name(visitor), + syntax::Preprocessor::Undef(pu) => pu.$mthd_name(visitor), + syntax::Preprocessor::Version(pv) => pv.$mthd_name(visitor), + syntax::Preprocessor::Extension(ext) => ext.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::PreprocessorDefine { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_define(self); + + if visit == Visit::Children { + match self { + syntax::PreprocessorDefine::ObjectLike { ident, .. } => { + ident.$mthd_name(visitor); + } + + syntax::PreprocessorDefine::FunctionLike { + ident, + args, + .. + } => { + ident.$mthd_name(visitor); + + for arg in args { + arg.$mthd_name(visitor); + } + } + } + } + } + } + + impl $host_ty for syntax::PreprocessorElseIf { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_elseif(self); + } + } + + impl $host_ty for syntax::PreprocessorError { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_error(self); + } + } + + impl $host_ty for syntax::PreprocessorIf { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_if(self); + } + } + + impl $host_ty for syntax::PreprocessorIfDef { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_ifdef(self); + + if visit == Visit::Children { + self.ident.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::PreprocessorIfNDef { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_ifndef(self); + + if visit == Visit::Children { + self.ident.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::PreprocessorInclude { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_include(self); + } + } + + impl $host_ty for syntax::PreprocessorLine { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_line(self); + } + } + + impl $host_ty for syntax::PreprocessorPragma { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_pragma(self); + } + } + + impl $host_ty for syntax::PreprocessorUndef { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_undef(self); + + if visit == Visit::Children { + self.name.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::PreprocessorVersion { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_version(self); + + if visit == Visit::Children { + self.profile.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::PreprocessorVersionProfile { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_version_profile(self); + } + } + + impl $host_ty for syntax::PreprocessorExtension { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_preprocessor_extension(self); + + if visit == Visit::Children { + self.name.$mthd_name(visitor); + self.behavior.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::PreprocessorExtensionBehavior { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_extension_behavior(self); + } + } + + impl $host_ty for syntax::PreprocessorExtensionName { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_preprocessor_extension_name(self); + } + } + + impl $host_ty for syntax::FunctionPrototype { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_function_prototype(self); + + if visit == Visit::Children { + self.ty.$mthd_name(visitor); + self.name.$mthd_name(visitor); + + for param in $($ref)* self.parameters { + param.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::FunctionParameterDeclaration { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_function_parameter_declaration(self); + + if visit == Visit::Children { + match self { + syntax::FunctionParameterDeclaration::Named(tq, fpd) => { + tq.$mthd_name(visitor); + fpd.$mthd_name(visitor); + } + + syntax::FunctionParameterDeclaration::Unnamed(tq, ty) => { + tq.$mthd_name(visitor); + ty.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::FunctionParameterDeclarator { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_function_parameter_declarator(self); + + if visit == Visit::Children { + self.ty.$mthd_name(visitor); + self.ident.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::FunctionDefinition { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_function_definition(self); + + if visit == Visit::Children { + self.prototype.$mthd_name(visitor); + self.statement.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::Declaration { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_declaration(self); + + if visit == Visit::Children { + match self { + syntax::Declaration::FunctionPrototype(fp) => fp.$mthd_name(visitor), + + syntax::Declaration::InitDeclaratorList(idl) => idl.$mthd_name(visitor), + + syntax::Declaration::Precision(pq, ty) => { + pq.$mthd_name(visitor); + ty.$mthd_name(visitor); + } + + syntax::Declaration::Block(block) => block.$mthd_name(visitor), + + syntax::Declaration::Global(tq, idents) => { + tq.$mthd_name(visitor); + + for ident in idents { + ident.$mthd_name(visitor); + } + } + } + } + } + } + + impl $host_ty for syntax::Block { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_block(self); + + if visit == Visit::Children { + self.qualifier.$mthd_name(visitor); + self.name.$mthd_name(visitor); + + for field in $($ref)* self.fields { + field.$mthd_name(visitor); + } + + self.identifier.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::InitDeclaratorList { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_init_declarator_list(self); + + if visit == Visit::Children { + self.head.$mthd_name(visitor); + + for d in $($ref)* self.tail { + d.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::SingleDeclaration { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_single_declaration(self); + + if visit == Visit::Children { + self.ty.$mthd_name(visitor); + self.name.$mthd_name(visitor); + self.array_specifier.$mthd_name(visitor); + self.initializer.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::SingleDeclarationNoType { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_single_declaration_no_type(self); + + if visit == Visit::Children { + self.ident.$mthd_name(visitor); + self.initializer.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::FullySpecifiedType { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_full_specified_type(self); + + if visit == Visit::Children { + self.qualifier.$mthd_name(visitor); + self.ty.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::TypeSpecifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_type_specifier(self); + + if visit == Visit::Children { + self.ty.$mthd_name(visitor); + self.array_specifier.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::TypeSpecifierNonArray { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_type_specifier_non_array(self); + + if visit == Visit::Children { + match self { + syntax::TypeSpecifierNonArray::Struct(ss) => ss.$mthd_name(visitor), + syntax::TypeSpecifierNonArray::TypeName(tn) => tn.$mthd_name(visitor), + _ => (), + } + } + } + } + + impl $host_ty for syntax::TypeQualifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_type_qualifier(self); + + if visit == Visit::Children { + for tqs in $($ref)* self.qualifiers.0 { + tqs.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::TypeQualifierSpec { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_type_qualifier_spec(self); + + if visit == Visit::Children { + match self { + syntax::TypeQualifierSpec::Storage(sq) => sq.$mthd_name(visitor), + syntax::TypeQualifierSpec::Layout(lq) => lq.$mthd_name(visitor), + syntax::TypeQualifierSpec::Precision(pq) => pq.$mthd_name(visitor), + syntax::TypeQualifierSpec::Interpolation(iq) => iq.$mthd_name(visitor), + _ => (), + } + } + } + } + + impl $host_ty for syntax::StorageQualifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_storage_qualifier(self); + + if visit == Visit::Children { + if let syntax::StorageQualifier::Subroutine(names) = self { + for name in names { + name.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::LayoutQualifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_layout_qualifier(self); + + if visit == Visit::Children { + for lqs in $($ref)* self.ids.0 { + lqs.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::LayoutQualifierSpec { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_layout_qualifier_spec(self); + + if visit == Visit::Children { + if let syntax::LayoutQualifierSpec::Identifier(ident, expr) = self { + ident.$mthd_name(visitor); + + if let Some(e) = expr { + e.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::PrecisionQualifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_precision_qualifier(self); + } + } + + impl $host_ty for syntax::InterpolationQualifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_interpolation_qualifier(self); + } + } + + impl $host_ty for syntax::TypeName { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_type_name(self); + } + } + + impl $host_ty for syntax::Identifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_identifier(self); + } + } + + impl $host_ty for syntax::ArrayedIdentifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_arrayed_identifier(self); + + if visit == Visit::Children { + self.ident.$mthd_name(visitor); + self.array_spec.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::Expr { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_expr(self); + + if visit == Visit::Children { + match self { + syntax::Expr::Variable(ident) => ident.$mthd_name(visitor), + + syntax::Expr::Unary(op, e) => { + op.$mthd_name(visitor); + e.$mthd_name(visitor); + } + + syntax::Expr::Binary(op, a, b) => { + op.$mthd_name(visitor); + a.$mthd_name(visitor); + b.$mthd_name(visitor); + } + + syntax::Expr::Ternary(a, b, c) => { + a.$mthd_name(visitor); + b.$mthd_name(visitor); + c.$mthd_name(visitor); + } + + syntax::Expr::Assignment(lhs, op, rhs) => { + lhs.$mthd_name(visitor); + op.$mthd_name(visitor); + rhs.$mthd_name(visitor); + } + + syntax::Expr::Bracket(e, arr_spec) => { + e.$mthd_name(visitor); + arr_spec.$mthd_name(visitor); + } + + syntax::Expr::FunCall(fi, params) => { + fi.$mthd_name(visitor); + + for param in params { + param.$mthd_name(visitor); + } + } + + syntax::Expr::Dot(e, i) => { + e.$mthd_name(visitor); + i.$mthd_name(visitor); + } + + syntax::Expr::PostInc(e) => e.$mthd_name(visitor), + + syntax::Expr::PostDec(e) => e.$mthd_name(visitor), + + syntax::Expr::Comma(a, b) => { + a.$mthd_name(visitor); + b.$mthd_name(visitor); + } + + _ => (), + } + } + } + } + + impl $host_ty for syntax::UnaryOp { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_unary_op(self); + } + } + + impl $host_ty for syntax::BinaryOp { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_binary_op(self); + } + } + + impl $host_ty for syntax::AssignmentOp { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let _ = visitor.visit_assignment_op(self); + } + } + + impl $host_ty for syntax::ArraySpecifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_array_specifier(self); + + if visit == Visit::Children { + for dimension in $($ref)* self.dimensions { + dimension.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::ArraySpecifierDimension { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_array_specifier_dimension(self); + + if visit == Visit::Children { + if let syntax::ArraySpecifierDimension::ExplicitlySized(e) = self { + e.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::FunIdentifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_fun_identifier(self); + + if visit == Visit::Children { + match self { + syntax::FunIdentifier::Identifier(i) => i.$mthd_name(visitor), + syntax::FunIdentifier::Expr(e) => e.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::StructSpecifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_struct_specifier(self); + + if visit == Visit::Children { + self.name.$mthd_name(visitor); + + for field in $($ref)* self.fields.0 { + field.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::StructFieldSpecifier { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_struct_field_specifier(self); + + if visit == Visit::Children { + self.qualifier.$mthd_name(visitor); + self.ty.$mthd_name(visitor); + + for identifier in $($ref)* self.identifiers.0 { + identifier.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::Statement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_statement(self); + + if visit == Visit::Children { + match self { + syntax::Statement::Compound(cs) => cs.$mthd_name(visitor), + syntax::Statement::Simple(ss) => ss.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::SimpleStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_simple_statement(self); + + if visit == Visit::Children { + match self { + syntax::SimpleStatement::Declaration(d) => d.$mthd_name(visitor), + syntax::SimpleStatement::Expression(e) => e.$mthd_name(visitor), + syntax::SimpleStatement::Selection(s) => s.$mthd_name(visitor), + syntax::SimpleStatement::Switch(s) => s.$mthd_name(visitor), + syntax::SimpleStatement::CaseLabel(cl) => cl.$mthd_name(visitor), + syntax::SimpleStatement::Iteration(i) => i.$mthd_name(visitor), + syntax::SimpleStatement::Jump(j) => j.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::CompoundStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_compound_statement(self); + + if visit == Visit::Children { + for stmt in $($ref)* self.statement_list { + stmt.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::SelectionStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_selection_statement(self); + + if visit == Visit::Children { + self.cond.$mthd_name(visitor); + self.rest.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::SelectionRestStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_selection_rest_statement(self); + + if visit == Visit::Children { + match self { + syntax::SelectionRestStatement::Statement(s) => s.$mthd_name(visitor), + + syntax::SelectionRestStatement::Else(a, b) => { + a.$mthd_name(visitor); + b.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::SwitchStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_switch_statement(self); + + if visit == Visit::Children { + self.head.$mthd_name(visitor); + + for s in $($ref)* self.body { + s.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::CaseLabel { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_case_label(self); + + if visit == Visit::Children { + if let syntax::CaseLabel::Case(e) = self { + e.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::IterationStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_iteration_statement(self); + + if visit == Visit::Children { + match self { + syntax::IterationStatement::While(c, s) => { + c.$mthd_name(visitor); + s.$mthd_name(visitor); + } + + syntax::IterationStatement::DoWhile(s, e) => { + s.$mthd_name(visitor); + e.$mthd_name(visitor); + } + + syntax::IterationStatement::For(fis, frs, s) => { + fis.$mthd_name(visitor); + frs.$mthd_name(visitor); + s.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::ForInitStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_for_init_statement(self); + + if visit == Visit::Children { + match self { + syntax::ForInitStatement::Expression(e) => e.$mthd_name(visitor), + syntax::ForInitStatement::Declaration(d) => d.$mthd_name(visitor), + } + } + } + } + + impl $host_ty for syntax::ForRestStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_for_rest_statement(self); + + if visit == Visit::Children { + self.condition.$mthd_name(visitor); + self.post_expr.$mthd_name(visitor); + } + } + } + + impl $host_ty for syntax::JumpStatement { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_jump_statement(self); + + if visit == Visit::Children { + if let syntax::JumpStatement::Return(r) = self { + r.$mthd_name(visitor); + } + } + } + } + + impl $host_ty for syntax::Condition { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_condition(self); + + if visit == Visit::Children { + match self { + syntax::Condition::Expr(e) => e.$mthd_name(visitor), + + syntax::Condition::Assignment(fst, ident, init) => { + fst.$mthd_name(visitor); + ident.$mthd_name(visitor); + init.$mthd_name(visitor); + } + } + } + } + } + + impl $host_ty for syntax::Initializer { + fn $mthd_name<V>($($ref)* self, visitor: &mut V) + where + V: $visitor_ty, + { + let visit = visitor.visit_initializer(self); + + if visit == Visit::Children { + match self { + syntax::Initializer::Simple(e) => e.$mthd_name(visitor), + + syntax::Initializer::List(i) => { + for i in $($ref)* i.0 { + i.$mthd_name(visitor); + } + } + } + } + } + } + } +} + +// immutable +make_visitor_trait!(Visitor, &); +make_host_trait!(Host, Visitor, visit, &); + +// mutable +make_visitor_trait!(VisitorMut, &mut); +make_host_trait!(HostMut, VisitorMut, visit_mut, &mut); + +#[cfg(test)] +mod tests { + use std::iter::FromIterator; + + use super::*; + use syntax; + + #[test] + fn count_variables() { + let decl0 = syntax::Statement::declare_var( + syntax::TypeSpecifierNonArray::Float, + "x", + None, + Some(syntax::Expr::from(3.14).into()), + ); + + let decl1 = syntax::Statement::declare_var(syntax::TypeSpecifierNonArray::Int, "y", None, None); + + let decl2 = + syntax::Statement::declare_var(syntax::TypeSpecifierNonArray::Vec4, "z", None, None); + + let compound = syntax::CompoundStatement::from_iter(vec![decl0, decl1, decl2]); + + // our visitor that will count the number of variables it saw + struct Counter { + var_nb: usize, + } + + impl Visitor for Counter { + // we are only interested in single declaration with a name + fn visit_single_declaration(&mut self, declaration: &syntax::SingleDeclaration) -> Visit { + if declaration.name.is_some() { + self.var_nb += 1; + } + + // do not go deeper + Visit::Parent + } + } + + let mut counter = Counter { var_nb: 0 }; + compound.visit(&mut counter); + assert_eq!(counter.var_nb, 3); + } +} diff --git a/third_party/rust/glsl/tests/incorrect_statement.rs b/third_party/rust/glsl/tests/incorrect_statement.rs new file mode 100644 index 0000000000..2e39a7f536 --- /dev/null +++ b/third_party/rust/glsl/tests/incorrect_statement.rs @@ -0,0 +1,19 @@ +use glsl::parser::Parse; +use glsl::syntax; + +#[test] +fn incorrect_statement() { + let r = syntax::TranslationUnit::parse( + " + int fetch_transform(int id) { + return id; + } + + bool ray_plane() { + if 1 { + } + ", + ); + + assert!(r.is_err()); +} diff --git a/third_party/rust/glsl/tests/left_associativity.rs b/third_party/rust/glsl/tests/left_associativity.rs new file mode 100644 index 0000000000..7e2a19cfb4 --- /dev/null +++ b/third_party/rust/glsl/tests/left_associativity.rs @@ -0,0 +1,58 @@ +use glsl::parser::Parse; +use glsl::syntax; + +#[test] +fn left_associativity() { + for (opstr, opname) in [ + ("+", syntax::BinaryOp::Add), + ("&&", syntax::BinaryOp::And), + ("||", syntax::BinaryOp::Or), + ] + .iter() + { + let r = syntax::TranslationUnit::parse(format!( + " + void main() {{ + x = a {op} b {op} c; + }} + ", + op = opstr + )); + + let expected = syntax::TranslationUnit::from_non_empty_iter(vec![ + syntax::ExternalDeclaration::FunctionDefinition(syntax::FunctionDefinition { + prototype: syntax::FunctionPrototype { + ty: syntax::FullySpecifiedType { + qualifier: None, + ty: syntax::TypeSpecifier { + ty: syntax::TypeSpecifierNonArray::Void, + array_specifier: None, + }, + }, + name: "main".into(), + parameters: Vec::new(), + }, + statement: syntax::CompoundStatement { + statement_list: vec![syntax::Statement::Simple(Box::new( + syntax::SimpleStatement::Expression(Some(syntax::Expr::Assignment( + Box::new(syntax::Expr::Variable("x".into())), + syntax::AssignmentOp::Equal, + Box::new(syntax::Expr::Binary( + opname.clone(), + Box::new(syntax::Expr::Binary( + opname.clone(), + Box::new(syntax::Expr::Variable("a".into())), + Box::new(syntax::Expr::Variable("b".into())), + )), + Box::new(syntax::Expr::Variable("c".into())), + )), + ))), + ))], + }, + }), + ]) + .unwrap(); + + assert_eq!(r, Ok(expected)); + } +} diff --git a/third_party/rust/glsl/tests/missing_zero_float.rs b/third_party/rust/glsl/tests/missing_zero_float.rs new file mode 100644 index 0000000000..35ad5cf761 --- /dev/null +++ b/third_party/rust/glsl/tests/missing_zero_float.rs @@ -0,0 +1,28 @@ +extern crate glsl; + +use glsl::parser::Parse; +use glsl::syntax::TranslationUnit; + +#[test] +fn missing_zero_float_is_valid() { + let r = TranslationUnit::parse( + " + void main() { + float x = 1. * .5; + }", + ); + + assert!(r.is_ok()); +} + +#[test] +fn float_exp_is_valid() { + let r = TranslationUnit::parse( + " + void main() { + float x = 1e-5; + }", + ); + + assert!(r.is_ok()); +} |