glsl-lang 0.5.2

GLSL 4.6 language LALR parser and AST
Documentation
use lang_util::position::LexerPosition;

use glsl_lang_lexer::{IdentifierContext, Token, HasLexerError};

use crate::{
    ast::{self, NodeContent},
    parse::ParseContext,
};

grammar<'i, L>(ctx: &ParseContext) where L: HasLexerError;

comma<T>: Vec<T> = {
    <e:T?> <v:("," <T>)*> => match e {
        None => v,
        Some(e) => {
            let mut v = v;
            v.insert(0, e);
            v
        }
    }
};

comma_trailing<T>: Vec<T> = {
    <v:(<T> ",")*> <e:T?> => match e {
        None => v,
        Some(e) => {
            let mut v = v;
            v.push(e);
            v
        }
    }
};

identifier: ast::Identifier = {
    <l:@L> <i:ident> <r:@R> => ast::IdentifierData::from(i.as_str()).spanned(l, r),
};

type_name: ast::TypeName = {
    <l:@L> <i:ty_name> <r:@R> => ast::TypeNameData::from(i.as_str()).spanned(l, r),
};

primary_expression: ast::Expr = {
    <l:@L> <e:identifier>      <r:@R> => ast::ExprData::Variable(e).spanned(l, r),
    <l:@L> <e:double_constant> <r:@R> => ast::ExprData::DoubleConst(e.into()).spanned(l, r),
    <l:@L> <e:float_constant>  <r:@R> => ast::ExprData::FloatConst(e.into()).spanned(l, r),
    <l:@L> <e:uint_constant>   <r:@R> => ast::ExprData::UIntConst(e.into()).spanned(l, r),
    <l:@L> <e:int_constant>    <r:@R> => ast::ExprData::IntConst(e.into()).spanned(l, r),
    <l:@L> <e:bool_constant>   <r:@R> => ast::ExprData::BoolConst(e.into()).spanned(l, r),
    "(" <expr> ")",
};

postfix_expression: ast::Expr = {
    primary_expression,
    <a:@L> <l:postfix_expression> "[" <e:expr> "]" <b:@R> =>
        ast::ExprData::Bracket(Box::new(l), Box::new(e)).spanned(a, b),
    function_call,
    <a:@L> <l:postfix_expression> "." <r:identifier> <b:@R> => ast::ExprData::Dot(Box::new(l), r).spanned(a, b),
    <a:@L> <e:postfix_expression> "++"               <b:@R> => ast::ExprData::PostInc(Box::new(e)).spanned(a, b),
    <a:@L> <e:postfix_expression> "--"               <b:@R> => ast::ExprData::PostDec(Box::new(e)).spanned(a, b),
};

integer_expression = {
    expr
};

function_call: ast::Expr = {
    <a:@L> <i:fun_identifier> "(" <e:comma<assignment_expression>> ")" <b:@R> =>
        ast::ExprData::FunCall(i, e).spanned(a, b),
    <a:@L> <i:fun_identifier> "(" "void" ")" <b:@R> =>
        ast::ExprData::FunCall(i, vec![]).spanned(a, b),
};

array_specifier_dimension_data: ast::ArraySpecifierDimensionData = {
    "[" "]" => ast::ArraySpecifierDimensionData::Unsized,
    "[" <conditional_expression> "]" => ast::ArraySpecifierDimensionData::ExplicitlySized(Box::new(<>)),
};

array_specifier_dimension: ast::ArraySpecifierDimension = {
    <l:@L> <a:array_specifier_dimension_data> <r:@R> => a.spanned(l, r)
};

array_specifier: ast::ArraySpecifier = {
    <l:@L> <a:array_specifier_dimension+> <r:@R> => ast::ArraySpecifierData {
        dimensions: a,
    }.spanned(l, r)
};

struct_field_specifier: ast::StructFieldSpecifier = {
    <l:@L> <q:type_qualifier?> <s:type_specifier> <f:comma<arrayed_identifier>> ";" <r:@R> => ast::StructFieldSpecifierData {
        qualifier: q,
        ty: s,
        identifiers: f,
    }.spanned(l, r)
};

struct_specifier: ast::StructSpecifier = {
    <l:@L> "struct" <i:identifier?> "{" <s:struct_field_specifier*> "}" <r:@R> => ast::StructSpecifierData {
        name: i.map(|ident| ctx.add_type_name(ident)),
        fields: s,
    }.spanned(l, r),
};

