summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glsl
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/glsl')
-rw-r--r--third_party/rust/glsl/.cargo-checksum.json1
-rw-r--r--third_party/rust/glsl/CHANGELOG.md465
-rw-r--r--third_party/rust/glsl/Cargo.toml44
-rw-r--r--third_party/rust/glsl/LICENSE30
-rw-r--r--third_party/rust/glsl/README.md126
-rw-r--r--third_party/rust/glsl/data/tests/buffer_block_0.glsl6
-rw-r--r--third_party/rust/glsl/data/tests/layout_buffer_block_0.glsl3
-rw-r--r--third_party/rust/glsl/src/lib.rs125
-rw-r--r--third_party/rust/glsl/src/parse_tests.rs2847
-rw-r--r--third_party/rust/glsl/src/parser.rs121
-rw-r--r--third_party/rust/glsl/src/parsers.rs1866
-rw-r--r--third_party/rust/glsl/src/parsers/nom_helpers.rs95
-rw-r--r--third_party/rust/glsl/src/syntax.rs1368
-rw-r--r--third_party/rust/glsl/src/transpiler/glsl.rs1870
-rw-r--r--third_party/rust/glsl/src/transpiler/mod.rs8
-rw-r--r--third_party/rust/glsl/src/transpiler/spirv.rs95
-rw-r--r--third_party/rust/glsl/src/visitor.rs1465
-rw-r--r--third_party/rust/glsl/tests/incorrect_statement.rs19
-rw-r--r--third_party/rust/glsl/tests/left_associativity.rs58
-rw-r--r--third_party/rust/glsl/tests/missing_zero_float.rs28
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());
+}