summaryrefslogtreecommitdiffstats
path: root/third_party/rust/glsl/src/parsers.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/glsl/src/parsers.rs')
-rw-r--r--third_party/rust/glsl/src/parsers.rs1856
1 files changed, 1856 insertions, 0 deletions
diff --git a/third_party/rust/glsl/src/parsers.rs b/third_party/rust/glsl/src/parsers.rs
new file mode 100644
index 0000000000..3cf9f44b81
--- /dev/null
+++ b/third_party/rust/glsl/src/parsers.rs
@@ -0,0 +1,1856 @@
+//! 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_list};
+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 Fn(&'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_list(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, opt(float_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_list(
+ 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_list(
+ 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 with no size information.
+pub fn array_specifier(i: &str) -> ParserResult<syntax::ArraySpecifier> {
+ alt((
+ value(
+ syntax::ArraySpecifier::Unsized,
+ delimited(char('['), blank, char(']')),
+ ),
+ map(
+ delimited(
+ terminated(char('['), blank),
+ cut(cond_expr),
+ preceded(blank, cut(char(']'))),
+ ),
+ |e| syntax::ArraySpecifier::ExplicitlySized(Box::new(e)),
+ ),
+ ))(i)
+}
+
+/// Parse a primary expression.
+pub fn primary_expr(i: &str) -> ParserResult<syntax::Expr> {
+ alt((
+ parens_expr,
+ map(double_lit, syntax::Expr::DoubleConst),
+ map(float_lit, syntax::Expr::FloatConst),
+ 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_list(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_list(
+ 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_list(
+ 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),
+ )),
+ a,
+ 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_or_expr),
+ a,
+ 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_xor_expr),
+ a,
+ 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), logical_and_expr),
+ a,
+ 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),
+ a,
+ 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),
+ a,
+ 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),
+ a,
+ 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,
+ ),
+ a,
+ 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,
+ ),
+ a,
+ 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,
+ ),
+ a,
+ 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,
+ ),
+ a,
+ 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,
+ ),
+ a,
+ 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_list(
+ 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)
+}