type_specifier_non_struct: ast::TypeSpecifierNonArrayData = {
    "void"                   => ast::TypeSpecifierNonArrayData::Void,
    "bool"                   => ast::TypeSpecifierNonArrayData::Bool,
    "int"                    => ast::TypeSpecifierNonArrayData::Int,
    "uint"                   => ast::TypeSpecifierNonArrayData::UInt,
    "float"                  => ast::TypeSpecifierNonArrayData::Float,
    "double"                 => ast::TypeSpecifierNonArrayData::Double,
    "vec2"                   => ast::TypeSpecifierNonArrayData::Vec2,
    "vec3"                   => ast::TypeSpecifierNonArrayData::Vec3,
    "vec4"                   => ast::TypeSpecifierNonArrayData::Vec4,
    "dvec2"                  => ast::TypeSpecifierNonArrayData::DVec2,
    "dvec3"                  => ast::TypeSpecifierNonArrayData::DVec3,
    "dvec4"                  => ast::TypeSpecifierNonArrayData::DVec4,
    "bvec2"                  => ast::TypeSpecifierNonArrayData::BVec2,
    "bvec3"                  => ast::TypeSpecifierNonArrayData::BVec3,
    "bvec4"                  => ast::TypeSpecifierNonArrayData::BVec4,
    "ivec2"                  => ast::TypeSpecifierNonArrayData::IVec2,
    "ivec3"                  => ast::TypeSpecifierNonArrayData::IVec3,
    "ivec4"                  => ast::TypeSpecifierNonArrayData::IVec4,
    "uvec2"                  => ast::TypeSpecifierNonArrayData::UVec2,
    "uvec3"                  => ast::TypeSpecifierNonArrayData::UVec3,
    "uvec4"                  => ast::TypeSpecifierNonArrayData::UVec4,
    "mat2"                   => ast::TypeSpecifierNonArrayData::Mat2,
    "mat3"                   => ast::TypeSpecifierNonArrayData::Mat3,
    "mat4"                   => ast::TypeSpecifierNonArrayData::Mat4,
    "mat2x2"                 => ast::TypeSpecifierNonArrayData::Mat22,
    "mat2x3"                 => ast::TypeSpecifierNonArrayData::Mat23,
    "mat2x4"                 => ast::TypeSpecifierNonArrayData::Mat24,
    "mat3x2"                 => ast::TypeSpecifierNonArrayData::Mat32,
    "mat3x3"                 => ast::TypeSpecifierNonArrayData::Mat33,
    "mat3x4"                 => ast::TypeSpecifierNonArrayData::Mat34,
    "mat4x2"                 => ast::TypeSpecifierNonArrayData::Mat42,
    "mat4x3"                 => ast::TypeSpecifierNonArrayData::Mat43,
    "mat4x4"                 => ast::TypeSpecifierNonArrayData::Mat44,
    "dmat2"                  => ast::TypeSpecifierNonArrayData::DMat2,
    "dmat3"                  => ast::TypeSpecifierNonArrayData::DMat3,
    "dmat4"                  => ast::TypeSpecifierNonArrayData::DMat4,
    "dmat2x2"                => ast::TypeSpecifierNonArrayData::DMat22,
    "dmat2x3"                => ast::TypeSpecifierNonArrayData::DMat23,
    "dmat2x4"                => ast::TypeSpecifierNonArrayData::DMat24,
    "dmat3x2"                => ast::TypeSpecifierNonArrayData::DMat32,
    "dmat3x3"                => ast::TypeSpecifierNonArrayData::DMat33,
    "dmat3x4"                => ast::TypeSpecifierNonArrayData::DMat34,
    "dmat4x2"                => ast::TypeSpecifierNonArrayData::DMat42,
    "dmat4x3"                => ast::TypeSpecifierNonArrayData::DMat43,
    "dmat4x4"                => ast::TypeSpecifierNonArrayData::DMat44,
    "sampler1D"              => ast::TypeSpecifierNonArrayData::Sampler1D,
    "image1D"                => ast::TypeSpecifierNonArrayData::Image1D,
    "sampler2D"              => ast::TypeSpecifierNonArrayData::Sampler2D,
    "image2D"                => ast::TypeSpecifierNonArrayData::Image2D,
    "sampler3D"              => ast::TypeSpecifierNonArrayData::Sampler3D,
    "image3D"                => ast::TypeSpecifierNonArrayData::Image3D,
    "samplerCube"            => ast::TypeSpecifierNonArrayData::SamplerCube,
    "imageCube"              => ast::TypeSpecifierNonArrayData::ImageCube,
    "sampler2DRect"          => ast::TypeSpecifierNonArrayData::Sampler2DRect,
    "image2DRect"            => ast::TypeSpecifierNonArrayData::Image2DRect,
    "sampler1DArray"         => ast::TypeSpecifierNonArrayData::Sampler1DArray,
    "image1DArray"           => ast::TypeSpecifierNonArrayData::Image1DArray,
    "sampler2DArray"         => ast::TypeSpecifierNonArrayData::Sampler2DArray,
    "image2DArray"           => ast::TypeSpecifierNonArrayData::Image2DArray,
    "samplerBuffer"          => ast::TypeSpecifierNonArrayData::SamplerBuffer,
    "imageBuffer"            => ast::TypeSpecifierNonArrayData::ImageBuffer,
    "sampler2DMS"            => ast::TypeSpecifierNonArrayData::Sampler2DMs,
    "image2DMS"              => ast::TypeSpecifierNonArrayData::Image2DMs,
    "sampler2DMSArray"       => ast::TypeSpecifierNonArrayData::Sampler2DMsArray,
    "image2DMSArray"         => ast::TypeSpecifierNonArrayData::Image2DMsArray,
    "samplerCubeArray"       => ast::TypeSpecifierNonArrayData::SamplerCubeArray,
    "imageCubeArray"         => ast::TypeSpecifierNonArrayData::ImageCubeArray,
    "sampler1DShadow"        => ast::TypeSpecifierNonArrayData::Sampler1DShadow,
    "sampler2DShadow"        => ast::TypeSpecifierNonArrayData::Sampler2DShadow,
    "sampler2DRectShadow"    => ast::TypeSpecifierNonArrayData::Sampler2DRectShadow,
    "sampler1DArrayShadow"   => ast::TypeSpecifierNonArrayData::Sampler1DArrayShadow,
    "sampler2DArrayShadow"   => ast::TypeSpecifierNonArrayData::Sampler2DArrayShadow,
    "samplerCubeShadow"      => ast::TypeSpecifierNonArrayData::SamplerCubeShadow,
    "samplerCubeArrayShadow" => ast::TypeSpecifierNonArrayData::SamplerCubeArrayShadow,
    "isampler1D"             => ast::TypeSpecifierNonArrayData::ISampler1D,
    "iimage1D"               => ast::TypeSpecifierNonArrayData::IImage1D,
    "isampler2D"             => ast::TypeSpecifierNonArrayData::ISampler2D,
    "iimage2D"               => ast::TypeSpecifierNonArrayData::IImage2D,
    "isampler3D"             => ast::TypeSpecifierNonArrayData::ISampler3D,
    "iimage3D"               => ast::TypeSpecifierNonArrayData::IImage3D,
    "isamplerCube"           => ast::TypeSpecifierNonArrayData::ISamplerCube,
    "iimageCube"             => ast::TypeSpecifierNonArrayData::IImageCube,
    "isampler2DRect"         => ast::TypeSpecifierNonArrayData::ISampler2DRect,
    "iimage2DRect"           => ast::TypeSpecifierNonArrayData::IImage2DRect,
    "isampler1DArray"        => ast::TypeSpecifierNonArrayData::ISampler1DArray,
    "iimage1DArray"          => ast::TypeSpecifierNonArrayData::IImage1DArray,
    "isampler2DArray"        => ast::TypeSpecifierNonArrayData::ISampler2DArray,
    "iimage2DArray"          => ast::TypeSpecifierNonArrayData::IImage2DArray,
    "isamplerBuffer"         => ast::TypeSpecifierNonArrayData::ISamplerBuffer,
    "iimageBuffer"           => ast::TypeSpecifierNonArrayData::IImageBuffer,
    "isampler2DMS"           => ast::TypeSpecifierNonArrayData::ISampler2DMs,
    "iimage2DMS"             => ast::TypeSpecifierNonArrayData::IImage2DMs,
    "isampler2DMSArray"      => ast::TypeSpecifierNonArrayData::ISampler2DMsArray,
    "iimage2DMSArray"        => ast::TypeSpecifierNonArrayData::IImage2DMsArray,
    "isamplerCubeArray"      => ast::TypeSpecifierNonArrayData::ISamplerCubeArray,
    "iimageCubeArray"        => ast::TypeSpecifierNonArrayData::IImageCubeArray,
    "atomic_uint"            => ast::TypeSpecifierNonArrayData::AtomicUInt,
    "usampler1D"             => ast::TypeSpecifierNonArrayData::USampler1D,
    "uimage1D"               => ast::TypeSpecifierNonArrayData::UImage1D,
    "usampler2D"             => ast::TypeSpecifierNonArrayData::USampler2D,
    "uimage2D"               => ast::TypeSpecifierNonArrayData::UImage2D,
    "usampler3D"             => ast::TypeSpecifierNonArrayData::USampler3D,
    "uimage3D"               => ast::TypeSpecifierNonArrayData::UImage3D,
    "usamplerCube"           => ast::TypeSpecifierNonArrayData::USamplerCube,
    "uimageCube"             => ast::TypeSpecifierNonArrayData::UImageCube,
    "usampler2DRect"         => ast::TypeSpecifierNonArrayData::USampler2DRect,
    "uimage2DRect"           => ast::TypeSpecifierNonArrayData::UImage2DRect,
    "usampler1DArray"        => ast::TypeSpecifierNonArrayData::USampler1DArray,
    "uimage1DArray"          => ast::TypeSpecifierNonArrayData::UImage1DArray,
    "usampler2DArray"        => ast::TypeSpecifierNonArrayData::USampler2DArray,
    "uimage2DArray"          => ast::TypeSpecifierNonArrayData::UImage2DArray,
    "usamplerBuffer"         => ast::TypeSpecifierNonArrayData::USamplerBuffer,
    "uimageBuffer"           => ast::TypeSpecifierNonArrayData::UImageBuffer,
    "usampler2DMS"           => ast::TypeSpecifierNonArrayData::USampler2DMs,
    "uimage2DMS"             => ast::TypeSpecifierNonArrayData::UImage2DMs,
    "usampler2DMSArray"      => ast::TypeSpecifierNonArrayData::USampler2DMsArray,
    "uimage2DMSArray"        => ast::TypeSpecifierNonArrayData::UImage2DMsArray,
    "usamplerCubeArray"      => ast::TypeSpecifierNonArrayData::USamplerCubeArray,
    "uimageCubeArray"        => ast::TypeSpecifierNonArrayData::UImageCubeArray,
    // added by GL_KHR_vulkan_glsl
    "texture1D"              => ast::TypeSpecifierNonArrayData::Texture1D,
    "texture2D"              => ast::TypeSpecifierNonArrayData::Texture2D,
    "texture3D"              => ast::TypeSpecifierNonArrayData::Texture3D,
    "textureCube"            => ast::TypeSpecifierNonArrayData::TextureCube,
    "texture2DRect"          => ast::TypeSpecifierNonArrayData::Texture2DRect,
    "texture1DArray"         => ast::TypeSpecifierNonArrayData::Texture1DArray,
    "texture2DArray"         => ast::TypeSpecifierNonArrayData::Texture2DArray,
    "textureBuffer"          => ast::TypeSpecifierNonArrayData::TextureBuffer,
    "texture2DMS"            => ast::TypeSpecifierNonArrayData::Texture2DMs,
    "texture2DMSArray"       => ast::TypeSpecifierNonArrayData::Texture2DMsArray,
    "textureCubeArray"       => ast::TypeSpecifierNonArrayData::TextureCubeArray,
    "itexture1D"             => ast::TypeSpecifierNonArrayData::ITexture1D,
    "itexture2D"             => ast::TypeSpecifierNonArrayData::ITexture2D,
    "itexture3D"             => ast::TypeSpecifierNonArrayData::ITexture3D,
    "itextureCube"           => ast::TypeSpecifierNonArrayData::ITextureCube,
    "itexture2DRect"         => ast::TypeSpecifierNonArrayData::ITexture2DRect,
    "itexture1DArray"        => ast::TypeSpecifierNonArrayData::ITexture1DArray,
    "itexture2DArray"        => ast::TypeSpecifierNonArrayData::ITexture2DArray,
    "itextureBuffer"         => ast::TypeSpecifierNonArrayData::ITextureBuffer,
    "itexture2DMS"           => ast::TypeSpecifierNonArrayData::ITexture2DMs,
    "itexture2DMSArray"      => ast::TypeSpecifierNonArrayData::ITexture2DMsArray,
    "itextureCubeArray"      => ast::TypeSpecifierNonArrayData::ITextureCubeArray,
    "sampler"                => ast::TypeSpecifierNonArrayData::Sampler,
    "samplerShadow"          => ast::TypeSpecifierNonArrayData::SamplerShadow,
    "subpassInput"           => ast::TypeSpecifierNonArrayData::SubpassInput,
    "isubpassInput"          => ast::TypeSpecifierNonArrayData::ISubpassInput,
    "usubpassInput"          => ast::TypeSpecifierNonArrayData::USubpassInput,
    "subpassInputMS"         => ast::TypeSpecifierNonArrayData::SubpassInputMs,
    "isubpassInputMS"        => ast::TypeSpecifierNonArrayData::ISubpassInputMs,
    "usubpassInputMS"        => ast::TypeSpecifierNonArrayData::USubpassInputMs,
};

type_specifier_non_array: ast::TypeSpecifierNonArray = {
    <l:@L> <t:type_specifier_non_struct> <r:@R> => t.spanned(l, r),
    <l:@L> <s:struct_specifier>          <r:@R> => ast::TypeSpecifierNonArrayData::Struct(s).spanned(l, r),
    <l:@L> <t:type_name>                 <r:@R> => ast::TypeSpecifierNonArrayData::TypeName(t).spanned(l, r),
};

type_specifier: ast::TypeSpecifier = {
    <l:@L> <t:type_specifier_non_array> <a:array_specifier?> <r:@R> =>
        ast::TypeSpecifierData { ty: t, array_specifier: a }.spanned(l, r)
};

type_qualifier_spec_data: ast::TypeQualifierSpecData = {
    <storage_qualifier>       => ast::TypeQualifierSpecData::Storage(<>),
    <layout_qualifier>        => ast::TypeQualifierSpecData::Layout(<>),
    <precision_qualifier>     => ast::TypeQualifierSpecData::Precision(<>),
    <interpolation_qualifier> => ast::TypeQualifierSpecData::Interpolation(<>),
    "precise"                 => ast::TypeQualifierSpecData::Precise,
};

type_qualifier_spec: ast::TypeQualifierSpec = {
    <l:@L> <s:type_qualifier_spec_data> <r:@R> => s.spanned(l, r)
};

tq_invariant_data: ast::TypeQualifierSpecData = {
    "invariant"               => ast::TypeQualifierSpecData::Invariant,
};

tq_invariant: ast::TypeQualifierSpec = {
    <l:@L> <s:tq_invariant_data> <r:@R> => s.spanned(l, r)
};

type_qualifier: ast::TypeQualifier = {
    <l:@L> <q1:tq_invariant?> <q2:type_qualifier_spec+> <r:@R> => ast::TypeQualifierData {
        qualifiers: q1.into_iter().chain(q2.into_iter()).collect()
    }.spanned(l, r)
};

fully_specified_type: ast::FullySpecifiedType = {
    <l:@L> <q:type_qualifier?> <s:type_specifier> <r:@R> =>
        ast::FullySpecifiedTypeData { qualifier: q, ty: s }.spanned(l, r)
};

fun_identifier_data: ast::FunIdentifierData = {
    type_specifier     => ast::FunIdentifierData::TypeSpecifier(Box::new(<>)),
    postfix_expression => ast::FunIdentifierData::Expr(Box::new(<>)),
};

fun_identifier: ast::FunIdentifier = {
    <l:@L> <i:fun_identifier_data> <r:@R> => i.spanned(l, r)
};

unary_expression: ast::Expr = {
    postfix_expression,
    <a:@L> <o:unary_op> <e:unary_expression> <b:@R> => ast::ExprData::Unary(o, Box::new(e)).spanned(a, b),
};

unary_op_data: ast::UnaryOpData = {
    "+"  => ast::UnaryOpData::Add,
    "-"  => ast::UnaryOpData::Minus,
    "!"  => ast::UnaryOpData::Not,
    "~"  => ast::UnaryOpData::Complement,
    "++" => ast::UnaryOpData::Inc,
    "--" => ast::UnaryOpData::Dec,
};

unary_op: ast::UnaryOp = {
    <l:@L> <u:unary_op_data> <r:@R> => u.spanned(l, r)
};

arrayed_identifier: ast::ArrayedIdentifier = {
    <l:@L> <i:identifier> <a:array_specifier?> <r:@R> =>
        ast::ArrayedIdentifierData { ident: i, array_spec: a }.spanned(l, r),
};

multiplicative_expression: ast::Expr = {
    unary_expression,
    <a:@L> <l:multiplicative_expression> <c:@L> "*" <d:@R> <r:unary_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Mult.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:multiplicative_expression> <c:@L> "/" <d:@R> <r:unary_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Div.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:multiplicative_expression> <c:@L> "%" <d:@R> <r:unary_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Mod.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

additive_expression: ast::Expr = {
    multiplicative_expression,
    <a:@L> <l:additive_expression> <c:@L> "+" <d:@R> <r:multiplicative_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Add.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:additive_expression> <c:@L> "-" <d:@R> <r:multiplicative_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Sub.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

shift_expression: ast::Expr = {
    additive_expression,
    <a:@L> <l:shift_expression> <c:@L> "<<" <d:@R> <r:additive_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::LShift.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:shift_expression> <c:@L> ">>" <d:@R> <r:additive_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::RShift.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

relational_expression: ast::Expr = {
    shift_expression,
    <a:@L> <l:relational_expression> <c:@L> "<" <d:@R> <r:shift_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Lt.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:relational_expression> <c:@L> ">" <d:@R> <r:shift_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Gt.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:relational_expression> <c:@L> "<=" <d:@R> <r:shift_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Lte.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:relational_expression> <c:@L> ">=" <d:@R> <r:shift_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Gte.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

equality_expression: ast::Expr = {
    relational_expression,
    <a:@L> <l:equality_expression> <c:@L> "==" <d:@R> <r:relational_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Equal.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
    <a:@L> <l:equality_expression> <c:@L> "!=" <d:@R> <r:relational_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::NonEqual.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

and_expression: ast::Expr = {
    equality_expression,
    <a:@L> <l:and_expression> <c:@L> "&" <d:@R> <r:equality_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::BitAnd.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

exclusive_or_expression: ast::Expr = {
    and_expression,
    <a:@L> <l:exclusive_or_expression> <c:@L> "^" <d:@R> <r:and_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::BitXor.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

inclusive_or_expression: ast::Expr = {
    exclusive_or_expression,
    <a:@L> <l:inclusive_or_expression> <c:@L> "|" <d:@R> <r:exclusive_or_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::BitOr.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

logical_and_expression: ast::Expr = {
    inclusive_or_expression,
    <a:@L> <l:logical_and_expression> <c:@L> "&&" <d:@R> <r:inclusive_or_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::And.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

logical_xor_expression: ast::Expr = {
    logical_and_expression,
    <a:@L> <l:logical_xor_expression> <c:@L> "^^" <d:@R> <r:logical_and_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Xor.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

logical_or_expression: ast::Expr = {
    logical_xor_expression,
    <a:@L> <l:logical_or_expression> <c:@L> "||" <d:@R> <r:logical_xor_expression> <b:@R> =>
        ast::ExprData::Binary(ast::BinaryOpData::Or.spanned(c, d), Box::new(l), Box::new(r)).spanned(a, b),
};

conditional_expression: ast::Expr = {
    logical_or_expression,
    <a:@L> <c:logical_or_expression> "?" <l:expr> ":" <r:assignment_expression> <b:@R> =>
        ast::ExprData::Ternary(Box::new(c), Box::new(l), Box::new(r)).spanned(a, b),
};

assignment_expression: ast::Expr = {
    conditional_expression,
    <a:@L> <l:unary_expression> <o:assignment_op> <r:assignment_expression> <b:@R> =>
        ast::ExprData::Assignment(Box::new(l), o, Box::new(r)).spanned(a, b),
};

assignment_op_data: ast::AssignmentOpData = {
    "="   => ast::AssignmentOpData::Equal,
    "*="  => ast::AssignmentOpData::Mult,
    "/="  => ast::AssignmentOpData::Div,
    "%="  => ast::AssignmentOpData::Mod,
    "+="  => ast::AssignmentOpData::Add,
    "-="  => ast::AssignmentOpData::Sub,
    "<<=" => ast::AssignmentOpData::LShift,
    ">>=" => ast::AssignmentOpData::RShift,
    "&="  => ast::AssignmentOpData::And,
    "^="  => ast::AssignmentOpData::Xor,
    "|="  => ast::AssignmentOpData::Or,
};

assignment_op: ast::AssignmentOp = {
    <l:@L> <o:assignment_op_data> <r:@R> => o.spanned(l, r)
};

expr: ast::Expr = {
    assignment_expression,
    <a:@L> <l:expr> "," <r:assignment_expression> <b:@R> =>
        ast::ExprData::Comma(Box::new(l), Box::new(r)).spanned(a, b),
};

expr_statement: ast::ExprStatement = {
    <l:@L> <e:expr?> ";" <r:@R> => ast::ExprStatementData(e).spanned(l, r),
};

selection_statement_else<S>: ast::SelectionStatement = {
    <a:@L> "if" "(" <e:expr> ")" <c:@L> <r:statement_no_short_if> "else" <s:S> <d:@R> <b:@R> => ast::SelectionStatementData {
        cond: Box::new(e),
        rest: ast::SelectionRestStatementData::Else(Box::new(r), Box::new(s)).spanned(c, d),
    }.spanned(a, b)
};

selection_statement<S>: ast::SelectionStatement = {
    <a:@L> "if" "(" <e:expr> ")" <c:@L> <r:statement> <d:@R> <b:@R> => ast::SelectionStatementData {
        cond: Box::new(e),
        rest: ast::SelectionRestStatementData::Statement(Box::new(r)).spanned(c, d),
    }.spanned(a, b),
    selection_statement_else<S>,
};

switch_statement: ast::SwitchStatement = {
    <a:@L> "switch" "(" <e:expr> ")" "{" <s:statement*> "}" <b:@R> => ast::SwitchStatementData {
        head: Box::new(e),
        body: s,
    }.spanned(a, b)
};

case_label_data: ast::CaseLabelData = {
    "case" <expr> ":" => ast::CaseLabelData::Case(Box::new(<>)),
    "default" ":"     => ast::CaseLabelData::Def,
};

case_label: ast::CaseLabel = {
    <l:@L> <c:case_label_data> <r:@R> => c.spanned(l, r)
};

initializer_data: ast::InitializerData = {
    <assignment_expression>               => ast::InitializerData::Simple(Box::new(<>)),
    "{" <comma_trailing<initializer>> "}" => ast::InitializerData::List(<>),
};

initializer: ast::Initializer = {
    <l:@L> <i:initializer_data> <r:@R> => i.spanned(l, r)
};

condition_data: ast::ConditionData = {
    <expr> => ast::ConditionData::Expr(<>),
    <t:fully_specified_type> <i:identifier> "=" <e:initializer> => ast::ConditionData::Assignment(Box::new(t), i, e),
};

condition: ast::Condition = {
    <l:@L> <c:condition_data> <r:@R> => c.spanned(l, r)
};

for_init_statement_data: ast::ForInitStatementData = {
    <expr_statement> => ast::ForInitStatementData::Expression(<>.content.0),
    <declaration>    => ast::ForInitStatementData::Declaration(Box::new(<>)),
};

for_init_statement: ast::ForInitStatement = {
    <l:@L> <f:for_init_statement_data> <r:@R> => f.spanned(l, r)
};

for_rest_statement: ast::ForRestStatement = {
    <l:@L> <c:condition?> ";" <e:expr?> <r:@R> =>
        ast::ForRestStatementData { condition: c, post_expr: e.map(Box::new) }.spanned(l, r)
};

iteration_statement<S>: ast::IterationStatement = {
    <a:@L> "while" "(" <c:condition> ")" <s:S> <b:@R> =>
        ast::IterationStatementData::While(c, Box::new(s)).spanned(a, b),
    <a:@L> "do" <s:statement> "while" "(" <c:expr> ")" ";" <b:@R> =>
        ast::IterationStatementData::DoWhile(Box::new(s), Box::new(c)).spanned(a, b),
    <a:@L> "for" "(" <init:for_init_statement> <rest:for_rest_statement> ")" <s:S> <b:@R> =>
        ast::IterationStatementData::For(init, rest, Box::new(s)).spanned(a, b),
};

jump_statement_data: ast::JumpStatementData = {
    "continue" ";"         => ast::JumpStatementData::Continue,
    "break" ";"            => ast::JumpStatementData::Break,
    "discard" ";"          => ast::JumpStatementData::Discard,
    "return" <e:expr?> ";" => ast::JumpStatementData::Return(e.map(Box::new)),
};

jump_statement: ast::JumpStatement = {
    <l:@L> <j:jump_statement_data> <r:@R> => j.spanned(l, r)
};

simple_statement<S, I>: ast::Statement = {
    <l:@L> <s:jump_statement>         <r:@R> => ast::StatementData::Jump(s).spanned(l, r),
    <l:@L> <s:iteration_statement<S>> <r:@R> => ast::StatementData::Iteration(s).spanned(l, r),
    <l:@L> <s:case_label>             <r:@R> => ast::StatementData::CaseLabel(s).spanned(l, r),
    <l:@L> <s:switch_statement>       <r:@R> => ast::StatementData::Switch(s).spanned(l, r),
    <l:@L> <s:I>                      <r:@R> => ast::StatementData::Selection(s).spanned(l, r),
    <l:@L> <s:declaration>            <r:@R> => ast::StatementData::Declaration(s).spanned(l, r),
    <l:@L> <s:expr_statement>         <r:@R> => ast::StatementData::Expression(s).spanned(l, r),
};

compound_statement: ast::CompoundStatement = {
    <l:@L> "{" <s:statement*> "}" <r:@R> => ast::CompoundStatementData { statement_list: s }.spanned(l, r),
};

statement: ast::Statement = {
    compound_statement => <>.map_spanned(ast::StatementData::Compound),
    simple_statement<statement, selection_statement<statement>>,
};

statement_no_short_if: ast::Statement = {
    compound_statement => <>.map_spanned(ast::StatementData::Compound),
    simple_statement<statement_no_short_if, selection_statement_else<statement_no_short_if>>,
};

interpolation_qualifier_data: ast::InterpolationQualifierData = {
    "smooth"        => ast::InterpolationQualifierData::Smooth,
    "flat"          => ast::InterpolationQualifierData::Flat,
    "noperspective" => ast::InterpolationQualifierData::NoPerspective,
};

interpolation_qualifier: ast::InterpolationQualifier = {
    <l:@L> <i:interpolation_qualifier_data> <r:@R> => i.spanned(l, r)
};

precision_qualifier_data: ast::PrecisionQualifierData = {
    "highp"   => ast::PrecisionQualifierData::High,
    "mediump" => ast::PrecisionQualifierData::Medium,
    "lowp"    => ast::PrecisionQualifierData::Low,
};

precision_qualifier: ast::PrecisionQualifier = {
    <l:@L> <p:precision_qualifier_data> <r:@R> => p.spanned(l, r)
};

storage_qualifier_data: ast::StorageQualifierData = {
    "const"     => ast::StorageQualifierData::Const,
    "inout"     => ast::StorageQualifierData::InOut,
    "in"        => ast::StorageQualifierData::In,
    "out"       => ast::StorageQualifierData::Out,
    "centroid"  => ast::StorageQualifierData::Centroid,
    "patch"     => ast::StorageQualifierData::Patch,
    "sample"    => ast::StorageQualifierData::Sample,
    "uniform"   => ast::StorageQualifierData::Uniform,
    "buffer"    => ast::StorageQualifierData::Buffer,
    "shared"    => ast::StorageQualifierData::Shared,
    "coherent"  => ast::StorageQualifierData::Coherent,
    "volatile"  => ast::StorageQualifierData::Volatile,
    "restrict"  => ast::StorageQualifierData::Restrict,
    "readonly"  => ast::StorageQualifierData::ReadOnly,
    "writeonly" => ast::StorageQualifierData::WriteOnly,
    "attribute" => ast::StorageQualifierData::Attribute,
    "varying"   => ast::StorageQualifierData::Varying,
    "subroutine" <t:("(" <comma<type_specifier>> ")")?> =>
        ast::StorageQualifierData::Subroutine(t.unwrap_or_else(|| vec![])),
};

storage_qualifier: ast::StorageQualifier = {
    <l:@L> <s:storage_qualifier_data> <r:@R> => s.spanned(l, r)
};

layout_qualifier_spec_data: ast::LayoutQualifierSpecData = {
    "shared" => ast::LayoutQualifierSpecData::Shared,
    <i:identifier> "=" <c:conditional_expression> => ast::LayoutQualifierSpecData::Identifier(i, Some(Box::new(c))),
    <identifier> => ast::LayoutQualifierSpecData::Identifier(<>, None),
};

layout_qualifier_spec: ast::LayoutQualifierSpec = {
    <l:@L> <s:layout_qualifier_spec_data> <r:@R> => s.spanned(l, r)
};

layout_qualifier: ast::LayoutQualifier = {
    <l:@L> "layout" "(" <s:comma<layout_qualifier_spec>> ")" <r:@R> =>
        ast::LayoutQualifierData { ids: s }.spanned(l, r),
};

precision_declaration: ast::DeclarationData = {
    "precision" <q:precision_qualifier> <t:type_specifier> => ast::DeclarationData::Precision(q, t),
};

function_parameter_declarator: ast::FunctionParameterDeclarator = {
    <l:@L> <s:type_specifier> <i:arrayed_identifier> <r:@R> => ast::FunctionParameterDeclaratorData {
        ty: s,
        ident: i,
    }.spanned(l, r)
};

function_parameter_declaration: ast::FunctionParameterDeclaration = {
    <l:@L> <q:type_qualifier?> <s:type_specifier> <r:@R> =>
        ast::FunctionParameterDeclarationData::Unnamed(q, s).spanned(l, r),
    <l:@L> <q:type_qualifier?> <d:function_parameter_declarator> <r:@R> =>
        ast::FunctionParameterDeclarationData::Named(q, d).spanned(l, r),
}

function_prototype: ast::FunctionPrototype = {
    <l:@L> <rt:fully_specified_type> <n:identifier> "(" <p:comma<function_parameter_declaration>> ")" <r:@R> =>
        ast::FunctionPrototypeData {
            ty: rt,
            name: n,
            parameters: p,
        }.spanned(l, r),
};

single_declaration_data: ast::SingleDeclarationData = {
    <t:fully_specified_type> <i:arrayed_identifier> <e:("=" <initializer>)?> => {
        let i = i.into_inner();
        ast::SingleDeclarationData {
            ty: t,
            name: Some(i.ident),
            array_specifier: i.array_spec,
            initializer: e,
        }
    },
    <t:fully_specified_type> => ast::SingleDeclarationData {
        ty: t,
        name: None,
        array_specifier: None,
        initializer: None,
    },
};

single_declaration: ast::SingleDeclaration = {
    <l:@L> <s:single_declaration_data> <r:@R> => s.spanned(l, r)
};

single_declaration_no_type: ast::SingleDeclarationNoType = {
    <l:@L> <i:arrayed_identifier> <e:("=" <initializer>)?> <r:@R> => ast::SingleDeclarationNoTypeData {
        ident: i,
        initializer: e,
    }.spanned(l, r)
};

init_declarator_list: ast::InitDeclaratorList = {
    <l:@L> <h:single_declaration> <t:("," <single_declaration_no_type>)*> <r:@R> => ast::InitDeclaratorListData {
        head: h,
        tail: t,
    }.spanned(l, r)
};

block_declaration: ast::Block = {
    <l:@L> <q:type_qualifier> <n:identifier> "{" <f:struct_field_specifier*> "}" <a:arrayed_identifier?> <r:@R> =>
        ast::BlockData { qualifier: q, name: n, fields: f, identifier: a }.spanned(l, r)
};

declaration: ast::Declaration = {
    <l:@L> <p:function_prototype> ";"    <r:@R> => {
        ctx.new_identifier(&p.name, IdentifierContext::FunctionPrototype);
        ast::DeclarationData::FunctionPrototype(p).spanned(l, r)
    },
    <l:@L> <i:init_declarator_list> ";"  <r:@R> => ast::DeclarationData::InitDeclaratorList(i).spanned(l, r),
    <l:@L> <p:precision_declaration> ";" <r:@R> => p.spanned(l, r),
    <l:@L> <b:block_declaration> ";"     <r:@R> => ast::DeclarationData::Block(b).spanned(l, r),
    <l:@L> "invariant" <i:identifier> ";" <r:@R> => ast::DeclarationData::Invariant(i).spanned(l, r),
};

function_definition: ast::FunctionDefinition = {
    <l:@L> <p:function_prototype> <s:compound_statement> <r:@R> => ast::FunctionDefinitionData {
        prototype: p,
        statement: s,
    }.spanned(l, r)
};

pp_define_object_like: ast::PreprocessorDefineData = {
    <i:identifier> <r:pp_rest> => ast::PreprocessorDefineData::ObjectLike {
        ident: i,
        value: r.into(),
    }
}

pp_define_function_like: ast::PreprocessorDefineData = {
    <i:identifier> "(" <p:comma<identifier>> ")" <r:pp_rest> => ast::PreprocessorDefineData::FunctionLike {
        ident: i,
        args: p,
        value: r.into(),
    }
}

preprocessor_define_data = {
    pp_define_function_like,
    pp_define_object_like,
};

preprocessor_define: ast::PreprocessorDefine = {
    <l:@L> <d:preprocessor_define_data> <r:@R> => d.spanned(l, r),
};

preprocessor_else_if: ast::PreprocessorElseIf = {
    <l:@L> <p:pp_rest> <r:@R> => ast::PreprocessorElseIfData { condition: p.into() }.spanned(l, r)
};

preprocessor_error: ast::PreprocessorError = {
    <l:@L> <p:pp_rest> <r:@R> => ast::PreprocessorErrorData { message: p.into() }.spanned(l, r)
};

preprocessor_if: ast::PreprocessorIf = {
    <l:@L> <p:pp_rest> <r:@R> => ast::PreprocessorIfData { condition: p.into() }.spanned(l, r)
};

preprocessor_if_def: ast::PreprocessorIfDef = {
    <l:@L> <i:identifier> <r:@R> => ast::PreprocessorIfDefData { ident: i }.spanned(l, r)
};

preprocessor_if_n_def: ast::PreprocessorIfNDef = {
    <l:@L> <i:identifier> <r:@R> => ast::PreprocessorIfNDefData { ident: i }.spanned(l, r)
};

preprocessor_include: ast::PreprocessorInclude = {
    <l:@L> <p:pp_path_relative> <r:@R> =>
        ast::PreprocessorIncludeData {
            path: ast::PathData::Relative(p.as_str().to_owned()).spanned(l, r)
        }.spanned(l, r),
    <l:@L> <p:pp_path_absolute> <r:@R> =>
        ast::PreprocessorIncludeData {
            path: ast::PathData::Absolute(p.as_str().to_owned()).spanned(l, r)
        }.spanned(l, r),
};

preprocessor_line: ast::PreprocessorLine = {
    <l:@L> <i:int_constant> <f:int_constant?> <r:@R> => ast::PreprocessorLineData {
        line: <Token as Into<i32>>::into(i) as u32,
        source_string_number: f.map(|n| <Token as Into<i32>>::into(n) as u32),
    }.spanned(l, r)
};

preprocessor_pragma: ast::PreprocessorPragma = {
    <l:@L> <p:pp_rest> <r:@R> => ast::PreprocessorPragmaData { command: p.into() }.spanned(l, r)
};

preprocessor_undef: ast::PreprocessorUndef = {
    <l:@L> <n:identifier> <r:@R> => ast::PreprocessorUndefData { name: n }.spanned(l, r)
};

preprocessor_version_profile_data: ast::PreprocessorVersionProfileData = {
    "core"          => ast::PreprocessorVersionProfileData::Core,
    "compatibility" => ast::PreprocessorVersionProfileData::Compatibility,
    "es"            => ast::PreprocessorVersionProfileData::Es,
};

preprocessor_version_profile: ast::PreprocessorVersionProfile = {
    <l:@L> <p:preprocessor_version_profile_data> <r:@R> => p.spanned(l, r)
};

preprocessor_version: ast::PreprocessorVersion = {
    <l:@L> <v:int_constant> <p:preprocessor_version_profile?> <r:@R> => ast::PreprocessorVersionData {
        version: <Token as Into<i32>>::into(v) as u16,
        profile: p,
    }.spanned(l, r)
};

preprocessor_extension_behavior_data: ast::PreprocessorExtensionBehaviorData = {
    "require" => ast::PreprocessorExtensionBehaviorData::Require,
    "enable"  => ast::PreprocessorExtensionBehaviorData::Enable,
    "warn"    => ast::PreprocessorExtensionBehaviorData::Warn,
    "disable" => ast::PreprocessorExtensionBehaviorData::Disable,
};

preprocessor_extension_behavior: ast::PreprocessorExtensionBehavior = {
    <l:@L> <p:preprocessor_extension_behavior_data> <r:@R> => p.spanned(l, r)
};

preprocessor_extension: ast::PreprocessorExtension = {
    <l:@L> <i:ident> <b:(":" <preprocessor_extension_behavior>)?> <r:@R> => ast::PreprocessorExtensionData {
        name: if i.as_str() == "all" {
            ast::PreprocessorExtensionNameData::All.spanned(l, r)
        } else {
            ast::PreprocessorExtensionNameData::Specific(i.as_str().into()).spanned(l, r)
        },
        behavior: b,
    }.spanned(l, r)
};

preprocessor: ast::Preprocessor = {
    <l:@L> "#define"    <p:preprocessor_define>    <r:@R> => ast::PreprocessorData::Define(p).spanned(l, r),
    <l:@L> "#else"                                 <r:@R> => ast::PreprocessorData::Else.spanned(l, r),
    <l:@L> "#elif"      <p:preprocessor_else_if>   <r:@R> => ast::PreprocessorData::ElseIf(p).spanned(l, r),
    <l:@L> "#endif"                                <r:@R> => ast::PreprocessorData::EndIf.spanned(l, r),
    <l:@L> "#error"     <p:preprocessor_error>     <r:@R> => ast::PreprocessorData::Error(p).spanned(l, r),
    <l:@L> "#if"        <p:preprocessor_if>        <r:@R> => ast::PreprocessorData::If(p).spanned(l, r),
    <l:@L> "#ifdef"     <p:preprocessor_if_def>    <r:@R> => ast::PreprocessorData::IfDef(p).spanned(l, r),
    <l:@L> "#ifndef"    <p:preprocessor_if_n_def>  <r:@R> => ast::PreprocessorData::IfNDef(p).spanned(l, r),
    <l:@L> "#include"   <p:preprocessor_include>   <r:@R> => ast::PreprocessorData::Include(p).spanned(l, r),
    <l:@L> "#line"      <p:preprocessor_line>      <r:@R> => ast::PreprocessorData::Line(p).spanned(l, r),
    <l:@L> "#pragma"    <p:preprocessor_pragma>    <r:@R> => ast::PreprocessorData::Pragma(p).spanned(l, r),
    <l:@L> "#undef"     <p:preprocessor_undef>     <r:@R> => ast::PreprocessorData::Undef(p).spanned(l, r),
    <l:@L> "#version"   <p:preprocessor_version>   <r:@R> => ast::PreprocessorData::Version(p).spanned(l, r),
    <l:@L> "#extension" <p:preprocessor_extension> <r:@R> => ast::PreprocessorData::Extension(p).spanned(l, r),
};

external_declaration: Option<ast::ExternalDeclaration> = {
    <l:@L> <p:preprocessor> <r:@R>        => Some(ast::ExternalDeclarationData::Preprocessor(p).spanned(l, r)),
    <l:@L> <f:function_definition> <r:@R> => Some(ast::ExternalDeclarationData::FunctionDefinition(f).spanned(l, r)),
    <l:@L> <d:declaration> <r:@R>         => Some(ast::ExternalDeclarationData::Declaration(d).spanned(l, r)),
    ";"                                   => None,
};

translation_unit: ast::TranslationUnit = {
    <external_declaration*> => ast::TranslationUnit(<>.into_iter().filter_map(|d| d).collect())
};

#[cfg(feature = "parser-expr")]
pub Expr            = { expr };
#[cfg(feature = "parser-statement")]
pub Statement       = { statement };
pub TranslationUnit = { translation_unit };

extern {
    type Location = LexerPosition;
    type Error = L::Error;

    enum Token {
        ident                    => Token::Identifier(_),
        ty_name                  => Token::TypeName(_),
        int_constant             => Token::IntConstant(_),
        uint_constant            => Token::UIntConstant(_),
        bool_constant            => Token::BoolConstant(_),
        float_constant           => Token::FloatConstant(_),
        double_constant          => Token::DoubleConstant(_),
        "("                      => Token::LeftParen,
        ")"                      => Token::RightParen,
        "["                      => Token::LeftBracket,
        "]"                      => Token::RightBracket,
        "."                      => Token::Dot,
        "++"                     => Token::IncOp,
        "--"                     => Token::DecOp,
        "void"                   => Token::Void,
        "+"                      => Token::Plus,
        "-"                      => Token::Dash,
        "!"                      => Token::Bang,
        "~"                      => Token::Tilde,
        "*"                      => Token::Star,
        "/"                      => Token::Slash,
        "%"                      => Token::Percent,
        "<<"                     => Token::LeftOp,
        ">>"                     => Token::RightOp,
        "<"                      => Token::LeftAngle,
        ">"                      => Token::RightAngle,
        "<="                     => Token::LeOp,
        ">="                     => Token::GeOp,
        "=="                     => Token::EqOp,
        "!="                     => Token::NeOp,
        "&"                      => Token::Ampersand,
        "^"                      => Token::Caret,
        "|"                      => Token::VerticalBar,
        "&&"                     => Token::AndOp,
        "^^"                     => Token::XorOp,
        "||"                     => Token::OrOp,
        "?"                      => Token::Question,
        ":"                      => Token::Colon,
        "="                      => Token::Equal,
        "*="                     => Token::MulAssign,
        "/="                     => Token::DivAssign,
        "%="                     => Token::ModAssign,
        "+="                     => Token::AddAssign,
        "-="                     => Token::SubAssign,
        "<<="                    => Token::LeftAssign,
        ">>="                    => Token::RightAssign,
        "&="                     => Token::AndAssign,
        "^="                     => Token::XorAssign,
        "|="                     => Token::OrAssign,
        ","                      => Token::Comma,
        "{"                      => Token::LeftBrace,
        "}"                      => Token::RightBrace,
        ";"                      => Token::Semicolon,
        "smooth"                 => Token::Smooth,
        "flat"                   => Token::Flat,
        "noperspective"          => Token::NoPerspective,
        "highp"                  => Token::HighPrecision,
        "mediump"                => Token::MediumPrecision,
        "lowp"                   => Token::LowPrecision,
        "const"                  => Token::Const,
        "inout"                  => Token::InOut,
        "in"                     => Token::In,
        "out"                    => Token::Out,
        "centroid"               => Token::Centroid,
        "patch"                  => Token::Patch,
        "sample"                 => Token::Sample,
        "uniform"                => Token::Uniform,
        "buffer"                 => Token::Buffer,
        "shared"                 => Token::Shared,
        "coherent"               => Token::Coherent,
        "volatile"               => Token::Volatile,
        "restrict"               => Token::Restrict,
        "readonly"               => Token::ReadOnly,
        "writeonly"              => Token::WriteOnly,
        "attribute"              => Token::Attribute,
        "varying"                => Token::Varying,
        "subroutine"             => Token::Subroutine,
        "layout"                 => Token::Layout,
        "precision"              => Token::Precision,
        "void"                   => Token::Void,
        "bool"                   => Token::Bool,
        "int"                    => Token::Int,
        "uint"                   => Token::UInt,
        "float"                  => Token::Float,
        "double"                 => Token::Double,
        "vec2"                   => Token::Vec2,
        "vec3"                   => Token::Vec3,
        "vec4"                   => Token::Vec4,
        "dvec2"                  => Token::DVec2,
        "dvec3"                  => Token::DVec3,
        "dvec4"                  => Token::DVec4,
        "bvec2"                  => Token::BVec2,
        "bvec3"                  => Token::BVec3,
        "bvec4"                  => Token::BVec4,
        "ivec2"                  => Token::IVec2,
        "ivec3"                  => Token::IVec3,
        "ivec4"                  => Token::IVec4,
        "uvec2"                  => Token::UVec2,
        "uvec3"                  => Token::UVec3,
        "uvec4"                  => Token::UVec4,
        "mat2"                   => Token::Mat2,
        "mat3"                   => Token::Mat3,
        "mat4"                   => Token::Mat4,
        "mat2x2"                 => Token::Mat2x2,
        "mat2x3"                 => Token::Mat2x3,
        "mat2x4"                 => Token::Mat2x4,
        "mat3x2"                 => Token::Mat3x2,
        "mat3x3"                 => Token::Mat3x3,
        "mat3x4"                 => Token::Mat3x4,
        "mat4x2"                 => Token::Mat4x2,
        "mat4x3"                 => Token::Mat4x3,
        "mat4x4"                 => Token::Mat4x4,
        "dmat2"                  => Token::DMat2,
        "dmat3"                  => Token::DMat3,
        "dmat4"                  => Token::DMat4,
        "dmat2x2"                => Token::DMat2x2,
        "dmat2x3"                => Token::DMat2x3,
        "dmat2x4"                => Token::DMat2x4,
        "dmat3x2"                => Token::DMat3x2,
        "dmat3x3"                => Token::DMat3x3,
        "dmat3x4"                => Token::DMat3x4,
        "dmat4x2"                => Token::DMat4x2,
        "dmat4x3"                => Token::DMat4x3,
        "dmat4x4"                => Token::DMat4x4,
        "sampler1D"              => Token::Sampler1D,
        "image1D"                => Token::Image1D,
        "sampler2D"              => Token::Sampler2D,
        "image2D"                => Token::Image2D,
        "sampler3D"              => Token::Sampler3D,
        "image3D"                => Token::Image3D,
        "samplerCube"            => Token::SamplerCube,
        "imageCube"              => Token::ImageCube,
        "sampler2DRect"          => Token::Sampler2DRect,
        "image2DRect"            => Token::Image2DRect,
        "sampler1DArray"         => Token::Sampler1DArray,
        "image1DArray"           => Token::Image1DArray,
        "sampler2DArray"         => Token::Sampler2DArray,
        "image2DArray"           => Token::Image2DArray,
        "samplerBuffer"          => Token::SamplerBuffer,
        "imageBuffer"            => Token::ImageBuffer,
        "sampler2DMS"            => Token::Sampler2DMs,
        "image2DMS"              => Token::Image2DMs,
        "sampler2DMSArray"       => Token::Sampler2DMsArray,
        "image2DMSArray"         => Token::Image2DMsArray,
        "samplerCubeArray"       => Token::SamplerCubeArray,
        "imageCubeArray"         => Token::ImageCubeArray,
        "sampler1DShadow"        => Token::Sampler1DShadow,
        "sampler2DShadow"        => Token::Sampler2DShadow,
        "sampler2DRectShadow"    => Token::Sampler2DRectShadow,
        "sampler1DArrayShadow"   => Token::Sampler1DArrayShadow,
        "sampler2DArrayShadow"   => Token::Sampler2DArrayShadow,
        "samplerCubeShadow"      => Token::SamplerCubeShadow,
        "samplerCubeArrayShadow" => Token::SamplerCubeArrayShadow,
        "isampler1D"             => Token::ISampler1D,
        "iimage1D"               => Token::IImage1D,
        "isampler2D"             => Token::ISampler2D,
        "iimage2D"               => Token::IImage2D,
        "isampler3D"             => Token::ISampler3D,
        "iimage3D"               => Token::IImage3D,
        "isamplerCube"           => Token::ISamplerCube,
        "iimageCube"             => Token::IImageCube,
        "isampler2DRect"         => Token::ISampler2DRect,
        "iimage2DRect"           => Token::IImage2DRect,
        "isampler1DArray"        => Token::ISampler1DArray,
        "iimage1DArray"          => Token::IImage1DArray,
        "isampler2DArray"        => Token::ISampler2DArray,
        "iimage2DArray"          => Token::IImage2DArray,
        "isamplerBuffer"         => Token::ISamplerBuffer,
        "iimageBuffer"           => Token::IImageBuffer,
        "isampler2DMS"           => Token::ISampler2DMs,
        "iimage2DMS"             => Token::IImage2DMs,
        "isampler2DMSArray"      => Token::ISampler2DMsArray,
        "iimage2DMSArray"        => Token::IImage2DMsArray,
        "isamplerCubeArray"      => Token::ISamplerCubeArray,
        "iimageCubeArray"        => Token::IImageCubeArray,
        "atomic_uint"            => Token::AtomicUInt,
        "usampler1D"             => Token::USampler1D,
        "uimage1D"               => Token::UImage1D,
        "usampler2D"             => Token::USampler2D,
        "uimage2D"               => Token::UImage2D,
        "usampler3D"             => Token::USampler3D,
        "uimage3D"               => Token::UImage3D,
        "usamplerCube"           => Token::USamplerCube,
        "uimageCube"             => Token::UImageCube,
        "usampler2DRect"         => Token::USampler2DRect,
        "uimage2DRect"           => Token::UImage2DRect,
        "usampler1DArray"        => Token::USampler1DArray,
        "uimage1DArray"          => Token::UImage1DArray,
        "usampler2DArray"        => Token::USampler2DArray,
        "uimage2DArray"          => Token::UImage2DArray,
        "usamplerBuffer"         => Token::USamplerBuffer,
        "uimageBuffer"           => Token::UImageBuffer,
        "usampler2DMS"           => Token::USampler2DMs,
        "uimage2DMS"             => Token::UImage2DMs,
        "usampler2DMSArray"      => Token::USampler2DMsArray,
        "uimage2DMSArray"        => Token::UImage2DMsArray,
        "usamplerCubeArray"      => Token::USamplerCubeArray,
        "uimageCubeArray"        => Token::UImageCubeArray,
        "texture1D"              => Token::Texture1D,
        "texture2D"              => Token::Texture2D,
        "texture3D"              => Token::Texture3D,
        "textureCube"            => Token::TextureCube,
        "texture2DRect"          => Token::Texture2DRect,
        "texture1DArray"         => Token::Texture1DArray,
        "texture2DArray"         => Token::Texture2DArray,
        "textureBuffer"          => Token::TextureBuffer,
        "texture2DMS"            => Token::Texture2DMs,
        "texture2DMSArray"       => Token::Texture2DMsArray,
        "textureCubeArray"       => Token::TextureCubeArray,
        "itexture1D"             => Token::ITexture1D,
        "itexture2D"             => Token::ITexture2D,
        "itexture3D"             => Token::ITexture3D,
        "itextureCube"           => Token::ITextureCube,
        "itexture2DRect"         => Token::ITexture2DRect,
        "itexture1DArray"        => Token::ITexture1DArray,
        "itexture2DArray"        => Token::ITexture2DArray,
        "itextureBuffer"         => Token::ITextureBuffer,
        "itexture2DMS"           => Token::ITexture2DMs,
        "itexture2DMSArray"      => Token::ITexture2DMsArray,
        "itextureCubeArray"      => Token::ITextureCubeArray,
        "sampler"                => Token::Sampler,
        "samplerShadow"          => Token::SamplerShadow,
        "subpassInput"           => Token::SubpassInput,
        "isubpassInput"          => Token::ISubpassInput,
        "usubpassInput"          => Token::USubpassInput,
        "subpassInputMS"         => Token::SubpassInputMs,
        "isubpassInputMS"        => Token::ISubpassInputMs,
        "usubpassInputMS"        => Token::USubpassInputMs,
        "invariant"              => Token::Invariant,
        "precise"                => Token::Precise,
        "else"                   => Token::Else,
        "if"                     => Token::If,
        "switch"                 => Token::Switch,
        "case"                   => Token::Case,
        "default"                => Token::Default,
        "while"                  => Token::While,
        "do"                     => Token::Do,
        "for"                    => Token::For,
        "continue"               => Token::Continue,
        "break"                  => Token::Break,
        "discard"                => Token::Discard,
        "return"                 => Token::Return,
        "struct"                 => Token::Struct,
        "#define"                => Token::PpDefine,
        "#else"                  => Token::PpElse,
        "#elif"                  => Token::PpElif,
        "#endif"                 => Token::PpEndIf,
        "#error"                 => Token::PpError,
        "#if"                    => Token::PpIf,
        "#ifdef"                 => Token::PpIfDef,
        "#ifndef"                => Token::PpIfNDef,
        "#include"               => Token::PpInclude,
        "#line"                  => Token::PpLine,
        "#pragma"                => Token::PpPragma,
        "#undef"                 => Token::PpUndef,
        "#version"               => Token::PpVersion,
        "#extension"             => Token::PpExtension,
        pp_rest                  => Token::PpRest(_),
        "core"                   => Token::PpCore,
        "compatibility"          => Token::PpCompatibility,
        "es"                     => Token::PpEs,
        "require"                => Token::PpExtRequire,
        "enable"                 => Token::PpExtEnable,
        "warn"                   => Token::PpExtWarn,
        "disable"                => Token::PpExtDisable,
        pp_path_absolute         => Token::PpPathAbsolute(_),
        pp_path_relative         => Token::PpPathRelative(_),
    }
}

// vim: ft=rust